From bb384243cdc8ca8e256ec4a605a2d01f262d0657 Mon Sep 17 00:00:00 2001 From: MajorBarnulf <52676082+MajorBarnulf@users.noreply.github.com> Date: Wed, 25 May 2022 08:08:11 +0300 Subject: [PATCH 01/10] Initial commit --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE 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. From 7aa98bdd04f48e34469f25a2586b5cbfbaed4019 Mon Sep 17 00:00:00 2001 From: Jolimaitre Matthieu Date: Wed, 25 May 2022 20:09:59 +0300 Subject: [PATCH 02/10] started grammar parsing --- Cargo.lock | 99 ++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 7 +- src/syntax_tree.rs | 72 +++++++++++++++++++ src/syntax_tree/parser.rs | 144 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 src/syntax_tree/parser.rs diff --git a/Cargo.lock b/Cargo.lock index 1018079..9dce2db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,105 @@ # It is not intended for manual editing. version = 3 +[[package]] +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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4" +dependencies = [ + "const-random-macro", + "proc-macro-hack", +] + +[[package]] +name = "const-random-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40" +dependencies = [ + "getrandom", + "lazy_static", + "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 = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "prout" version = "0.1.0" +dependencies = [ + "chumsky", +] + +[[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..6b24bee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chumsky = "0.8.0" diff --git a/src/main.rs b/src/main.rs index 38b3733..9879362 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,12 @@ pub mod syntax_tree; pub mod value; fn main() { - println!("Hello, world!"); + use prelude::std_prelude; + let input = todo!(); + // let parsed = syntax_tree::parser::Parser::parse(input); + // let executable = execution_tree::parser::Parser::parse(parsed, |b| std_prelude(b)); + // let runtime = runtime::Runtime::new(); + // runtime.execute(&executable); } #[test] 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..5fed656 --- /dev/null +++ b/src/syntax_tree/parser.rs @@ -0,0 +1,144 @@ +use chumsky::prelude::*; + +use super::*; + +pub fn scope_parser() -> impl Parser> { + expression_parser() + .chain( + just(';') + .padded() + .ignore_then(expression_parser()) + .repeated(), + ) + .delimited_by(just('{'), just('}')) + .map(|instructions| Scope { instructions }) +} + +pub fn literal_value() -> impl Parser> { + 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(); + let literal = number.clone().map(|n| Value::Number(n)); // TODO: add other types + literal +} + +pub fn name() -> impl Parser> { + let name = just('a') + .or(just('b')) + .or(just('c')) + .or(just('d')) + .or(just('e')) + .or(just('f')) + .or(just('g')) + .or(just('h')) + .or(just('i')) + .or(just('j')) + .or(just('k')) + .or(just('l')) + .or(just('m')) + .or(just('n')) + .or(just('o')) + .or(just('p')) + .or(just('q')) + .or(just('r')) + .or(just('s')) + .or(just('t')) + .or(just('u')) + .or(just('v')) + .or(just('w')) + .or(just('x')) + .or(just('y')) + .or(just('z')) + .or(just('-')) + .or(just('_')) + .repeated() + .map(|v| String::from_iter(&v)); + name +} + +pub fn variable_definition_parser() -> impl Parser> { + name() + .padded() + .then_ignore(just(":")) + .padded() + .then(expression_parser()) + .map(|(name, value)| VarDef { name, value }) +} + +pub fn variable_assignement_parser() -> impl Parser> { + expression_parser().then() name() +} + +pub fn expression_parser() -> impl Parser> { + let scope = scope_parser().map(|s| s.into()); + let litteral = literal_value().map(|v| Expr::new_literal(v)); + let variable_definition = variable_definition_parser().map(|s| s.into()); + let variable_assignment; + let variable_call; + let function_definition; + let function_call; + let function_return; + let loop_; + let loop_break; + let condition; + scope + .or(litteral) + .or(variable_definition) + .or(variable_assignment) + .or(variable_call) + .or(function_definition) + .or(function_call) + .or(function_return) + .or(loop_) + .or(loop_break) + .or(condition) +} + +pub fn parser() -> impl Parser> { + let scope = expression_parser().chain( + just(';') + .padded() + .ignore_then(expression_parser()) + .repeated(), + ); + + let program = scope + .map(|instructions| { + let body = Scope { instructions }; + Program { body } + }) + .then_ignore(end().recover_with(skip_then_retry_until([]))); + program +} + +// impl Parser { +// pub fn parse(input: &str) -> Program { +// todo!() +// } +// } + +/* +// 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 - 1 -> a + } +} + +*/ From dac8ac1e9c8d19c15684cda3156efb00362fe2ea Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Wed, 1 Jun 2022 03:41:20 +0300 Subject: [PATCH 03/10] made the parser work --- Cargo.lock | 2 +- Cargo.toml | 4 +- README.md | 6 +- examples/hello-world.pr | 36 +++ src/main.rs | 15 +- src/prelude.rs | 140 +++++++++++- src/runtime.rs | 4 +- src/syntax_tree/parser.rs | 456 ++++++++++++++++++++++++++++++-------- src/value.rs | 37 +++- 9 files changed, 588 insertions(+), 112 deletions(-) create mode 100644 examples/hello-world.pr diff --git a/Cargo.lock b/Cargo.lock index 9dce2db..2445052 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "prout" -version = "0.1.0" +version = "0.2.0" dependencies = [ "chumsky", ] diff --git a/Cargo.toml b/Cargo.toml index 6b24bee..7e0c12a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "prout" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chumsky = "0.8.0" +chumsky = "0.8" diff --git a/README.md b/README.md index 2ee8839..2e25b19 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,11 @@ PROUT is a minimal programming language providing tools to automate file managem ```sh -$ prout hello_world.pr +$ prout hello-world.pr ``` -## Author +## Authors -- JOLIMAITRE Matthieu , developper +- JOLIMAITRE Matthieu diff --git a/examples/hello-world.pr b/examples/hello-world.pr new file mode 100644 index 0000000..c7862c1 --- /dev/null +++ b/examples/hello-world.pr @@ -0,0 +1,36 @@ + +hello: "hello"; +prout: "prout"; + +line: hello; +line <- add(line, " "); +line <- add(line, prout); + +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/main.rs b/src/main.rs index 9879362..35335b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::{env::args, fs}; + pub mod execution_tree; pub mod prelude; pub mod runtime; @@ -6,11 +8,14 @@ pub mod value; fn main() { use prelude::std_prelude; - let input = todo!(); - // let parsed = syntax_tree::parser::Parser::parse(input); - // let executable = execution_tree::parser::Parser::parse(parsed, |b| std_prelude(b)); - // let runtime = runtime::Runtime::new(); - // runtime.execute(&executable); + + let path = args().nth(1).expect("[error]: usage 'prout '"); + 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, |b| std_prelude(b)); + let mut runtime = runtime::Runtime::new(); + runtime.execute(&executable); } #[test] diff --git a/src/prelude.rs b/src/prelude.rs index ac3d6bd..5387ce4 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,14 +1,146 @@ +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()) + let functions: Vec<(_, _, fn(Vec) -> Value)> = 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(), + _ => unreachable!(), + } +} + +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(), + _ => panic!("comparing different types"), + } +} + +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(mut args: Vec) -> Value { + let name = args.get(1).unwrap().as_string().unwrap().to_string(); + let value = args.get(2).unwrap().clone(); + let object = args.get_mut(0).unwrap().as_object_mut().unwrap(); + object.insert(name, value.clone()); + value +} + +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) + .map(|value| value.clone()) + .unwrap_or_else(|| false.into()) } diff --git a/src/runtime.rs b/src/runtime.rs index a3bbcbd..4ee2cf8 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -29,7 +29,7 @@ impl FrameBuilder { } pub struct Frame { - scope_id: Id, + _scope_id: Id, variables: HashMap, } @@ -54,7 +54,7 @@ impl Frame { let FrameBuilder { variables } = frame_builder; let scope_id = scope_id.clone(); Self { - scope_id, + _scope_id: scope_id, variables, } } diff --git a/src/syntax_tree/parser.rs b/src/syntax_tree/parser.rs index 5fed656..ad53f16 100644 --- a/src/syntax_tree/parser.rs +++ b/src/syntax_tree/parser.rs @@ -1,20 +1,47 @@ -use chumsky::prelude::*; - use super::*; +use chumsky::{prelude::*, text::whitespace}; -pub fn scope_parser() -> impl Parser> { - expression_parser() - .chain( - just(';') - .padded() - .ignore_then(expression_parser()) - .repeated(), - ) - .delimited_by(just('{'), just('}')) - .map(|instructions| Scope { instructions }) +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 literal_value() -> impl Parser> { +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() @@ -22,105 +49,346 @@ pub fn literal_value() -> impl Parser> { .chain::(frac.or_not().flatten()) .collect::() .from_str() - .unwrapped(); - let literal = number.clone().map(|n| Value::Number(n)); // TODO: add other types + .unwrapped() + .map(|n: f64| n.into()); + + let literal = bool.or(none).or(string).or(number); literal } -pub fn name() -> impl Parser> { - let name = just('a') - .or(just('b')) - .or(just('c')) - .or(just('d')) - .or(just('e')) - .or(just('f')) - .or(just('g')) - .or(just('h')) - .or(just('i')) - .or(just('j')) - .or(just('k')) - .or(just('l')) - .or(just('m')) - .or(just('n')) - .or(just('o')) - .or(just('p')) - .or(just('q')) - .or(just('r')) - .or(just('s')) - .or(just('t')) - .or(just('u')) - .or(just('v')) - .or(just('w')) - .or(just('x')) - .or(just('y')) - .or(just('z')) - .or(just('-')) - .or(just('_')) - .repeated() - .map(|v| String::from_iter(&v)); +#[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-_+/*")); + let name = first.then(rest.repeated()).map(|(f, v)| { + let rest = String::from_iter(&v); + format!("{f}{rest}") + }); name } -pub fn variable_definition_parser() -> impl Parser> { +pub fn variable_definition_parser( + expression: impl AbstractParser, +) -> impl AbstractParser { name() - .padded() - .then_ignore(just(":")) - .padded() - .then(expression_parser()) + .then_ignore(just(":").padded()) + .then(expression) .map(|(name, value)| VarDef { name, value }) } -pub fn variable_assignement_parser() -> impl Parser> { - expression_parser().then() name() +#[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 expression_parser() -> impl Parser> { - let scope = scope_parser().map(|s| s.into()); - let litteral = literal_value().map(|v| Expr::new_literal(v)); - let variable_definition = variable_definition_parser().map(|s| s.into()); - let variable_assignment; - let variable_call; - let function_definition; - let function_call; - let function_return; - let loop_; - let loop_break; - let condition; - scope - .or(litteral) - .or(variable_definition) - .or(variable_assignment) - .or(variable_call) - .or(function_definition) - .or(function_call) - .or(function_return) - .or(loop_) - .or(loop_break) - .or(condition) +pub fn variable_assignement_parser( + expression: impl AbstractParser, +) -> impl AbstractParser { + name() + .then_ignore(just("<-").padded()) + .then(expression) + .map(|(name, value)| VarAssign { name, value }) } -pub fn parser() -> impl Parser> { - let scope = expression_parser().chain( - just(';') - .padded() - .ignore_then(expression_parser()) - .repeated(), - ); +#[test] +fn test_variable_assignement_parser() { + let parser = variable_assignement_parser(debugging_expression_parser()); + let value = parser.parse("arbre <- expr"); + dbg!(value.unwrap()); +} - let program = scope - .map(|instructions| { - let body = Scope { instructions }; - Program { body } +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, }) - .then_ignore(end().recover_with(skip_then_retry_until([]))); +} + +#[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(')'))) + .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.clone()).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.clone()) + .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(|v| Expr::new_literal(v)); + 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()); + let program = scope.map(|instructions| { + let body = Scope { instructions }; + Program { body } + }); program } -// impl Parser { -// pub fn parse(input: &str) -> Program { -// todo!() -// } -// } +#[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<'i>(&self, input: &'i str) -> Result>> { + self.inner.parse(input) + } +} /* // example @@ -137,7 +405,7 @@ loop { if a <= 0 { break true } else { - a - 1 -> a + a <- a - 1 } } diff --git a/src/value.rs b/src/value.rs index 2a82608..73b79e7 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; @@ -72,6 +72,41 @@ 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_object_mut(&mut self) -> Option<&mut HashMap> { + match self { + Self::Object(h) => Some(h), + _ => None, + } + } + pub fn as_function(&self) -> Option<&Function> { match self { Self::Function(function) => Some(function), From a32f62847ed26944babea82f3d3a840b26050d10 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Wed, 1 Jun 2022 03:43:00 +0300 Subject: [PATCH 04/10] disabled dead code --- src/syntax_tree/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syntax_tree/parser.rs b/src/syntax_tree/parser.rs index ad53f16..64075b6 100644 --- a/src/syntax_tree/parser.rs +++ b/src/syntax_tree/parser.rs @@ -215,7 +215,7 @@ fn test_condition_parser() { dbg!(value.unwrap()); } -fn sugar_parser(expression: impl AbstractParser + Clone) -> impl AbstractParser { +fn _sugar_parser(expression: impl AbstractParser + Clone) -> impl AbstractParser { let add = expression .clone() .then_ignore(just(" + ")) From dcf2bac9b6f0f7ac6d571364c9e8afe054c3cc84 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Wed, 1 Jun 2022 03:45:29 +0300 Subject: [PATCH 05/10] added cargo meta --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7e0c12a..b88c834 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,10 @@ name = "prout" version = "0.2.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/prout" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From fe99fddc2963943a41a0492012969d924c9f9fc4 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Wed, 1 Jun 2022 04:00:06 +0300 Subject: [PATCH 06/10] changed Object API --- src/prelude.rs | 8 ++++---- src/value.rs | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/prelude.rs b/src/prelude.rs index 5387ce4..433e268 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -128,12 +128,12 @@ fn obj(_: Vec) -> Value { Value::Object(HashMap::new()) } -fn set(mut args: Vec) -> Value { +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(); - let object = args.get_mut(0).unwrap().as_object_mut().unwrap(); - object.insert(name, value.clone()); - value + object.insert(name, value); + object.into() } fn get(args: Vec) -> Value { diff --git a/src/value.rs b/src/value.rs index 73b79e7..fdfe9a4 100644 --- a/src/value.rs +++ b/src/value.rs @@ -139,6 +139,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, From cefff00db3e0a889e43f5d5175f3f8a92b7ecfeb Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Wed, 1 Jun 2022 04:35:42 +0300 Subject: [PATCH 07/10] fixed function calls, added array example --- examples/array.pr | 68 ++++++++++++++++++++++++++++++++++++ src/execution_tree/parser.rs | 4 +-- src/prelude.rs | 6 +++- src/syntax_tree/parser.rs | 7 +++- src/value.rs | 7 ---- 5 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 examples/array.pr diff --git a/examples/array.pr b/examples/array.pr new file mode 100644 index 0000000..abd9097 --- /dev/null +++ b/examples/array.pr @@ -0,0 +1,68 @@ + +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)) +}; + +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); + + r: obj(); + r <- set(r, "tail", e); + r <- set(r, "rest", self); + r +}; + +array_print: (self) => { + l: array_len(self); + r: "["; + for(0, l, (i) => { + r <- add(r, array_get(self, i)); + r <- add(r, ", ") + }); + r <- add(r, "]"); + out(r) +}; + +main: () => { + a: array_new(); + a <- array_push(a, 1); + a <- array_push(a, 2); + a <- array_push(a, 3); + a <- array_push(a, 4); + array_print(a) +}; + +main(); diff --git a/src/execution_tree/parser.rs b/src/execution_tree/parser.rs index cf0c394..d7960c0 100644 --- a/src/execution_tree/parser.rs +++ b/src/execution_tree/parser.rs @@ -187,7 +187,7 @@ impl Parser { parameter_names, } = function_definition; - let parser_scope = parser_scope.make_child_common(); + let parser_scope = parser_scope.make_child_function(); let parameter_ids = parameter_names .into_iter() .map(|name| parser_scope.add_name(name)) @@ -209,7 +209,7 @@ impl Parser { let variable_id = parser_scope .get_variable_id(&name) - .expect("call of undeclared function"); + .expect(&format!("call of undeclared function '{name}'")); let parameters = arguments .into_iter() .map(|argument| self.parse_expression(argument, parser_scope)) diff --git a/src/prelude.rs b/src/prelude.rs index 433e268..a6a3a71 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -132,7 +132,11 @@ 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(); - object.insert(name, value); + if let Value::Bool(false) = value { + object.remove(&name); + } else { + object.insert(name, value); + } object.into() } diff --git a/src/syntax_tree/parser.rs b/src/syntax_tree/parser.rs index 64075b6..3ce7100 100644 --- a/src/syntax_tree/parser.rs +++ b/src/syntax_tree/parser.rs @@ -142,7 +142,12 @@ fn test_function_definition_parser() { 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(')'))) + .then( + parameters + .padded() + .delimited_by(just('('), just(')')) + .padded(), + ) .map(|(name, arguments)| FnCall { arguments, name }) } diff --git a/src/value.rs b/src/value.rs index fdfe9a4..463e766 100644 --- a/src/value.rs +++ b/src/value.rs @@ -100,13 +100,6 @@ impl Value { } } - pub fn as_object_mut(&mut self) -> Option<&mut HashMap> { - match self { - Self::Object(h) => Some(h), - _ => None, - } - } - pub fn as_function(&self) -> Option<&Function> { match self { Self::Function(function) => Some(function), From a4ff72d8ea4b6a8fea9c94aec58ea031c1c9fa66 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Wed, 1 Jun 2022 04:36:16 +0300 Subject: [PATCH 08/10] updated version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2445052..5dea35e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "prout" -version = "0.2.0" +version = "0.3.0" dependencies = [ "chumsky", ] diff --git a/Cargo.toml b/Cargo.toml index b88c834..be6f6a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prout" -version = "0.2.0" +version = "0.3.0" edition = "2021" authors = ["JOLIMAITRE Matthieu "] description = "A minimal programming language providing tools to automate file management like backups, building process or unit testing." From c08c263f322633aa4ed5bbf20add3d54ef43ea55 Mon Sep 17 00:00:00 2001 From: Jolimaitre Matthieu Date: Wed, 1 Jun 2022 05:33:52 +0300 Subject: [PATCH 09/10] added demo code --- src/demo.rs | 21 ++++++++++ src/execution_tree/parser.rs | 79 ++++++++++++++++++++---------------- src/main.rs | 1 + 3 files changed, 66 insertions(+), 35 deletions(-) create mode 100644 src/demo.rs 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 d7960c0..6d2e59a 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,39 +35,39 @@ 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(); @@ -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_function(); + 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, @@ -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,7 +387,7 @@ 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()), @@ -386,7 +395,7 @@ impl ParserScope { } } - 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,7 +405,7 @@ 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(), @@ -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 35335b5..6cb7da7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ pub mod prelude; pub mod runtime; pub mod syntax_tree; pub mod value; +pub mod demo; fn main() { use prelude::std_prelude; From 28377bd3c271e70afc27c297c19a28884d70d288 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Mon, 12 Jun 2023 01:02:07 +0200 Subject: [PATCH 10/10] various fixes --- Cargo.lock | 32 +++++++-------- Cargo.toml | 6 +-- README.md | 10 ++--- examples/array.pr | 77 +++++++++++++++++++++++++++++------- examples/hello-world.pr | 4 +- src/execution_tree/parser.rs | 16 ++++---- src/main.rs | 10 ++--- src/prelude.rs | 12 +++--- src/runtime.rs | 34 ++++++++++------ src/syntax_tree/parser.rs | 29 ++++++++------ src/value.rs | 6 +-- 11 files changed, 149 insertions(+), 87 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5dea35e..402a8a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "const-random" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4" +checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" dependencies = [ "const-random-macro", "proc-macro-hack", @@ -38,12 +38,12 @@ dependencies = [ [[package]] name = "const-random-macro" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40" +checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" dependencies = [ "getrandom", - "lazy_static", + "once_cell", "proc-macro-hack", "tiny-keccak", ] @@ -65,12 +65,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.126" @@ -78,18 +72,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] -name = "proc-macro-hack" -version = "0.5.19" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "prout" -version = "0.3.0" +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" diff --git a/Cargo.toml b/Cargo.toml index be6f6a9..ad121e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "prout" -version = "0.3.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/prout" +repository = "https://github.com/MajorBarnulf/porte" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 2e25b19..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,7 +17,7 @@ PROUT is a minimal programming language providing tools to automate file managem ```sh -$ prout hello-world.pr +$ porte hello-world.pr ``` diff --git a/examples/array.pr b/examples/array.pr index abd9097..2b65370 100644 --- a/examples/array.pr +++ b/examples/array.pr @@ -22,6 +22,13 @@ 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); @@ -38,31 +45,73 @@ array_pop: (self) => { 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", self); + 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: "["; + r: "[ "; for(0, l, (i) => { r <- add(r, array_get(self, i)); - r <- add(r, ", ") + if not(eq(i, sub(l, 1))) r <- add(r, ", ") }); - r <- add(r, "]"); + r <- add(r, " ]"); out(r) }; -main: () => { - a: array_new(); - a <- array_push(a, 1); - a <- array_push(a, 2); - a <- array_push(a, 3); - a <- array_push(a, 4); - array_print(a) -}; +"########################"; +"# main #"; +"########################"; -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 index c7862c1..0b3072d 100644 --- a/examples/hello-world.pr +++ b/examples/hello-world.pr @@ -1,10 +1,10 @@ hello: "hello"; -prout: "prout"; +porte: "porte"; line: hello; line <- add(line, " "); -line <- add(line, prout); +line <- add(line, porte); say-it: () => { out(line) diff --git a/src/execution_tree/parser.rs b/src/execution_tree/parser.rs index 6d2e59a..c504ac3 100644 --- a/src/execution_tree/parser.rs +++ b/src/execution_tree/parser.rs @@ -73,7 +73,7 @@ impl Parser { 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(); @@ -218,7 +218,7 @@ impl Parser { let variable_id = parser_scope .get_variable_id(&name) - .expect(&format!("call of undeclared function '{name}'")); + .unwrap_or_else(|| panic!("call of undeclared function '{name}'")); let parameters = arguments .into_iter() .map(|argument| self.parse_expression(argument, parser_scope)) @@ -390,7 +390,7 @@ impl ParserScope { 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, } } @@ -409,24 +409,24 @@ impl ParserScope { 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 { diff --git a/src/main.rs b/src/main.rs index 6cb7da7..bedf467 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,20 @@ use std::{env::args, fs}; +pub mod demo; pub mod execution_tree; pub mod prelude; pub mod runtime; pub mod syntax_tree; pub mod value; -pub mod demo; fn main() { use prelude::std_prelude; - let path = args().nth(1).expect("[error]: usage 'prout '"); + 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, |b| std_prelude(b)); + let executable = execution_tree::parser::Parser::parse(parsed, std_prelude); let mut runtime = runtime::Runtime::new(); runtime.execute(&executable); } @@ -40,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 a6a3a71..0869c9e 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -6,7 +6,8 @@ use crate::{ }; pub fn std_prelude(builder: &mut ParserBuilder) { - let functions: Vec<(_, _, fn(Vec) -> Value)> = vec![ + type FunctOper = fn(Vec) -> Value; + let functions: Vec<(_, _, FunctOper)> = vec![ ("out", 1, out), ("add", 2, add), ("sub", 2, sub), @@ -42,7 +43,7 @@ fn add(args: Vec) -> Value { (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(), - _ => unreachable!(), + _ => panic!("adding incompatible types"), } } @@ -62,7 +63,7 @@ fn eq(args: Vec) -> Value { (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(), - _ => panic!("comparing different types"), + _ => false.into(), } } @@ -143,8 +144,5 @@ fn set(args: Vec) -> Value { 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) - .map(|value| value.clone()) - .unwrap_or_else(|| false.into()) + object.get(name).cloned().unwrap_or_else(|| false.into()) } diff --git a/src/runtime.rs b/src/runtime.rs index 4ee2cf8..22df2d4 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -24,7 +24,7 @@ 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); } } @@ -52,7 +52,7 @@ 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, 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/parser.rs b/src/syntax_tree/parser.rs index 3ce7100..fbdb93f 100644 --- a/src/syntax_tree/parser.rs +++ b/src/syntax_tree/parser.rs @@ -52,8 +52,7 @@ pub fn literal_value() -> impl AbstractParser { .unwrapped() .map(|n: f64| n.into()); - let literal = bool.or(none).or(string).or(number); - literal + bool.or(none).or(string).or(number) } #[test] @@ -66,11 +65,11 @@ fn test_literal_value() { pub fn name() -> impl AbstractParser + Clone { let first = one_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); let rest = first.clone().or(one_of("1234567890-_+/*")); - let name = first.then(rest.repeated()).map(|(f, v)| { + + first.then(rest.repeated()).map(|(f, v)| { let rest = String::from_iter(&v); format!("{f}{rest}") - }); - name + }) } pub fn variable_definition_parser( @@ -205,7 +204,7 @@ pub fn condition_parser( just("if ") .ignore_then(expression.clone()) .then(expression.clone()) - .then(just("else ").ignore_then(expression.clone()).or_not()) + .then(just("else ").ignore_then(expression).or_not()) .map(|((condition, arm_true), arm_false)| Cond { condition, arm_true, @@ -296,7 +295,7 @@ fn _sugar_parser(expression: impl AbstractParser + Clone) -> impl Abstract let and = expression .clone() .then_ignore(just(" && ")) - .then(expression.clone()) + .then(expression) .map(|(l, r)| { FnCall { name: "and".into(), @@ -317,7 +316,7 @@ pub fn expression_parser() -> impl AbstractParser { 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(|v| Expr::new_literal(v)); + 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()); @@ -354,11 +353,11 @@ fn parser() -> impl AbstractParser { .separated_by(just(';').padded()) .then_ignore(just(';').padded().or_not()) .then_ignore(end()); - let program = scope.map(|instructions| { + + scope.map(|instructions| { let body = Scope { instructions }; Program { body } - }); - program + }) } #[test] @@ -390,11 +389,17 @@ impl ParserWrapper { ParserWrapper { inner } } - pub fn parse<'i>(&self, input: &'i str) -> Result>> { + pub fn parse(&self, input: &str) -> Result>> { self.inner.parse(input) } } +impl Default for ParserWrapper { + fn default() -> Self { + Self::new() + } +} + /* // example diff --git a/src/value.rs b/src/value.rs index 463e766..7f4b424 100644 --- a/src/value.rs +++ b/src/value.rs @@ -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) } } }