diff --git a/src/ast.rs b/src/ast.rs index e25f4db..f659909 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -192,4 +192,4 @@ impl AstNode { pub fn null() -> AstNode { AstNode::Null } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 8f51669..ff4fdb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ pub fn compile(source: &str) -> Vec { pub fn compile_wat(source: &str) -> String { // let mut s = SymbolGenerator::new(); - let ast = parse::interpret(source); + let ast = parse::run(source); // println!("{:?}", ast); unimplemented!() // let wasm = ast.emit(&mut s); diff --git a/src/main.rs b/src/main.rs index cd7c8a4..b32e4f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ fn main() -> std::io::Result<()> { // Read the source from a file. let source = std::fs::read_to_string("test.pvt").unwrap(); // Compile it - let _value = pivot::parse::interpret(&source); + let _value = pivot::parse::run(&source); // let binary = pivot::compile(&source); // Write it to a file. // File::create("out.wasm")?.write_all(&binary)?; diff --git a/src/parse.rs b/src/parse.rs index 0231f0e..8614e84 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -4,11 +4,16 @@ pub enum Token { Float(f64), String(String), Boolean(bool), + Identifier(String), + Keyword(String), + Comma, Plus, Minus, Star, Slash, Bang, + Equals, + Semicolon, Quote(char), Parenthesis { closing: bool }, Whitespace(String), @@ -19,8 +24,21 @@ pub enum Token { pub enum InterpreterError { /// Error parsing source ParseError(String), + /// Unexpected token + UnexpectedToken, + /// Mismatched types + MismatchedTypes, + /// Type error + TypeError, /// Unexpected EOF UnexpectedEOF, + /// Expected value + ExpectedValue, +} +impl From> for InterpreterError { + fn from(value: Option) -> InterpreterError { + InterpreterError::ExpectedValue + } } pub fn tokenize(source: &str) -> Result, InterpreterError> { @@ -37,18 +55,37 @@ pub fn tokenize(source: &str) -> Result, InterpreterError> { if chars_consumed == 0 { Err(()) } else if current_value.contains(".") { - Ok((Token::Float(current_value.parse::().unwrap()), chars_consumed)) + Ok(( + Token::Float(current_value.parse::().unwrap()), + chars_consumed, + )) } else { - Ok((Token::Integer(current_value.parse::().unwrap()), chars_consumed)) + 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)) + fn tokenize_identifier(chars: &[char]) -> Result<(Token, usize), ()> { + let mut current_value = String::new(); + let mut chars_consumed = 0; + if chars[chars_consumed].is_alphabetic() { + current_value.push(chars[chars_consumed]); } else { - Err(()) + return Err(()); + } + chars_consumed += 1; + while chars_consumed < chars.len() + && (chars[chars_consumed].is_alphanumeric() || chars[chars_consumed] == '_') + { + current_value.push(chars[chars_consumed]); + chars_consumed += 1; + } + match ¤t_value[..] { + "true" => Ok((Token::Boolean(true), 4)), + "false" => Ok((Token::Boolean(false), 5)), + "let" => Ok((Token::Keyword(current_value), chars_consumed)), + _ => Ok((Token::Identifier(current_value), chars_consumed)), } } fn tokenize_string(chars: &[char]) -> Result<(Token, usize), ()> { @@ -103,6 +140,8 @@ pub fn tokenize(source: &str) -> Result, InterpreterError> { Ok((Token::Slash, 1)) } else if chars[0] == '!' { Ok((Token::Bang, 1)) + } else if chars[0] == '=' { + Ok((Token::Equals, 1)) } else { Err(()) } @@ -121,12 +160,18 @@ pub fn tokenize(source: &str) -> Result, InterpreterError> { } 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..]) { + } else if let Ok((num, chars_consumed)) = tokenize_identifier(&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::Comma); + index += 1; + } else if source[index] == ';' { + tokens.push(Token::Semicolon); + index += 1; } else if source[index] == '(' { tokens.push(Token::Parenthesis { closing: false }); index += 1; @@ -147,18 +192,53 @@ pub enum AstPrimitive { Float(f64), String(String), Boolean(bool), + Identifier(String), + Null, +} +impl std::hash::Hash for AstPrimitive { + fn hash(&self, state: &mut H) { + match self { + AstPrimitive::Float(f) => format!("{}", f).hash(state), + _ => self.hash(state), + } + } } #[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 }, + Negate { + body: Box, + }, + Add { + left: Box, + right: Box, + }, + Subtract { + left: Box, + right: Box, + }, + Multiply { + left: Box, + right: Box, + }, + Divide { + left: Box, + right: Box, + }, + Declare { + identifier: String, + }, + Assign { + left: Box, + right: Box, + }, + Program { + statements: Vec, + }, Null, } + pub fn parse(tokens: &[Token]) -> Result { fn parse_primary_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { if tokens.is_empty() { @@ -171,8 +251,23 @@ pub fn parse(tokens: &[Token]) -> Result { Ok((AstNode::Primitive(AstPrimitive::Boolean(*n)), 1)) } else if let Token::String(s) = &tokens[0] { Ok((AstNode::Primitive(AstPrimitive::String(s.clone())), 1)) + } else if let Token::Identifier(s) = &tokens[0] { + Ok((AstNode::Primitive(AstPrimitive::Identifier(s.clone())), 1)) + } else if tokens[0] == Token::Keyword("let".to_owned()) { + if tokens.len() < 2 { + Err(InterpreterError::UnexpectedEOF) + } else if let Token::Identifier(s) = &tokens[1] { + Ok(( + AstNode::Declare { + identifier: s.clone(), + }, + 2, + )) + } else { + Err(InterpreterError::UnexpectedToken) + } } else { - Err(InterpreterError::ParseError("Expected literal".to_owned())) + Err(InterpreterError::UnexpectedToken) } } fn parse_grouped_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { @@ -188,7 +283,9 @@ pub fn parse(tokens: &[Token]) -> Result { index += tokens_consumed; // ')' if !matches!(tokens[index], Token::Parenthesis { closing: true }) { - return Err(InterpreterError::ParseError("No closing parenthesis".to_owned())); + return Err(InterpreterError::ParseError( + "No closing parenthesis".to_owned(), + )); } else { index += 1; } @@ -205,15 +302,21 @@ pub fn parse(tokens: &[Token]) -> Result { index += tokens_consumed; Ok(( match operation { - Token::Minus => AstNode::Negate { body: Box::new(body) }, - Token::Bang => AstNode::Negate { body: Box::new(body) }, + Token::Minus => AstNode::Negate { + body: Box::new(body), + }, + Token::Bang => AstNode::Negate { + body: Box::new(body), + }, _ => return Err(InterpreterError::ParseError("Impossible".to_owned())), }, - index + index, )) } } - fn parse_multiplication_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + 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; @@ -265,117 +368,208 @@ pub fn parse(tokens: &[Token]) -> Result { } Ok((value, index)) } + fn parse_assign_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { + let mut index = 0; + let (identifier, tokens_consumed) = parse_addition_expression(&tokens[index..])?; + index += tokens_consumed; + if index < tokens.len() && tokens[index] == Token::Equals { + index += 1; + } else { + return Ok((identifier, index)); + } + let (value, tokens_consumed) = parse_addition_expression(&tokens[index..])?; + index += tokens_consumed; + Ok(( + AstNode::Assign { + left: Box::new(identifier), + right: Box::new(value), + }, + index, + )) + } fn parse_expression(tokens: &[Token]) -> Result<(AstNode, usize), InterpreterError> { - parse_addition_expression(tokens) + parse_assign_expression(tokens) } - let (ast, _) = parse_expression(tokens)?; - Ok(ast) + let mut statements = vec![]; + let mut index = 0; + loop { + if index >= tokens.len() { + break; + } + let statement = parse_expression(&tokens[index..]); + if let Ok((statement, tokens_consumed)) = statement { + statements.push(statement); + index += tokens_consumed; + } else { + break; + } + } + Ok(AstNode::Program { statements }) } -pub fn evaluate(ast: &AstNode) -> Result { - use std::mem::discriminant; +pub fn interpret(ast: &AstNode) -> Result<(), InterpreterError> { + use std::{collections::HashMap, 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())) - } - }, - 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)); - } - } - 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())) - } + let mut vars: HashMap> = HashMap::new(); + if let Program { statements } = ast { + for statement in statements { + let _ = interpret_statement(statement, &mut vars)?; } - 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())), } + fn interpret_statement( + ast: &AstNode, + vars: &mut HashMap>, + ) -> Result, InterpreterError> { + match ast { + Primitive(p) => { + if let AstPrimitive::Identifier(id) = p { + if let Some(val) = vars.get(id) { + if let Some(val) = val { + Ok(Some(val.clone())) + } else { + Err(InterpreterError::ParseError( + "Variable used before definition".to_owned(), + )) + } + } else { + Err(InterpreterError::ParseError( + "Variable used before declaration".to_owned(), + )) + } + } else { + Ok(Some(p.clone())) + } + } + Negate { body } => { + if let Some(AstPrimitive::Integer(body)) = interpret_statement(body, vars)? { + Ok(Some(AstPrimitive::Integer(body * -1))) + } else if let Some(AstPrimitive::Boolean(body)) = interpret_statement(body, vars)? { + Ok(Some(AstPrimitive::Boolean(!body))) + } else { + Err(InterpreterError::TypeError) + } + } + Declare { identifier } => { + vars.insert(identifier.clone(), None); + Ok(None) + } + Assign { left, right } => { + if let AstNode::Declare { identifier } = Box::leak(left.clone()) { + let _ = interpret(left)?; + let value = interpret_statement(right, vars)?; + vars.insert(identifier.clone(), value); + Ok(Some(vars.get(identifier).unwrap().clone().unwrap().clone())) + } else if let AstNode::Primitive(AstPrimitive::Identifier(id)) = + Box::leak(left.clone()) + { + let id = id.clone(); + let value = interpret_statement(right, vars)?; + vars.insert(id.clone(), value); + Ok(Some(vars.get(&id).unwrap().clone().unwrap().clone())) + } else { + Err(InterpreterError::TypeError) + } + } + Add { left, right } => { + let left = + interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; + let right = + interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::MismatchedTypes) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(Some(AstPrimitive::Integer(left + right))); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(Some(AstPrimitive::Float(left + right))); + } + } + if let AstPrimitive::String(left) = left { + if let AstPrimitive::String(right) = right { + return Ok(Some(AstPrimitive::String(format!("{}{}", left, right)))); + } + } + Err(InterpreterError::TypeError) + } + } + Subtract { left, right } => { + let left = + interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; + let right = + interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::MismatchedTypes) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(Some(AstPrimitive::Integer(left - right))); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(Some(AstPrimitive::Float(left - right))); + } + } + Err(InterpreterError::TypeError) + } + } + Multiply { left, right } => { + let left = + interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; + let right = + interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::MismatchedTypes) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(Some(AstPrimitive::Integer(left * right))); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(Some(AstPrimitive::Float(left * right))); + } + } + Err(InterpreterError::TypeError) + } + } + Divide { left, right } => { + let left = + interpret_statement(left, vars)?.ok_or(InterpreterError::ExpectedValue)?; + let right = + interpret_statement(right, vars)?.ok_or(InterpreterError::ExpectedValue)?; + if discriminant(&left) != discriminant(&right) { + Err(InterpreterError::MismatchedTypes) + } else { + if let AstPrimitive::Integer(left) = left { + if let AstPrimitive::Integer(right) = right { + return Ok(Some(AstPrimitive::Integer(left / right))); + } + } + if let AstPrimitive::Float(left) = left { + if let AstPrimitive::Float(right) = right { + return Ok(Some(AstPrimitive::Float(left / right))); + } + } + Err(InterpreterError::TypeError) + } + } + _ => Err(InterpreterError::TypeError), + } + } + Ok(()) } -pub fn interpret(source: &str) -> Result<(), InterpreterError> { +pub fn run(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?); + let value = interpret(&ast?); println!("value: {:?}", value); Ok(()) -} \ No newline at end of file +} diff --git a/test.pvt b/test.pvt index e1c7a7e..eac2de5 100644 --- a/test.pvt +++ b/test.pvt @@ -1 +1,2 @@ -"a" + "b" \ No newline at end of file +let asdf = 4 +asdf + 1 \ No newline at end of file diff --git a/test.txt b/test.txt index 0478b5f..b3985f2 100644 --- a/test.txt +++ b/test.txt @@ -10,4 +10,6 @@ MultiplyExpression = UnaryExpression | MultiplyExpression MultiplyOperator Unary AddOperator = `+` | `-` AddExpression = MultiplyExpression | AddExpression AddOperator MultiplyExpression -Expression = AddExpression \ No newline at end of file +AssignExpression = AddExpression | Identifier `=` AddExpression + +Expression = AssignExpression \ No newline at end of file