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),