diff --git a/rust_compiler/libs/compiler/src/test/branching.rs b/rust_compiler/libs/compiler/src/test/branching.rs index d23d880..1a852da 100644 --- a/rust_compiler/libs/compiler/src/test/branching.rs +++ b/rust_compiler/libs/compiler/src/test/branching.rs @@ -149,6 +149,7 @@ fn test_spilled_variable_update_in_branch() -> anyhow::Result<()> { sub r0 sp 1 put db r0 99 #h L1: + sub sp sp 1 " } ); diff --git a/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs b/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs index 2e0c3c2..43e3131 100644 --- a/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs +++ b/rust_compiler/libs/compiler/src/test/declaration_function_invocation.rs @@ -34,6 +34,8 @@ fn no_arguments() -> anyhow::Result<()> { #[test] fn let_var_args() -> anyhow::Result<()> { + // !IMPORTANT this needs to be stabilized as it currently incorrectly calculates sp offset at + // both ends of the cleanup lifecycle let compiled = compile! { debug " @@ -64,6 +66,7 @@ fn let_var_args() -> anyhow::Result<()> { get r8 db r0 sub sp sp 1 move r9 r15 #i + sub sp sp 1 " } ); @@ -123,6 +126,7 @@ fn inline_literal_args() -> anyhow::Result<()> { get r8 db r0 sub sp sp 1 move r9 r15 #returnedValue + sub sp sp 1 " } ); @@ -164,6 +168,7 @@ fn mixed_args() -> anyhow::Result<()> { get r8 db r0 sub sp sp 1 move r9 r15 #returnValue + sub sp sp 1 " } ); diff --git a/rust_compiler/libs/compiler/src/test/declaration_literal.rs b/rust_compiler/libs/compiler/src/test/declaration_literal.rs index 9316e54..c42624c 100644 --- a/rust_compiler/libs/compiler/src/test/declaration_literal.rs +++ b/rust_compiler/libs/compiler/src/test/declaration_literal.rs @@ -56,6 +56,7 @@ fn variable_declaration_numeric_literal_stack_spillover() -> anyhow::Result<()> push 7 #h push 8 #i push 9 #j + sub sp sp 3 " } ); diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index f293a4d..dd9f582 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -462,6 +462,15 @@ impl<'a> Parser<'a> { }) } + TokenType::Keyword(Keyword::Const) => { + let spanned_const = self.spanned(|p| p.const_declaration())?; + + Some(Spanned { + span: spanned_const.span, + node: Expression::ConstDeclaration(spanned_const), + }) + } + TokenType::Keyword(Keyword::Fn) => { let spanned_fn = self.spanned(|p| p.function())?; Some(Spanned { @@ -1220,6 +1229,46 @@ impl<'a> Parser<'a> { Ok(BlockExpression(expressions)) } + fn const_declaration(&mut self) -> Result { + // const + let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; + if !self_matches_current!(self, TokenType::Keyword(Keyword::Const)) { + return Err(Error::UnexpectedToken( + self.current_span(), + current_token.clone(), + )); + } + + // variable_name + let ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; + let ident_span = Self::token_to_span(ident_token); + let ident = match ident_token.token_type { + TokenType::Identifier(ref id) => id.clone(), + _ => return Err(Error::UnexpectedToken(ident_span, ident_token.clone())), + }; + + // `=` + let assign_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone(); + if !token_matches!(assign_token, TokenType::Symbol(Symbol::Assign)) { + return Err(Error::UnexpectedToken( + Self::token_to_span(&assign_token), + assign_token, + )); + } + + // literal value + self.assign_next()?; + let lit = self.spanned(|p| p.literal())?; + + Ok(ConstDeclarationExpression { + name: Spanned { + span: ident_span, + node: ident, + }, + value: lit, + }) + } + fn declaration(&mut self) -> Result { let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) { diff --git a/rust_compiler/libs/parser/src/test/docs.rs b/rust_compiler/libs/parser/src/test/docs.rs deleted file mode 100644 index 28aae6b..0000000 --- a/rust_compiler/libs/parser/src/test/docs.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::sys_call; -use helpers::Documentation; -use pretty_assertions::assert_eq; - -#[test] -fn test_token_tree_docs() -> anyhow::Result<()> { - let syscall = sys_call::System::Yield; - - assert_eq!(syscall.docs(), ""); - - Ok(()) -} diff --git a/rust_compiler/libs/parser/src/test/mod.rs b/rust_compiler/libs/parser/src/test/mod.rs index b7a7177..beb874c 100644 --- a/rust_compiler/libs/parser/src/test/mod.rs +++ b/rust_compiler/libs/parser/src/test/mod.rs @@ -1,12 +1,11 @@ #[macro_export] macro_rules! parser { ($input:expr) => { - Parser::new(Tokenizer::from($input.to_owned())) + Parser::new(Tokenizer::from($input)) }; } mod blocks; -mod docs; use super::Parser; use super::Tokenizer; use anyhow::Result; @@ -33,7 +32,7 @@ fn test_declarations() -> Result<()> { // The below line should fail let y = 234 "#; - let tokenizer = Tokenizer::from(input.to_owned()); + let tokenizer = Tokenizer::from(input); let mut parser = Parser::new(tokenizer); let expression = parser.parse()?.unwrap(); @@ -45,6 +44,36 @@ fn test_declarations() -> Result<()> { Ok(()) } +#[test] +fn test_const_declaration() -> Result<()> { + let input = r#" + const item = 20c; + const decimal = 200.15; + const nameConst = "str_lit"; + "#; + let tokenizer = Tokenizer::from(input); + let mut parser = Parser::new(tokenizer); + + assert_eq!( + "(const item = 293.15)", + parser.parse()?.unwrap().to_string() + ); + + assert_eq!( + "(const decimal = 200.15)", + parser.parse()?.unwrap().to_string() + ); + + assert_eq!( + r#"(const nameConst = "str_lit")"#, + parser.parse()?.unwrap().to_string() + ); + + assert_eq!(None, parser.parse()?); + + Ok(()) +} + #[test] fn test_function_expression() -> Result<()> { let input = r#" @@ -54,7 +83,7 @@ fn test_function_expression() -> Result<()> { } "#; - let tokenizer = Tokenizer::from(input.to_owned()); + let tokenizer = Tokenizer::from(input); let mut parser = Parser::new(tokenizer); let expression = parser.parse()?.unwrap(); @@ -73,7 +102,7 @@ fn test_function_invocation() -> Result<()> { add(); "#; - let tokenizer = Tokenizer::from(input.to_owned()); + let tokenizer = Tokenizer::from(input); let mut parser = Parser::new(tokenizer); let expression = parser.parse()?.unwrap(); @@ -89,7 +118,7 @@ fn test_priority_expression() -> Result<()> { let x = (4); "#; - let tokenizer = Tokenizer::from(input.to_owned()); + let tokenizer = Tokenizer::from(input); let mut parser = Parser::new(tokenizer); let expression = parser.parse()?.unwrap(); diff --git a/rust_compiler/libs/parser/src/tree_node.rs b/rust_compiler/libs/parser/src/tree_node.rs index 1d39d64..daca733 100644 --- a/rust_compiler/libs/parser/src/tree_node.rs +++ b/rust_compiler/libs/parser/src/tree_node.rs @@ -195,6 +195,18 @@ impl std::fmt::Display for LiteralOrVariable { } } +#[derive(Debug, PartialEq, Eq)] +pub struct ConstDeclarationExpression { + pub name: Spanned, + pub value: Spanned, +} + +impl std::fmt::Display for ConstDeclarationExpression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "(const {} = {})", self.name, self.value) + } +} + #[derive(Debug, PartialEq, Eq)] pub struct DeviceDeclarationExpression { /// any variable-like name @@ -316,6 +328,7 @@ pub enum Expression { Binary(Spanned), Block(Spanned), Break(Span), + ConstDeclaration(Spanned), Continue(Span), Declaration(Spanned, Box>), DeviceDeclaration(Spanned), @@ -342,6 +355,7 @@ impl std::fmt::Display for Expression { Expression::Binary(e) => write!(f, "{}", e), Expression::Block(e) => write!(f, "{}", e), Expression::Break(_) => write!(f, "break"), + Expression::ConstDeclaration(e) => write!(f, "{}", e), Expression::Continue(_) => write!(f, "continue"), Expression::Declaration(id, e) => write!(f, "(let {} = {})", id, e), Expression::DeviceDeclaration(e) => write!(f, "{}", e), @@ -362,4 +376,3 @@ impl std::fmt::Display for Expression { } } } - diff --git a/rust_compiler/libs/tokenizer/src/lib.rs b/rust_compiler/libs/tokenizer/src/lib.rs index c6a5fba..ed5619d 100644 --- a/rust_compiler/libs/tokenizer/src/lib.rs +++ b/rust_compiler/libs/tokenizer/src/lib.rs @@ -463,6 +463,7 @@ impl<'a> Tokenizer<'a> { "break" if next_ws!() => keyword!(Break), "while" if next_ws!() => keyword!(While), "continue" if next_ws!() => keyword!(Continue), + "const" if next_ws!() => keyword!(Const), "true" if next_ws!() => { return Ok(Token::new( TokenType::Boolean(true), diff --git a/rust_compiler/libs/tokenizer/src/token.rs b/rust_compiler/libs/tokenizer/src/token.rs index ca61cee..13af954 100644 --- a/rust_compiler/libs/tokenizer/src/token.rs +++ b/rust_compiler/libs/tokenizer/src/token.rs @@ -295,6 +295,20 @@ documented! { /// } /// ``` Continue, + /// Prepresents the `const` keyword. This allows you to define a variable that will never + /// change throughout the lifetime of the program, similar to `define` in IC10. If you are + /// not planning on mutating the variable (changing it), it is recommend you store it as a + /// const, as the compiler will not assign it to a register or stack variable. + /// + /// ## Example + /// ``` + /// const targetTemp = 20c; + /// device gasSensor = "d0"; + /// device airCon = "d1"; + /// + /// airCon.On = gasSensor.Temperature > targetTemp; + /// ``` + Const, /// Represents the `let` keyword, used to declare variables within Slang. /// ## Example /// ``` @@ -304,6 +318,9 @@ documented! { /// ``` Let, /// Represents the `fn` keyword, used to declare functions within Slang. + /// # WARNING + /// Functions are currently unstable and are subject to change until stabilized. Use at + /// your own risk! (They are also heavily not optimized and produce a LOT of code bloat) /// ## Example /// ``` /// // This allows you to now call `doSomething` with specific arguments.