sml

SML { ❄ }

Downloads Questions, bug reports, or just to say hi: sal@mail.swebase.cn 中文 EN

Chapter 7: Multilingual Use

SML is a format that needs to be parsed by the host language to be “usable”. Each language implementation is decoupled from each other and can be embedded separately (WASM/sandbox/editor are all acceptable). Below are the most commonly used integration methods.

Scope note first: only the Rust implementation (swsml) is the reference and is continuously maintained — grammar, contracts, vulnerability scanning and regression tests are all defined against it. Use Rust in production. C / JavaScript / Lua (Soup) / C++ / Python are experimental and not guaranteed: they may diverge from Rust behaviour, have no API stability promise, and are not covered by the routine vulnerability scan or regression tests. Statements like “aligned with Rust” below are historical results and are for reference only.

7.1 Rust(swsml)

use sml::parse;
let v = parse("name: John\nage: 27").unwrap();
assert_eq!(v["name"], "John");

Include file:

use sml::parse_file;
let v = parse_file("app.sml")?;

Serde bridging (optional feature):

# Cargo.toml
sml-rs = { version = "0.2", features = ["serde"] }
use sml::{parse, Value};
let v = parse("name: John\nage: 27")?;
let json = serde_json::to_string(&v)?;   // {"name":"John","age":27}

Value handwriting implements Serialize/Deserialize, serialized as natural 27 instead of {"Int":27}. When serde is not enabled, crate has zero dependencies.

7.2 C(sml.c)

# include "sml.h"
char err[256] = {0};
sml_value *v = sml_parse("name: John\nage: 27", err, sizeof(err));
/* v->type == SML_STR ("John") ... free it with sml_free(v) */

The contract system was once 100% aligned with Rust (the CONFIG_CONTRACT behaved consistently on all ends); note the C implementation is now experimental and not guaranteed.

7.3 JavaScript(sml.mjs)

Zero dependency ESM, browser/node compatible, including contracts and Playground:

import { parse, stringify } from "./sml.mjs";
const v = parse('name: John\nage: 27');
console.log(stringify(v));

SML ↔ JSON conversion (isomorphic):

const obj = parse(smlText);              // Ordinary JS object
const json = JSON.stringify(obj);
const sml = stringify(JSON.parse(json));

7.4 Lua / Soup(lib/sml.soup)

local sml = require("lib.sml")
local v, err = sml.load(text)   -- Parse
print(sml.dump(v))              -- Serialization
soupx lua/sml.sar config.sml     # Parse and print

7.5 Other

-* * C++* * (cpp/): header file+single compilation unit, zero third-party dependencies, parsing failure throws sml::ParseError (including row and column positions).

-Python: See py binding outside rust/.

Error codes: the same code on all five implementations

The same class of error raises the same code (of the form E-CONTRACT-002) in Rust / C / C++ / JS / Lua, so there is no need to remember each implementation’s wording — remember the code: the error code reference is the single entry point. The single source of truth is errors/codes.sml in the repository (141 codes today), distributed to per-implementation constants by a generator. How you extract the code differs: Rust appends it (message [E-XXX-NNN]), C / C++ / Lua put it at the front of the message, and JS exposes it as the e.code field.

One consequence worth knowing up front: inputs that used to “parse” now fail with a code. They only parsed because the resulting tree was wrong anyway (unterminated strings, a stray } inside an array, undefined fragment references, top-level scalars all used to yield a silently wrong tree). A loud rejection beats silent bad data.

Recent per-implementation progress (several faces of the same effort):

7.6 Which one to choose?

You are writing using Guarantee
Rust programs/command-line tools swsml ✅ Reference, production-ready
Embedded/System Layer C/C++ ⚠️ Experimental, not guaranteed
Front end/Node Services sml.mjs ⚠️ Experimental, not guaranteed
Soup Ecology/Script lib/sml.soup ⚠️ Experimental, not guaranteed

Chapter 8: Practical Projects (/book/ch08 project)

Hands on practice

After reading this chapter, directly modify SML in the editor below and click “Run” to immediately see the parsing results or validation errors - having output is necessary for efficient learning.

✍ Hands-on exercise Define @contract User { id: str, email: str, role: enum(user,admin,owner) default user }, write data using @is User (id=u_1, email=a @ b.c), and verify that the role automatically takes the default user.
💡 Hint: The same contract behaves consistently in Rust/C/JS/Lua - try changing role to owner.
✍ Self-check quiz: Chapter 7 Self Test: Multilingual Use Score 0 / 3
Q1. What is the behavior of SML contracts in multiple languages?
Q2. Judgment: The field names and nested structures obtained from parsing the same SML file using Rust and Lua should be exactly the same.
Q3. Which export is usually used to call a parser in JS?