2047 lines
73 KiB
Rust
2047 lines
73 KiB
Rust
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<Error<'a>> 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<Token<'a>>,
|
|
pub errors: Vec<Error<'a>>,
|
|
}
|
|
|
|
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<F, T>(&mut self, parser: F) -> Result<Spanned<T>, Error<'a>>
|
|
where
|
|
F: FnOnce(&mut Self) -> Result<T, Error<'a>>,
|
|
{
|
|
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<Option<tree_node::Expression<'a>>, 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::<Spanned<Expression<'a>>>::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<Option<Spanned<tree_node::Expression<'a>>>, 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<Option<Token<'a>>, Error<'a>> {
|
|
self.assign_next()?;
|
|
Ok(self.current_token.clone())
|
|
}
|
|
|
|
fn expression(&mut self) -> Result<Option<Spanned<tree_node::Expression<'a>>>, 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<Expression<'a>>,
|
|
) -> Result<Spanned<Expression<'a>>, 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::<Spanned<Expression<'a>>>::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<Option<Spanned<tree_node::Expression<'a>>>, 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<Spanned<tree_node::Expression<'a>>, 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<DeviceDeclarationExpression<'a>, 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<Expression<'a>>,
|
|
) -> Result<Spanned<Expression<'a>>, 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::<Symbol>::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<Option<Box<Spanned<Expression<'a>>>>, 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<InvocationExpression<'a>, 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::<Spanned<Expression>>::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<BlockExpression<'a>, Error<'a>> {
|
|
let mut expressions = Vec::<Spanned<Expression>>::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<ConstDeclarationExpression<'a>, 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<Expression<'a>, 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<Literal<'a>, 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<IfExpression<'a>, 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<LoopExpression<'a>, 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<WhileExpression<'a>, 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<FunctionExpression<'a>, 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::<Spanned<Cow<'a, str>>>::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<SysCall<'a>, 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)?,
|
|
)),
|
|
}
|
|
}
|
|
}
|