First pass with syscalls in the AST
This commit is contained in:
@@ -690,7 +690,178 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn syscall(&mut self) -> Result<SysCall, ParseError> {
|
fn syscall(&mut self) -> Result<SysCall, ParseError> {
|
||||||
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!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,22 +71,22 @@ pub enum Math {
|
|||||||
impl std::fmt::Display for Math {
|
impl std::fmt::Display for Math {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Math::Acos(a) => write!(f, "acos {}", a),
|
Math::Acos(a) => write!(f, "acos({})", a),
|
||||||
Math::Asin(a) => write!(f, "asin {}", a),
|
Math::Asin(a) => write!(f, "asin({})", a),
|
||||||
Math::Atan(a) => write!(f, "atan {}", a),
|
Math::Atan(a) => write!(f, "atan({})", a),
|
||||||
Math::Atan2(a, b) => write!(f, "atan2 {} {}", a, b),
|
Math::Atan2(a, b) => write!(f, "atan2({}, {})", a, b),
|
||||||
Math::Abs(a) => write!(f, "abs {}", a),
|
Math::Abs(a) => write!(f, "abs({})", a),
|
||||||
Math::Ceil(a) => write!(f, "ceil {}", a),
|
Math::Ceil(a) => write!(f, "ceil({})", a),
|
||||||
Math::Cos(a) => write!(f, "cos {}", a),
|
Math::Cos(a) => write!(f, "cos({})", a),
|
||||||
Math::Floor(a) => write!(f, "floor {}", a),
|
Math::Floor(a) => write!(f, "floor({})", a),
|
||||||
Math::Log(a) => write!(f, "log {}", a),
|
Math::Log(a) => write!(f, "log({})", a),
|
||||||
Math::Max(a, b) => write!(f, "max {} {}", a, b),
|
Math::Max(a, b) => write!(f, "max({}, {})", a, b),
|
||||||
Math::Min(a, b) => write!(f, "min {} {}", a, b),
|
Math::Min(a, b) => write!(f, "min({}, {})", a, b),
|
||||||
Math::Rand => write!(f, "rand"),
|
Math::Rand => write!(f, "rand()"),
|
||||||
Math::Sin(a) => write!(f, "sin {}", a),
|
Math::Sin(a) => write!(f, "sin({})", a),
|
||||||
Math::Sqrt(a) => write!(f, "sqrt {}", a),
|
Math::Sqrt(a) => write!(f, "sqrt({})", a),
|
||||||
Math::Tan(a) => write!(f, "tan {}", a),
|
Math::Tan(a) => write!(f, "tan({})", a),
|
||||||
Math::Trunc(a) => write!(f, "trunc {}", a),
|
Math::Trunc(a) => write!(f, "trunc({})", a),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,23 +111,23 @@ pub enum System {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
/// `l r0 d0 Setting`
|
/// `l r0 d0 Setting`
|
||||||
/// `l r1 d5 Pressure`
|
/// `l r1 d5 Pressure`
|
||||||
Load(String, LiteralOrVariable),
|
LoadFromDevice(LiteralOrVariable, String),
|
||||||
/// Represents a function which stores a setting into a specific device.
|
/// Represents a function which stores a setting into a specific device.
|
||||||
/// ## In Game
|
/// ## In Game
|
||||||
/// `s d? logicType r?`
|
/// `s d? logicType r?`
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// `s d0 Setting r0`
|
/// `s d0 Setting r0`
|
||||||
Store(String, LiteralOrVariable, LiteralOrVariable),
|
SetOnDevice(LiteralOrVariable, String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for System {
|
impl std::fmt::Display for System {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
System::Yield => write!(f, "yield"),
|
System::Yield => write!(f, "yield()"),
|
||||||
System::Sleep(a) => write!(f, "sleep {}", a),
|
System::Sleep(a) => write!(f, "sleep({})", a),
|
||||||
System::Hash(a) => write!(f, "HASH({})", a),
|
System::Hash(a) => write!(f, "HASH({})", a),
|
||||||
System::Load(a, b) => write!(f, "l {} {}", a, b),
|
System::LoadFromDevice(a, b) => write!(f, "loadFromDevice({}, {})", a, b),
|
||||||
System::Store(a, b, c) => write!(f, "s {} {} {}", a, b, c),
|
System::SetOnDevice(a, b, c) => write!(f, "setOnDevice({}, {}, {})", a, b, c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::tokenizer::token::Number;
|
|||||||
|
|
||||||
use super::sys_call::SysCall;
|
use super::sys_call::SysCall;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
String(String),
|
String(String),
|
||||||
@@ -169,7 +169,6 @@ impl std::fmt::Display for DeviceDeclarationExpression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
AssignmentExpression(AssignmentExpression),
|
AssignmentExpression(AssignmentExpression),
|
||||||
|
|||||||
@@ -413,6 +413,8 @@ impl Tokenizer {
|
|||||||
"return" if next_ws!() => keyword!(Return),
|
"return" if next_ws!() => keyword!(Return),
|
||||||
"enum" if next_ws!() => keyword!(Enum),
|
"enum" if next_ws!() => keyword!(Enum),
|
||||||
"device" if next_ws!() => keyword!(Device),
|
"device" if next_ws!() => keyword!(Device),
|
||||||
|
"loop" if next_ws!() => keyword!(Loop),
|
||||||
|
"break" if next_ws!() => keyword!(Break),
|
||||||
|
|
||||||
// boolean literals
|
// boolean literals
|
||||||
"true" if next_ws!() => {
|
"true" if next_ws!() => {
|
||||||
|
|||||||
@@ -217,4 +217,6 @@ pub enum Keyword {
|
|||||||
Enum,
|
Enum,
|
||||||
/// Represents the `loop` keyword
|
/// Represents the `loop` keyword
|
||||||
Loop,
|
Loop,
|
||||||
|
/// Represents the `break` keyword
|
||||||
|
Break,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ loop {
|
|||||||
let currentTemperature = loadFromDevice(roomTemperatureSensor, "Temperature");
|
let currentTemperature = loadFromDevice(roomTemperatureSensor, "Temperature");
|
||||||
|
|
||||||
if (currentTemperature > 25c) {
|
if (currentTemperature > 25c) {
|
||||||
setOnDevice(airConditioner, "On", true);
|
setOnDevice(airConditioner, "Mode", "On");
|
||||||
} else if (currentTemperature <= 20c) {
|
} else if (currentTemperature <= 20c) {
|
||||||
setOnDevice(airConditioner, "On", false);
|
setOnDevice(airConditioner, "Mode", "Off");
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user