diff --git a/Cargo.toml b/Cargo.toml index 6f00ccd..24fd957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,18 +6,18 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] ariadne = { git = "https://github.com/zesterer/ariadne" } chumsky = { git = "https://github.com/zesterer/chumsky", features = ["label"] } imbl = "3.0.0" -struct_scalpel = "0.1.1" +# struct_scalpel = "0.1.1" ran = "2.0.1" -rust-embed = "8.5.0" -boxing = "0.1.2" -ordered-float = "4.5.0" -index_vec = "0.1.4" +# rust-embed = "8.5.0" +# boxing = "0.1.2" +# ordered-float = "4.5.0" +# index_vec = "0.1.4" num-derive = "0.4.2" num-traits = "0.2.19" regex = "1.11.1" diff --git a/package.json b/package.json new file mode 100644 index 0000000..d5088f7 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "@ludus/rudus", + "version": "0.1.1", + "description": "A Rust-based Ludus bytecode interpreter.", + "type": "common", + "main": "pkg/ludus.js", + "directories": {}, + "keywords": [], + "author": "Scott Richmond", + "license": "GPL-3.0", + "files": [ + "pkg/rudus.js", + "pkg/ludus.js", + "pkg/rudus_bg.wasm", + "pkg/rudus_bg.wasm.d.ts", + "pkg/rudus.d.ts" + ], + "devDependencies": {} +} diff --git a/sandbox.ld b/sandbox.ld index ff914f7..5687e36 100644 --- a/sandbox.ld +++ b/sandbox.ld @@ -1,4 +1,4 @@ -print! (:foo, :bar, :baz) -print! ("I wrote something") -let false = true -print! (1, 2, 3) +repeat 1 { + fd! (100) + rt! (0.25) +} diff --git a/src/lib.rs b/src/lib.rs index a170a56..a507f6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ use vm::Vm; const PRELUDE: &str = include_str!("../assets/test_prelude.ld"); -pub fn prelude() -> HashMap<&'static str, Value> { +fn prelude() -> HashMap<&'static str, Value> { let tokens = lexer().parse(PRELUDE).into_output_errors().0.unwrap(); let (parsed, parse_errors) = parser() .parse(Stream::from_iter(tokens).map((0..PRELUDE.len()).into(), |(t, s)| (t, s))) @@ -91,7 +91,7 @@ pub fn prelude() -> HashMap<&'static str, Value> { #[wasm_bindgen] pub fn run(src: String) -> String { - let src = src.leak(); + let src = src.to_string().leak(); let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { return format!("{:?}", lex_errs); @@ -163,7 +163,9 @@ pub fn run(src: String) -> String { let Value::Box(commands) = turtle_commands else { unreachable!() }; - let commands = commands.borrow().to_json().unwrap(); + let commands = commands.borrow(); + dbg!(&commands); + let commands = commands.to_json().unwrap(); let output = match result { Ok(val) => val.show(), @@ -178,22 +180,11 @@ pub fn run(src: String) -> String { // TODO: use serde_json to make this more robust? format!( - "{{\"result\": \"{output}\", - \"io\": {{ - \"stdout\": {{ - \"proto\": [\"text-stream\", \"0.1.0\"], - \"data\": \"{console}\" - }}, - \"turtle\": {{ - \"proto\": [\"turtle-graphics\", \"0.1.0\"], - \"data\": {commands} - }} - }} - }}" +"{{\"result\":\"{output}\",\"io\":{{\"stdout\":{{\"proto\":[\"text-stream\",\"0.1.0\"],\"data\":\"{console}\"}},\"turtle\":{{\"proto\":[\"turtle-graphics\",\"0.1.0\"],\"data\":{commands}}}}}}}" ) } -pub fn ld_fmt(src: &'static str) -> Result { +pub fn fmt(src: &'static str) -> Result { let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { println!("{:?}", lex_errs); diff --git a/src/main.rs b/src/main.rs index 1103a54..1631597 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ -use crate::lib::run; +use rudus::run; use std::env; use std::fs; -mod lib; - pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = fs::read_to_string("sandbox.ld").unwrap(); diff --git a/src/parser.rs b/src/parser.rs index f882ff2..e13bc5c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,37 +1,11 @@ +// TODO: move AST to its own module +// TODO: remove StringMatcher cruft +// TODO: good error messages? + use crate::lexer::*; use crate::spans::*; use chumsky::{input::ValueInput, prelude::*, recursive::Recursive}; use std::fmt; -use struct_scalpel::Dissectible; - -// #[derive(Clone, Debug, PartialEq)] -// pub struct WhenClause { -// pub cond: Spanned, -// pub body: Spanned, -// } - -// impl fmt::Display for WhenClause { -// fn fmt(self: &WhenClause, f: &mut fmt::Formatter) -> fmt::Result { -// write!(f, "cond: {}, body: {}", self.cond.0, self.body.0) -// } -// } - -// #[derive(Clone, Debug, PartialEq)] -// pub struct MatchClause { -// pub patt: Spanned, -// pub guard: Option>, -// pub body: Spanned, -// } - -// impl fmt::Display for MatchClause { -// fn fmt(self: &MatchClause, f: &mut fmt::Formatter) -> fmt::Result { -// write!( -// f, -// "pattern: {}, guard: {:?} body: {}", -// self.patt.0, self.guard, self.body.0 -// ) -// } -// } #[derive(Clone, Debug, PartialEq, Eq)] pub enum StringPart { @@ -51,13 +25,7 @@ impl fmt::Display for StringPart { } } -pub struct LFn { - name: &'static str, - clauses: Vec>, - doc: Option<&'static str>, -} - -#[derive(Clone, Debug, PartialEq, Dissectible)] +#[derive(Clone, Debug, PartialEq)] pub enum Ast { // a special Error node // may come in handy? @@ -219,14 +187,11 @@ impl Ast { .collect::>() .join("\n ") ), - FnBody(clauses) => format!( - "{}", - clauses - .iter() - .map(|(clause, _)| clause.show()) - .collect::>() - .join("\n ") - ), + FnBody(clauses) => clauses + .iter() + .map(|(clause, _)| clause.show()) + .collect::>() + .join("\n "), Fn(name, body, doc) => { let mut out = format!("fn {name} {{\n"); if let Some(doc) = doc { @@ -267,7 +232,7 @@ impl Ast { .join(", ") ), MatchClause(pattern, guard, body) => { - let mut out = format!("{}", pattern.0.show()); + let mut out = pattern.0.show(); if let Some(guard) = guard.as_ref() { out = format!("{out} if {}", guard.0.show()); } @@ -523,75 +488,6 @@ impl fmt::Debug for StringMatcher { } } -// #[derive(Clone, Debug, PartialEq)] -// pub enum Pattern { -// Nil, -// Boolean(bool), -// Number(f64), -// String(&'static str), -// Interpolated(Vec>, StringMatcher), -// Keyword(&'static str), -// Word(&'static str), -// As(&'static str, &'static str), -// Splattern(Box>), -// Placeholder, -// Tuple(Vec>), -// List(Vec>), -// Pair(&'static str, Box>), -// Dict(Vec>), -// } - -// impl fmt::Display for Pattern { -// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -// match self { -// Pattern::Nil => write!(f, "nil"), -// Pattern::Boolean(b) => write!(f, "{}", b), -// Pattern::Number(n) => write!(f, "{}", n), -// Pattern::String(s) => write!(f, "{}", s), -// Pattern::Keyword(k) => write!(f, ":{}", k), -// Pattern::Word(w) => write!(f, "{}", w), -// Pattern::As(w, t) => write!(f, "{} as {}", w, t), -// Pattern::Splattern(p) => write!(f, "...{}", p.0), -// Pattern::Placeholder => write!(f, "_"), -// Pattern::Tuple(t) => write!( -// f, -// "({})", -// t.iter() -// .map(|x| x.0.to_string()) -// .collect::>() -// .join(", ") -// ), -// Pattern::List(l) => write!( -// f, -// "({})", -// l.iter() -// .map(|x| x.0.to_string()) -// .collect::>() -// .join(", ") -// ), -// Pattern::Dict(entries) => write!( -// f, -// "#{{{}}}", -// entries -// .iter() -// .map(|(pair, _)| pair.to_string()) -// .collect::>() -// .join(", ") -// ), -// Pattern::Pair(key, value) => write!(f, ":{} {}", key, value.0), -// Pattern::Interpolated(strprts, _) => write!( -// f, -// "interpolated: \"{}\"", -// strprts -// .iter() -// .map(|part| part.0.to_string()) -// .collect::>() -// .join("") -// ), -// } -// } -// } - fn is_word_char(c: char) -> bool { if c.is_ascii_alphanumeric() { return true; diff --git a/src/value.rs b/src/value.rs index 5d314c2..c5fdaa5 100644 --- a/src/value.rs +++ b/src/value.rs @@ -258,39 +258,50 @@ impl Value { pub fn to_json(&self) -> Option { use Value::*; match self { - True | False | String(..) | Interned(..) => Some(self.show()), + True | False | String(..) | Interned(..) | Number(..) => Some(self.show()), Keyword(str) => Some(format!("\"{str}\"")), List(members) => { let mut joined = "".to_string(); - for member in members.iter() { - match member.to_json() { - Some(json) => joined = format!("{joined}, {json}"), - None => return None, - } + let mut members = members.iter(); + if let Some(member) = members.next() { + joined = member.to_json()?; + } + for member in members { + let json = member.to_json()?; + joined = format!("{joined},{json}"); } Some(format!("[{joined}]")) } Tuple(members) => { let mut joined = "".to_string(); - for member in members.iter() { - match member.to_json() { - Some(json) => joined = format!("{joined}, {json}"), - None => return None, - } + let mut members = members.iter(); + if let Some(member) = members.next() { + joined = member.to_json()?; + } + for member in members { + let json = member.to_json()?; + joined = format!("{joined},{json}"); } Some(format!("[{joined}]")) } Dict(members) => { let mut joined = "".to_string(); - for (key, value) in members.iter() { - match value.to_json() { - Some(json) => joined = format!("{joined}, \"{key}\": {json}"), - None => return None, - } + let mut members = members.iter(); + if let Some((key, value)) = members.next() { + let json = value.to_json()?; + joined = format!("\"{key}\":{json}") + } + for (key, value) in members { + let json = value.to_json()?; + joined = format!("{joined},\"{key}\": {json}"); } Some(format!("{{{joined}}}")) } - _ => None, + not_serializable => { + println!("Cannot convert to json:"); + dbg!(not_serializable); + None + } } }