From fb99e8e30ce955d000d14912c196a0497172d83f Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Mon, 19 Aug 2024 22:38:51 +1000 Subject: [PATCH] variable reassignment --- src/main.rs | 36 +++++++++++++--- src/repr/ast/base/expression/assign.rs | 9 ++++ src/repr/ast/base/expression/mod.rs | 3 ++ src/repr/ast/base/macros.rs | 1 + src/stage/lower_ir/lowering/mod.rs | 8 +++- src/stage/parse/expression/e_assign.rs | 36 ++++++++++++++++ src/stage/parse/expression/e_boolean.rs | 2 +- src/stage/parse/expression/e_ident.rs | 2 +- src/stage/parse/expression/e_integer.rs | 2 +- src/stage/parse/expression/mod.rs | 7 +++- src/stage/parse/mod.rs | 50 +++++++++++++++++++---- src/stage/type_check/expression/assign.rs | 30 ++++++++++++++ src/stage/type_check/expression/mod.rs | 2 + 13 files changed, 171 insertions(+), 17 deletions(-) create mode 100644 src/repr/ast/base/expression/assign.rs create mode 100644 src/stage/parse/expression/e_assign.rs create mode 100644 src/stage/type_check/expression/assign.rs diff --git a/src/main.rs b/src/main.rs index f7e9913..e4bab03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,44 @@ use lumina::compile_and_run; fn main() { let source = r#" - fn main() -> int { - let counter = 5; + fn fib1(n: int) -> int { + let a = 0; + let b = 1; + + let count = 0; loop { - if counter == 5 { + if count == n { break; } - let a = 3; + count = count + 1; + + let temp = a; + a = b; + b = b + temp; + } + + return a; + } + + fn fib2(n: int) -> int { + if n == 0 || n == 1 { + return n; } - return counter; + return fib2(n - 1) + fib2(n - 2); + } + + fn main() -> int { + let result1 = fib1(19); + let result2 = fib2(19); + + if result1 == result2 { + return result1; + } else { + return 0; + } }"#; let result = compile_and_run(source, true); diff --git a/src/repr/ast/base/expression/assign.rs b/src/repr/ast/base/expression/assign.rs new file mode 100644 index 0000000..e6cab3b --- /dev/null +++ b/src/repr/ast/base/expression/assign.rs @@ -0,0 +1,9 @@ +use super::*; +use crate::ast_node; + +ast_node! { + typed struct Assign { + binding: IdentIdentifier, + value: Box>, + } +} diff --git a/src/repr/ast/base/expression/mod.rs b/src/repr/ast/base/expression/mod.rs index 91a1616..eb35016 100644 --- a/src/repr/ast/base/expression/mod.rs +++ b/src/repr/ast/base/expression/mod.rs @@ -1,3 +1,4 @@ +mod assign; mod block; mod boolean; mod call; @@ -7,6 +8,7 @@ mod infix; mod integer; mod loop_block; +pub use assign::*; pub use block::*; pub use boolean::*; pub use call::*; @@ -30,6 +32,7 @@ ast_node!( If(If), Call(Call), Loop(Loop), + Assign(Assign), } ); diff --git a/src/repr/ast/base/macros.rs b/src/repr/ast/base/macros.rs index a3b9eac..9014d13 100644 --- a/src/repr/ast/base/macros.rs +++ b/src/repr/ast/base/macros.rs @@ -82,6 +82,7 @@ macro_rules! generate_ast { pub type Loop = ast::Loop<$ty_info, $fn_identifier, $ident_identifier>; pub type Infix = ast::Infix<$ty_info, $fn_identifier, $ident_identifier>; pub type Integer = ast::Integer<$ty_info>; + pub type Assign = ast::Assign<$ty_info, $fn_identifier, $ident_identifier>; pub type Expression = ast::Expression<$ty_info, $fn_identifier, $ident_identifier>; pub type Function = ast::Function<$ty_info, $fn_identifier, $ident_identifier>; pub type Program = ast::Program<$ty_info, $fn_identifier, $ident_identifier>; diff --git a/src/stage/lower_ir/lowering/mod.rs b/src/stage/lower_ir/lowering/mod.rs index 0fdf7dd..1327518 100644 --- a/src/stage/lower_ir/lowering/mod.rs +++ b/src/stage/lower_ir/lowering/mod.rs @@ -349,7 +349,7 @@ fn lower_expression( // Continue on from where the loop ends builder.goto_bb(loop_end); - None + Some(Value::Unit) } ast::Expression::Call(call) => { let idx = call.name; @@ -360,5 +360,11 @@ fn lower_expression( .collect(); Some(Value::Triple(builder.add_triple(Triple::Call(idx, params)))) } + ast::Expression::Assign(assign) => { + let value = lower_expression(compiler, builder, &assign.value).unwrap(); + builder.add_triple(Triple::Assign(assign.binding, value)); + + Some(Value::Unit) + } } } diff --git a/src/stage/parse/expression/e_assign.rs b/src/stage/parse/expression/e_assign.rs new file mode 100644 index 0000000..8927eae --- /dev/null +++ b/src/stage/parse/expression/e_assign.rs @@ -0,0 +1,36 @@ +pub use super::*; + +pub fn parse_assign(compiler: &mut Compiler, tokens: &mut Lexer<'_>) -> Result { + let (binding, span_start) = match tokens.next_spanned().unwrap() { + (Token::Ident(ident), span) => (ident, span), + (token, _) => { + return Err(ParseError::ExpectedToken { + expected: Box::new(Token::Ident(String::new())), + found: Box::new(token), + reason: "assign must start with ident".to_string(), + }); + } + }; + + let binding = compiler.symbols.get_or_intern(binding); + + match tokens.next_token().unwrap() { + Token::Eq => (), + token => { + return Err(ParseError::ExpectedToken { + expected: Box::new(Token::Eq), + found: Box::new(token), + reason: "equals sign following binding for assign".to_string(), + }); + } + } + + let value = parse_expression(compiler, tokens, Precedence::Lowest)?; + + Ok(Assign { + span: span_start.start..value.span().end, + binding, + value: Box::new(value), + ty_info: None, + }) +} diff --git a/src/stage/parse/expression/e_boolean.rs b/src/stage/parse/expression/e_boolean.rs index 1994c8a..2cdc24d 100644 --- a/src/stage/parse/expression/e_boolean.rs +++ b/src/stage/parse/expression/e_boolean.rs @@ -44,6 +44,6 @@ mod test { let mut tokens = source.into(); let _ = parse_boolean(&mut Compiler::default(), &mut tokens); - assert_eq!(tokens.0.count(), 1); + assert_eq!(tokens.count(), 1); } } diff --git a/src/stage/parse/expression/e_ident.rs b/src/stage/parse/expression/e_ident.rs index 065bc07..344d26d 100644 --- a/src/stage/parse/expression/e_ident.rs +++ b/src/stage/parse/expression/e_ident.rs @@ -43,6 +43,6 @@ mod test { let mut tokens = source.into(); let _ = parse_ident(&mut Compiler::default(), &mut tokens); - assert_eq!(tokens.0.count(), 1); + assert_eq!(tokens.count(), 1); } } diff --git a/src/stage/parse/expression/e_integer.rs b/src/stage/parse/expression/e_integer.rs index 78f1fbb..9987702 100644 --- a/src/stage/parse/expression/e_integer.rs +++ b/src/stage/parse/expression/e_integer.rs @@ -44,6 +44,6 @@ mod test { fn single_token(#[case] source: &str) { let mut tokens = source.into(); let _ = parse_integer(&mut Compiler::default(), &mut tokens); - assert_eq!(tokens.0.count(), 1); + assert_eq!(tokens.count(), 1); } } diff --git a/src/stage/parse/expression/mod.rs b/src/stage/parse/expression/mod.rs index 3af581e..75848c3 100644 --- a/src/stage/parse/expression/mod.rs +++ b/src/stage/parse/expression/mod.rs @@ -1,5 +1,6 @@ use std::iter; +use e_assign::parse_assign; use e_loop::parse_loop; use super::*; @@ -8,6 +9,7 @@ use self::{ e_boolean::parse_boolean, e_ident::parse_ident, e_if::parse_if, e_integer::parse_integer, }; +mod e_assign; mod e_boolean; mod e_ident; mod e_if; @@ -36,8 +38,11 @@ impl Precedence { } fn parse_prefix(compiler: &mut Compiler, tokens: &mut Lexer<'_>) -> Result { - match tokens.peek_token().unwrap() { + match tokens.peek_token().unwrap().clone() { Token::Integer(_) => Ok(Expression::Integer(parse_integer(compiler, tokens)?)), + Token::Ident(_) if matches!(tokens.double_peek_token(), Some(Token::Eq)) => { + Ok(Expression::Assign(parse_assign(compiler, tokens)?)) + } Token::Ident(_) => Ok(Expression::Ident(parse_ident(compiler, tokens)?)), Token::True => Ok(Expression::Boolean(parse_boolean(compiler, tokens)?)), Token::False => Ok(Expression::Boolean(parse_boolean(compiler, tokens)?)), diff --git a/src/stage/parse/mod.rs b/src/stage/parse/mod.rs index efab3dd..88bf694 100644 --- a/src/stage/parse/mod.rs +++ b/src/stage/parse/mod.rs @@ -78,9 +78,19 @@ pub fn parse(compiler: &mut Compiler, source: &str) -> Result(Peekable>); +struct Lexer<'source> { + next: Option<(Token, Span)>, + lexer: Peekable>, +} impl<'source> Lexer<'source> { + fn new(lexer: logos::Lexer<'source, Token>) -> Self { + Self { + lexer: lexer.spanned().peekable(), + next: None, + } + } + fn next_token(&mut self) -> Option { self.next_spanned().map(|(token, _)| token) } @@ -90,13 +100,39 @@ impl<'source> Lexer<'source> { } fn next_spanned(&mut self) -> Option<(Token, Span)> { - self.0.next().map(|(result, span)| (result.unwrap(), span)) + self.next.take().or_else(|| self.next()) } fn peek_spanned(&mut self) -> Option<(&Token, &Span)> { - self.0 + self.next + .as_ref() + .map(|(token, span)| (token, span)) + .or_else(|| { + self.lexer + .peek() + .map(|(result, span)| (result.as_ref().unwrap(), span)) + }) + } + + fn double_peek_token(&mut self) -> Option<&Token> { + if self.next.is_none() { + self.next = self.next(); + } + + self.lexer .peek() - .map(|(result, span)| (result.as_ref().unwrap(), span)) + .map(|(result, _)| result.as_ref().unwrap()) + } + + fn next(&mut self) -> Option<(Token, Span)> { + self.lexer + .next() + .map(|(result, span)| (result.unwrap(), span)) + } + + #[allow(dead_code)] + fn count(self) -> usize { + self.lexer.count() + self.next.map(|_| 1).unwrap_or(0) } } @@ -108,7 +144,7 @@ impl<'source> From<&'source str> for Lexer<'source> { impl<'source> From> for Lexer<'source> { fn from(lexer: logos::Lexer<'source, Token>) -> Self { - Self(lexer.spanned().peekable()) + Self::new(lexer) } } @@ -116,12 +152,12 @@ impl<'source> Deref for Lexer<'source> { type Target = Peekable>; fn deref(&self) -> &Self::Target { - &self.0 + &self.lexer } } impl<'source> DerefMut for Lexer<'source> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + &mut self.lexer } } diff --git a/src/stage/type_check/expression/assign.rs b/src/stage/type_check/expression/assign.rs new file mode 100644 index 0000000..70f1f38 --- /dev/null +++ b/src/stage/type_check/expression/assign.rs @@ -0,0 +1,30 @@ +use crate::{compiler::Compiler, util::scope::Scope}; + +use super::*; + +impl parse_ast::Assign { + pub fn ty_solve(self, compiler: &mut Compiler, scope: &mut Scope) -> Result { + // Work out what type the variable has to be + let (binding, ty) = scope + .resolve(self.binding) + .ok_or(TyError::SymbolNotFound(self.binding))?; + + let value = self.value.ty_solve(compiler, scope)?; + + let value_ty = value.get_ty_info().ty; + + if value_ty != ty { + return Err(TyError::Mismatch(ty, value_ty)); + } + + Ok(Assign { + binding, + ty_info: TyInfo { + ty: Ty::Unit, + return_ty: value.get_ty_info().return_ty, + }, + value: Box::new(value), + span: self.span, + }) + } +} diff --git a/src/stage/type_check/expression/mod.rs b/src/stage/type_check/expression/mod.rs index 636330d..fb92dc7 100644 --- a/src/stage/type_check/expression/mod.rs +++ b/src/stage/type_check/expression/mod.rs @@ -2,6 +2,7 @@ use crate::{compiler::Compiler, util::scope::Scope}; use super::*; +mod assign; mod block; mod boolean; mod call; @@ -26,6 +27,7 @@ impl parse_ast::Expression { parse_ast::Expression::If(e) => Expression::If(e.ty_solve(compiler, scope)?), parse_ast::Expression::Loop(e) => Expression::Loop(e.ty_solve(compiler, scope)?), parse_ast::Expression::Call(e) => Expression::Call(e.ty_solve(compiler, scope)?), + parse_ast::Expression::Assign(e) => Expression::Assign(e.ty_solve(compiler, scope)?), }) } }