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> {
|
||||
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 {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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!() => {
|
||||
|
||||
@@ -217,4 +217,6 @@ pub enum Keyword {
|
||||
Enum,
|
||||
/// Represents the `loop` keyword
|
||||
Loop,
|
||||
/// Represents the `break` keyword
|
||||
Break,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user