diff --git a/Cargo.lock b/Cargo.lock index bd89f8f..a5181d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "aho-corasick" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" -dependencies = [ - "memchr", -] +version = 3 [[package]] name = "base64" @@ -21,34 +14,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "leb128" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - [[package]] name = "nyst" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e268c6d6d5d5da20b69ab42e6ca2900ae75104db56d9fc4922a831499925c246" -dependencies = [ - "regex", - "ron", - "serde", -] +version = "0.5.2" [[package]] name = "pivot" @@ -78,24 +52,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" - [[package]] name = "ron" version = "0.6.2" @@ -138,15 +94,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - [[package]] name = "unicode-xid" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index c330522..9e08fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ repository = "https://github.com/garentyler/pivot" ron = "0.6.2" serde = "1.0.117" wat = "1.0.29" -nyst = "*" +nyst = { path = "../nyst" } diff --git a/out.log b/out.log new file mode 100644 index 0000000..abb3c49 --- /dev/null +++ b/out.log @@ -0,0 +1 @@ +tokens: [String("a"), Plus, String("b")] diff --git a/src/ast.rs b/src/ast.rs index 85f4b44..e25f4db 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,253 +1,195 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum AstNodeKind { +pub enum AstNode { // Primitives - Integer, - Identifier, + Integer(i32), + Identifier(String), + String(String), + Boolean(bool), // Unary operators - Not, + Not { + operand: Box, + }, // Infix operators - NotEqual, - Equal, - Add, - Subtract, - Multiply, - Divide, + Equal { + left: Box, + right: Box, + }, + Add { + left: Box, + right: Box, + }, + Subtract { + left: Box, + right: Box, + }, + Multiply { + left: Box, + right: Box, + }, + Divide { + left: Box, + right: Box, + }, + Assign { + left: Box, + right: Box, + }, // Control flow - Block, - IfStatement, - WhileLoop, - Program, + Block { + statements: Vec, + }, + If { + condition: Box, + consequence: Box, + alternative: Option>, + }, + While { + condition: Box, + body: Box, + }, // Functions and variables - FunctionCall, - FunctionReturn, - FunctionDefinition, - VariableDefinition, - VariableDeclaration, - Assign, - // Import - Import, - // Blank node + FunctionCall { + identifier: Box, + arguments: Vec, + }, + FunctionReturn { + value: Box, + }, + FunctionDefinition { + identifier: Box, + arguments: Vec, + body: Box, + }, + VariableDeclaration { + identifier: Box, + }, + // Other + Import { + identifier: Box, + }, Null, } - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct AstNode { - pub kind: AstNodeKind, - pub value: String, - pub subnodes: Vec, -} impl AstNode { - pub fn new(kind: AstNodeKind, value: String, subnodes: Vec) -> AstNode { - AstNode { - kind, - value, - subnodes, - } - } - // Primitives - pub fn integer(num: i64) -> AstNode { - AstNode { - kind: AstNodeKind::Integer, - value: num.to_string(), - subnodes: vec![], - } + pub fn integer(value: i32) -> AstNode { + AstNode::Integer(value) } - pub fn identifier(id: String) -> AstNode { - AstNode { - kind: AstNodeKind::Identifier, - value: id, - subnodes: vec![], - } + pub fn identifier(value: String) -> AstNode { + AstNode::Identifier(value) + } + pub fn string(value: String) -> AstNode { + AstNode::String(value) + } + pub fn boolean(value: bool) -> AstNode { + AstNode::Boolean(value) } // Unary operators pub fn not(operand: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Not, - value: "not".into(), - subnodes: vec![operand], + AstNode::Not { + operand: Box::new(operand), } } // Infix operators - pub fn not_equal(left: AstNode, right: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::NotEqual, - value: "not_equal".into(), - subnodes: vec![left, right], + pub fn equal(left: AstNode, right: AstNode) -> AstNode { + AstNode::Equal { + left: Box::new(left), + right: Box::new(right), } } - pub fn equal(left: AstNode, right: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Equal, - value: "equal".into(), - subnodes: vec![left, right], - } + pub fn not_equal(left: AstNode, right: AstNode) -> AstNode { + AstNode::not(AstNode::Equal { + left: Box::new(left), + right: Box::new(right), + }) } pub fn add(left: AstNode, right: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Add, - value: "add".into(), - subnodes: vec![left, right], + AstNode::Add { + left: Box::new(left), + right: Box::new(right), } } pub fn subtract(left: AstNode, right: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Subtract, - value: "subtract".into(), - subnodes: vec![left, right], + AstNode::Subtract { + left: Box::new(left), + right: Box::new(right), } } pub fn multiply(left: AstNode, right: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Multiply, - value: "multiply".into(), - subnodes: vec![left, right], + AstNode::Multiply { + left: Box::new(left), + right: Box::new(right), } } pub fn divide(left: AstNode, right: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Divide, - value: "divide".into(), - subnodes: vec![left, right], + AstNode::Divide { + left: Box::new(left), + right: Box::new(right), + } + } + pub fn assign(left: AstNode, right: AstNode) -> AstNode { + AstNode::Assign { + left: Box::new(left), + right: Box::new(right), } } // Control flow pub fn block(statements: Vec) -> AstNode { - AstNode { - kind: AstNodeKind::Block, - value: "block".into(), - subnodes: statements, - } + AstNode::Block { statements } } pub fn if_statement( - conditional: AstNode, + condition: AstNode, consequence: AstNode, - alternative: AstNode, + alternative: Option, ) -> AstNode { - AstNode { - kind: AstNodeKind::IfStatement, - value: "if_statement".into(), - subnodes: vec![conditional, consequence, alternative], + AstNode::If { + condition: Box::new(condition), + consequence: Box::new(consequence), + alternative: alternative.and_then(|alt| Some(Box::new(alt))), } } - pub fn while_loop(conditional: AstNode, body: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::WhileLoop, - value: "while_loop".into(), - subnodes: vec![conditional, body], - } - } - pub fn program(statements: Vec) -> AstNode { - AstNode { - kind: AstNodeKind::Program, - value: "program".into(), - subnodes: statements, + pub fn while_loop(condition: AstNode, body: AstNode) -> AstNode { + AstNode::While { + condition: Box::new(condition), + body: Box::new(body), } } // Functions and variables - pub fn function_call(name: String, parameters: Vec) -> AstNode { - AstNode { - kind: AstNodeKind::FunctionCall, - value: name, - subnodes: parameters, + pub fn function_call(identifier: AstNode, arguments: Vec) -> AstNode { + AstNode::FunctionCall { + identifier: Box::new(identifier), + arguments, } } - pub fn function_return(operand: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::FunctionReturn, - value: "return".into(), - subnodes: vec![operand], + pub fn function_return(value: AstNode) -> AstNode { + AstNode::FunctionReturn { + value: Box::new(value), } } - pub fn function_definition(name: String, parameters: Vec, body: AstNode) -> AstNode { - let mut params = vec![body]; - for p in parameters { - params.push(p); - } - AstNode { - kind: AstNodeKind::FunctionDefinition, - value: name, - subnodes: params, + pub fn function_definition( + identifier: AstNode, + arguments: Vec, + body: AstNode, + ) -> AstNode { + AstNode::FunctionDefinition { + identifier: Box::new(identifier), + arguments, + body: Box::new(body), } } - pub fn variable_definition(name: String, value: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::VariableDefinition, - value: name, - subnodes: vec![value], + pub fn variable_declaration(identifier: AstNode) -> AstNode { + AstNode::VariableDeclaration { + identifier: Box::new(identifier), } } - pub fn variable_declaration(name: String) -> AstNode { - AstNode { - kind: AstNodeKind::VariableDeclaration, - value: name, - subnodes: vec![], - } - } - pub fn assign(name: String, value: AstNode) -> AstNode { - AstNode { - kind: AstNodeKind::Assign, - value: name, - subnodes: vec![value], - } - } - // Import - pub fn import(num_args: AstNode, returns_value: AstNode, mut fn_path: Vec) -> AstNode { - let mut data = vec![num_args, returns_value]; - data.append(&mut fn_path); - AstNode { - kind: AstNodeKind::Import, - value: "import".into(), - subnodes: data, - } - } - // Blank node - pub fn null() -> AstNode { - AstNode { - kind: AstNodeKind::Null, - value: "".into(), - subnodes: vec![], - } - } - // Other - pub fn pretty_print(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result { - for _ in 0..indent { - write!(f, " ")?; + pub fn import(identifier: AstNode) -> AstNode { + AstNode::Import { + identifier: Box::new(identifier), } - write!(f, "{{\n")?; - for _ in 0..indent + 2 { - write!(f, " ")?; - } - write!(f, "kind: {:?}\n", self.kind)?; - for _ in 0..indent + 2 { - write!(f, " ")?; - } - write!(f, "value: {:?}\n", self.value)?; - if self.subnodes.len() > 0 { - for _ in 0..indent + 2 { - write!(f, " ")?; - } - write!(f, "subnodes: [\n")?; - for subnode in &self.subnodes { - subnode.pretty_print(f, indent + 4)?; - write!(f, ",\n")?; - } - for _ in 0..indent + 2 { - write!(f, " ")?; - } - write!(f, "]\n")?; - } - for _ in 0..indent { - write!(f, " ")?; - } - write!(f, "}}") } -} -impl std::fmt::Display for AstNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.pretty_print(f, 0) + pub fn null() -> AstNode { + AstNode::Null } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ae59715..8f51669 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,19 @@ pub mod ast; -pub mod codegen; +// pub mod codegen; pub mod parse; -use codegen::{SymbolGenerator, Wasm}; +// use codegen::{SymbolGenerator, Wasm}; -pub fn compile>(src: T) -> Vec { - wat::parse_str(compile_wat(src)).unwrap() +pub fn compile(source: &str) -> Vec { + wat::parse_str(compile_wat(source)).unwrap() } -pub fn compile_wat>(src: T) -> String { - let mut s = SymbolGenerator::new(); - let ast = parse::parse(src); - println!("{}", ast); - let wasm = ast.emit(&mut s); - println!("{}", wasm); - wasm +pub fn compile_wat(source: &str) -> String { + // let mut s = SymbolGenerator::new(); + let ast = parse::interpret(source); + // println!("{:?}", ast); + unimplemented!() + // let wasm = ast.emit(&mut s); + // println!("{}", wasm); + // wasm } diff --git a/src/main.rs b/src/main.rs index 4a85abb..cd7c8a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,11 +3,11 @@ use std::io::prelude::*; fn main() -> std::io::Result<()> { // Read the source from a file. - let mut src = String::new(); - File::open("test.pvt")?.read_to_string(&mut src)?; + let source = std::fs::read_to_string("test.pvt").unwrap(); // Compile it - let binary = pivot::compile(src); + let _value = pivot::parse::interpret(&source); + // let binary = pivot::compile(&source); // Write it to a file. - File::create("out.wasm")?.write_all(&binary)?; + // File::create("out.wasm")?.write_all(&binary)?; Ok(()) } diff --git a/src/parse.rs b/src/parse.rs index f295c45..0231f0e 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,401 +1,381 @@ -use crate::ast::{AstNode, AstNodeKind}; -use nyst::Parser; -use ron::{from_str, to_string}; +#[derive(Clone, PartialEq, Debug)] +pub enum Token { + Integer(i64), + Float(f64), + String(String), + Boolean(bool), + Plus, + Minus, + Star, + Slash, + Bang, + Quote(char), + Parenthesis { closing: bool }, + Whitespace(String), + Unknown, +} -pub fn parse>(src: T) -> AstNode { - let src: String = src.into(); - let whitespace = Parser::regex(r"[ \n\r\t]+"); - let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]")); - let ignored = whitespace.or(comments).repeat_range(0..usize::MAX); - let statement = ignored - .optional() - .ignore() - .and(Parser::custom(parse_statement)); - let parse_program = statement - .clone() - .repeat_range(0..usize::MAX) - .map(|matched| { - let data = from_str::>(&matched)?; - let mut statements = vec![]; - for d in data { - statements.push(from_str::(&d)?); - } - Ok(to_string(&AstNode::program(statements))?) - }); - from_str::(&parse_program.parse(src).unwrap().0).unwrap() +#[derive(Debug)] +pub enum InterpreterError { + /// Error parsing source + ParseError(String), + /// Unexpected EOF + UnexpectedEOF, } -fn parse_statement(src: String) -> Result<(String, String), String> { - let whitespace = Parser::regex(r"[ \n\r\t]+"); - let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]")); - let ignored = whitespace.or(comments).repeat_range(0..usize::MAX); - // Token parser constructor. - let i = ignored.clone(); - let token = move |pattern: &str| i.clone().ignore().and(Parser::regex(pattern)); - // Token helper parsers. - let function = Parser::regex(r"function\b").or(ignored.clone()); - let return_token = token(r"return\b"); - let semicolon = token(r"[;]"); - let if_token = token(r"if\b"); - let else_token = token(r"else\b"); - let left_paren = token(r"[(]"); - let right_paren = token(r"[)]"); - let left_brace = token(r"[{]"); - let right_brace = token(r"[}]"); - let while_token = token(r"while\b"); - let var = token(r"var\b"); - let number = token(r"[0-9]+").map(|matched| { - Ok(to_string(&AstNode::integer( - matched.parse::().unwrap(), - ))?) - }); - let identifier = token(r"[a-zA-Z_][a-zA-Z0-9_]*") - .map(|matched| Ok(to_string(&AstNode::identifier(matched))?)); - let assign = - token(r"=").map(|_matched| Ok(to_string(&AstNode::assign("".into(), AstNode::null()))?)); - let comma = token(r"[,]"); - let period = token(r"[.]"); - let expression = Parser::custom(parse_expression); - let statement = Parser::custom(parse_statement); - let import = token(r"import\b"); - // Statement parsers. - let import_statement = import - .clone() - .ignore() - .and(identifier.clone()) - .and( - period - .clone() - .ignore() - .and(identifier.clone()) - .repeat_range(0..usize::MAX), - ) - .and(number.clone()) - .and(number.clone()) - .and(semicolon.clone().ignore()) - .map(|matched| { - let data = from_str::>(&matched)?; - let import_fn_returns_value = from_str::(&data[1])?; - let data = from_str::>(&data[0])?; - let num_import_fn_args = from_str::(&data[1])?; - let data = from_str::>(&data[0])?; - let mut import_fn_path = vec![]; - import_fn_path.push(from_str::(&data[0])?); - let data = from_str::>(&data[1])?; - for d in data { - import_fn_path.push(from_str::(&d)?); + +pub fn tokenize(source: &str) -> Result, InterpreterError> { + fn tokenize_number(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + for c in chars { + if !c.is_digit(10) && *c != '.' { + break; } - Ok(to_string(&AstNode::import( - num_import_fn_args, - import_fn_returns_value, - import_fn_path, - ))?) - }); - let return_statement = return_token - .clone() - .ignore() - .and(expression.clone()) - .and(semicolon.clone().ignore()) - .map(|matched| { - let data = from_str::(&matched)?; - Ok(to_string(&AstNode::function_return(data))?) - }); - let expression_statement = expression.clone().and(semicolon.clone().ignore()); - let if_statement = if_token - .clone() - .ignore() - .and( - left_paren - .clone() - .ignore() - .and(expression.clone()) - .and(right_paren.clone().ignore()), - ) - .and(statement.clone()) - .and( - else_token - .clone() - .ignore() - .and(statement.clone()) - .optional(), - ) - .map(|matched| { - let data = from_str::>(&matched)?; - let alternative = from_str::>(&data[1])?; - let alternative = match alternative.get(0) { - Some(s) => from_str::(&s)?, - None => AstNode::null(), + current_value.push(*c); + chars_consumed += 1; + } + if chars_consumed == 0 { + Err(()) + } else if current_value.contains(".") { + Ok((Token::Float(current_value.parse::().unwrap()), chars_consumed)) + } else { + Ok((Token::Integer(current_value.parse::().unwrap()), chars_consumed)) + } + } + fn tokenize_bool(chars: &[char]) -> Result<(Token, usize), ()> { + if chars.len() >= 5 && chars[0..5] == ['f', 'a', 'l', 's', 'e'] { + Ok((Token::Boolean(false), 5)) + } else if chars.len() >= 4 && chars[0..4] == ['t', 'r', 'u', 'e'] { + Ok((Token::Boolean(true), 4)) + } else { + Err(()) + } + } + fn tokenize_string(chars: &[char]) -> Result<(Token, usize), ()> { + let start_quote; + let mut current_value = String::new(); + let mut chars_consumed = 0; + fn is_quote(c: char) -> bool { + match c { + '\'' | '"' | '`' => true, + _ => false, + } + } + if is_quote(chars[chars_consumed]) { + start_quote = chars[chars_consumed]; + } else { + return Err(()); + } + chars_consumed += 1; + while chars_consumed < chars.len() && chars[chars_consumed] != start_quote { + current_value.push(chars[chars_consumed]); + chars_consumed += 1; + } + chars_consumed += 1; + Ok((Token::String(current_value), chars_consumed)) + } + fn tokenize_whitespace(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + for c in chars { + if !c.is_whitespace() { + break; + } + chars_consumed += 1; + current_value.push(*c); + } + if chars_consumed == 0 { + Err(()) + } else { + Ok((Token::Whitespace(current_value), chars_consumed)) + } + } + fn tokenize_operator(chars: &[char]) -> Result<(Token, usize), ()> { + if chars.is_empty() { + Err(()) + } else if chars[0] == '+' { + Ok((Token::Plus, 1)) + } else if chars[0] == '-' { + Ok((Token::Minus, 1)) + } else if chars[0] == '*' { + Ok((Token::Star, 1)) + } else if chars[0] == '/' { + Ok((Token::Slash, 1)) + } else if chars[0] == '!' { + Ok((Token::Bang, 1)) + } else { + Err(()) + } + } + + let source = source.chars().collect::>(); + let mut tokens = vec![]; + let mut index = 0; + while index < source.len() { + if let Ok((_whitespace, chars_consumed)) = tokenize_whitespace(&source[index..]) { + // Ignore whitespace + index += chars_consumed; + } else if let Ok((num, chars_consumed)) = tokenize_number(&source[index..]) { + tokens.push(num); + index += chars_consumed; + } else if let Ok((num, chars_consumed)) = tokenize_string(&source[index..]) { + tokens.push(num); + index += chars_consumed; + } else if let Ok((num, chars_consumed)) = tokenize_bool(&source[index..]) { + tokens.push(num); + index += chars_consumed; + } else if let Ok((operator, chars_consumed)) = tokenize_operator(&source[index..]) { + tokens.push(operator); + index += chars_consumed; + } else if source[index] == '(' { + tokens.push(Token::Parenthesis { closing: false }); + index += 1; + } else if source[index] == ')' { + tokens.push(Token::Parenthesis { closing: true }); + index += 1; + } else { + // Skip if things fail + index += 1; + } + } + Ok(tokens) +} + +#[derive(Clone, PartialEq, Debug)] +pub enum AstPrimitive { + Integer(i64), + Float(f64), + String(String), + Boolean(bool), +} + +#[derive(Clone, PartialEq, Debug)] +pub enum AstNode { + Primitive(AstPrimitive), + Negate { body: Box }, + Add { left: Box, right: Box }, + Subtract { left: Box, right: Box }, + Multiply { left: Box, right: Box }, + Divide { left: Box, right: Box }, + Null, +} +pub fn parse(tokens: &[Token]) -> Result { + fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + if tokens.is_empty() { + Err(InterpreterError::UnexpectedEOF) + } else if let Token::Integer(n) = &tokens[0] { + Ok((AstNode::Primitive(AstPrimitive::Integer(*n)), 1)) + } else if let Token::Float(n) = &tokens[0] { + Ok((AstNode::Primitive(AstPrimitive::Float(*n)), 1)) + } else if let Token::Boolean(n) = &tokens[0] { + Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1)) + } else if let Token::String(s) = &tokens[0] { + Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1)) + } else { + Err(InterpreterError::ParseError("Expected literal".to_owned())) + } + } + fn parse_grouped_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + let mut index = 0; + // '(' + if !matches!(tokens[index], Token::Parenthesis { closing: false }) { + return parse_primary_expression(tokens); + } else { + index += 1; + } + // expression of any kind + let (value, tokens_consumed) = parse_expression(&tokens[index..])?; + index += tokens_consumed; + // ')' + if !matches!(tokens[index], Token::Parenthesis { closing: true }) { + return Err(InterpreterError::ParseError("No closing parenthesis".to_owned())); + } else { + index += 1; + } + Ok((value, index)) + } + fn parse_unary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + let mut index = 0; + if tokens[index] != Token::Minus && tokens[index] != Token::Bang { + return parse_grouped_expression(&tokens[index..]); + } else { + let operation = tokens[index].clone(); + index += 1; + let (body, tokens_consumed) = parse_unary_expression(&tokens[index..])?; + index += tokens_consumed; + Ok(( + match operation { + Token::Minus => AstNode::Negate { body: Box::new(body) }, + Token::Bang => AstNode::Negate { body: Box::new(body) }, + _ => return Err(InterpreterError::ParseError("Impossible".to_owned())), + }, + index + )) + } + } + fn parse_multiplication_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + let mut index = 0; + let (mut value, tokens_consumed) = parse_unary_expression(&tokens[index..])?; + index += tokens_consumed; + while index < tokens.len() { + if tokens[index] != Token::Star && tokens[index] != Token::Slash { + break; + } + let operation = tokens[index].clone(); + index += 1; + let (right, tokens_consumed) = parse_unary_expression(&tokens[index..])?; + index += tokens_consumed; + value = match operation { + Token::Star => AstNode::Multiply { + left: Box::new(value), + right: Box::new(right), + }, + Token::Slash => AstNode::Divide { + left: Box::new(value), + right: Box::new(right), + }, + _ => return Err(InterpreterError::ParseError("Impossible".to_owned())), }; - let others = from_str::>(&data[0])?; - let conditional = from_str::(&others[0])?; - let consequence = from_str::(&others[1])?; - Ok(to_string(&AstNode::if_statement( - conditional, - consequence, - alternative, - ))?) - }); - let while_statement = while_token - .clone() - .ignore() - .and( - left_paren - .clone() - .ignore() - .and(expression.clone()) - .and(right_paren.clone().ignore()), - ) - .and(statement.clone()) - .map(|matched| { - let data = from_str::>(&matched)?; - let conditional = from_str::(&data[0])?; - let body = from_str::(&data[1])?; - Ok(to_string(&AstNode::while_loop(conditional, body))?) - }); - let var_statement = var - .clone() - .ignore() - .and(identifier.clone()) - .and(assign.clone().ignore()) - .and(expression.clone()) - .and(semicolon.clone().ignore()) - .map(|matched| { - let data = from_str::>(&matched)?; - let name = from_str::(&data[0])?.value; - let value = from_str::(&data[1])?; - Ok(to_string(&AstNode::variable_definition(name, value))?) - }); - let assignment_statement = identifier - .clone() - .and(assign.clone().ignore()) - .and(expression.clone()) - .and(semicolon.clone().ignore()) - .map(|matched| { - let data = from_str::>(&matched)?; - let name = from_str::(&data[0])?.value; - let value = from_str::(&data[1])?; - Ok(to_string(&AstNode::assign(name, value))?) - }); - let block_statement = left_brace - .clone() - .ignore() - .and(statement.clone().repeat_range(0..usize::MAX)) - .and(right_brace.clone().ignore()) - .map(|matched| { - let data = from_str::>(&matched)?; - let mut statements = vec![]; - for d in data { - statements.push(from_str::(&d)?); + } + Ok((value, index)) + } + fn parse_addition_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + let mut index = 0; + let (mut value, tokens_consumed) = parse_multiplication_expression(&tokens[index..])?; + index += tokens_consumed; + while index < tokens.len() { + if tokens[index] != Token::Plus && tokens[index] != Token::Minus { + break; } - Ok(to_string(&AstNode::block(statements))?) - }); - let args = identifier - .clone() - .and( - comma - .clone() - .ignore() - .and(identifier.clone()) - .repeat_range(0..usize::MAX), - ) - .map(|matched| { - let mut args = vec![]; - let data = from_str::>(&matched)?; - args.push(data[0].clone()); - let others = from_str::>(&data[1])?; - for o in others { - args.push(o.clone()); + let operation = tokens[index].clone(); + index += 1; + let (right, tokens_consumed) = parse_multiplication_expression(&tokens[index..])?; + index += tokens_consumed; + value = match operation { + Token::Plus => AstNode::Add { + left: Box::new(value), + right: Box::new(right), + }, + Token::Minus => AstNode::Subtract { + left: Box::new(value), + right: Box::new(right), + }, + _ => return Err(InterpreterError::ParseError("Impossible".to_owned())), + }; + } + Ok((value, index)) + } + fn parse_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + parse_addition_expression(tokens) + } + let (ast, _) = parse_expression(tokens)?; + Ok(ast) +} +pub fn evaluate(ast: &AstNode) -> Result { + use std::mem::discriminant; + use AstNode::*; + match ast { + Primitive(p) => Ok(p.clone()), + Negate { body } => { + if let AstPrimitive::Integer(body) = evaluate(body)? { + Ok(AstPrimitive::Integer(body * -1)) + } else if let AstPrimitive::Boolean(body) = evaluate(body)? { + Ok(AstPrimitive::Boolean(!body)) + }else { + Err(InterpreterError::ParseError("Can only negate integers and bools".to_owned())) } - Ok(to_string(&args)?) - }); - let function_statement = function - .clone() - .ignore() - .and(identifier.clone()) - .and( - left_paren - .clone() - .ignore() - .and(args.clone().optional()) - .and(right_paren.clone().ignore()), - ) - .and(block_statement.clone()) - .map(|matched| { - let data = from_str::>(&matched)?; - let mut body = from_str::(&data[1])?; - let data = from_str::>(&data[0])?; - let name = from_str::(&data[0])?.value; - let params = from_str::>(&data[1])?; - let mut parameters = vec![]; - if params.len() != 0 { - for p in from_str::>(¶ms[0])? { - parameters.push(from_str::(&p)?); - } - } - // Hoist variable definitions. - let mut vars = vec![]; - let mut others = vec![]; - for node in &body.subnodes { - match node.kind { - AstNodeKind::VariableDefinition => { - vars.push(AstNode::variable_declaration(node.value.clone())); - others.push(AstNode::assign( - node.value.clone(), - node.subnodes[0].clone(), - )) + }, + Add { left, right } => { + let left = evaluate(left)?; + let right = evaluate(right)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::ParseError("Mismatched types".to_owned())) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(AstPrimitive::Integer(left + right)); } - _ => others.push(node.clone()), } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(AstPrimitive::Float(left + right)); + } + } + if let AstPrimitive::String(left) = left { + if let AstPrimitive::String(right) = right { + return Ok(AstPrimitive::String(format!("{}{}", left, right))); + } + } + Err(InterpreterError::ParseError("Can only add integers, strings, and floats".to_owned())) } - vars.append(&mut others); - body.subnodes = vars; - Ok(to_string(&AstNode::function_definition( - name, parameters, body, - ))?) - }); - return_statement - .clone() - .or(import_statement.clone()) - .or(if_statement.clone()) - .or(while_statement.clone()) - .or(var_statement.clone()) - .or(assignment_statement.clone()) - .or(block_statement.clone()) - .or(function_statement.clone()) - .or(expression_statement.clone()) - .parse(src) -} -fn parse_expression(src: String) -> Result<(String, String), String> { - let whitespace = Parser::regex(r"[ \n\r\t]+"); - let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]")); - let ignored = whitespace.or(comments).repeat_range(0..usize::MAX); - // Token parser constructor. - let i = ignored.clone(); - let token = move |pattern: &str| i.clone().ignore().and(Parser::regex(pattern)); - // Token helper parsers. - let comma = token(r"[,]"); - let left_paren = token(r"[(]"); - let right_paren = token(r"[)]"); - let number = token(r"[0-9]+").map(|matched| { - Ok(to_string(&AstNode::integer( - matched.parse::().unwrap(), - ))?) - }); - let identifier = token(r"[a-zA-Z_][a-zA-Z0-9_]*") - .map(|matched| Ok(to_string(&AstNode::identifier(matched))?)); - let not = token(r"!").map(|_matched| Ok(to_string(&AstNode::not(AstNode::null()))?)); - let equal = token(r"==").map(|_matched| { - Ok(to_string(&AstNode::equal( - AstNode::null(), - AstNode::null(), - ))?) - }); - let not_equal = token(r"!=").map(|_matched| { - Ok(to_string(&AstNode::not_equal( - AstNode::null(), - AstNode::null(), - ))?) - }); - let plus = token(r"[+]") - .map(|_matched| Ok(to_string(&AstNode::add(AstNode::null(), AstNode::null()))?)); - let minus = token(r"[-]").map(|_matched| { - Ok(to_string(&AstNode::subtract( - AstNode::null(), - AstNode::null(), - ))?) - }); - let star = token(r"[*]").map(|_matched| { - Ok(to_string(&AstNode::multiply( - AstNode::null(), - AstNode::null(), - ))?) - }); - let slash = token(r"[/]").map(|_matched| { - Ok(to_string(&AstNode::divide( - AstNode::null(), - AstNode::null(), - ))?) - }); - // Expression parsers. - let expression = Parser::custom(parse_expression); - let args = expression - .clone() - .and( - comma - .clone() - .ignore() - .and(expression.clone()) - .repeat_range(0..usize::MAX), - ) - .map(|matched| { - let mut args = vec![]; - let data = from_str::>(&matched)?; - args.push(data[0].clone()); - let others = from_str::>(&data[1])?; - for o in others { - args.push(o.clone()); - } - Ok(to_string(&args)?) - }); - let call = identifier - .clone() - .and(left_paren.clone().ignore()) - .and(args.clone().optional()) - .and(right_paren.clone().ignore()) - .map(|matched| { - let data = from_str::>(&matched)?; - let callee = data[0].clone(); - let args = from_str::>(&data[1])?; - let mut ast_args = vec![]; - if let Some(args) = args.get(0) { - let args = from_str::>(&args)?; - for arg in &args { - ast_args.push(from_str::(arg)?); - } - } - Ok(to_string(&AstNode::function_call(callee, ast_args))?) - }); - let atom = call - .clone() - .or(identifier.clone()) - .or(number.clone()) - .or(left_paren - .clone() - .ignore() - .and(expression.clone()) - .and(right_paren.clone().ignore())); - let unary = not.clone().optional().and(atom.clone()).map(|matched| { - let data = from_str::>(&matched)?; - let atom_data = from_str::(&data[1])?; - Ok(to_string(&match &data[0][..] { - "!" => AstNode::not(atom_data), - _ => atom_data, - })?) - }); - let infix = |operator_parser: Parser, term_parser: Parser| { - term_parser - .clone() - .and( - operator_parser - .and(term_parser.clone()) - .repeat_range(0..usize::MAX), - ) - .map(|matched| { - let data = from_str::>(&matched)?; - let others = from_str::>(&data[1])?; - let mut current = from_str::(&data[0])?; - for i in 0..others.len() { - let o = from_str::>(&others[i])?; // Parse the [operator, unary] - let mut op = from_str::(&o[0])?; // Pull the operator out. - let t = from_str::(&o[1])?; // Pull the term out. - op.subnodes[0] = current; // Put current on the left side. - op.subnodes[1] = t; // Put the term on the right side. - current = op; // Replace current with the operator. - } - Ok(to_string(¤t)?) - }) - }; - let product = infix(star.clone().or(slash.clone()), unary.clone()); - let sum = infix(plus.clone().or(minus.clone()), product.clone()); - let comparison = infix(equal.clone().or(not_equal.clone()), sum.clone()); - comparison.parse(src) + } + Subtract { left, right } => { + let left = evaluate(left)?; + let right = evaluate(right)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::ParseError("Mismatched types".to_owned())) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(AstPrimitive::Integer(left - right)); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(AstPrimitive::Float(left - right)); + } + } + Err(InterpreterError::ParseError("Can only subtract integers and floats".to_owned())) + } + } + Multiply { left, right } => { + let left = evaluate(left)?; + let right = evaluate(right)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::ParseError("Mismatched types".to_owned())) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(AstPrimitive::Integer(left * right)); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(AstPrimitive::Float(left * right)); + } + } + Err(InterpreterError::ParseError("Can only multiply integers and floats".to_owned())) + } + } + Divide { left, right } => { + let left = evaluate(left)?; + let right = evaluate(right)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::ParseError("Mismatched types".to_owned())) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(AstPrimitive::Integer(left / right)); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(AstPrimitive::Float(left / right)); + } + } + Err(InterpreterError::ParseError("Can only divide integers and floats".to_owned())) + } + } + Null => Err(InterpreterError::ParseError("Cannot evaluate null".to_owned())), + } } +pub fn interpret(source: &str) -> Result<(), InterpreterError> { + println!("source: {:?}", source); + let tokens = tokenize(source); + println!("tokens: {:?}", tokens); + let ast = parse(&tokens?); + println!("ast: {:?}", ast); + let value = evaluate(&ast?); + println!("value: {:?}", value); + Ok(()) +} \ No newline at end of file diff --git a/test.pvt b/test.pvt index 688f5cc..e1c7a7e 100644 --- a/test.pvt +++ b/test.pvt @@ -1,4 +1 @@ -import log 1 0; -function main() { - log(3); -} +"a" + "b" \ No newline at end of file diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..0478b5f --- /dev/null +++ b/test.txt @@ -0,0 +1,13 @@ +PrimaryExpression = Identifier | Literal +GroupedExpression = PrimaryExpression | `(` Expression `)` + +UnaryOperator = `-` | `!` +UnaryExpression = GroupedExpression | UnaryOperator UnaryExpression + +MultiplyOperator = `*` | `/` +MultiplyExpression = UnaryExpression | MultiplyExpression MultiplyOperator UnaryExpression + +AddOperator = `+` | `-` +AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression + +Expression = AddExpression \ No newline at end of file