feat: add JSON parsing and manipulation support
Add comprehensive JSON support via the Json module: - Parse JSON strings with Json.parse() returning Result<Json, String> - Stringify with Json.stringify() and Json.prettyPrint() - Extract values with Json.get(), getIndex(), asString(), asInt(), etc. - Build JSON with constructors: Json.null(), bool(), int(), string(), array(), object() - Query with Json.isNull() and Json.keys() Includes example at examples/json.lux demonstrating building, parsing, and extracting JSON data with file I/O integration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
112
examples/json.lux
Normal file
112
examples/json.lux
Normal file
@@ -0,0 +1,112 @@
|
||||
// JSON example - demonstrates JSON parsing and manipulation
|
||||
//
|
||||
// This script parses JSON, extracts values, and builds new JSON structures
|
||||
|
||||
fn main(): Unit with {Console, File} = {
|
||||
Console.print("=== Lux JSON Example ===")
|
||||
Console.print("")
|
||||
|
||||
// First, build some JSON programmatically
|
||||
Console.print("=== Building JSON ===")
|
||||
Console.print("")
|
||||
|
||||
let name = Json.string("Alice")
|
||||
let age = Json.int(30)
|
||||
let active = Json.bool(true)
|
||||
let scores = Json.array([Json.int(95), Json.int(87), Json.int(92)])
|
||||
|
||||
let person = Json.object([("name", name), ("age", age), ("active", active), ("scores", scores)])
|
||||
|
||||
Console.print("Built JSON:")
|
||||
let pretty = Json.prettyPrint(person)
|
||||
Console.print(pretty)
|
||||
Console.print("")
|
||||
|
||||
// Stringify to a compact string
|
||||
let jsonStr = Json.stringify(person)
|
||||
Console.print("Compact: " + jsonStr)
|
||||
Console.print("")
|
||||
|
||||
// Write to file and read back to test parsing
|
||||
File.write("/tmp/test.json", jsonStr)
|
||||
Console.print("Written to /tmp/test.json")
|
||||
Console.print("")
|
||||
|
||||
// Read and parse from file
|
||||
Console.print("=== Parsing JSON ===")
|
||||
Console.print("")
|
||||
let content = File.read("/tmp/test.json")
|
||||
Console.print("Read from file: " + content)
|
||||
Console.print("")
|
||||
|
||||
match Json.parse(content) {
|
||||
Ok(json) => {
|
||||
Console.print("Parse succeeded!")
|
||||
Console.print("")
|
||||
|
||||
// Get string field
|
||||
Console.print("Extracting fields:")
|
||||
match Json.get(json, "name") {
|
||||
Some(nameJson) => match Json.asString(nameJson) {
|
||||
Some(n) => Console.print(" name: " + n),
|
||||
None => Console.print(" name: (not a string)")
|
||||
},
|
||||
None => Console.print(" name: (not found)")
|
||||
}
|
||||
|
||||
// Get int field
|
||||
match Json.get(json, "age") {
|
||||
Some(ageJson) => match Json.asInt(ageJson) {
|
||||
Some(a) => Console.print(" age: " + toString(a)),
|
||||
None => Console.print(" age: (not an int)")
|
||||
},
|
||||
None => Console.print(" age: (not found)")
|
||||
}
|
||||
|
||||
// Get bool field
|
||||
match Json.get(json, "active") {
|
||||
Some(activeJson) => match Json.asBool(activeJson) {
|
||||
Some(a) => Console.print(" active: " + toString(a)),
|
||||
None => Console.print(" active: (not a bool)")
|
||||
},
|
||||
None => Console.print(" active: (not found)")
|
||||
}
|
||||
|
||||
// Get array field
|
||||
match Json.get(json, "scores") {
|
||||
Some(scoresJson) => match Json.asArray(scoresJson) {
|
||||
Some(arr) => {
|
||||
Console.print(" scores: " + toString(List.length(arr)) + " items")
|
||||
// Get first score
|
||||
match Json.getIndex(scoresJson, 0) {
|
||||
Some(firstJson) => match Json.asInt(firstJson) {
|
||||
Some(first) => Console.print(" first score: " + toString(first)),
|
||||
None => Console.print(" first score: (not an int)")
|
||||
},
|
||||
None => Console.print(" (no first element)")
|
||||
}
|
||||
},
|
||||
None => Console.print(" scores: (not an array)")
|
||||
},
|
||||
None => Console.print(" scores: (not found)")
|
||||
}
|
||||
Console.print("")
|
||||
|
||||
// Get the keys
|
||||
Console.print("Object keys:")
|
||||
match Json.keys(json) {
|
||||
Some(ks) => Console.print(" " + String.join(ks, ", ")),
|
||||
None => Console.print(" (not an object)")
|
||||
}
|
||||
},
|
||||
Err(e) => Console.print("Parse error: " + e)
|
||||
}
|
||||
|
||||
Console.print("")
|
||||
Console.print("=== JSON Null Check ===")
|
||||
let nullVal = Json.null()
|
||||
Console.print("Is null value null? " + toString(Json.isNull(nullVal)))
|
||||
Console.print("Is person null? " + toString(Json.isNull(person)))
|
||||
}
|
||||
|
||||
let result = run main() with {}
|
||||
@@ -83,6 +83,27 @@ pub enum BuiltinFn {
|
||||
StringToUpper,
|
||||
StringToLower,
|
||||
StringSubstring,
|
||||
|
||||
// JSON operations
|
||||
JsonParse,
|
||||
JsonStringify,
|
||||
JsonPrettyPrint,
|
||||
JsonGet,
|
||||
JsonGetIndex,
|
||||
JsonAsString,
|
||||
JsonAsNumber,
|
||||
JsonAsInt,
|
||||
JsonAsBool,
|
||||
JsonAsArray,
|
||||
JsonIsNull,
|
||||
JsonKeys,
|
||||
JsonNull,
|
||||
JsonBool,
|
||||
JsonNumber,
|
||||
JsonInt,
|
||||
JsonString,
|
||||
JsonArray,
|
||||
JsonObject,
|
||||
}
|
||||
|
||||
/// Runtime value
|
||||
@@ -112,6 +133,8 @@ pub enum Value {
|
||||
version: u32,
|
||||
value: Box<Value>,
|
||||
},
|
||||
/// JSON value (for JSON parsing/manipulation)
|
||||
Json(serde_json::Value),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@@ -131,6 +154,7 @@ impl Value {
|
||||
Value::Builtin(_) => "Function",
|
||||
Value::Constructor { .. } => "Constructor",
|
||||
Value::Versioned { .. } => "Versioned",
|
||||
Value::Json(_) => "Json",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +303,7 @@ impl fmt::Display for Value {
|
||||
} => {
|
||||
write!(f, "{} @v{}", value, version)
|
||||
}
|
||||
Value::Json(json) => write!(f, "{}", json),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -856,6 +881,30 @@ impl Interpreter {
|
||||
("round".to_string(), Value::Builtin(BuiltinFn::MathRound)),
|
||||
]));
|
||||
env.define("Math", math_module);
|
||||
|
||||
// JSON module
|
||||
let json_module = Value::Record(HashMap::from([
|
||||
("parse".to_string(), Value::Builtin(BuiltinFn::JsonParse)),
|
||||
("stringify".to_string(), Value::Builtin(BuiltinFn::JsonStringify)),
|
||||
("prettyPrint".to_string(), Value::Builtin(BuiltinFn::JsonPrettyPrint)),
|
||||
("get".to_string(), Value::Builtin(BuiltinFn::JsonGet)),
|
||||
("getIndex".to_string(), Value::Builtin(BuiltinFn::JsonGetIndex)),
|
||||
("asString".to_string(), Value::Builtin(BuiltinFn::JsonAsString)),
|
||||
("asNumber".to_string(), Value::Builtin(BuiltinFn::JsonAsNumber)),
|
||||
("asInt".to_string(), Value::Builtin(BuiltinFn::JsonAsInt)),
|
||||
("asBool".to_string(), Value::Builtin(BuiltinFn::JsonAsBool)),
|
||||
("asArray".to_string(), Value::Builtin(BuiltinFn::JsonAsArray)),
|
||||
("isNull".to_string(), Value::Builtin(BuiltinFn::JsonIsNull)),
|
||||
("keys".to_string(), Value::Builtin(BuiltinFn::JsonKeys)),
|
||||
("null".to_string(), Value::Builtin(BuiltinFn::JsonNull)),
|
||||
("bool".to_string(), Value::Builtin(BuiltinFn::JsonBool)),
|
||||
("number".to_string(), Value::Builtin(BuiltinFn::JsonNumber)),
|
||||
("int".to_string(), Value::Builtin(BuiltinFn::JsonInt)),
|
||||
("string".to_string(), Value::Builtin(BuiltinFn::JsonString)),
|
||||
("array".to_string(), Value::Builtin(BuiltinFn::JsonArray)),
|
||||
("object".to_string(), Value::Builtin(BuiltinFn::JsonObject)),
|
||||
]));
|
||||
env.define("Json", json_module);
|
||||
}
|
||||
|
||||
/// Execute a program
|
||||
@@ -2243,6 +2292,278 @@ impl Interpreter {
|
||||
let result: String = chars[start..end].iter().collect();
|
||||
Ok(EvalResult::Value(Value::String(result)))
|
||||
}
|
||||
|
||||
// JSON operations
|
||||
BuiltinFn::JsonParse => {
|
||||
let s = Self::expect_arg_1::<String>(&args, "Json.parse", span)?;
|
||||
match serde_json::from_str::<serde_json::Value>(&s) {
|
||||
Ok(json) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Ok".to_string(),
|
||||
fields: vec![Value::Json(json)],
|
||||
})),
|
||||
Err(e) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Err".to_string(),
|
||||
fields: vec![Value::String(e.to_string())],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonStringify => {
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j.clone(),
|
||||
v => return Err(err(&format!("Json.stringify expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
Ok(EvalResult::Value(Value::String(json.to_string())))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonPrettyPrint => {
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j.clone(),
|
||||
v => return Err(err(&format!("Json.prettyPrint expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match serde_json::to_string_pretty(&json) {
|
||||
Ok(s) => Ok(EvalResult::Value(Value::String(s))),
|
||||
Err(e) => Err(err(&format!("Json.prettyPrint error: {}", e))),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonGet => {
|
||||
// Json.get(json, key) -> Option<Json>
|
||||
if args.len() != 2 {
|
||||
return Err(err("Json.get requires 2 arguments: json, key"));
|
||||
}
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.get expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
let key = match &args[1] {
|
||||
Value::String(s) => s.clone(),
|
||||
v => return Err(err(&format!("Json.get expects String key, got {}", v.type_name()))),
|
||||
};
|
||||
match json.get(&key) {
|
||||
Some(v) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::Json(v.clone())],
|
||||
})),
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonGetIndex => {
|
||||
// Json.getIndex(json, index) -> Option<Json>
|
||||
if args.len() != 2 {
|
||||
return Err(err("Json.getIndex requires 2 arguments: json, index"));
|
||||
}
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.getIndex expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
let idx = match &args[1] {
|
||||
Value::Int(n) => *n as usize,
|
||||
v => return Err(err(&format!("Json.getIndex expects Int index, got {}", v.type_name()))),
|
||||
};
|
||||
match json.get(idx) {
|
||||
Some(v) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::Json(v.clone())],
|
||||
})),
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonAsString => {
|
||||
// Json.asString(json) -> Option<String>
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.asString expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match json.as_str() {
|
||||
Some(s) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::String(s.to_string())],
|
||||
})),
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonAsNumber => {
|
||||
// Json.asNumber(json) -> Option<Float>
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.asNumber expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match json.as_f64() {
|
||||
Some(n) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::Float(n)],
|
||||
})),
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonAsInt => {
|
||||
// Json.asInt(json) -> Option<Int>
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.asInt expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match json.as_i64() {
|
||||
Some(n) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::Int(n)],
|
||||
})),
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonAsBool => {
|
||||
// Json.asBool(json) -> Option<Bool>
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.asBool expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match json.as_bool() {
|
||||
Some(b) => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::Bool(b)],
|
||||
})),
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonAsArray => {
|
||||
// Json.asArray(json) -> Option<List<Json>>
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.asArray expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match json.as_array() {
|
||||
Some(arr) => {
|
||||
let items: Vec<Value> = arr.iter().map(|v| Value::Json(v.clone())).collect();
|
||||
Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::List(items)],
|
||||
}))
|
||||
}
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
BuiltinFn::JsonIsNull => {
|
||||
// Json.isNull(json) -> Bool
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.isNull expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
Ok(EvalResult::Value(Value::Bool(json.is_null())))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonKeys => {
|
||||
// Json.keys(json) -> Option<List<String>>
|
||||
let json = match &args[0] {
|
||||
Value::Json(j) => j,
|
||||
v => return Err(err(&format!("Json.keys expects Json, got {}", v.type_name()))),
|
||||
};
|
||||
match json.as_object() {
|
||||
Some(obj) => {
|
||||
let keys: Vec<Value> = obj.keys().map(|k| Value::String(k.clone())).collect();
|
||||
Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "Some".to_string(),
|
||||
fields: vec![Value::List(keys)],
|
||||
}))
|
||||
}
|
||||
None => Ok(EvalResult::Value(Value::Constructor {
|
||||
name: "None".to_string(),
|
||||
fields: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
// JSON constructors
|
||||
BuiltinFn::JsonNull => {
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::Null)))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonBool => {
|
||||
let b = Self::expect_arg_1::<bool>(&args, "Json.bool", span)?;
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::Bool(b))))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonNumber => {
|
||||
let n = match &args[0] {
|
||||
Value::Float(f) => serde_json::Number::from_f64(*f)
|
||||
.ok_or_else(|| err("Invalid float for JSON"))?,
|
||||
Value::Int(i) => serde_json::Number::from(*i),
|
||||
v => return Err(err(&format!("Json.number expects Float or Int, got {}", v.type_name()))),
|
||||
};
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::Number(n))))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonInt => {
|
||||
let n = Self::expect_arg_1::<i64>(&args, "Json.int", span)?;
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::Number(serde_json::Number::from(n)))))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonString => {
|
||||
let s = Self::expect_arg_1::<String>(&args, "Json.string", span)?;
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::String(s))))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonArray => {
|
||||
// Json.array(list: List<Json>) -> Json
|
||||
let list = Self::expect_arg_1::<Vec<Value>>(&args, "Json.array", span)?;
|
||||
let arr: Result<Vec<serde_json::Value>, RuntimeError> = list.into_iter().map(|v| {
|
||||
match v {
|
||||
Value::Json(j) => Ok(j),
|
||||
_ => Err(err("Json.array expects List<Json>")),
|
||||
}
|
||||
}).collect();
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::Array(arr?))))
|
||||
}
|
||||
|
||||
BuiltinFn::JsonObject => {
|
||||
// Json.object(entries: List<(String, Json)>) -> Json
|
||||
let list = Self::expect_arg_1::<Vec<Value>>(&args, "Json.object", span)?;
|
||||
let mut map = serde_json::Map::new();
|
||||
for item in list {
|
||||
match item {
|
||||
Value::Tuple(fields) if fields.len() == 2 => {
|
||||
let key = match &fields[0] {
|
||||
Value::String(s) => s.clone(),
|
||||
_ => return Err(err("Json.object expects (String, Json) tuples")),
|
||||
};
|
||||
let value = match &fields[1] {
|
||||
Value::Json(j) => j.clone(),
|
||||
_ => return Err(err("Json.object expects (String, Json) tuples")),
|
||||
};
|
||||
map.insert(key, value);
|
||||
}
|
||||
_ => return Err(err("Json.object expects List<(String, Json)>")),
|
||||
}
|
||||
}
|
||||
Ok(EvalResult::Value(Value::Json(serde_json::Value::Object(map))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
147
src/types.rs
147
src/types.rs
@@ -732,6 +732,37 @@ impl TypeEnv {
|
||||
]),
|
||||
);
|
||||
|
||||
// Add Json type (represents JSON values)
|
||||
env.types.insert(
|
||||
"Json".to_string(),
|
||||
TypeDef::Enum(vec![
|
||||
VariantDef {
|
||||
name: "JsonNull".to_string(),
|
||||
fields: VariantFieldsDef::Unit,
|
||||
},
|
||||
VariantDef {
|
||||
name: "JsonBool".to_string(),
|
||||
fields: VariantFieldsDef::Tuple(vec![Type::Bool]),
|
||||
},
|
||||
VariantDef {
|
||||
name: "JsonNumber".to_string(),
|
||||
fields: VariantFieldsDef::Tuple(vec![Type::Float]),
|
||||
},
|
||||
VariantDef {
|
||||
name: "JsonString".to_string(),
|
||||
fields: VariantFieldsDef::Tuple(vec![Type::String]),
|
||||
},
|
||||
VariantDef {
|
||||
name: "JsonArray".to_string(),
|
||||
fields: VariantFieldsDef::Tuple(vec![Type::List(Box::new(Type::Named("Json".to_string())))]),
|
||||
},
|
||||
VariantDef {
|
||||
name: "JsonObject".to_string(),
|
||||
fields: VariantFieldsDef::Tuple(vec![Type::List(Box::new(Type::Tuple(vec![Type::String, Type::Named("Json".to_string())])))]),
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
// Add Console effect
|
||||
env.effects.insert(
|
||||
"Console".to_string(),
|
||||
@@ -1191,6 +1222,122 @@ impl TypeEnv {
|
||||
]);
|
||||
env.bind("String", TypeScheme::mono(string_module_type));
|
||||
|
||||
// Json module
|
||||
let json_type = Type::Named("Json".to_string());
|
||||
let json_module_type = Type::Record(vec![
|
||||
(
|
||||
"parse".to_string(),
|
||||
Type::function(
|
||||
vec![Type::String],
|
||||
Type::App {
|
||||
constructor: Box::new(Type::Named("Result".to_string())),
|
||||
args: vec![json_type.clone(), Type::String],
|
||||
},
|
||||
),
|
||||
),
|
||||
(
|
||||
"stringify".to_string(),
|
||||
Type::function(vec![json_type.clone()], Type::String),
|
||||
),
|
||||
(
|
||||
"prettyPrint".to_string(),
|
||||
Type::function(vec![json_type.clone()], Type::String),
|
||||
),
|
||||
(
|
||||
"get".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone(), Type::String],
|
||||
Type::Option(Box::new(json_type.clone())),
|
||||
),
|
||||
),
|
||||
(
|
||||
"getIndex".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone(), Type::Int],
|
||||
Type::Option(Box::new(json_type.clone())),
|
||||
),
|
||||
),
|
||||
(
|
||||
"asString".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone()],
|
||||
Type::Option(Box::new(Type::String)),
|
||||
),
|
||||
),
|
||||
(
|
||||
"asNumber".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone()],
|
||||
Type::Option(Box::new(Type::Float)),
|
||||
),
|
||||
),
|
||||
(
|
||||
"asInt".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone()],
|
||||
Type::Option(Box::new(Type::Int)),
|
||||
),
|
||||
),
|
||||
(
|
||||
"asBool".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone()],
|
||||
Type::Option(Box::new(Type::Bool)),
|
||||
),
|
||||
),
|
||||
(
|
||||
"asArray".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone()],
|
||||
Type::Option(Box::new(Type::List(Box::new(json_type.clone())))),
|
||||
),
|
||||
),
|
||||
(
|
||||
"isNull".to_string(),
|
||||
Type::function(vec![json_type.clone()], Type::Bool),
|
||||
),
|
||||
(
|
||||
"keys".to_string(),
|
||||
Type::function(
|
||||
vec![json_type.clone()],
|
||||
Type::List(Box::new(Type::String)),
|
||||
),
|
||||
),
|
||||
// Constructors for building JSON
|
||||
(
|
||||
"null".to_string(),
|
||||
Type::function(vec![], json_type.clone()),
|
||||
),
|
||||
(
|
||||
"bool".to_string(),
|
||||
Type::function(vec![Type::Bool], json_type.clone()),
|
||||
),
|
||||
(
|
||||
"number".to_string(),
|
||||
Type::function(vec![Type::Float], json_type.clone()),
|
||||
),
|
||||
(
|
||||
"int".to_string(),
|
||||
Type::function(vec![Type::Int], json_type.clone()),
|
||||
),
|
||||
(
|
||||
"string".to_string(),
|
||||
Type::function(vec![Type::String], json_type.clone()),
|
||||
),
|
||||
(
|
||||
"array".to_string(),
|
||||
Type::function(vec![Type::List(Box::new(json_type.clone()))], json_type.clone()),
|
||||
),
|
||||
(
|
||||
"object".to_string(),
|
||||
Type::function(
|
||||
vec![Type::List(Box::new(Type::Tuple(vec![Type::String, json_type.clone()])))],
|
||||
json_type.clone(),
|
||||
),
|
||||
),
|
||||
]);
|
||||
env.bind("Json", TypeScheme::mono(json_module_type));
|
||||
|
||||
// Option module
|
||||
let option_module_type = Type::Record(vec![
|
||||
(
|
||||
|
||||
Reference in New Issue
Block a user