diff --git a/Cargo.lock b/Cargo.lock index 73c4047..994ba3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,181 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + [[package]] name = "sunflower" version = "0.1.0" +dependencies = [ + "pest", + "pest_derive", +] + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" diff --git a/Cargo.toml b/Cargo.toml index 1f06f15..1957222 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +pest = "2.1.3" +pest_derive = "2.1.0" diff --git a/src/env.rs b/src/env.rs index fb27045..68f989d 100644 --- a/src/env.rs +++ b/src/env.rs @@ -26,6 +26,10 @@ impl Env<'_> { } } + pub fn set_here(&self, name: &str, value: Value) { + self.values.write().unwrap().insert(name.to_string(), value); + } + pub fn set(&self, name: &str, value: impl Into) { let value = value.into(); if !self.set_recursive(name, &value) { diff --git a/src/expression.rs b/src/expression.rs index 753b1a9..8da50ab 100644 --- a/src/expression.rs +++ b/src/expression.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use crate::{env::Env, function::FunctionMap, value::Value}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -6,6 +8,7 @@ pub enum BinaryOperation { Sub, Mul, Div, + Mod, LessThan, GreaterThan, LessThanOrEqual, @@ -14,6 +17,27 @@ pub enum BinaryOperation { NotEqual, } +impl FromStr for BinaryOperation { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "+" => Ok(BinaryOperation::Add), + "-" => Ok(BinaryOperation::Sub), + "*" => Ok(BinaryOperation::Mul), + "/" => Ok(BinaryOperation::Div), + "%" => Ok(BinaryOperation::Mod), + "<" => Ok(BinaryOperation::LessThan), + ">" => Ok(BinaryOperation::GreaterThan), + "<=" => Ok(BinaryOperation::LessThanOrEqual), + ">=" => Ok(BinaryOperation::GreaterThanOrEqual), + "==" => Ok(BinaryOperation::Equal), + "!=" => Ok(BinaryOperation::NotEqual), + _ => Err("Invalid binary operation"), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expression { Constant(Value), @@ -27,9 +51,13 @@ impl Expression { match self { Expression::Constant(v) => v.clone(), Expression::Variable(name) => e.get(name).unwrap_or_default(), - Expression::Call(name, args) => { - f.call(name, e, &args.iter().map(|ex| ex.eval(e, f)).collect::>()).unwrap() - }, + Expression::Call(name, args) => f + .call( + name, + e, + &args.iter().map(|ex| ex.eval(e, f)).collect::>(), + ) + .unwrap(), Expression::Binary(lhs, op, rhs) => { let lhs = lhs.eval(e, f); let rhs = rhs.eval(e, f); @@ -38,6 +66,7 @@ impl Expression { BinaryOperation::Sub => lhs - rhs, BinaryOperation::Mul => lhs * rhs, BinaryOperation::Div => lhs / rhs, + BinaryOperation::Mod => lhs % rhs, BinaryOperation::LessThan => (lhs < rhs).into(), BinaryOperation::GreaterThan => (lhs > rhs).into(), BinaryOperation::LessThanOrEqual => (lhs <= rhs).into(), diff --git a/src/grammar.pest b/src/grammar.pest new file mode 100644 index 0000000..0587eba --- /dev/null +++ b/src/grammar.pest @@ -0,0 +1,77 @@ +WHITESPACE = _{ " " | "\t" | NEWLINE } + +number = @{ + "-"? + ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) + ~ ("." ~ ASCII_DIGIT*)? + ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? +} + +string = ${ "\"" ~ inner ~ "\"" } + +inner = @{ character* } + +character = { + !("\"" | "\\") ~ ANY + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) +} + +char = ${ "\'" ~ character ~ "\'" } + +boolean = { "true" | "false" } + +identifier = @{ ASCII_ALPHA ~ ASCII_ALPHANUMERIC* } + +call = ${ + identifier ~ call_params +} + +call_params = !{ "(" ~ (expression ~ ","?)* ~ ")" } + +parens = { "(" ~ expression ~ ")" } + +const_value = { number | string | char | boolean } + +value = { const_value | call | identifier | parens } + +expression = { equality } + +equality = { inequality ~ ((equals | not_equals) ~ inequality)? } + +inequality = { sum ~ ((less_than | greater_than | less_than_or_equal | greater_than_or_equal) ~ sum)? } + +sum = { product ~ ((plus | minus) ~ product)* } + +product = { value ~ ((times | divide | modulo) ~ value)* } + +root_statement = { + SOI ~ statement* ~ EOI +} + +statement = { ( assignment | assignment_let | expression ) ~ ";" | while_block | if_block | block } + +block = { "{" ~ statement* ~ "}" } + +assignment_let = { "let" ~ identifier ~ "=" ~ expression } +assignment = { identifier ~ "=" ~ expression } + + +while_block = { "while" ~ expression ~ block } + +if_block = { "if" ~ expression ~ block ~ ("else if" ~ expression ~ block)* ~ ("else" ~ block)? } + +equals = { "==" } +not_equals = { "!=" } + +less_than = { "<" } +greater_than = { ">" } +less_than_or_equal = { "<=" } +greater_than_or_equal = { ">=" } + +plus = { "+" } +minus = { "-" } + +times = { "*" } +divide = { "/" } +modulo = { "%" } diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..b517fd7 --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,200 @@ +use std::hash::Hash; + +use chumsky::{ + prelude::{choice, filter, just, Simple}, + text::{self, TextParser}, + Parser, +}; + +#[derive(Debug, Clone, PartialEq)] +pub enum Token { + POpen, + PClose, + SOpen, + SClose, + COpen, + CClose, + Comma, + Semicolon, + KeyWhile, + KeyReturn, + KeyLet, + Assign, + Operator(String), + Identifier(String), + ConstInt(i32), + ConstFloat(f32), + ConstString(String), + ConstBool(bool), + ConstChar(char), +} + +impl Hash for Token { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + } +} + +impl Eq for Token {} + +fn lexer() -> impl Parser, Error = Simple> { + let escape = just('\\').ignore_then( + just('\\') + .or(just('/')) + .or(just('"')) + .or(just('b').to('\x08')) + .or(just('f').to('\x0C')) + .or(just('n').to('\n')) + .or(just('r').to('\r')) + .or(just('t').to('\t')), + ); + + let string = just('"') + .ignore_then(filter(|c| *c != '\\' && *c != '"').or(escape).repeated()) + .then_ignore(just('"')) + .collect::() + .map(Token::ConstString); + + let char = just('\'') + .ignore_then(chumsky::prelude::any()) + .then_ignore(just('\'')) + .map(Token::ConstChar); + + let number = just('-') + .or_not() + .chain::(text::int(10)) + .collect::() + .from_str() + .unwrapped() + .map(Token::ConstInt); + + let float = just('-') + .or_not() + .chain(text::int(10)) + .chain::(just('.').chain(text::digits(10))) + .collect::() + .from_str() + .unwrapped() + .map(Token::ConstFloat); + + let op = just("+") + .or(just("-")) + .or(just("*")) + .or(just("/")) + .or(just("<")) + .or(just(">")) + .or(just("<=")) + .or(just(">=")) + .or(just("==")) + .or(just("!=")) + .map(|op| Token::Operator(op.to_string())); + + choice(( + just("(").to(Token::POpen), + just(")").to(Token::PClose), + just("[").to(Token::SOpen), + just("]").to(Token::SClose), + just("{").to(Token::COpen), + just("}").to(Token::CClose), + just(",").to(Token::Comma), + just(";").to(Token::Semicolon), + just("=").to(Token::Assign), + op, + text::keyword("while").to(Token::KeyWhile), + text::keyword("return").to(Token::KeyReturn), + text::keyword("let").to(Token::KeyLet), + text::keyword("true").to(Token::ConstBool(true)), + text::keyword("false").to(Token::ConstBool(false)), + text::ident().map(Token::Identifier), + float, + number, + string, + char, + )) + .padded() + .repeated() +} + +#[cfg(test)] +mod tests { + use chumsky::Parser; + + use crate::lexer::Token; + + use super::lexer; + + #[test] + fn parse_string() { + let x = "\"hello\""; + let tokens = lexer().parse(x).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!(tokens[0], Token::ConstString("hello".to_string())); + } + + #[test] + fn parse_char() { + let x = "'a'"; + let tokens = lexer().parse(x).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!(tokens[0], Token::ConstChar('a')); + } + + #[test] + fn parse_int() { + let x = "123"; + let tokens = lexer().parse(x).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!(tokens[0], Token::ConstInt(123)); + } + + #[test] + fn parse_float() { + let x = "123.456"; + let tokens = lexer().parse(x).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!(tokens[0], Token::ConstFloat(123.456)); + } + + #[test] + fn parse_bool() { + let x = "true"; + let tokens = lexer().parse(x).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!(tokens[0], Token::ConstBool(true)); + } + + #[test] + fn parse_example_program() { + let x = r" + let x = 10; + let y = 200; + let z = x + y; + print(z); + "; + + let tokens = lexer().parse(x).unwrap(); + assert_eq!(tokens.len(), 22); + assert_eq!(tokens[0], Token::KeyLet); + assert_eq!(tokens[1], Token::Identifier("x".to_string())); + assert_eq!(tokens[2], Token::Assign); + assert_eq!(tokens[3], Token::ConstInt(10)); + assert_eq!(tokens[4], Token::Semicolon); + assert_eq!(tokens[5], Token::KeyLet); + assert_eq!(tokens[6], Token::Identifier("y".to_string())); + assert_eq!(tokens[7], Token::Assign); + assert_eq!(tokens[8], Token::ConstInt(200)); + assert_eq!(tokens[9], Token::Semicolon); + assert_eq!(tokens[10], Token::KeyLet); + assert_eq!(tokens[11], Token::Identifier("z".to_string())); + assert_eq!(tokens[12], Token::Assign); + assert_eq!(tokens[13], Token::Identifier("x".to_string())); + assert_eq!(tokens[14], Token::Operator("+".to_string())); + assert_eq!(tokens[15], Token::Identifier("y".to_string())); + assert_eq!(tokens[16], Token::Semicolon); + assert_eq!(tokens[17], Token::Identifier("print".to_string())); + assert_eq!(tokens[18], Token::POpen); + assert_eq!(tokens[19], Token::Identifier("z".to_string())); + assert_eq!(tokens[20], Token::PClose); + assert_eq!(tokens[21], Token::Semicolon); + } +} diff --git a/src/main.rs b/src/main.rs index bdfc116..daea01e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ +use std::fs::read_to_string; + use env::Env; use expression::{BinaryOperation, Expression}; use function::{Function, FunctionMap, NativeFunction}; +use pest::Parser; use statement::Statement; use value::Value; @@ -10,6 +13,9 @@ mod function; mod macros; mod statement; mod value; +// mod lexer; +// mod parser; +mod pest_parser; fn run(functions: FunctionMap, statements: &[Statement]) { let env = Env::new(); @@ -19,107 +25,117 @@ fn run(functions: FunctionMap, statements: &[Statement]) { } fn main() { - let statements = vec![ - Statement::Assignment("a".to_string(), Expression::from(1)), - Statement::Assignment("b".to_string(), Expression::from(2)), - Statement::Assignment("c".to_string(), Expression::from(3)), - Statement::Print(Expression::Binary( - Box::new(Expression::Variable("a".to_string())), - BinaryOperation::Add, - Box::new(Expression::Variable("b".to_string())), - )), - Statement::Print(Expression::Binary( - Box::new(Expression::Variable("a".to_string())), - BinaryOperation::Add, - Box::new(Expression::Variable("c".to_string())), - )), - Statement::Print(Expression::Binary( - Box::new(Expression::Variable("b".to_string())), - BinaryOperation::Add, - Box::new(Expression::Variable("c".to_string())), - )), - ]; - run(FunctionMap::new(), &statements); + // let statements = vec![ + // Statement::Assignment("a".to_string(), Expression::from(1)), + // Statement::Assignment("b".to_string(), Expression::from(2)), + // Statement::Assignment("c".to_string(), Expression::from(3)), + // Statement::Print(Expression::Binary( + // Box::new(Expression::Variable("a".to_string())), + // BinaryOperation::Add, + // Box::new(Expression::Variable("b".to_string())), + // )), + // Statement::Print(Expression::Binary( + // Box::new(Expression::Variable("a".to_string())), + // BinaryOperation::Add, + // Box::new(Expression::Variable("c".to_string())), + // )), + // Statement::Print(Expression::Binary( + // Box::new(Expression::Variable("b".to_string())), + // BinaryOperation::Add, + // Box::new(Expression::Variable("c".to_string())), + // )), + // ]; + // run(FunctionMap::new(), &statements); - let while_test = vec![ - Statement::Print("while test".to_string().into()), - Statement::Assignment("a".to_string(), Expression::from(1)), - Statement::While( - Expression::Binary( - Box::new(Expression::Variable("a".to_string())), - BinaryOperation::LessThan, - Box::new(10.into()), - ), - vec![ - Statement::Assignment( - "a".to_string(), - Expression::Binary( - Box::new(Expression::Variable("a".to_string())), - BinaryOperation::Add, - Box::new(1.into()), - ), - ), - Statement::Print(Expression::Variable("a".to_string())), - ], - ), - ]; - run(FunctionMap::new(), &while_test); + // let while_test = vec![ + // Statement::Print("while test".to_string().into()), + // Statement::Assignment("a".to_string(), Expression::from(1)), + // Statement::While( + // Expression::Binary( + // Box::new(Expression::Variable("a".to_string())), + // BinaryOperation::LessThan, + // Box::new(10.into()), + // ), + // vec![ + // Statement::Assignment( + // "a".to_string(), + // Expression::Binary( + // Box::new(Expression::Variable("a".to_string())), + // BinaryOperation::Add, + // Box::new(1.into()), + // ), + // ), + // Statement::Print(Expression::Variable("a".to_string())), + // ], + // ), + // ]; + // run(FunctionMap::new(), &while_test); - { - let env = Env::new(); + // { + // let env = Env::new(); - let res = (|arg: &i32| arg + 1).native_execute(&env, &[Value::Int(1)]); - println!("{:?}", res); - } + // let res = (|arg: &i32| arg + 1).native_execute(&env, &[Value::Int(1)]); + // println!("{:?}", res); + // } - { - let z = || 100; - let env = Env::new(); - let res = z.native_execute(&env, &[]); - println!("{:?}", res); - } + // { + // let z = || 100; + // let env = Env::new(); + // let res = z.native_execute(&env, &[]); + // println!("{:?}", res); + // } - { - let z = |a: &Value| a.clone(); - let env = Env::new(); - let res = z.into_function().execute(&env, &FunctionMap::new(), &[]); - println!("{:?}", res); - } + // { + // let z = |a: &Value| a.clone(); + // let env = Env::new(); + // let res = z.into_function().execute(&env, &FunctionMap::new(), &[]); + // println!("{:?}", res); + // } - { - let z = |a: &String| a.clone(); - let env = Env::new(); - let res = z.native_execute(&env, &[100.into()]); - println!("{:?}", res); - } + // { + // let z = |a: &String| a.clone(); + // let env = Env::new(); + // let res = z.native_execute(&env, &[100.into()]); + // println!("{:?}", res); + // } - { - let v: Vec> = vec![ - Box::new((|| 100).into_function()), - Box::new((|a: &i32| a + 1).into_function()), - ]; - for f in v { - let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]); - println!("{:?}", r); - } - } + // { + // let v: Vec> = vec![ + // Box::new((|| 100).into_function()), + // Box::new((|a: &i32| a + 1).into_function()), + // ]; + // for f in v { + // let r = f.execute(&Env::new(), &FunctionMap::new(), &[100.into()]); + // println!("{:?}", r); + // } + // } + // let mut m: FunctionMap = Default::default(); + // m.insert_native("print", |a: &Value| { + // println!("{}", a); + // }); + + // m.insert_native("waluigi", || "Waluigi"); + + // m.insert_interpreter("call_print", vec!["what".to_string()], vec![ + // Statement::Expression(Expression::Call("print".to_string(), vec![Expression::Variable("what".to_string())])), + // ]); + + // run( + // m, + // &vec![Statement::Expression(Expression::Call( + // "call_print".to_string(), + // vec![Expression::Call("waluigi".to_string(), vec![])], + // ))], + // ); + + let f = read_to_string("./test.foo").unwrap(); let mut m: FunctionMap = Default::default(); m.insert_native("print", |a: &Value| { println!("{}", a); }); - m.insert_native("waluigi", || "Waluigi"); - - m.insert_interpreter("call_print", vec!["what".to_string()], vec![ - Statement::Expression(Expression::Call("print".to_string(), vec![Expression::Variable("what".to_string())])), - ]); - - run( - m, - &vec![Statement::Expression(Expression::Call( - "call_print".to_string(), - vec![Expression::Call("waluigi".to_string(), vec![])], - ))], - ); + let res = pest_parser::parse(f); + let env = &Env::new(); + res.eval(env, &m); } diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..f1f2afc --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,88 @@ +use chumsky::{prelude::*, Parser}; + +use crate::{ + expression::{BinaryOperation, Expression}, + lexer::Token, + statement::Statement, + value::Value, +}; + +fn parser() -> impl Parser, Error = Simple> { + let operator = filter_map(|span, a| match a { + Token::Operator(ref op) => match op.as_str() { + "+" => Ok(BinaryOperation::Add), + "-" => Ok(BinaryOperation::Sub), + "*" => Ok(BinaryOperation::Mul), + "/" => Ok(BinaryOperation::Div), + "<" => Ok(BinaryOperation::LessThan), + ">" => Ok(BinaryOperation::GreaterThan), + "<=" => Ok(BinaryOperation::LessThanOrEqual), + ">=" => Ok(BinaryOperation::GreaterThanOrEqual), + "==" => Ok(BinaryOperation::Equal), + "!=" => Ok(BinaryOperation::NotEqual), + _ => Err(Simple::expected_input_found(span, None, Some(a))), + }, + _ => Err(Simple::expected_input_found(span, None, Some(a))), + }); + let tokens = filter_map(|span, a| match a { + Token::ConstInt(i) => Ok(Expression::Constant(Value::Int(i))), + Token::ConstFloat(f) => Ok(Expression::Constant(Value::Float(f))), + Token::ConstString(s) => Ok(Expression::Constant(Value::String(s))), + Token::ConstBool(b) => Ok(Expression::Constant(Value::Bool(b))), + Token::ConstChar(c) => Ok(Expression::Constant(Value::Char(c))), + _ => Err(chumsky::error::Error::expected_input_found( + span, + None, + Some(a), + )), + }); + + let expr = recursive(|rec| { + choice(( + tokens + .then(operator) + .then(rec) + .map(|((a, b), c)| Expression::Binary(Box::new(a), b, Box::new(c))), + tokens, + )) + }); + let val = expr.map(Statement::Expression); + val.repeated() +} + +#[cfg(test)] +mod tests { + use chumsky::Parser; + + use crate::{ + expression::{BinaryOperation, Expression}, + lexer::Token, + parser::parser, + statement::Statement, + value::Value, + }; + + #[test] + fn parse_int() { + let input = vec![Token::ConstInt(10)]; + let expected = vec![Statement::Expression(Expression::Constant(Value::Int(10)))]; + let result = parser().parse(input).unwrap(); + assert_eq!(result, expected); + } + + #[test] + fn parse_10_plus_10() { + let input = vec![ + Token::ConstInt(10), + Token::Operator("+".into()), + Token::ConstInt(10), + ]; + let expected = vec![Statement::Expression(Expression::Binary( + Box::new(Expression::Constant(Value::Int(10))), + BinaryOperation::Add, + Box::new(Expression::Constant(Value::Int(10))), + ))]; + let result = parser().parse(input).unwrap(); + assert_eq!(result, expected); + } +} diff --git a/src/pest_parser.rs b/src/pest_parser.rs new file mode 100644 index 0000000..5c3758c --- /dev/null +++ b/src/pest_parser.rs @@ -0,0 +1,221 @@ +use pest::{iterators::Pair, Parser}; +use pest_derive::Parser; + +use crate::{ + expression::{BinaryOperation, Expression}, + statement::Statement, + value::Value, +}; + +#[derive(Parser)] +#[grammar = "grammar.pest"] +pub struct LangParser; + +pub fn parse(source: S) -> Statement { + let source = source.to_string(); + let pairs = LangParser::parse(Rule::root_statement, &source).unwrap(); + handle_rules(pairs) +} + +pub fn parser_test() { + // let p = LangParser::parse(Rule::number, "-273.15").unwrap(); + // println!("{:?}", p); + // let p = LangParser::parse(Rule::number, "25565"); + // println!("{:?}", p); + + // let str_test = LangParser::parse(Rule::string, "\"hello\"").unwrap(); + // println!("{:?}", str_test); + + // let str_test = LangParser::parse(Rule::char, "'a'").unwrap(); + // println!("{:?}", str_test); + + // let foo = LangParser::parse(Rule::statement, "10 + foo + bar(20) * 40;").unwrap(); + // println!("{:?}", foo); + + // let bar = " + // let x = 10; + // let y = 20; + // let z = x + y; + // print(z); + // while (x < y) { + // x = x + 1; + // } + // let x = x + \"lmao\"; + // "; + // let bar = LangParser::parse(Rule::root_statement, bar); + // println!("{:?}", bar); + + let baz = " + if x { + a; + } else if y { + b; + } else { + c; + }"; + + let baz = LangParser::parse(Rule::root_statement, baz); + println!("{:?}", baz); + + let x = "x == 10;"; + let x = LangParser::parse(Rule::statement, x); + handle_rules(x.unwrap()); +} +fn handle_rules(mut p: pest::iterators::Pairs) -> Statement { + fn parse_statement(pair: Pair) -> Statement { + match pair.as_rule() { + Rule::root_statement => { + Statement::Body(pair.into_inner().map(parse_statement).collect()) + } + Rule::while_block => { + let mut inner = pair.into_inner(); + let expr = inner.next().unwrap(); + let body = inner.next().unwrap(); + Statement::While(parse_expression(expr), Box::new(parse_statement(body))) + } + Rule::if_block => { + let mut inner = pair.into_inner().into_iter(); + let mut the_list = vec![]; + let mut the_else = None; + while let Some(expr) = inner.next() { + if expr.as_rule() == Rule::expression { + let body = inner.next().unwrap(); + the_list.push((parse_expression(expr), parse_statement(body))); + } else { + the_else = Some(Box::new(parse_statement(expr))); + } + } + Statement::If(the_list, the_else) + } + Rule::assignment => { + let mut inner = pair.into_inner(); + let name = inner.next().unwrap(); + let expr = inner.next().unwrap(); + Statement::Assignment(name.as_str().to_string(), parse_expression(expr), false) + } + Rule::assignment_let => { + let mut inner = pair.into_inner(); + let name = inner.next().unwrap(); + let expr = inner.next().unwrap(); + Statement::Assignment(name.as_str().to_string(), parse_expression(expr), true) + } + Rule::expression => Statement::Expression(parse_expression(pair)), + Rule::statement => { + let inner = pair.into_inner().next().unwrap(); + parse_statement(inner) + } + Rule::EOI => Statement::Body(vec![]), + x => unreachable!("How did we get to {:?}", x), + } + } + + fn parse_expression(pair: Pair) -> Expression { + match pair.as_rule() { + Rule::expression => { + let mut inner = pair.into_inner(); + parse_expression(inner.next().unwrap()) + } + Rule::equality => { + let mut inner = pair.into_inner(); + let lhs = parse_expression(inner.next().unwrap()); + if let Some(op) = inner.next() { + let rhs = parse_expression(inner.next().unwrap()); + Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs)) + } else { + lhs + } + } + Rule::inequality => { + let mut inner = pair.into_inner(); + let lhs = parse_expression(inner.next().unwrap()); + if let Some(op) = inner.next() { + let rhs = parse_expression(inner.next().unwrap()); + Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs)) + } else { + lhs + } + } + Rule::sum => { + let mut inner = pair.into_inner(); + let lhs = parse_expression(inner.next().unwrap()); + if let Some(op) = inner.next() { + let rhs = parse_expression(inner.next().unwrap()); + Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs)) + } else { + lhs + } + } + Rule::product => { + let mut inner = pair.into_inner(); + let lhs = parse_expression(inner.next().unwrap()); + if let Some(op) = inner.next() { + let rhs = parse_expression(inner.next().unwrap()); + Expression::Binary(Box::new(lhs), op.as_str().parse().unwrap(), Box::new(rhs)) + } else { + lhs + } + } + Rule::value => { + let inner = pair.into_inner().next().unwrap(); + parse_value(inner) + } + _ => unreachable!(), + } + } + + fn parse_value(pair: Pair) -> Expression { + match pair.as_rule() { + Rule::const_value => { + Expression::Constant(parse_const_value(pair.into_inner().next().unwrap())) + } + Rule::identifier => Expression::Variable(pair.as_str().to_string()), + Rule::call => { + let mut inner = pair.into_inner(); + let name = inner.next().unwrap(); + let args = inner + .next() + .unwrap() + .into_inner() + .map(parse_expression) + .collect(); + Expression::Call(name.as_str().to_string(), args) + } + Rule::parens => { + let mut inner = pair.into_inner(); + parse_expression(inner.next().unwrap()) + } + _ => unreachable!(), + } + } + + fn parse_const_value(pair: Pair) -> Value { + match pair.as_rule() { + Rule::number => { + if let Ok(f) = pair.as_str().parse::() { + Value::Int(f) + } else if let Ok(f) = pair.as_str().parse::() { + Value::Float(f) + } else { + unreachable!() + } + } + Rule::string => Value::String(pair.as_str().to_string()), + Rule::boolean => Value::Bool(pair.as_str() == "true"), + Rule::char => Value::Char(pair.as_str().chars().next().unwrap()), + _ => unreachable!(), + } + } + + let z = p.next().unwrap(); + parse_statement(z) +} + +#[cfg(test)] +mod tests { + use pest::Parser; + + use super::{LangParser, Rule}; + + #[test] + fn foo() {} +} diff --git a/src/statement.rs b/src/statement.rs index 8c142ef..105ad64 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -1,34 +1,55 @@ use crate::{env::Env, expression::Expression, function::FunctionMap}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Statement { - Assignment(String, Expression), + Assignment(String, Expression, bool), Expression(Expression), - Print(Expression), - While(Expression, Vec), + While(Expression, Box), + If(Vec<(Expression, Statement)>, Option>), + Body(Vec), } impl Statement { pub fn eval(&self, env: &Env, f: &FunctionMap) { match self { - Statement::Assignment(name, expr) => { + Statement::Assignment(name, expr, explicit_let) => { let value = expr.eval(env, f); - env.set(name, value); + if *explicit_let { + env.set_here(name, value); + } else { + env.set(name, value); + } } Statement::Expression(expr) => { expr.eval(env, f); } - Statement::Print(expr) => { - let value = expr.eval(env, f); - println!("{}", value); - } Statement::While(expr, body) => { while expr.eval(env, f).is_truthy() { let body_env = Env::new_with_parent(env); - for stmt in body.iter() { - stmt.eval(&body_env, f); + + body.eval(&body_env, f); + } + } + Statement::If(if_side, else_side) => { + let mut did_if = false; + for (expr, body) in if_side { + if expr.eval(env, f).is_truthy() { + body.eval(env, f); + did_if = true; + break; } } + if !did_if { + if let Some(else_side) = else_side { + else_side.eval(env, f); + } + } + } + Statement::Body(body) => { + let body_env = Env::new_with_parent(env); + for stmt in body.iter() { + stmt.eval(&body_env, f); + } } } } diff --git a/src/value.rs b/src/value.rs index ed2a80f..3271601 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,6 @@ use std::{ fmt::Display, - ops::{Add, Div, Mul, Sub}, + ops::{Add, Div, Mul, Sub, Rem}, }; #[derive(Debug, Clone, PartialEq, Default)] @@ -17,39 +17,6 @@ pub enum Value { impl Eq for Value {} -// impl Value { -// pub fn as_int(&self) -> Option { -// match self { -// Value::Int(v) => Some(*v), -// _ => None, -// } -// } -// pub fn as_float(&self) -> Option { -// match self { -// Value::Float(v) => Some(*v), -// _ => None, -// } -// } -// pub fn as_string(&self) -> Option { -// match self { -// Value::String(v) => Some(v.clone()), -// _ => None, -// } -// } -// pub fn as_bool(&self) -> Option { -// match self { -// Value::Bool(v) => Some(*v), -// _ => None, -// } -// } -// pub fn as_char(&self) -> Option { -// match self { -// Value::Char(v) => Some(*v), -// _ => None, -// } -// } -// } - impl Value { pub fn is_truthy(&self) -> bool { match self { @@ -95,6 +62,7 @@ math_trait!(Add, add); math_trait!(Sub, sub); math_trait!(Mul, mul); math_trait!(Div, div); +math_trait!(Rem, rem); impl Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/test.foo b/test.foo new file mode 100644 index 0000000..dc2c189 --- /dev/null +++ b/test.foo @@ -0,0 +1,4 @@ +let x = 10; +let y = x-5; + +print(y); \ No newline at end of file