diff --git a/src/parser/mod.rs b/src/parser/mod.rs index dc88d66..4283d25 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -690,7 +690,178 @@ impl Parser { } fn syscall(&mut self) -> Result { - todo!("Syscalls are not implemented yet.") + /// Checks the length of the arguments and returns an error if the length is not equal to the expected length + fn check_length( + parser: &Parser, + arguments: &[Expression], + length: usize, + ) -> Result<(), ParseError> { + if arguments.len() != length { + return Err(ParseError::InvalidSyntax( + token_from_option!(parser.current_token).clone(), + format!("Expected {} arguments", length), + )); + } + Ok(()) + } + /// Converts an expression to "literal or variable" expression + macro_rules! literal_or_variable { + ($iter:expr) => { + match $iter { + Some(Expression::Literal(literal)) => { + LiteralOrVariable::Literal(literal.clone()) + } + Some(Expression::Variable(ident)) => LiteralOrVariable::Variable(ident.clone()), + _ => { + return Err(ParseError::UnexpectedToken( + token_from_option!(self.current_token).clone(), + )) + } + } + }; + } + + /// Gets the argument from the expression and returns an error if the expression does not match the expected pattern + macro_rules! get_arg { + ($matcher: ident, $arg: expr) => { + match $arg { + LiteralOrVariable::$matcher(i) => i, + _ => { + return Err(ParseError::InvalidSyntax( + token_from_option!(self.current_token).clone(), + String::from("Expected a variable"), + )) + } + } + }; + } + + // A syscall is essentially an invocation expression with a syscall identifier. So we can reuse the invocation function + let invocation = self.invocation()?; + + match invocation.name.as_str() { + // system calls + "yield" => return Ok(SysCall::System(sys_call::System::Yield)), + "sleep" => { + check_length(self, &invocation.arguments, 1)?; + let mut arg = invocation.arguments.iter(); + let argument = literal_or_variable!(arg.next()); + return Ok(SysCall::System(sys_call::System::Sleep(argument))); + } + "loadFromDevice" => { + check_length(&self, &invocation.arguments, 2)?; + let mut args = invocation.arguments.iter(); + + let device = literal_or_variable!(args.next()); + + let variable = get_arg!(Variable, literal_or_variable!(args.next())); + + return Ok(SysCall::System(sys_call::System::LoadFromDevice( + device, variable, + ))); + } + "setOnDevice" => { + check_length(&self, &invocation.arguments, 3)?; + let mut args = invocation.arguments.iter(); + + let device = literal_or_variable!(args.next()); + + let logic_type = get_arg!(Variable, literal_or_variable!(args.next())); + + let register = get_arg!(Variable, literal_or_variable!(args.next())); + + return Ok(SysCall::System(sys_call::System::SetOnDevice( + device, logic_type, register, + ))); + } + // math calls + "acos" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Acos(arg))); + } + "asin" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Asin(arg))); + } + "atan" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Atan(arg))); + } + "atan2" => { + check_length(&self, &invocation.arguments, 2)?; + let mut args = invocation.arguments.iter(); + let arg1 = literal_or_variable!(args.next()); + let arg2 = literal_or_variable!(args.next()); + return Ok(SysCall::Math(sys_call::Math::Atan2(arg1, arg2))); + } + "abs" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Abs(arg))); + } + "ceil" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Ceil(arg))); + } + "cos" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Cos(arg))); + } + "floor" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Floor(arg))); + } + "log" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Log(arg))); + } + "max" => { + check_length(&self, &invocation.arguments, 2)?; + let mut args = invocation.arguments.iter(); + let arg1 = literal_or_variable!(args.next()); + let arg2 = literal_or_variable!(args.next()); + return Ok(SysCall::Math(sys_call::Math::Max(arg1, arg2))); + } + "min" => { + check_length(&self, &invocation.arguments, 2)?; + let mut args = invocation.arguments.iter(); + let arg1 = literal_or_variable!(args.next()); + let arg2 = literal_or_variable!(args.next()); + return Ok(SysCall::Math(sys_call::Math::Min(arg1, arg2))); + } + "rand" => { + check_length(&self, &invocation.arguments, 0)?; + return Ok(SysCall::Math(sys_call::Math::Rand)); + } + "sin" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Sin(arg))); + } + "sqrt" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Sqrt(arg))); + } + "tan" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Tan(arg))); + } + "trunc" => { + check_length(&self, &invocation.arguments, 1)?; + let arg = literal_or_variable!(invocation.arguments.first()); + return Ok(SysCall::Math(sys_call::Math::Trunc(arg))); + } + _ => todo!(), + } } } diff --git a/src/parser/sys_call.rs b/src/parser/sys_call.rs index cbc3575..a8c8be3 100644 --- a/src/parser/sys_call.rs +++ b/src/parser/sys_call.rs @@ -71,22 +71,22 @@ pub enum Math { impl std::fmt::Display for Math { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Math::Acos(a) => write!(f, "acos {}", a), - Math::Asin(a) => write!(f, "asin {}", a), - Math::Atan(a) => write!(f, "atan {}", a), - Math::Atan2(a, b) => write!(f, "atan2 {} {}", a, b), - Math::Abs(a) => write!(f, "abs {}", a), - Math::Ceil(a) => write!(f, "ceil {}", a), - Math::Cos(a) => write!(f, "cos {}", a), - Math::Floor(a) => write!(f, "floor {}", a), - Math::Log(a) => write!(f, "log {}", a), - Math::Max(a, b) => write!(f, "max {} {}", a, b), - Math::Min(a, b) => write!(f, "min {} {}", a, b), - Math::Rand => write!(f, "rand"), - Math::Sin(a) => write!(f, "sin {}", a), - Math::Sqrt(a) => write!(f, "sqrt {}", a), - Math::Tan(a) => write!(f, "tan {}", a), - Math::Trunc(a) => write!(f, "trunc {}", a), + Math::Acos(a) => write!(f, "acos({})", a), + Math::Asin(a) => write!(f, "asin({})", a), + Math::Atan(a) => write!(f, "atan({})", a), + Math::Atan2(a, b) => write!(f, "atan2({}, {})", a, b), + Math::Abs(a) => write!(f, "abs({})", a), + Math::Ceil(a) => write!(f, "ceil({})", a), + Math::Cos(a) => write!(f, "cos({})", a), + Math::Floor(a) => write!(f, "floor({})", a), + Math::Log(a) => write!(f, "log({})", a), + Math::Max(a, b) => write!(f, "max({}, {})", a, b), + Math::Min(a, b) => write!(f, "min({}, {})", a, b), + Math::Rand => write!(f, "rand()"), + Math::Sin(a) => write!(f, "sin({})", a), + Math::Sqrt(a) => write!(f, "sqrt({})", a), + Math::Tan(a) => write!(f, "tan({})", a), + Math::Trunc(a) => write!(f, "trunc({})", a), } } } @@ -111,23 +111,23 @@ pub enum System { /// ## Examples /// `l r0 d0 Setting` /// `l r1 d5 Pressure` - Load(String, LiteralOrVariable), + LoadFromDevice(LiteralOrVariable, String), /// Represents a function which stores a setting into a specific device. /// ## In Game /// `s d? logicType r?` /// ## Example /// `s d0 Setting r0` - Store(String, LiteralOrVariable, LiteralOrVariable), + SetOnDevice(LiteralOrVariable, String, String), } impl std::fmt::Display for System { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - System::Yield => write!(f, "yield"), - System::Sleep(a) => write!(f, "sleep {}", a), + System::Yield => write!(f, "yield()"), + System::Sleep(a) => write!(f, "sleep({})", a), System::Hash(a) => write!(f, "HASH({})", a), - System::Load(a, b) => write!(f, "l {} {}", a, b), - System::Store(a, b, c) => write!(f, "s {} {} {}", a, b, c), + System::LoadFromDevice(a, b) => write!(f, "loadFromDevice({}, {})", a, b), + System::SetOnDevice(a, b, c) => write!(f, "setOnDevice({}, {}, {})", a, b, c), } } } diff --git a/src/parser/tree_node.rs b/src/parser/tree_node.rs index 9786bdf..0940a83 100644 --- a/src/parser/tree_node.rs +++ b/src/parser/tree_node.rs @@ -2,7 +2,7 @@ use crate::tokenizer::token::Number; use super::sys_call::SysCall; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub enum Literal { Number(Number), String(String), @@ -157,7 +157,7 @@ impl std::fmt::Display for LiteralOrVariable { #[derive(Debug, PartialEq, Eq)] pub struct DeviceDeclarationExpression { - /// any variable-like name + /// any variable-like name pub name: String, /// The device port, ex. (db, d0, d1, d2, d3, d4, d5) pub device: String, @@ -169,7 +169,6 @@ impl std::fmt::Display for DeviceDeclarationExpression { } } - #[derive(Debug, PartialEq, Eq)] pub enum Expression { AssignmentExpression(AssignmentExpression), diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index 0828bdc..643dd6a 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -413,6 +413,8 @@ impl Tokenizer { "return" if next_ws!() => keyword!(Return), "enum" if next_ws!() => keyword!(Enum), "device" if next_ws!() => keyword!(Device), + "loop" if next_ws!() => keyword!(Loop), + "break" if next_ws!() => keyword!(Break), // boolean literals "true" if next_ws!() => { diff --git a/src/tokenizer/token.rs b/src/tokenizer/token.rs index bbf04bf..f029855 100644 --- a/src/tokenizer/token.rs +++ b/src/tokenizer/token.rs @@ -217,4 +217,6 @@ pub enum Keyword { Enum, /// Represents the `loop` keyword Loop, + /// Represents the `break` keyword + Break, } diff --git a/tests/file.stlg b/tests/file.stlg index 530a510..34570b1 100644 --- a/tests/file.stlg +++ b/tests/file.stlg @@ -6,9 +6,9 @@ loop { let currentTemperature = loadFromDevice(roomTemperatureSensor, "Temperature"); if (currentTemperature > 25c) { - setOnDevice(airConditioner, "On", true); + setOnDevice(airConditioner, "Mode", "On"); } else if (currentTemperature <= 20c) { - setOnDevice(airConditioner, "On", false); + setOnDevice(airConditioner, "Mode", "Off"); }; };