Skip to content

Commit eb3cc25

Browse files
authored
Merge pull request #16 from Cyteon/json
Add JSON support
2 parents 8c20985 + a34d1e7 commit eb3cc25

18 files changed

+608
-100
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
import "os" as os;
2+
import "json" as json;
23

34
fn get(url) {
45
let cmd = "curl -s " + url;
5-
os.exec(cmd);
6+
let res = os.exec(cmd);
7+
8+
return res;
9+
}
10+
11+
let res = get("https://jsonplaceholder.typicode.com/todos/8");
12+
let data = json.parse(res);
13+
14+
if data.completed == true {
15+
print("Task ", str(data.id), " is completed");
16+
print("Title: ", data.title);
617
}
718

8-
let res = get("https://modu.cyteon.hackclub.app/");
9-
print(res);
19+
if data.completed == false {
20+
print("Task ", str(data.id), " is not completed");
21+
print("Title: ", data.title);
22+
}

lang/examples/json.modu

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import "json" as json;
2+
3+
let obj = json.new();
4+
let obj2 = json.new();
5+
6+
obj2.set("name", "test2");
7+
obj2.set("age", 500);
8+
obj.set("name", "test");
9+
obj.set("obj", obj2);
10+
11+
print(obj.name);
12+
print(obj);
13+
print(json.new());
14+
print(json.stringify(obj));
15+
print(json.stringify(json.new()));
16+
17+
let everything = json.new();
18+
everything.set("string", "hello");
19+
everything.set("int", 5);
20+
everything.set("float", 5.5);
21+
everything.set("bool", true);
22+
everything.set("null", null);
23+
everything.set("obj", obj);
24+
25+
print();
26+
27+
let string = json.stringify(everything);
28+
let parsed = json.parse(string);
29+
30+
print(parsed.get("string"));
31+
print(parsed.has("int"));
32+
print(parsed.obj);
33+
print(parsed);
34+
35+
parsed.delete("obj");
36+
print(parsed);
37+
print(parsed.get("obj"));
38+
print(parsed.has("obj"));

lang/src/ast.rs

+37-5
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ pub enum AST {
6060
InternalFunction {
6161
name: String,
6262
args: Vec<String>,
63-
call_fn: fn(Vec<AST>, &mut HashMap<String, AST>) -> Result<AST, String>,
63+
call_fn: fn(Vec<AST>, &mut HashMap<String, AST>) -> Result<(AST, AST), String>,
6464
},
6565

6666
Exists {
@@ -151,20 +151,52 @@ impl std::fmt::Display for AST {
151151
match self {
152152
// TODO: Implement more
153153
AST::String(s) => {
154-
let s = s.replace("\\t", "\t")
154+
let mut s = s.replace("\\t", "\t")
155155
.replace("\\n", "\n")
156156
.replace("\\r", "\r")
157157
.replace("\\\"", "\"")
158-
.replace("\\\\", "\\")
159-
.replace("\"", "")
160-
.replace("'", "");
158+
.replace("\\\\", "\\");
159+
160+
if s.starts_with("\"") && s.ends_with("\"") {
161+
s = s[1..s.len() - 1].to_string();
162+
} else if s.starts_with("'") && s.ends_with("'") {
163+
s = s[1..s.len() - 1].to_string();
164+
}
161165

162166
write!(f, "{}", s)
163167
},
164168
AST::Number(n) => write!(f, "{}", n),
165169
AST::Float(n) => write!(f, "{}", n),
166170
AST::Boolean(b) => write!(f, "{}", b),
167171
AST::Null => write!(f, "null"),
172+
173+
AST::Object { properties, line: _ } => {
174+
write!(f, "{{ ")?;
175+
176+
if properties.len() as i32 - crate::packages::json::BUILTINS.len() as i32 == 0 {
177+
write!(f, "}}")?;
178+
} else {
179+
let mut str = String::new();
180+
181+
for (key, value) in properties {
182+
if crate::packages::json::BUILTINS.contains(&key.as_str()) {
183+
continue;
184+
}
185+
186+
str.push_str(&format!("\"{}\": {}, ", key, value));
187+
}
188+
189+
if str.len() > 0 {
190+
write!(f, "{}", &str[..str.len() - 2])?;
191+
}
192+
193+
194+
write!(f, " }}")?;
195+
}
196+
197+
Ok(())
198+
}
199+
168200
_ => write!(f, "{:?}", self),
169201
}
170202
}

lang/src/cli/server.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub fn server() {
1717
rouille::start_server(format!("0.0.0.0:{}", port), move |request| {
1818
router!(request,
1919
(GET) (/) => {
20-
rouille::Response::text("Modu interpreter server is running")
20+
rouille::Response::text(format!("OK v{}", env!("CARGO_PKG_VERSION")))
2121
},
2222

2323
(POST) (/eval) => {

lang/src/eval.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub fn eval(expr: AST, context: &mut HashMap<String, AST>) -> Result<AST, String
4848

4949
AST::InternalFunction { name: _, args: f_args, call_fn } => {
5050
if args.len() == f_args.len() || f_args.last().unwrap() == "__args__" {
51-
return call_fn(args, context);
51+
return Ok(call_fn(args, context)?.0);
5252
} else {
5353
return Err(format!("{} takes {} argument(s)", name, f_args.len()));
5454
}
@@ -245,7 +245,27 @@ pub fn eval(expr: AST, context: &mut HashMap<String, AST>) -> Result<AST, String
245245

246246
AST::InternalFunction { name, args: f_args, call_fn } => {
247247
if args.len() == f_args.len() || f_args.last().unwrap() == "__args__" {
248-
return call_fn(args, context);
248+
return Ok(call_fn(args, context)?.0);
249+
} else if f_args[0] == "self" && args.len() == f_args.len() - 1 || f_args.last().unwrap() == "__args__" {
250+
let mut new_args = vec![AST::Object { properties: properties.clone(), line: 0 }];
251+
252+
for arg in args {
253+
new_args.push(arg);
254+
}
255+
256+
let result = call_fn(new_args, context)?;
257+
258+
match result.1.clone() {
259+
AST::Object { properties, line: _ } => {
260+
context.insert(object.unwrap(), AST::Object { properties, line: 0 });
261+
}
262+
263+
_ => {}
264+
}
265+
266+
return Ok(result.0);
267+
} else if f_args[0] == "self" {
268+
return Err(format!("{} takes {} argument(s)", name, f_args.len() - 1));
249269
} else {
250270
return Err(format!("{} takes {} argument(s)", name, f_args.len()));
251271
}

lang/src/internal.rs

+21-21
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::collections::HashMap;
55
use crate::ast::AST;
66
use crate::eval::eval;
77

8-
pub fn print(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
8+
pub fn print(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
99
for arg in args {
1010
match eval(arg, context) {
1111
Ok(value) => {
@@ -20,10 +20,10 @@ pub fn print(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST,
2020

2121
println!();
2222

23-
Ok(AST::Null)
23+
Ok((AST::Null, AST::Null))
2424
}
2525

26-
pub fn input(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
26+
pub fn input(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
2727
if args.len() > 0 {
2828
use std::io::Write;
2929

@@ -46,30 +46,30 @@ pub fn input(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST,
4646

4747
std::io::stdin().read_line(&mut input).unwrap();
4848

49-
Ok(AST::String(input.trim().to_string()))
49+
Ok((AST::String(input.trim().to_string()), AST::Null))
5050
}
5151

52-
pub fn int(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
52+
pub fn int(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
5353
match eval(args[0].clone(), context) {
5454
Ok(v) => {
5555
match v {
5656
AST::String(value) => {
5757
match value.parse::<i64>() {
58-
Ok(value) => {return Ok(AST::Number(value));},
58+
Ok(value) => { return Ok((AST::Number(value), AST::Null)); },
5959
Err(_) => (),
6060
}
6161

6262
match value.parse::<f64>() {
63-
Ok(value) => {return Ok(AST::Number(value as i64));},
63+
Ok(value) => { return Ok((AST::Number(value as i64), AST::Null)); },
6464
Err(_) => (),
6565
}
6666

6767
return Err("int() requires a string or boolean".to_string());
6868
}
6969

70-
AST::Boolean(value) => Ok(AST::Number(if value {1} else {0})),
70+
AST::Boolean(value) => Ok((AST::Number(if value {1} else {0}), AST::Null)),
7171

72-
AST::Number(value) => Ok(AST::Number(value)),
72+
AST::Number(value) => Ok((AST::Number(value), AST::Null)),
7373

7474
_ => Err("int() requires a string or boolean".to_string())
7575
}
@@ -79,21 +79,21 @@ pub fn int(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, St
7979
}
8080
}
8181

82-
pub fn float(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
82+
pub fn float(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
8383
match eval(args[0].clone(), context) {
8484
Ok(v) => {
8585
match v {
8686
AST::String(value) => {
8787
match value.parse::<f64>() {
88-
Ok(value) => Ok(AST::Float(value)),
88+
Ok(value) => Ok((AST::Float(value), AST::Null)),
8989
Err(_) => Err("float() requires a string or boolean".to_string())
9090
}
9191
}
9292

93-
AST::Boolean(value) => Ok(AST::Float(if value {1.0} else {0.0})),
93+
AST::Boolean(value) => Ok((AST::Float(if value {1.0} else {0.0}), AST::Null)),
9494

95-
AST::Number(value) => Ok(AST::Float(value as f64)),
96-
AST::Float(value) => Ok(AST::Float(value)),
95+
AST::Number(value) => Ok((AST::Float(value as f64), AST::Null)),
96+
AST::Float(value) => Ok((AST::Float(value), AST::Null)),
9797

9898
_ => Err("float() requires a string or boolean".to_string())
9999
}
@@ -103,16 +103,16 @@ pub fn float(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST,
103103
}
104104
}
105105

106-
pub fn str(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
106+
pub fn str(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
107107
match eval(args[0].clone(), context) {
108108
Ok(v) => {
109109
match v {
110-
AST::String(value) => Ok(AST::String(value)),
110+
AST::String(value) => Ok((AST::String(value), AST::Null)),
111111

112-
AST::Number(value) => Ok(AST::String(value.to_string())),
113-
AST::Float(value) => Ok(AST::String(value.to_string())),
114-
AST::Boolean(value) => Ok(AST::String(value.to_string())),
115-
AST::Null => Ok(AST::String("null".to_string())),
112+
AST::Number(value) => Ok((AST::String(value.to_string()), AST::Null)),
113+
AST::Float(value) => Ok((AST::String(value.to_string()), AST::Null)),
114+
AST::Boolean(value) => Ok((AST::String(value.to_string()), AST::Null)),
115+
AST::Null => Ok((AST::String("null".to_string()), AST::Null)),
116116

117117
_ => Err("str() requires a string, number or boolean".to_string())
118118
}
@@ -122,6 +122,6 @@ pub fn str(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, St
122122
}
123123
}
124124

125-
pub fn exit(_: Vec<AST>, _: &mut HashMap<String, AST>) -> Result<AST, String> {
125+
pub fn exit(_: Vec<AST>, _: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
126126
std::process::exit(0);
127127
}

lang/src/packages/ffi.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::HashMap;
22
use crate::ast::AST;
33
use crate::eval::eval;
44

5-
pub fn call(mut args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
5+
pub fn call(mut args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
66
// (path_to_lib, function_name, arg1, arg2, ...)
77

88
if args.len() < 2 {
@@ -69,14 +69,14 @@ pub fn call(mut args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AS
6969
lib.close().unwrap();
7070

7171
if result_ptr.is_null() {
72-
return Ok(AST::Null);
72+
return Ok((AST::Null, AST::Null));
7373
};
7474

7575
if (result_ptr as i64) <= i32::MAX as i64 && (result_ptr as i64) >= i32::MIN as i64 {
76-
return Ok(AST::Number(result_ptr as i64));
76+
return Ok((AST::Number(result_ptr as i64), AST::Null));
7777
} else {
7878
let str = std::ffi::CStr::from_ptr(result_ptr as *const _);
79-
return Ok(AST::String(str.to_string_lossy().into_owned()))
79+
return Ok((AST::String(str.to_string_lossy().into_owned()), AST::Null))
8080
}
8181
}
8282
}

lang/src/packages/file.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ use std::io::prelude::*;
55
use crate::ast::AST;
66
use crate::eval::eval;
77

8-
pub fn read(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
8+
pub fn read(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
99
let path = eval(args[0].clone(), context)?;
1010

1111
match path {
1212
AST::String(val) => {
1313
let contents = std::fs::read_to_string(val).map_err(|e| e.to_string())?;
14-
Ok(AST::String(contents))
14+
Ok((AST::String(contents), AST::Null))
1515
}
1616

1717
_ => Err("read() expects a string".to_string())
1818
}
1919
}
2020

21-
pub fn write(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
21+
pub fn write(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
2222
let path = eval(args[0].clone(), context)?;
2323
let contents = eval(args[1].clone(), context)?;
2424

@@ -29,14 +29,14 @@ pub fn write(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST,
2929
.replace("\\t", "\t");
3030

3131
std::fs::write(path, contents).map_err(|e| e.to_string())?;
32-
Ok(AST::Null)
32+
Ok((AST::Null, AST::Null))
3333
}
3434

3535
_ => Err("write() expects two strings".to_string())
3636
}
3737
}
3838

39-
pub fn write_append(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<AST, String> {
39+
pub fn write_append(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Result<(AST, AST), String> {
4040
let path = eval(args[0].clone(), context)?;
4141
let contents = eval(args[1].clone(), context)?;
4242

@@ -56,7 +56,7 @@ pub fn write_append(args: Vec<AST>, context: &mut HashMap<String, AST>) -> Resul
5656
return Err(e.to_string());
5757
}
5858

59-
Ok(AST::Null)
59+
Ok((AST::Null, AST::Null))
6060
}
6161

6262
_ => Err("write_append() expects two strings".to_string())

0 commit comments

Comments
 (0)