First pass with syscalls in the AST

This commit is contained in:
2024-12-01 22:18:09 -07:00
parent bce9a55721
commit 8132241eb3
6 changed files with 202 additions and 28 deletions

View File

@@ -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!(),
}
}
}

View File

@@ -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),
}
}
}

View File

@@ -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),

View File

@@ -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!() => {

View File

@@ -217,4 +217,6 @@ pub enum Keyword {
Enum,
/// Represents the `loop` keyword
Loop,
/// Represents the `break` keyword
Break,
}