pub mod sys_call; #[cfg(test)] mod test; pub mod tree_node; use crate::sys_call::{Math, System}; use helpers::Span; use std::{borrow::Cow, io::SeekFrom}; use sys_call::SysCall; use thiserror::Error; use tokenizer::{ self, Tokenizer, TokenizerBuffer, token::{Keyword, Symbol, Token, TokenType}, }; use tree_node::*; pub trait Documentation { fn docs(&self) -> String; } #[macro_export] /// A macro to create a boxed value. macro_rules! boxed { ($e:expr) => { Box::new($e) }; } #[derive(Error, Debug)] pub enum Error<'a> { #[error(transparent)] Tokenizer(#[from] tokenizer::Error), #[error("Unexpected token: {1}")] UnexpectedToken(Span, Token<'a>), #[error("Duplicate identifier: {1}")] DuplicateIdentifier(Span, Token<'a>), #[error("Invalid Syntax: {1}")] InvalidSyntax(Span, String), #[error("Unsupported Keyword: {1}")] UnsupportedKeyword(Span, Token<'a>), #[error("Unexpected End of File")] UnexpectedEOF, } impl<'a> From> for lsp_types::Diagnostic { fn from(value: Error) -> Self { use Error::*; use lsp_types::*; match value { Tokenizer(e) => e.into(), UnexpectedToken(span, _) | DuplicateIdentifier(span, _) | InvalidSyntax(span, _) | UnsupportedKeyword(span, _) => Diagnostic { message: value.to_string(), severity: Some(DiagnosticSeverity::ERROR), range: span.into(), ..Default::default() }, UnexpectedEOF => Diagnostic { message: value.to_string(), severity: Some(DiagnosticSeverity::ERROR), ..Default::default() }, } } } macro_rules! self_matches_peek { ($self:ident, $pattern:pat) => { matches!($self.tokenizer.peek()?, Some(Token { token_type: $pattern, .. })) }; ($self:ident, $pattern:pat if $cond:expr) => { matches!($self.tokenizer.peek()?, Some(Token { token_type: $pattern, .. }) if $cond) }; } macro_rules! token_matches { ($token:ident, $pattern:pat) => { matches!($token.token_type, $pattern) }; ($token:expr, $pattern:pat) => { matches!($token.token_type, $pattern) }; ($token:ident, $pattern:pat if $cond:expr) => { matches!($token.token_type, $pattern if $cond) }; ($token:expr, $pattern:pat if $cond:expr) => { matches!($token.token_type, $pattern if $cond) }; } macro_rules! self_matches_current { ($self:ident, $pattern:pat) => { matches!($self.current_token, Some(Token { token_type: $pattern, .. })) }; ($self:ident, $pattern:pat if $cond:expr) => { matches!($self.current_token, Some(Token { token_type: $pattern, .. }) if $cond) }; } pub struct Parser<'a> { tokenizer: TokenizerBuffer<'a>, current_token: Option>, pub errors: Vec>, } impl<'a> Parser<'a> { pub fn new(tokenizer: Tokenizer<'a>) -> Self { Parser { tokenizer: TokenizerBuffer::new(tokenizer), current_token: None, errors: Vec::new(), } } /// Calculates a Span from a given Token reference. fn token_to_span(t: &Token<'a>) -> Span { Span { start_line: t.line, start_col: t.span.start, end_line: t.line, end_col: t.span.end, } } fn current_span(&self) -> Span { self.current_token .as_ref() .map(Self::token_to_span) .unwrap_or(Span { start_line: 0, start_col: 0, end_line: 0, end_col: 0, }) } /// Helper to run a parsing closure and wrap the result in a Spanned struct fn spanned(&mut self, parser: F) -> Result, Error<'a>> where F: FnOnce(&mut Self) -> Result>, { let start_token = if self.current_token.is_some() { self.current_token.clone() } else { self.tokenizer.peek()? }; let (start_line, start_col) = start_token .as_ref() .map(|t| (t.line, t.span.start)) .unwrap_or((0, 0)); let node = parser(self)?; let end_token = &self.current_token; let (end_line, end_col) = end_token .clone() .map(|t| (t.line, t.span.end)) .unwrap_or((start_line, start_col)); Ok(Spanned { span: Span { start_line, start_col, end_line, end_col, }, node, }) } fn synchronize(&mut self) -> Result<(), Error<'a>> { self.assign_next()?; while let Some(token) = &self.current_token { if token.token_type == TokenType::Symbol(Symbol::Semicolon) { self.assign_next()?; return Ok(()); } match token.token_type { TokenType::Keyword(Keyword::Fn) | TokenType::Keyword(Keyword::Let) | TokenType::Keyword(Keyword::If) | TokenType::Keyword(Keyword::While) | TokenType::Keyword(Keyword::Loop) | TokenType::Keyword(Keyword::Device) | TokenType::Keyword(Keyword::Return) => return Ok(()), _ => {} } self.assign_next()?; } Ok(()) } pub fn parse_all(&mut self) -> Result>, Error<'a>> { let first_token = self.tokenizer.peek().unwrap_or(None); let (start_line, start_col) = first_token .as_ref() .map(|tok| (tok.line, tok.span.start)) .unwrap_or((0, 0)); let mut expressions = Vec::>>::new(); loop { match self.tokenizer.peek() { Ok(None) => break, Err(e) => { self.errors.push(Error::Tokenizer(e)); break; } _ => {} } match self.parse() { Ok(Some(expression)) => { expressions.push(expression); } Ok(None) => break, Err(e) => { self.errors.push(e); if self.synchronize().is_err() { break; } } } } let end_token_opt = self.tokenizer.peek().unwrap_or(None); let (end_line, end_col) = end_token_opt .map(|tok| (tok.line, tok.span.end)) .unwrap_or((start_line, start_col)); let span = Span { start_line, end_line, start_col, end_col, }; Ok(Some(Expression::Block(Spanned { node: BlockExpression(expressions), span, }))) } pub fn parse(&mut self) -> Result>>, Error<'a>> { self.assign_next()?; if self.current_token.is_none() { return Ok(None); } let expr = self.expression()?; if self_matches_peek!(self, TokenType::Symbol(Symbol::Semicolon)) { self.assign_next()?; } Ok(expr) } fn assign_next(&mut self) -> Result<(), Error<'a>> { self.current_token = self.tokenizer.next_token()?; Ok(()) } fn get_next(&mut self) -> Result>, Error<'a>> { self.assign_next()?; Ok(self.current_token.clone()) } fn expression(&mut self) -> Result>>, Error<'a>> { // Parse the Left Hand Side (unary/primary expression) let lhs = self.unary()?; let Some(lhs) = lhs else { return Ok(None); }; // Handle Postfix operators (Member Access, Method Call) immediately after unary let lhs = self.parse_postfix(lhs)?; // Handle Infix operators (Binary, Logical, Assignment) if self_matches_peek!( self, TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign | Symbol::Question) ) { return Ok(Some(self.infix(lhs)?)); } else if self_matches_current!( self, TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign | Symbol::Question) ) { self.tokenizer.seek(SeekFrom::Current(-1))?; return Ok(Some(self.infix(lhs)?)); } Ok(Some(lhs)) } /// Handles dot notation chains: x.y.z() fn parse_postfix( &mut self, mut lhs: Spanned>, ) -> Result>, Error<'a>> { loop { if self_matches_peek!(self, TokenType::Symbol(Symbol::Dot)) { self.assign_next()?; // consume Dot let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let identifier_span = Self::token_to_span(&identifier_token); let identifier = match identifier_token.token_type { TokenType::Identifier(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken( identifier_span, identifier_token.clone(), )); } }; // Check for Method Call '()' if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) { // Method Call self.assign_next()?; // consume '(' let mut arguments = Vec::>>::new(); while !token_matches!( self.get_next()?.ok_or(Error::UnexpectedEOF)?, TokenType::Symbol(Symbol::RParen) ) { let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?; // Block expressions not allowed in args if let Expression::Block(_) = expression.node { return Err(Error::InvalidSyntax( self.current_span(), String::from("Block expressions are not allowed in method calls"), )); } arguments.push(expression); if !self_matches_peek!(self, TokenType::Symbol(Symbol::Comma)) && !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; return Err(Error::UnexpectedToken( Self::token_to_span(&next_token), next_token, )); } if !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { self.assign_next()?; } } // End span is the ')' let end_span = self.current_span(); let combined_span = Span { start_line: lhs.span.start_line, start_col: lhs.span.start_col, end_line: end_span.end_line, end_col: end_span.end_col, }; lhs = Spanned { span: combined_span, node: Expression::MethodCall(Spanned { span: combined_span, node: MethodCallExpression { object: boxed!(lhs), method: Spanned { span: identifier_span, node: identifier, }, arguments, }, }), }; } else { // Member Access let combined_span = Span { start_line: lhs.span.start_line, start_col: lhs.span.start_col, end_line: identifier_span.end_line, end_col: identifier_span.end_col, }; lhs = Spanned { span: combined_span, node: Expression::MemberAccess(Spanned { span: combined_span, node: MemberAccessExpression { object: boxed!(lhs), member: Spanned { span: identifier_span, node: identifier, }, }, }), }; } } else { break; } } Ok(lhs) } fn unary(&mut self) -> Result>>, Error<'a>> { macro_rules! matches_keyword { ($keyword:expr, $($pattern:pat),+) => { matches!($keyword, $($pattern)|+) }; } let Some(current_token) = self.current_token.as_ref() else { return Ok(None); }; if token_matches!(current_token, TokenType::EOF) { return Ok(None); } let expr = match current_token.token_type { TokenType::Keyword(e) if matches_keyword!(e, Keyword::Enum) => { return Err(Error::UnsupportedKeyword( self.current_span(), current_token.clone(), )); } TokenType::Keyword(Keyword::Let) => Some(self.spanned(|p| p.declaration())?), TokenType::Keyword(Keyword::Device) => { let spanned_dev = self.spanned(|p| p.device())?; Some(Spanned { span: spanned_dev.span, node: Expression::DeviceDeclaration(spanned_dev), }) } 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 { span: spanned_fn.span, node: Expression::Function(spanned_fn), }) } TokenType::Keyword(Keyword::If) => { let spanned_if = self.spanned(|p| p.if_expression())?; Some(Spanned { span: spanned_if.span, node: Expression::If(spanned_if), }) } TokenType::Keyword(Keyword::Loop) => { let spanned_loop = self.spanned(|p| p.loop_expression())?; Some(Spanned { span: spanned_loop.span, node: Expression::Loop(spanned_loop), }) } TokenType::Keyword(Keyword::While) => { let spanned_while = self.spanned(|p| p.while_expression())?; Some(Spanned { span: spanned_while.span, node: Expression::While(spanned_while), }) } TokenType::Keyword(Keyword::Break) => { let span = self.current_span(); let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } Some(Spanned { span, node: Expression::Break(span), }) } TokenType::Keyword(Keyword::Continue) => { let span = self.current_span(); let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } Some(Spanned { span, node: Expression::Continue(span), }) } TokenType::Identifier(ref id) if SysCall::is_syscall(id) => { let spanned_call = self.spanned(|p| p.syscall())?; Some(Spanned { span: spanned_call.span, node: Expression::Syscall(spanned_call), }) } TokenType::Identifier(_) if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) => { let spanned_invoke = self.spanned(|p| p.invocation())?; Some(Spanned { span: spanned_invoke.span, node: Expression::Invocation(spanned_invoke), }) } TokenType::Identifier(ref id) => { let span = self.current_span(); Some(Spanned { span, node: Expression::Variable(Spanned { span, node: id.clone(), }), }) } TokenType::Symbol(Symbol::LBrace) => { let spanned_block = self.spanned(|p| p.block())?; Some(Spanned { span: spanned_block.span, node: Expression::Block(spanned_block), }) } TokenType::Number(_) | TokenType::String(_) | TokenType::Boolean(_) => { let spanned_lit = self.spanned(|p| p.literal())?; Some(Spanned { span: spanned_lit.span, node: Expression::Literal(spanned_lit), }) } TokenType::Symbol(Symbol::LParen) => { self.spanned(|p| p.priority())?.node.map(|node| *node) } TokenType::Symbol(Symbol::Minus) => { let start_span = self.current_span(); self.assign_next()?; let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?; let inner_with_postfix = self.parse_postfix(inner_expr)?; let combined_span = Span { start_line: start_span.start_line, start_col: start_span.start_col, end_line: inner_with_postfix.span.end_line, end_col: inner_with_postfix.span.end_col, }; Some(Spanned { span: combined_span, node: Expression::Negation(boxed!(inner_with_postfix)), }) } TokenType::Symbol(Symbol::LogicalNot) => { let start_span = self.current_span(); self.assign_next()?; let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?; let inner_with_postfix = self.parse_postfix(inner_expr)?; let combined_span = Span { start_line: start_span.start_line, start_col: start_span.start_col, end_line: inner_with_postfix.span.end_line, end_col: inner_with_postfix.span.end_col, }; Some(Spanned { span: combined_span, node: Expression::Logical(Spanned { span: combined_span, node: LogicalExpression::Not(boxed!(inner_with_postfix)), }), }) } _ => { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } }; Ok(expr) } fn get_infix_child_node(&mut self) -> Result>, Error<'a>> { let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let start_span = self.current_span(); let expr = match current_token.token_type { TokenType::Number(_) | TokenType::Boolean(_) => { let lit = self.spanned(|p| p.literal())?; Spanned { span: lit.span, node: Expression::Literal(lit), } } TokenType::Identifier(ref ident) if !self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) => { // This is a Variable. We need to check for Postfix operations on it. let span = self.current_span(); Spanned { span, node: Expression::Variable(Spanned { span, node: ident.clone(), }), } } TokenType::Symbol(Symbol::LParen) => *self .spanned(|p| p.priority())? .node .ok_or(Error::UnexpectedEOF)?, TokenType::Identifier(ref id) if SysCall::is_syscall(id) => { let spanned_call = self.spanned(|p| p.syscall())?; Spanned { span: spanned_call.span, node: Expression::Syscall(spanned_call), } } TokenType::Identifier(_) if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) => { let inv = self.spanned(|p| p.invocation())?; Spanned { span: inv.span, node: Expression::Invocation(inv), } } TokenType::Symbol(Symbol::Minus) => { self.assign_next()?; let inner = self.get_infix_child_node()?; let span = Span { start_line: start_span.start_line, start_col: start_span.start_col, end_line: inner.span.end_line, end_col: inner.span.end_col, }; Spanned { span, node: Expression::Negation(boxed!(inner)), } } TokenType::Symbol(Symbol::LogicalNot) => { self.assign_next()?; let inner = self.get_infix_child_node()?; let span = Span { start_line: start_span.start_line, start_col: start_span.start_col, end_line: inner.span.end_line, end_col: inner.span.end_col, }; Spanned { span, node: Expression::Logical(Spanned { span, node: LogicalExpression::Not(boxed!(inner)), }), } } _ => { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } }; // Important: We must check for postfix operations here too // e.g. a + b.c self.parse_postfix(expr) } fn device(&mut self) -> Result, Error<'a>> { let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; if !self_matches_current!(self, TokenType::Keyword(Keyword::Device)) { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let identifier_span = Self::token_to_span(&identifier_token); let identifier = match identifier_token.token_type { TokenType::Identifier(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken( Self::token_to_span(&identifier_token), identifier_token.clone(), )); } }; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::Assign)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token.clone(), )); } let device_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let device = match device_token.token_type { TokenType::String(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken( Self::token_to_span(&device_token), device_token.clone(), )); } }; Ok(DeviceDeclarationExpression { name: Spanned { span: identifier_span, node: identifier, }, device, }) } fn infix( &mut self, previous: Spanned>, ) -> Result>, Error<'a>> { let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone(); match previous.node { Expression::Binary(_) | Expression::Logical(_) | Expression::Invocation(_) | Expression::Syscall(_) | Expression::Priority(_) | Expression::Literal(_) | Expression::Variable(_) | Expression::Ternary(_) | Expression::Negation(_) | Expression::MemberAccess(_) | Expression::MethodCall(_) => {} _ => { return Err(Error::InvalidSyntax( self.current_span(), String::from("Invalid expression for binary/logical operation"), )); } } let mut expressions = vec![previous]; let mut operators = Vec::::new(); let mut temp_token = current_token.clone(); // Include Assign in the operator loop while token_matches!( temp_token, TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign | Symbol::Question | Symbol::Colon) ) { let operator = match temp_token.token_type { TokenType::Symbol(s) => s, _ => unreachable!(), }; operators.push(operator); self.assign_next()?; expressions.push(self.get_infix_child_node()?); temp_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone(); } if operators.len() != expressions.len() - 1 { return Err(Error::InvalidSyntax( self.current_span(), String::from("Invalid number of operators"), )); } // --- PRECEDENCE LEVEL 1: Exponent (**) --- for (i, operator) in operators.iter().enumerate().rev() { if operator == &Symbol::Exp { let right = expressions.remove(i + 1); let left = expressions.remove(i); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; expressions.insert( i, Spanned { span, node: Expression::Binary(Spanned { span, node: BinaryExpression::Exponent(boxed!(left), boxed!(right)), }), }, ); } } operators.retain(|symbol| symbol != &Symbol::Exp); // Common macro for binary ops macro_rules! process_binary_ops { ($ops:pat, $variant:ident) => { let mut current_iteration = 0; for (i, operator) in operators.iter().enumerate() { if matches!(operator, $ops) { let index = i - current_iteration; let left = expressions.remove(index); let right = expressions.remove(index); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; let node = match operator { Symbol::Asterisk => { BinaryExpression::Multiply(boxed!(left), boxed!(right)) } Symbol::Slash => BinaryExpression::Divide(boxed!(left), boxed!(right)), Symbol::Percent => { BinaryExpression::Modulo(boxed!(left), boxed!(right)) } Symbol::Plus => BinaryExpression::Add(boxed!(left), boxed!(right)), Symbol::Minus => { BinaryExpression::Subtract(boxed!(left), boxed!(right)) } _ => unreachable!(), }; expressions.insert( index, Spanned { span, node: Expression::Binary(Spanned { span, node }), }, ); current_iteration += 1; } } operators.retain(|symbol| !matches!(symbol, $ops)); }; } // --- PRECEDENCE LEVEL 2: Multiplicative (*, /, %) --- process_binary_ops!( Symbol::Slash | Symbol::Asterisk | Symbol::Percent, BinaryExpression ); // --- PRECEDENCE LEVEL 3: Additive (+, -) --- process_binary_ops!(Symbol::Plus | Symbol::Minus, BinaryExpression); // --- PRECEDENCE LEVEL 4: Comparison (<, >, <=, >=) --- let mut current_iteration = 0; for (i, operator) in operators.iter().enumerate() { if operator.is_comparison() && !matches!(operator, Symbol::Equal | Symbol::NotEqual) { let index = i - current_iteration; let left = expressions.remove(index); let right = expressions.remove(index); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; let node = match operator { Symbol::LessThan => LogicalExpression::LessThan(boxed!(left), boxed!(right)), Symbol::GreaterThan => { LogicalExpression::GreaterThan(boxed!(left), boxed!(right)) } Symbol::LessThanOrEqual => { LogicalExpression::LessThanOrEqual(boxed!(left), boxed!(right)) } Symbol::GreaterThanOrEqual => { LogicalExpression::GreaterThanOrEqual(boxed!(left), boxed!(right)) } _ => unreachable!(), }; expressions.insert( index, Spanned { span, node: Expression::Logical(Spanned { span, node }), }, ); current_iteration += 1; } } operators.retain(|symbol| { !symbol.is_comparison() || matches!(symbol, Symbol::Equal | Symbol::NotEqual) }); // --- PRECEDENCE LEVEL 5: Equality (==, !=) --- current_iteration = 0; for (i, operator) in operators.iter().enumerate() { if matches!(operator, Symbol::Equal | Symbol::NotEqual) { let index = i - current_iteration; let left = expressions.remove(index); let right = expressions.remove(index); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; let node = match operator { Symbol::Equal => LogicalExpression::Equal(boxed!(left), boxed!(right)), Symbol::NotEqual => LogicalExpression::NotEqual(boxed!(left), boxed!(right)), _ => unreachable!(), }; expressions.insert( index, Spanned { span, node: Expression::Logical(Spanned { span, node }), }, ); current_iteration += 1; } } operators.retain(|symbol| !matches!(symbol, Symbol::Equal | Symbol::NotEqual)); // --- PRECEDENCE LEVEL 6: Logical AND (&&) --- current_iteration = 0; for (i, operator) in operators.iter().enumerate() { if matches!(operator, Symbol::LogicalAnd) { let index = i - current_iteration; let left = expressions.remove(index); let right = expressions.remove(index); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; expressions.insert( index, Spanned { span, node: Expression::Logical(Spanned { span, node: LogicalExpression::And(boxed!(left), boxed!(right)), }), }, ); current_iteration += 1; } } operators.retain(|symbol| !matches!(symbol, Symbol::LogicalAnd)); // --- PRECEDENCE LEVEL 7: Logical OR (||) --- current_iteration = 0; for (i, operator) in operators.iter().enumerate() { if matches!(operator, Symbol::LogicalOr) { let index = i - current_iteration; let left = expressions.remove(index); let right = expressions.remove(index); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; expressions.insert( index, Spanned { span, node: Expression::Logical(Spanned { span, node: LogicalExpression::Or(boxed!(left), boxed!(right)), }), }, ); current_iteration += 1; } } operators.retain(|symbol| !matches!(symbol, Symbol::LogicalOr)); // -- PRECEDENCE LEVEL 8: Ternary (x ? 1 : 2) for i in (0..operators.len()).rev() { if matches!(operators[i], Symbol::Question) { // Ensure next operator is a colon if i + 1 >= operators.len() || !matches!(operators[i + 1], Symbol::Colon) { return Err(Error::InvalidSyntax( self.current_span(), "Ternary operator '?' missing matching ':'".to_string(), )); } let false_branch = expressions.remove(i + 2); let true_branch = expressions.remove(i + 1); let condition = expressions.remove(i); let span = Span { start_line: condition.span.start_line, end_line: false_branch.span.end_line, start_col: condition.span.start_col, end_col: false_branch.span.end_col, }; let ternary_node = Spanned { span, node: TernaryExpression { condition: Box::new(condition), true_value: Box::new(true_branch), false_value: Box::new(false_branch), }, }; expressions.insert( i, Spanned { node: Expression::Ternary(ternary_node), span, }, ); // Remove the `?` and the `:` from the operators list operators.remove(i); operators.remove(i); } } // --- PRECEDENCE LEVEL 9: Assignment (=) --- // Assignment is Right Associative: a = b = c => a = (b = c) // We iterate Right to Left for (i, operator) in operators.iter().enumerate().rev() { if matches!(operator, Symbol::Assign) { let right = expressions.remove(i + 1); let left = expressions.remove(i); let span = Span { start_line: left.span.start_line, start_col: left.span.start_col, end_line: right.span.end_line, end_col: right.span.end_col, }; expressions.insert( i, Spanned { span, node: Expression::Assignment(Spanned { span, node: AssignmentExpression { assignee: boxed!(left), expression: boxed!(right), }, }), }, ); } } operators.retain(|symbol| !matches!(symbol, Symbol::Assign)); if expressions.len() != 1 || !operators.is_empty() { return Err(Error::InvalidSyntax( self.current_span(), String::from("Invalid number of operators"), )); } if token_matches!( temp_token, TokenType::Symbol(Symbol::Semicolon) | TokenType::Symbol(Symbol::RParen) | TokenType::Symbol(Symbol::Comma) ) { self.tokenizer.seek(SeekFrom::Current(-1))?; } expressions.pop().ok_or(Error::UnexpectedEOF) } fn priority(&mut self) -> Result>>>, Error<'a>> { let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } self.assign_next()?; let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::RParen)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token, )); } Ok(Some(boxed!(expression))) } fn invocation(&mut self) -> Result, Error<'a>> { let identifier_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let identifier_span = Self::token_to_span(identifier_token); let identifier = match identifier_token.token_type { TokenType::Identifier(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken( self.current_span(), self.current_token.clone().ok_or(Error::UnexpectedEOF)?, )); } }; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token, )); } let mut arguments = Vec::>::new(); while !token_matches!( self.get_next()?.ok_or(Error::UnexpectedEOF)?, TokenType::Symbol(Symbol::RParen) ) { let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?; if let Expression::Block(_) = expression.node { return Err(Error::InvalidSyntax( self.current_span(), String::from("Block expressions are not allowed in function invocations"), )); } arguments.push(expression); if !self_matches_peek!(self, TokenType::Symbol(Symbol::Comma)) && !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; return Err(Error::UnexpectedToken( Self::token_to_span(&next_token), next_token, )); } if !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { self.assign_next()?; } } Ok(InvocationExpression { name: Spanned { span: identifier_span, node: identifier, }, arguments, }) } fn block(&mut self) -> Result, Error<'a>> { let mut expressions = Vec::>::new(); let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::LBrace)) { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } while !self_matches_peek!( self, TokenType::Symbol(Symbol::RBrace) | TokenType::Keyword(Keyword::Return) ) { let expression = self.parse()?.ok_or(Error::UnexpectedEOF)?; expressions.push(expression); } let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if token_matches!(current_token, TokenType::Keyword(Keyword::Return)) { // Need to capture return span let ret_start_span = Self::token_to_span(¤t_token); self.assign_next()?; let expr = if token_matches!( self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?, TokenType::Symbol(Symbol::Semicolon) ) { // rewind 1 token so we can check for the semicolon at the bottom of this function. self.tokenizer.seek(SeekFrom::Current(-1))?; None } else { Some(self.expression()?.ok_or(Error::UnexpectedEOF)?) }; let ret_span = Span { start_line: ret_start_span.start_line, start_col: ret_start_span.start_col, end_line: expr .as_ref() .map(|e| e.span.end_line) .unwrap_or(ret_start_span.end_line), end_col: expr .as_ref() .map(|e| e.span.end_col) .unwrap_or(ret_start_span.end_col), }; let return_expr = Spanned { span: ret_span, node: Expression::Return(expr.map(Box::new)), }; expressions.push(return_expr); let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::RBrace)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } } Ok(BlockExpression(expressions)) } fn const_declaration(&mut self) -> Result, Error<'a>> { // 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 or syscall, making sure the syscall is supported in hash self.assign_next()?; // cache the current token location let current_token_index = self.tokenizer.loc(); if let Ok(lit) = self.spanned(|p| p.literal()) { Ok(ConstDeclarationExpression { name: Spanned { span: ident_span, node: ident, }, value: LiteralOr::Literal(lit), }) } else { // we need to rewind our tokenizer to our previous location self.tokenizer.seek(SeekFrom::Current( self.tokenizer.loc() - current_token_index, ))?; let syscall = self.spanned(|p| p.syscall())?; if !matches!( syscall, Spanned { node: SysCall::System(sys_call::System::Hash(_)), .. } ) { return Err(Error::UnexpectedToken( syscall.span, self.current_token.clone().ok_or(Error::UnexpectedEOF)?, )); } Ok(ConstDeclarationExpression { name: Spanned { span: ident_span, node: ident, }, value: LiteralOr::Or(syscall), }) } } fn declaration(&mut self) -> Result, Error<'a>> { let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let identifier_span = Self::token_to_span(&identifier_token); let identifier = match identifier_token.token_type { TokenType::Identifier(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken( Self::token_to_span(&identifier_token), identifier_token, )); } }; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone(); if !token_matches!(current_token, TokenType::Symbol(Symbol::Assign)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token.clone(), )); } self.assign_next()?; let assignment_expression = self.expression()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::Semicolon)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token, )); } Ok(Expression::Declaration( Spanned { span: identifier_span, node: identifier, }, boxed!(assignment_expression), )) } fn literal(&mut self) -> Result, Error<'a>> { let current_token = self.current_token.clone().ok_or(Error::UnexpectedEOF)?; let literal = match current_token.token_type { TokenType::Number(num) => Literal::Number(num), TokenType::String(ref string) => Literal::String(string.clone()), TokenType::Boolean(boolean) => Literal::Boolean(boolean), TokenType::Symbol(Symbol::Minus) => match self.get_next()? { Some(Token { token_type: TokenType::Number(num), .. }) => Literal::Number(-num), Some(wrong_token) => { return Err(Error::UnexpectedToken( Self::token_to_span(&wrong_token), wrong_token, )); } None => return Err(Error::UnexpectedEOF), }, _ => { return Err(Error::UnexpectedToken( self.current_span(), current_token.clone(), )); } }; Ok(literal) } fn if_expression(&mut self) -> Result, Error<'a>> { // 'if' is current let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } self.assign_next()?; let condition = self.expression()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } let body = self.spanned(|p| p.block())?; let else_branch = if self_matches_peek!(self, TokenType::Keyword(Keyword::Else)) { self.assign_next()?; if self_matches_peek!(self, TokenType::Keyword(Keyword::If)) { self.assign_next()?; // Recurse for else if let if_expr = self.spanned(|p| p.if_expression())?; Some(boxed!(Spanned { span: if_expr.span, node: Expression::If(if_expr), })) } else if self_matches_peek!(self, TokenType::Symbol(Symbol::LBrace)) { self.assign_next()?; let block = self.spanned(|p| p.block())?; Some(boxed!(Spanned { span: block.span, node: Expression::Block(block), })) } else { let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } } else { None }; Ok(IfExpression { condition: boxed!(condition), body, else_branch, }) } fn loop_expression(&mut self) -> Result, Error<'a>> { let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } let body = self.spanned(|p| p.block())?; Ok(LoopExpression { body }) } fn while_expression(&mut self) -> Result, Error<'a>> { let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } self.assign_next()?; let condition = self.expression()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) { return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } let body = self.block()?; Ok(WhileExpression { condition: boxed!(condition), body, }) } fn function(&mut self) -> Result, Error<'a>> { // 'fn' is current let fn_ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let fn_ident_span = Self::token_to_span(&fn_ident_token); let fn_ident = match fn_ident_token.token_type { TokenType::Identifier(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken(fn_ident_span, fn_ident_token)); } }; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token, )); } let mut arguments = Vec::>>::new(); while !token_matches!( self.get_next()?.ok_or(Error::UnexpectedEOF)?, TokenType::Symbol(Symbol::RParen) ) { let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let arg_span = Self::token_to_span(current_token); let argument = match current_token.token_type { TokenType::Identifier(ref id) => id.clone(), _ => { return Err(Error::UnexpectedToken(arg_span, current_token.clone())); } }; let spanned_arg = Spanned { span: arg_span, node: argument, }; if arguments.contains(&spanned_arg) { return Err(Error::DuplicateIdentifier( Self::token_to_span(current_token), current_token.clone(), )); } arguments.push(spanned_arg); if !self_matches_peek!(self, TokenType::Symbol(Symbol::Comma)) && !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; return Err(Error::UnexpectedToken(Self::token_to_span(&next), next)); } if !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { self.assign_next()?; } } let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; if !token_matches!(current_token, TokenType::Symbol(Symbol::LBrace)) { return Err(Error::UnexpectedToken( Self::token_to_span(¤t_token), current_token, )); }; Ok(FunctionExpression { name: Spanned { span: fn_ident_span, node: fn_ident, }, arguments, body: self.block()?, }) } fn syscall(&mut self) -> Result, Error<'a>> { let invocation = self.invocation()?; let check_length = |len: usize| -> Result<(), Error> { if invocation.arguments.len() != len { return Err(Error::InvalidSyntax( self.current_span(), format!("Expected {} arguments", len), )); } Ok(()) }; macro_rules! args { ($count:expr) => {{ check_length($count)?; invocation.arguments.into_iter() }}; } macro_rules! literal_or_variable { ($iter:expr) => { match &$iter { Some(expr) => { let span = expr.span; match &expr.node { Expression::Literal(literal) => Spanned { span, node: LiteralOrVariable::Literal(literal.node.clone()), }, Expression::Variable(ident) => Spanned { span, node: LiteralOrVariable::Variable(ident.clone()), }, _ => { return Err(Error::InvalidSyntax( expr.span, "Expected a literal or variable".to_string(), )); } } } _ => { return Err(Error::UnexpectedToken( self.current_span(), self.current_token.clone().ok_or(Error::UnexpectedEOF)?, )) } } }; } macro_rules! get_arg { ($matcher: ident, $arg: expr) => { match $arg.node { LiteralOrVariable::$matcher(i) => Spanned { node: i, span: $arg.span, }, _ => { return Err(Error::InvalidSyntax( $arg.span, format!("Expected a {}", stringify!($matcher).to_lowercase()), )) } } }; } match invocation.name.node.as_ref() { // System SysCalls "yield" => { check_length(0)?; Ok(SysCall::System(sys_call::System::Yield)) } "sleep" => { let mut args = args!(1); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(System::Sleep(boxed!(expr)))) } "hash" => { let mut args = args!(1); let lit_str = literal_or_variable!(args.next()); let Spanned { node: LiteralOrVariable::Literal(lit_str), span, } = lit_str else { return Err(Error::InvalidSyntax( lit_str.span, "Expected a string literal".to_string(), )); }; Ok(SysCall::System(System::Hash(Spanned { node: lit_str, span, }))) } "load" | "l" => { let mut args = args!(2); let tmp = args.next(); let device = literal_or_variable!(tmp); let next_arg = args.next(); let variable = match next_arg { Some(expr) => match expr.node { Expression::Literal(spanned_lit) => match spanned_lit.node { Literal::String(s) => Spanned { node: s, span: spanned_lit.span, }, _ => { return Err(Error::InvalidSyntax( spanned_lit.span, "Expected a string literal".to_string(), )); } }, _ => { return Err(Error::InvalidSyntax( expr.span, "Expected a string literal".to_string(), )); } }, _ => { return Err(Error::UnexpectedToken( self.current_span(), self.current_token.clone().ok_or(Error::UnexpectedEOF)?, )); } }; Ok(SysCall::System(sys_call::System::LoadFromDevice( device, Spanned { node: Literal::String(variable.node), span: variable.span, }, ))) } "loadBatched" | "lb" => { let mut args = args!(3); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); let tmp = args.next(); let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); let tmp = args.next(); let batch_mode = get_arg!(Literal, literal_or_variable!(tmp)); Ok(SysCall::System(System::LoadBatch( device_hash, logic_type, batch_mode, ))) } "loadBatchedNamed" | "lbn" => { let mut args = args!(4); let tmp = args.next(); let dev_hash = literal_or_variable!(tmp); let tmp = args.next(); let name_hash = literal_or_variable!(tmp); let tmp = args.next(); let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); let tmp = args.next(); let batch_mode = get_arg!(Literal, literal_or_variable!(tmp)); Ok(SysCall::System(System::LoadBatchNamed( dev_hash, name_hash, logic_type, batch_mode, ))) } "set" | "s" => { let mut args = args!(3); let tmp = args.next(); let device = literal_or_variable!(tmp); let tmp = args.next(); let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); let variable = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(sys_call::System::SetOnDevice( device, Spanned { node: Literal::String(Cow::from( logic_type.node.to_string().replace("\"", ""), )), span: logic_type.span, }, boxed!(variable), ))) } "setBatched" | "sb" => { let mut args = args!(3); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); let tmp = args.next(); let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); let variable = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(sys_call::System::SetOnDeviceBatched( device_hash, Spanned { node: Literal::String(Cow::from(logic_type.to_string().replace("\"", ""))), span: logic_type.span, }, boxed!(variable), ))) } "setBatchedNamed" | "sbn" => { let mut args = args!(4); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); let tmp = args.next(); let name_hash = literal_or_variable!(tmp); let tmp = args.next(); let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); let tmp = args.next(); let expr = Box::new(tmp.ok_or(Error::UnexpectedEOF)?); Ok(SysCall::System(System::SetOnDeviceBatchedNamed( device_hash, name_hash, logic_type, expr, ))) } "loadSlot" | "ls" => { let mut args = args!(3); let next = args.next(); let dev_name = literal_or_variable!(next); let next = args.next(); let slot_index = get_arg!(Literal, literal_or_variable!(next)); if !matches!( slot_index, Spanned { node: Literal::Number(_), .. }, ) { return Err(Error::InvalidSyntax( slot_index.span, "Expected a number".to_string(), )); } let next = args.next(); let slot_logic = get_arg!(Literal, literal_or_variable!(next)); if !matches!( slot_logic, Spanned { node: Literal::String(_), .. } ) { return Err(Error::InvalidSyntax( slot_logic.span, "Expected a String".into(), )); } Ok(SysCall::System(System::LoadSlot( dev_name, slot_index, slot_logic, ))) } "setSlot" | "ss" => { let mut args = args!(4); let next = args.next(); let dev_name = literal_or_variable!(next); let next = args.next(); let slot_index = get_arg!(Literal, literal_or_variable!(next)); if !matches!( slot_index, Spanned { node: Literal::Number(_), .. } ) { return Err(Error::InvalidSyntax( slot_index.span, "Expected a number".into(), )); } let next = args.next(); let slot_logic = get_arg!(Literal, literal_or_variable!(next)); if !matches!( slot_logic, Spanned { node: Literal::String(_), .. } ) { return Err(Error::InvalidSyntax( slot_logic.span, "Expected a string".into(), )); } let next = args.next(); let expr = next.ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(System::SetSlot( dev_name, slot_index, slot_logic, Box::new(expr), ))) } "loadReagent" | "lr" => { let mut args = args!(3); let next = args.next(); let device = literal_or_variable!(next); let next = args.next(); let reagent_mode = get_arg!(Literal, literal_or_variable!(next)); let reagent_hash = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(System::LoadReagent( device, reagent_mode, Box::new(reagent_hash), ))) } // Math SysCalls "acos" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Acos(boxed!(tmp)))) } "asin" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Asin(boxed!(tmp)))) } "atan" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Atan(boxed!(expr)))) } "atan2" => { check_length(2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Atan2(boxed!(arg1), boxed!(arg2)))) } "abs" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Abs(boxed!(expr)))) } "ceil" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Ceil(boxed!(arg)))) } "cos" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Cos(boxed!(arg)))) } "floor" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Floor(boxed!(arg)))) } "log" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Log(boxed!(arg)))) } "max" => { check_length(2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Max(boxed!(arg1), boxed!(arg2)))) } "min" => { check_length(2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Min(boxed!(arg1), boxed!(arg2)))) } "rand" => { check_length(0)?; Ok(SysCall::Math(Math::Rand)) } "sin" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Sin(boxed!(arg)))) } "sqrt" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Sqrt(boxed!(arg)))) } "tan" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Tan(boxed!(arg)))) } "trunc" => { check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Trunc(boxed!(arg)))) } _ => Err(Error::UnsupportedKeyword( self.current_span(), self.current_token.clone().ok_or(Error::UnexpectedEOF)?, )), } } }