diff --git a/Cargo.lock b/Cargo.lock index 1018079..402a8a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,5 +3,104 @@ version = 3 [[package]] -name = "prout" -version = "0.1.0" +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +dependencies = [ + "const-random", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chumsky" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" +dependencies = [ + "ahash", +] + +[[package]] +name = "const-random" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" +dependencies = [ + "getrandom", + "once_cell", + "proc-macro-hack", + "tiny-keccak", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "porte" +version = "0.4.0" +dependencies = [ + "chumsky", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/Cargo.toml b/Cargo.toml index b345a3d..ad121e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,13 @@ [package] -name = "prout" -version = "0.1.0" +name = "porte" +version = "0.4.0" edition = "2021" +authors = ["JOLIMAITRE Matthieu "] +description = "A minimal programming language providing tools to automate file management like backups, building process or unit testing." +license = "MIT" +repository = "https://github.com/MajorBarnulf/porte" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chumsky = "0.8" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c0f19fd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 MajorBarnulf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 2ee8839..527c8b3 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# PROUT +# PORTE * Programmable -* Repeatable * Opinionated -* Understandable +* Repeatable * Tiny +* Extensible ## Description -PROUT is a minimal programming language providing tools to automate file management like backups, building process or unit testing. +PORTE is a minimal programming language providing tools to automate file management like backups, building process or unit testing. ## Usage @@ -17,11 +17,11 @@ PROUT is a minimal programming language providing tools to automate file managem ```sh -$ prout hello_world.pr +$ porte hello-world.pr ``` -## Author +## Authors -- JOLIMAITRE Matthieu , developper +- JOLIMAITRE Matthieu diff --git a/examples/array.pr b/examples/array.pr new file mode 100644 index 0000000..2b65370 --- /dev/null +++ b/examples/array.pr @@ -0,0 +1,117 @@ + +for: (from, to, f) => { + index: from; + loop { + if not(inf(index, to)) break true; + f(index); + index <- add(index, 1) + } +}; + +array_new: () => { + r: obj(); + r <- set(r, "len", 0); + r +}; + +array_len: (self) => { + get(self, "len") +}; + +array_get: (self, index) => { + get(self, str(index)) +}; + +foreach: (arr, f) => { + l: array_len(arr); + for(0, l, (i) => { + f(array_get(arr, i)) + }) +}; + +array_push: (self, e) => { + i: array_len(self); + self <- set(self, str(i), e); + + len: add (i, 1); + self <- set(self, "len", len); + self +}; + +array_pop: (self) => { + l: array_len(self); + if eq(l, 0) return false; + + i: sub(l, 1); + e: array_get(self, i); + self <- set(self, str(i), false); + + rest: array_new(); + foreach(self, (item) => { if not(eq(item, false)) rest <- array_push(rest, item) }); + + r: obj(); + r <- set(r, "tail", e); + r <- set(r, "rest", rest); + r +}; + +array_swap: (self, i, j) => { + i_value: array_get(self, i); + j_value: array_get(self, j); + self <- set(self, str(j), i_value); + self <- set(self, str(i), j_value); + self +}; + +array_sort: (self, cmp) => { + l: array_len(self); + for(0, sub(l, 1), (i) => { + i_min: i; + for(add(i, 1), l, (j) => { + e_j: array_get(self, j); + e_min: array_get(self, i_min); + if inf(cmp(e_j, e_min), 0) i_min <- j + }); + self <- array_swap(self, i, i_min) + }); + self +}; + +array_print: (self) => { + l: array_len(self); + r: "[ "; + for(0, l, (i) => { + r <- add(r, array_get(self, i)); + if not(eq(i, sub(l, 1))) r <- add(r, ", ") + }); + r <- add(r, " ]"); + out(r) +}; + +"########################"; +"# main #"; +"########################"; + +a: array_new(); +out("new:"); +array_print(a); + +a <- array_push(a, 1); +a <- array_push(a, 4); +a <- array_push(a, 6); +a <- array_push(a, 3); +a <- array_push(a, 2); +out(""); +out("pushed:"); +array_print(a); + +r: array_pop(a); +a <- get(r, "rest"); +out(""); +out("popped:"); +array_print(a); + +a <- array_sort(a, (a, b) => { if sup(a, b) 1 else -1 }); +out(""); +out("sorted:"); +array_print(a); diff --git a/examples/hello-world.pr b/examples/hello-world.pr new file mode 100644 index 0000000..0b3072d --- /dev/null +++ b/examples/hello-world.pr @@ -0,0 +1,36 @@ + +hello: "hello"; +porte: "porte"; + +line: hello; +line <- add(line, " "); +line <- add(line, porte); + +say-it: () => { + out(line) +}; + +say-it(); + + +for: (from, to, incr, fn) => { + index: from; + loop { + if not(inf(index, to)) break true; + result: fn(index); + index <- add(index, incr) + } +}; + +for(0, 3, 0.5, (index) => { + out(add("index: ", index)) +}); + + +a: 1; +b: 2; +c: 3; +a <- b <- c; +out(add("a :", a)); +out(add("b :", b)); +out(add("c :", c)); diff --git a/src/demo.rs b/src/demo.rs new file mode 100644 index 0000000..92c9e43 --- /dev/null +++ b/src/demo.rs @@ -0,0 +1,21 @@ +#[test] +fn ast() { + use crate::execution_tree; + use crate::syntax_tree; + + let text = r#" + a: 3; + { + a <- 4; + a: 5 + } + "#; + + let tree = syntax_tree::parser::ParserWrapper::new() + .parse(text) + .unwrap(); + dbg!(&tree); + + let executable = execution_tree::parser::Parser::parse(tree, |_| ()); + dbg!(&executable); +} diff --git a/src/execution_tree/parser.rs b/src/execution_tree/parser.rs index cf0c394..c504ac3 100644 --- a/src/execution_tree/parser.rs +++ b/src/execution_tree/parser.rs @@ -19,6 +19,15 @@ impl ParserBuilder { pub fn prelude(&mut self, name: String, value: Value) { self.prelude.push((name, value)); } + + fn append_globals(&self, body: &mut syntax_tree::Scope) { + let prelude = self.prelude.clone(); + for (name, value) in prelude.into_iter().rev() { + let value = syntax_tree::Expr::new_literal(value); + let expression = syntax_tree::Expr::new_variable_definition(name, value); + body.instructions.insert(0, expression); + } + } } pub struct Parser { @@ -26,45 +35,45 @@ pub struct Parser { } impl Parser { - pub fn parse(ast: syntax_tree::Program, builder: F) -> execution_tree::Program + fn new() -> Self { + let scopes = HashMap::new(); + Self { scopes } + } + + pub fn parse(syntax_tree: syntax_tree::Program, builder: F) -> execution_tree::Program where F: FnOnce(&mut ParserBuilder), { - let syntax_tree::Program { mut body } = ast; + let operation = builder; + let syntax_tree::Program { mut body } = syntax_tree; - let mut parser = Self { - scopes: HashMap::new(), - }; + let mut builder = ParserBuilder::new(); + operation(&mut builder); + builder.append_globals(&mut body); + + let mut parser = Self::new(); let parser_scope = ParserScope::new_root(); - let mut parser_builder = ParserBuilder::new(); - builder(&mut parser_builder); - for (name, value) in parser_builder.prelude.into_iter().rev() { - let value = syntax_tree::Expr::new_literal(value); - let expression = syntax_tree::Expr::new_variable_definition(name, value); - body.instructions.insert(0, expression); - } - - let body_scope_id = parser.parse_ast_scope(body, &parser_scope); - + let main_scope_id = parser.parse_syntax_tree_scope(body, &parser_scope); let Self { scopes } = parser; + execution_tree::Program { - main_scope_id: body_scope_id, + main_scope_id, scopes, } } - pub fn parse_ast_scope( + pub fn parse_syntax_tree_scope( &mut self, - ast_scope: syntax_tree::Scope, + syntax_tree_scope: syntax_tree::Scope, parser_scope: &ParserScope, ) -> execution_tree::Id { - let syntax_tree::Scope { instructions } = ast_scope; + let syntax_tree::Scope { instructions } = syntax_tree_scope; let scope_id = parser_scope.get_current_id(); let parent_scope_id = parser_scope.get_parent_id(); let expressions = instructions .into_iter() - .map(|expression| self.parse_expression(expression, &parser_scope)) + .map(|expression| self.parse_expression(expression, parser_scope)) .collect(); let local_variables = parser_scope.local_variable_ids(); @@ -86,8 +95,8 @@ impl Parser { let inner = expression.into_inner(); match inner { syntax_tree::ExprInner::Scope(scope) => { - let parser_scope = parser_scope.make_child_common(); - let scope = self.parse_ast_scope(scope, &parser_scope); + let parser_scope = parser_scope.child_common(); + let scope = self.parse_syntax_tree_scope(scope, &parser_scope); execution_tree::Expr::new_scope(scope) } syntax_tree::ExprInner::Literal(literal) => { @@ -187,12 +196,12 @@ impl Parser { parameter_names, } = function_definition; - let parser_scope = parser_scope.make_child_common(); + let parser_scope = parser_scope.child_function(); let parameter_ids = parameter_names .into_iter() .map(|name| parser_scope.add_name(name)) .collect(); - let body_scope_id = self.parse_ast_scope(body, &parser_scope); + let body_scope_id = self.parse_syntax_tree_scope(body, &parser_scope); execution_tree::FnDef { body_scope_id, @@ -209,7 +218,7 @@ impl Parser { let variable_id = parser_scope .get_variable_id(&name) - .expect("call of undeclared function"); + .unwrap_or_else(|| panic!("call of undeclared function '{name}'")); let parameters = arguments .into_iter() .map(|argument| self.parse_expression(argument, parser_scope)) @@ -246,8 +255,8 @@ impl Parser { ) -> execution_tree::Loop { let syntax_tree::Loop { body } = loop_; - let parser_scope = parser_scope.make_child_loop(); - let body_scope_id = self.parse_ast_scope(body, &parser_scope); + let parser_scope = parser_scope.child_loop(); + let body_scope_id = self.parse_syntax_tree_scope(body, &parser_scope); execution_tree::Loop { body_scope_id } } @@ -328,7 +337,7 @@ impl ParserScopeVariables { #[derive(Debug, Clone)] pub struct ParserScope { variables: Rc>, - next_global_id: Rc>, + next_id: Rc>, current_id: Id, parent_id: Option, current_function_scope_id: Option, @@ -338,7 +347,7 @@ pub struct ParserScope { impl ParserScope { pub fn new_root() -> Self { let current_id = Id::zero(); - let next_global_id = current_id.next(); + let next_id = current_id.next(); let variables = ParserScopeVariables { local_variables: HashMap::new(), parent_scope: None, @@ -346,21 +355,21 @@ impl ParserScope { Self { parent_id: None, current_id, - next_global_id: Rc::new(Mutex::new(next_global_id)), + next_id: Rc::new(Mutex::new(next_id)), variables: Rc::new(Mutex::new(variables)), current_function_scope_id: None, current_loop_scope_id: None, } } - pub fn make_child_common(&self) -> Self { + pub fn child_common(&self) -> Self { let variables = ParserScopeVariables { local_variables: HashMap::new(), parent_scope: Some(self.variables.clone()), }; Self { parent_id: Some(self.get_current_id()), - next_global_id: self.next_global_id.clone(), + next_id: self.next_id.clone(), variables: Rc::new(Mutex::new(variables)), current_id: self.request_new_id(), current_function_scope_id: self.get_current_function_id(), @@ -368,7 +377,7 @@ impl ParserScope { } } - pub fn make_child_function(&self) -> Self { + pub fn child_function(&self) -> Self { let variables = ParserScopeVariables { local_variables: HashMap::new(), parent_scope: Some(self.variables.clone()), @@ -378,15 +387,15 @@ impl ParserScope { Self { parent_id: Some(self.get_current_id()), - next_global_id: self.next_global_id.clone(), + next_id: self.next_id.clone(), variables: Rc::new(Mutex::new(variables)), current_id, - current_function_scope_id: Some(current_id.clone()), + current_function_scope_id: Some(current_id), current_loop_scope_id: None, } } - pub fn make_child_loop(&self) -> Self { + pub fn child_loop(&self) -> Self { let variables = ParserScopeVariables { local_variables: HashMap::new(), parent_scope: Some(self.variables.clone()), @@ -396,28 +405,28 @@ impl ParserScope { Self { parent_id: Some(self.get_current_id()), - next_global_id: self.next_global_id.clone(), + next_id: self.next_id.clone(), variables: Rc::new(Mutex::new(variables)), current_id, current_function_scope_id: self.get_current_function_id(), - current_loop_scope_id: Some(current_id.clone()), + current_loop_scope_id: Some(current_id), } } pub fn get_current_id(&self) -> Id { - self.current_id.clone() + self.current_id } pub fn get_current_function_id(&self) -> Option { - self.current_function_scope_id.clone() + self.current_function_scope_id } pub fn get_current_loop_id(&self) -> Option { - self.current_loop_scope_id.clone() + self.current_loop_scope_id } pub fn get_parent_id(&self) -> Option { - self.parent_id.clone() + self.parent_id } pub fn get_variable_id(&self, name: &str) -> Option { @@ -425,7 +434,7 @@ impl ParserScope { } fn request_new_id(&self) -> Id { - let mut next_id_ref = self.next_global_id.lock().unwrap(); + let mut next_id_ref = self.next_id.lock().unwrap(); let id = *next_id_ref; *next_id_ref = id.next(); id diff --git a/src/main.rs b/src/main.rs index 38b3733..bedf467 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +use std::{env::args, fs}; + +pub mod demo; pub mod execution_tree; pub mod prelude; pub mod runtime; @@ -5,7 +8,15 @@ pub mod syntax_tree; pub mod value; fn main() { - println!("Hello, world!"); + use prelude::std_prelude; + + let path = args().nth(1).expect("[error]: usage 'porte '"); + let input = fs::read_to_string(path).expect("file not found"); + let ast_parser = syntax_tree::parser::ParserWrapper::new(); + let parsed = ast_parser.parse(&input).unwrap(); + let executable = execution_tree::parser::Parser::parse(parsed, std_prelude); + let mut runtime = runtime::Runtime::new(); + runtime.execute(&executable); } #[test] @@ -29,11 +40,11 @@ fn it_works() { ]), ), ), - Expr::new_function_call("my_print", vec!["hello, PROUT".into()]), + Expr::new_function_call("my_print", vec!["hello, PORTE".into()]), Expr::new_function_call("print", vec![Expr::new_variable_call("a")]), ]), }; - let exec = Parser::parse(ast, |builder| std_prelude(builder)); + let exec = Parser::parse(ast, std_prelude); println!("\n\n\n-- running: --"); let _result = Runtime::new().execute(&exec); } diff --git a/src/prelude.rs b/src/prelude.rs index ac3d6bd..0869c9e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,14 +1,148 @@ +use std::collections::HashMap; + use crate::{ execution_tree::parser::ParserBuilder, value::{function::Function, Value}, }; pub fn std_prelude(builder: &mut ParserBuilder) { - builder.prelude("print".into(), Function::new_native(1, print).into()) + type FunctOper = fn(Vec) -> Value; + let functions: Vec<(_, _, FunctOper)> = vec![ + ("out", 1, out), + ("add", 2, add), + ("sub", 2, sub), + ("eq", 2, eq), + ("sup", 2, sup), + ("inf", 2, inf), + ("and", 2, and), + ("or", 2, or), + ("not", 1, not), + ("str", 2, str), + ("obj", 0, obj), + ("set", 3, set), + ("get", 2, get), + ]; + + for (name, arg_count, closure) in functions { + builder.prelude(name.into(), Function::new_native(arg_count, closure).into()); + } } -fn print(args: Vec) -> Value { +fn out(args: Vec) -> Value { let to_print = args.get(0).unwrap(); - println!("{to_print:?}"); - Value::Bool(true) + let str = value_to_string(to_print); + println!("{str}"); + to_print.clone() +} + +fn add(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Number(l), Value::Number(r)) => (l + r).into(), + (Value::String(l), Value::String(r)) => format!("{l}{r}").into(), + (Value::Number(l), Value::String(r)) => format!("{l}{r}").into(), + (Value::String(l), Value::Number(r)) => format!("{l}{r}").into(), + _ => panic!("adding incompatible types"), + } +} + +fn sub(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Number(l), Value::Number(r)) => (l - r).into(), + _ => panic!("substracting non-numbers"), + } +} + +fn eq(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Bool(l), Value::Bool(r)) => (l == r).into(), + (Value::Number(l), Value::Number(r)) => (l == r).into(), + (Value::String(l), Value::String(r)) => (l == r).into(), + _ => false.into(), + } +} + +fn sup(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Number(l), Value::Number(r)) => (l > r).into(), + _ => panic!("comparing non-numeric"), + } +} + +fn inf(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Number(l), Value::Number(r)) => (l < r).into(), + _ => panic!("comparing non-numeric"), + } +} + +fn and(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Bool(l), Value::Bool(r)) => (*l && *r).into(), + _ => panic!("intersection of non-boolean"), + } +} + +fn or(args: Vec) -> Value { + let lhs = args.get(0).unwrap(); + let rhs = args.get(1).unwrap(); + match (lhs, rhs) { + (Value::Bool(l), Value::Bool(r)) => (*l || *r).into(), + _ => panic!("union of non-boolean"), + } +} + +fn not(args: Vec) -> Value { + let input = args.get(0).unwrap(); + let result = !input.as_bool().expect("complementing non-bool"); + result.into() +} + +fn value_to_string(input: &Value) -> String { + match input { + Value::None => "None".to_string(), + Value::Bool(b) => format!("{b}"), + Value::Number(n) => format!("{n}"), + Value::String(s) => s.clone(), + Value::Object(_) => "[object]".into(), + Value::Function(_) => "[function]".into(), + } +} + +fn str(args: Vec) -> Value { + let input = args.get(0).unwrap(); + value_to_string(input).into() +} + +fn obj(_: Vec) -> Value { + Value::Object(HashMap::new()) +} + +fn set(args: Vec) -> Value { + let mut object = args.get(0).unwrap().as_object().unwrap().clone(); + let name = args.get(1).unwrap().as_string().unwrap().to_string(); + let value = args.get(2).unwrap().clone(); + if let Value::Bool(false) = value { + object.remove(&name); + } else { + object.insert(name, value); + } + object.into() +} + +fn get(args: Vec) -> Value { + let object = args.get(0).unwrap().as_object().unwrap(); + let name = args.get(1).unwrap().as_string().unwrap(); + object.get(name).cloned().unwrap_or_else(|| false.into()) } diff --git a/src/runtime.rs b/src/runtime.rs index a3bbcbd..22df2d4 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -24,12 +24,12 @@ impl FrameBuilder { } pub fn variable(&mut self, variable_id: &Id, default: Value) { - self.variables.insert(variable_id.clone(), default); + self.variables.insert(*variable_id, default); } } pub struct Frame { - scope_id: Id, + _scope_id: Id, variables: HashMap, } @@ -52,9 +52,9 @@ impl Frame { builder(&mut frame_builder); let FrameBuilder { variables } = frame_builder; - let scope_id = scope_id.clone(); + let scope_id = *scope_id; Self { - scope_id, + _scope_id: scope_id, variables, } } @@ -99,6 +99,12 @@ impl Stack { } } +impl Default for Stack { + fn default() -> Self { + Self::new() + } +} + pub struct ShortCircuit { value: Value, destination_scope_id: Id, @@ -138,9 +144,9 @@ impl ExecReturn { } } -impl Into for Value { - fn into(self) -> ExecReturn { - ExecReturn::new_value(self) +impl From for ExecReturn { + fn from(val: Value) -> Self { + ExecReturn::new_value(val) } } @@ -169,7 +175,7 @@ impl Runtime { where F: FnOnce(&mut FrameBuilder), { - let scope = program.scopes.get(&scope_id).unwrap(); + let scope = program.scopes.get(scope_id).unwrap(); let Scope { parent_scope_id: _, expressions, @@ -210,9 +216,7 @@ impl Runtime { ExprInner::FnDef(function_definition) => { self.execute_function_definition(function_definition) } - ExprInner::FnCall(function_call) => { - self.execute_function_call(function_call, program).into() - } + ExprInner::FnCall(function_call) => self.execute_function_call(function_call, program), ExprInner::FnRet(function_return) => { self.execute_function_return(function_return, program) } @@ -272,7 +276,7 @@ impl Runtime { parameter_ids: argument_ids, body_scope_id, } = function_definition; - let value = Function::new_constructed(argument_ids.clone(), body_scope_id.clone()); + let value = Function::new_constructed(argument_ids.clone(), *body_scope_id); let value = Value::Function(value); value.into() } @@ -367,7 +371,7 @@ impl Runtime { }) => value, }; - ExecReturn::new_short_circuit(value, function_scope_id.clone()) + ExecReturn::new_short_circuit(value, *function_scope_id) } pub fn execute_loop(&mut self, loop_: &Loop, program: &Program) -> ExecReturn { @@ -401,7 +405,7 @@ impl Runtime { } }; - ExecReturn::new_short_circuit(value, loop_scope_id.clone()) + ExecReturn::new_short_circuit(value, *loop_scope_id) } pub fn execute_condition(&mut self, condition: &Cond, program: &Program) -> ExecReturn { @@ -432,3 +436,9 @@ impl Runtime { } } } + +impl Default for Runtime { + fn default() -> Self { + Self::new() + } +} diff --git a/src/syntax_tree.rs b/src/syntax_tree.rs index d81bbb5..18f24af 100644 --- a/src/syntax_tree.rs +++ b/src/syntax_tree.rs @@ -9,6 +9,10 @@ pub struct Program { pub struct Expr(pub Box); impl Expr { + pub fn new(inner: ExprInner) -> Self { + Self(Box::new(inner)) + } + pub fn inner(&self) -> &ExprInner { let Self(inner) = self; inner @@ -76,6 +80,72 @@ impl Expr { } } +impl From for Expr { + fn from(input: Scope) -> Self { + Self::new(ExprInner::Scope(input)) + } +} + +impl From for Expr { + fn from(input: Literal) -> Self { + Self::new(ExprInner::Literal(input)) + } +} + +impl From for Expr { + fn from(input: VarDef) -> Self { + Self::new(ExprInner::VarDef(input)) + } +} + +impl From for Expr { + fn from(input: VarAssign) -> Self { + Self::new(ExprInner::VarAssign(input)) + } +} + +impl From for Expr { + fn from(input: VarCall) -> Self { + Self::new(ExprInner::VarCall(input)) + } +} + +impl From for Expr { + fn from(input: FnDef) -> Self { + Self::new(ExprInner::FnDef(input)) + } +} + +impl From for Expr { + fn from(input: FnCall) -> Self { + Self::new(ExprInner::FnCall(input)) + } +} + +impl From for Expr { + fn from(input: FnRet) -> Self { + Self::new(ExprInner::FnRet(input)) + } +} + +impl From for Expr { + fn from(input: Loop) -> Self { + Self::new(ExprInner::Loop(input)) + } +} + +impl From for Expr { + fn from(input: LoopBr) -> Self { + Self::new(ExprInner::LoopBr(input)) + } +} + +impl From for Expr { + fn from(input: Cond) -> Self { + Self::new(ExprInner::Cond(input)) + } +} + impl From for Expr where T: Into, @@ -164,3 +234,5 @@ pub struct Cond { pub arm_true: Expr, pub arm_false: Option, } + +pub mod parser; diff --git a/src/syntax_tree/parser.rs b/src/syntax_tree/parser.rs new file mode 100644 index 0000000..fbdb93f --- /dev/null +++ b/src/syntax_tree/parser.rs @@ -0,0 +1,422 @@ +use super::*; +use chumsky::{prelude::*, text::whitespace}; + +pub trait AbstractParser: Parser> {} + +impl>> AbstractParser for U {} + +pub fn debugging_expression_parser() -> impl AbstractParser + Clone { + variable_call_parser().padded().map(|v| v.into()) +} + +pub fn scope_parser(expression: impl AbstractParser + Clone) -> impl AbstractParser { + let open_list = expression.separated_by(just(';')); + let closed_list = open_list.clone().then_ignore(just(';').then(whitespace())); + open_list + .map(|instructions| Scope { instructions }) + .or(closed_list.map(|mut instructions| { + instructions.push(Value::None.into()); + Scope { instructions } + })) + .padded() + .delimited_by(just('{'), just('}')) +} + +#[test] +fn test_scope_parser() { + let parser = scope_parser(debugging_expression_parser()); + let value = parser.parse("{ arbre }"); + dbg!(value.unwrap()); +} + +// TODO: add objects ? +pub fn literal_value() -> impl AbstractParser { + let bool = just("false") + .map(|_| false.into()) + .or(just("true").map(|_| true.into())); + + let none = just("none").map(|_| Value::None); + + let string = just('\"') + .ignore_then(none_of("\"").repeated()) + .then_ignore(just('\"')) + .map(|v| String::from_iter(&v).into()); + + let frac = just('.').chain(text::digits(10)); + let number = just('-') + .or_not() + .chain(text::int(10)) + .chain::(frac.or_not().flatten()) + .collect::() + .from_str() + .unwrapped() + .map(|n: f64| n.into()); + + bool.or(none).or(string).or(number) +} + +#[test] +fn test_literal_value() { + let parser = literal_value(); + let value = parser.parse("5"); + assert_eq!(value.unwrap().as_number().unwrap(), 5.); +} + +pub fn name() -> impl AbstractParser + Clone { + let first = one_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + let rest = first.clone().or(one_of("1234567890-_+/*")); + + first.then(rest.repeated()).map(|(f, v)| { + let rest = String::from_iter(&v); + format!("{f}{rest}") + }) +} + +pub fn variable_definition_parser( + expression: impl AbstractParser, +) -> impl AbstractParser { + name() + .then_ignore(just(":").padded()) + .then(expression) + .map(|(name, value)| VarDef { name, value }) +} + +#[test] +fn test_variable_definition_parser() { + let parser = variable_definition_parser(debugging_expression_parser()); + let value = parser.parse("a : b"); + dbg!(value.unwrap()); +} + +pub fn variable_assignement_parser( + expression: impl AbstractParser, +) -> impl AbstractParser { + name() + .then_ignore(just("<-").padded()) + .then(expression) + .map(|(name, value)| VarAssign { name, value }) +} + +#[test] +fn test_variable_assignement_parser() { + let parser = variable_assignement_parser(debugging_expression_parser()); + let value = parser.parse("arbre <- expr"); + dbg!(value.unwrap()); +} + +pub fn variable_call_parser() -> impl AbstractParser + Clone { + name().map(|name| VarCall { name }) +} + +#[test] +fn test_variable_call_parser() { + let parser = variable_call_parser(); + let value = parser.parse("arbre"); + assert_eq!(value.unwrap().name, "arbre".to_string()); +} + +pub fn function_definition_parser( + expression: impl AbstractParser + Clone, +) -> impl AbstractParser { + let parameters = name().separated_by(just(',').padded()); + let body = scope_parser(expression); + parameters + .padded() + .delimited_by(just('('), just(')')) + .then_ignore(just("=>").padded()) + .then(body) + .map(|(parameter_names, body)| FnDef { + body, + parameter_names, + }) +} + +#[test] +fn test_function_definition_parser() { + let parser = function_definition_parser(debugging_expression_parser()); + let value = parser.parse("(a) => { b }"); + dbg!(value.unwrap()); +} + +pub fn function_call_parser(expression: impl AbstractParser) -> impl AbstractParser { + let parameters = expression.separated_by(just(',').padded()); + name() + .then( + parameters + .padded() + .delimited_by(just('('), just(')')) + .padded(), + ) + .map(|(name, arguments)| FnCall { arguments, name }) +} + +#[test] +fn test_function_call_parser() { + let parser = function_call_parser(debugging_expression_parser()); + let value = parser.parse("f( a , b )"); + dbg!(value.unwrap()); +} + +pub fn function_return_parser(expression: impl AbstractParser) -> impl AbstractParser { + just("return") + .ignore_then(expression.or_not()) + .map(|expr| expr.unwrap_or(From::::from(true.into()))) + .map(|value| FnRet { value }) +} + +#[test] +fn test_function_return_parser() { + let parser = function_return_parser(debugging_expression_parser()); + let value = parser.parse("return a"); + dbg!(value.unwrap()); +} + +pub fn loop_parser(expression: impl AbstractParser + Clone) -> impl AbstractParser { + just("loop") + .then(whitespace()) + .ignore_then(scope_parser(expression)) + .map(|body| Loop { body }) +} + +#[test] +fn test_loop_parser() { + let parser = loop_parser(debugging_expression_parser()); + let value = parser.parse("loop { a}"); + dbg!(value.unwrap()); +} + +pub fn loop_break_parser(expression: impl AbstractParser) -> impl AbstractParser { + just("break") + .ignore_then(just(" ").then(whitespace()).ignore_then(expression)) + .map(|value| LoopBr { value }) +} + +#[test] +fn test_loop_break_parser() { + let parser = loop_break_parser(debugging_expression_parser()); + let value = parser.parse("break a"); + dbg!(value.unwrap()); +} + +pub fn condition_parser( + expression: impl AbstractParser + Clone, +) -> impl AbstractParser { + just("if ") + .ignore_then(expression.clone()) + .then(expression.clone()) + .then(just("else ").ignore_then(expression).or_not()) + .map(|((condition, arm_true), arm_false)| Cond { + condition, + arm_true, + arm_false, + }) +} + +#[test] +fn test_condition_parser() { + let parser = condition_parser(debugging_expression_parser()); + let value = parser.parse("if a t else f"); + dbg!(value.unwrap()); +} + +fn _sugar_parser(expression: impl AbstractParser + Clone) -> impl AbstractParser { + let add = expression + .clone() + .then_ignore(just(" + ")) + .then(expression.clone()) + .map(|(l, r)| { + FnCall { + name: "add".into(), + arguments: vec![l, r], + } + .into() + }); + + let sub = expression + .clone() + .then_ignore(just(" - ")) + .then(expression.clone()) + .map(|(l, r)| { + FnCall { + name: "sub".into(), + arguments: vec![l, r], + } + .into() + }); + + let eq = expression + .clone() + .then_ignore(just(" == ")) + .then(expression.clone()) + .map(|(l, r)| { + FnCall { + name: "eq".into(), + arguments: vec![l, r], + } + .into() + }); + + let sup = expression + .clone() + .then_ignore(just(" > ")) + .then(expression.clone()) + .map(|(l, r)| { + FnCall { + name: "sup".into(), + arguments: vec![l, r], + } + .into() + }); + + let inf = expression + .clone() + .then_ignore(just(" < ")) + .then(expression.clone()) + .map(|(l, r)| { + FnCall { + name: "inf".into(), + arguments: vec![l, r], + } + .into() + }); + + let or = expression + .clone() + .then_ignore(just(" || ")) + .then(expression.clone()) + .map(|(l, r)| { + FnCall { + name: "or".into(), + arguments: vec![l, r], + } + .into() + }); + + let and = expression + .clone() + .then_ignore(just(" && ")) + .then(expression) + .map(|(l, r)| { + FnCall { + name: "and".into(), + arguments: vec![l, r], + } + .into() + }); + + eq.or(add).or(sub).or(sup).or(inf).or(or).or(and) +} + +pub fn expression_parser() -> impl AbstractParser { + let expression = recursive(|expression| { + //let sugar = sugar_parser(expression.clone()); + let condition = condition_parser(expression.clone()).map(|i| i.into()); + let function_definition = function_definition_parser(expression.clone()).map(|i| i.into()); + let function_return = function_return_parser(expression.clone()).map(|i| i.into()); + let loop_ = loop_parser(expression.clone()).map(|i| i.into()); + let loop_break = loop_break_parser(expression.clone()).map(|i| i.into()); + let scope = scope_parser(expression.clone()).map(|i| i.into()); + let litteral = literal_value().map(Expr::new_literal); + let variable_definition = variable_definition_parser(expression.clone()).map(|i| i.into()); + let variable_assignment = variable_assignement_parser(expression.clone()).map(|i| i.into()); + let function_call = function_call_parser(expression.clone()).map(|i| i.into()); + let variable_call = variable_call_parser().map(|i| i.into()); + + //sugar + // .or(condition) + condition + .or(function_definition) + .or(function_return) + .or(loop_) + .or(loop_break) + .or(scope) + .or(litteral) + .or(variable_definition) + .or(variable_assignment) + .or(function_call) + .or(variable_call) + .padded() + }); + expression +} + +#[test] +fn test_expression_parser() { + let text = r##"if a b"##; + let parser = expression_parser(); + let value = parser.parse(text); + dbg!(value.unwrap()); +} + +fn parser() -> impl AbstractParser { + let scope = expression_parser() + .separated_by(just(';').padded()) + .then_ignore(just(';').padded().or_not()) + .then_ignore(end()); + + scope.map(|instructions| { + let body = Scope { instructions }; + Program { body } + }) +} + +#[test] +fn test_parser() { + let example = r##" +print(3); + +a: 3; + +b: (a) => { + print("a") +}; + +f(a) + +"##; + let parser = parser(); + let e = parser.parse(example); + dbg!(e.unwrap()); +} + +pub struct ParserWrapper { + inner: Box>, +} + +impl ParserWrapper { + pub fn new() -> Self { + let inner = Box::new(parser()); + ParserWrapper { inner } + } + + pub fn parse(&self, input: &str) -> Result>> { + self.inner.parse(input) + } +} + +impl Default for ParserWrapper { + fn default() -> Self { + Self::new() + } +} + +/* +// example + +my-print: (input) => { + concatenated: { + "now I would like to interject for a moment" + input + }; + print(input); +} + +a: 5.2; +loop { + if a <= 0 { + break true + } else { + a <- a - 1 + } +} + +*/ diff --git a/src/value.rs b/src/value.rs index 2a82608..7f4b424 100644 --- a/src/value.rs +++ b/src/value.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use self::function::Function; pub mod function { - use crate::execution_tree::{Expr, Id}; + use crate::execution_tree::Id; use super::Value; @@ -55,9 +55,9 @@ pub mod function { } } - impl Into for Function { - fn into(self) -> Value { - Value::Function(self) + impl From for Value { + fn from(val: Function) -> Self { + Value::Function(val) } } } @@ -72,6 +72,34 @@ pub enum Value { } impl Value { + pub fn as_bool(&self) -> Option { + match self { + Self::Bool(b) => Some(*b), + _ => None, + } + } + + pub fn as_number(&self) -> Option { + match self { + Self::Number(n) => Some(*n), + _ => None, + } + } + + pub fn as_string(&self) -> Option<&str> { + match self { + Self::String(s) => Some(s), + _ => None, + } + } + + pub fn as_object(&self) -> Option<&HashMap> { + match self { + Self::Object(h) => Some(h), + _ => None, + } + } + pub fn as_function(&self) -> Option<&Function> { match self { Self::Function(function) => Some(function), @@ -104,6 +132,12 @@ impl From<&str> for Value { } } +impl From> for Value { + fn from(value: HashMap) -> Self { + Self::Object(value) + } +} + impl From> for Value where T: Into,