Merge pull request #23 from dbidwell94/slot-logic

Slot logic
This commit is contained in:
2025-12-10 00:11:45 -07:00
committed by GitHub
13 changed files with 300 additions and 56 deletions

View File

@@ -1,5 +1,10 @@
# Changelog # Changelog
[0.2.1]
- Added support for `loadSlot` and `setSlot`
- Fixed bug where syscalls like `max(1, 2)` were not allowed in assignment expressions
[0.2.0] [0.2.0]
- Completely re-wrote the tokenizer to use `logos` - Completely re-wrote the tokenizer to use `logos`

View File

@@ -2,7 +2,7 @@
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Slang</Name> <Name>Slang</Name>
<Author>JoeDiertay</Author> <Author>JoeDiertay</Author>
<Version>0.2.0</Version> <Version>0.2.1</Version>
<Description> <Description>
[h1]Slang: High-Level Programming for Stationeers[/h1] [h1]Slang: High-Level Programming for Stationeers[/h1]

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyName>StationeersSlang</AssemblyName> <AssemblyName>StationeersSlang</AssemblyName>
<Description>Slang Compiler Bridge</Description> <Description>Slang Compiler Bridge</Description>
<Version>0.2.0</Version> <Version>0.2.1</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@@ -909,7 +909,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "slang" name = "slang"
version = "0.2.0" version = "0.2.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "slang" name = "slang"
version = "0.2.0" version = "0.2.1"
edition = "2021" edition = "2021"
[workspace] [workspace]

View File

@@ -243,6 +243,32 @@ fn test_max() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_max_from_game() -> Result<()> {
let compiled = compile! {
debug
r#"
let item = 0;
item = max(1 + 2, 2);
"#
};
assert_eq!(
compiled,
indoc! {
"
j main
main:
move r8 0 #item
max r15 3 2
move r8 r15 #item
"
}
);
Ok(())
}
#[test] #[test]
fn test_min() -> Result<()> { fn test_min() -> Result<()> {
let compiled = compile! { let compiled = compile! {

View File

@@ -157,3 +157,54 @@ fn test_load_from_device() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_load_from_slot() -> anyhow::Result<()> {
let compiled = compile! {
debug
r#"
device airCon = "d0";
let setting = ls(airCon, 0, "Occupied");
"#
};
assert_eq!(
compiled,
indoc! {
"
j main
main:
ls r15 d0 0 Occupied
move r8 r15 #setting
"
}
);
Ok(())
}
#[test]
fn test_set_slot() -> anyhow::Result<()> {
let compiled = compile! {
debug
r#"
device airCon = "d0";
ss(airCon, 0, "Occupied", true);
"#
};
assert_eq!(
compiled,
indoc! {
"
j main
main:
ss d0 0 Occupied 1
"
}
);
Ok(())
}

View File

@@ -865,6 +865,7 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> {
scope.free_temp(c, None)?; scope.free_temp(c, None)?;
} }
} }
_ => { _ => {
return Err(Error::Unknown( return Err(Error::Unknown(
"Invalid assignment target. Only variables and member access are supported." "Invalid assignment target. Only variables and member access are supported."
@@ -1952,6 +1953,55 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> {
temp_name: None, temp_name: None,
})) }))
} }
System::LoadSlot(dev_name, slot_index, logic_type) => {
let (dev_hash, hash_cleanup) =
self.compile_literal_or_variable(dev_name.node, scope)?;
let (slot_index, slot_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(slot_index.node),
scope,
)?;
let (logic_type, logic_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(logic_type.node),
scope,
)?;
self.write_output(format!(
"ls r{} {} {} {}",
VariableScope::RETURN_REGISTER,
dev_hash,
slot_index,
logic_type
))?;
cleanup!(hash_cleanup, slot_cleanup, logic_cleanup);
Ok(Some(CompilationResult {
location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
temp_name: None,
}))
}
System::SetSlot(dev_name, slot_index, logic_type, var) => {
let (dev_name, name_cleanup) =
self.compile_literal_or_variable(dev_name.node, scope)?;
let (slot_index, index_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(slot_index.node),
scope,
)?;
let (logic_type, type_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(logic_type.node),
scope,
)?;
let (var, var_cleanup) = self.compile_operand(*var, scope)?;
self.write_output(format!(
"ss {} {} {} {}",
dev_name, slot_index, logic_type, var
))?;
cleanup!(name_cleanup, index_cleanup, type_cleanup, var_cleanup);
Ok(None)
}
} }
} }

View File

@@ -52,7 +52,7 @@ pub enum LocationRequest {
Stack, Stack,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum VariableLocation<'a> { pub enum VariableLocation<'a> {
/// Represents a temporary register (r1 - r7) /// Represents a temporary register (r1 - r7)
Temporary(u8), Temporary(u8),
@@ -66,7 +66,6 @@ pub enum VariableLocation<'a> {
Device(Cow<'a, str>), Device(Cow<'a, str>),
} }
// FIX: Added 'b lifetime for the parent reference
pub struct VariableScope<'a, 'b> { pub struct VariableScope<'a, 'b> {
temporary_vars: VecDeque<u8>, temporary_vars: VecDeque<u8>,
persistant_vars: VecDeque<u8>, persistant_vars: VecDeque<u8>,
@@ -75,7 +74,6 @@ pub struct VariableScope<'a, 'b> {
parent: Option<&'b VariableScope<'a, 'b>>, parent: Option<&'b VariableScope<'a, 'b>>,
} }
// FIX: Updated Default impl to include 'b
impl<'a, 'b> Default for VariableScope<'a, 'b> { impl<'a, 'b> Default for VariableScope<'a, 'b> {
fn default() -> Self { fn default() -> Self {
Self { Self {
@@ -88,7 +86,6 @@ impl<'a, 'b> Default for VariableScope<'a, 'b> {
} }
} }
// FIX: Updated impl block to include 'b
impl<'a, 'b> VariableScope<'a, 'b> { impl<'a, 'b> VariableScope<'a, 'b> {
#[allow(dead_code)] #[allow(dead_code)]
pub const TEMP_REGISTER_COUNT: u8 = 7; pub const TEMP_REGISTER_COUNT: u8 = 7;
@@ -112,7 +109,6 @@ impl<'a, 'b> VariableScope<'a, 'b> {
}) })
} }
// FIX: parent is now &'b VariableScope<'a, 'b>
pub fn scoped(parent: &'b VariableScope<'a, 'b>) -> Self { pub fn scoped(parent: &'b VariableScope<'a, 'b>) -> Self {
Self { Self {
parent: Option::Some(parent), parent: Option::Some(parent),

View File

@@ -9,9 +9,11 @@ macro_rules! with_syscalls {
"load", "load",
"loadBatched", "loadBatched",
"loadBatchedNamed", "loadBatchedNamed",
"loadSlot",
"set", "set",
"setBatched", "setBatched",
"setBatchedNamed", "setBatchedNamed",
"setSlot",
"acos", "acos",
"asin", "asin",
"atan", "atan",
@@ -32,9 +34,11 @@ macro_rules! with_syscalls {
"l", "l",
"lb", "lb",
"lbn", "lbn",
"ls",
"s", "s",
"sb", "sb",
"sbn" "sbn",
"ss"
); );
}; };
} }

View File

@@ -645,6 +645,15 @@ impl<'a> Parser<'a> {
.node .node
.ok_or(Error::UnexpectedEOF)?, .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(_) TokenType::Identifier(_)
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) => if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
{ {
@@ -1050,7 +1059,9 @@ impl<'a> Parser<'a> {
if token_matches!( if token_matches!(
temp_token, temp_token,
TokenType::Symbol(Symbol::Semicolon) | TokenType::Symbol(Symbol::RParen) TokenType::Symbol(Symbol::Semicolon)
| TokenType::Symbol(Symbol::RParen)
| TokenType::Symbol(Symbol::Comma)
) { ) {
self.tokenizer.seek(SeekFrom::Current(-1))?; self.tokenizer.seek(SeekFrom::Current(-1))?;
} }
@@ -1518,18 +1529,23 @@ impl<'a> Parser<'a> {
} }
fn syscall(&mut self) -> Result<SysCall<'a>, Error<'a>> { fn syscall(&mut self) -> Result<SysCall<'a>, Error<'a>> {
fn check_length<'a>( let invocation = self.invocation()?;
span: Span,
arguments: &[Spanned<Expression<'a>>], let check_length = |len: usize| -> Result<(), Error> {
length: usize, if invocation.arguments.len() != len {
) -> Result<(), Error<'a>> {
if arguments.len() != length {
return Err(Error::InvalidSyntax( return Err(Error::InvalidSyntax(
span, self.current_span(),
format!("Expected {} arguments", length), format!("Expected {} arguments", len),
)); ));
} }
Ok(()) Ok(())
};
macro_rules! args {
($count:expr) => {{
check_length($count)?;
invocation.arguments.into_iter()
}};
} }
macro_rules! literal_or_variable { macro_rules! literal_or_variable {
@@ -1581,23 +1597,19 @@ impl<'a> Parser<'a> {
}; };
} }
let invocation = self.invocation()?;
match invocation.name.node.as_ref() { match invocation.name.node.as_ref() {
// System SysCalls // System SysCalls
"yield" => { "yield" => {
check_length(self.current_span(), &invocation.arguments, 0)?; check_length(0)?;
Ok(SysCall::System(sys_call::System::Yield)) Ok(SysCall::System(sys_call::System::Yield))
} }
"sleep" => { "sleep" => {
check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = args!(1);
let mut arg = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?;
let expr = arg.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::System(System::Sleep(boxed!(expr)))) Ok(SysCall::System(System::Sleep(boxed!(expr))))
} }
"hash" => { "hash" => {
check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = args!(1);
let mut args = invocation.arguments.into_iter();
let lit_str = literal_or_variable!(args.next()); let lit_str = literal_or_variable!(args.next());
let Spanned { let Spanned {
@@ -1617,8 +1629,7 @@ impl<'a> Parser<'a> {
}))) })))
} }
"load" | "l" => { "load" | "l" => {
check_length(self.current_span(), &invocation.arguments, 2)?; let mut args = args!(2);
let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
let device = literal_or_variable!(tmp); let device = literal_or_variable!(tmp);
@@ -1662,8 +1673,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"loadBatched" | "lb" => { "loadBatched" | "lb" => {
check_length(self.current_span(), &invocation.arguments, 3)?; let mut args = args!(3);
let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
let device_hash = literal_or_variable!(tmp); let device_hash = literal_or_variable!(tmp);
@@ -1680,8 +1690,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"loadBatchedNamed" | "lbn" => { "loadBatchedNamed" | "lbn" => {
check_length(self.current_span(), &invocation.arguments, 4)?; let mut args = args!(4);
let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
let dev_hash = literal_or_variable!(tmp); let dev_hash = literal_or_variable!(tmp);
@@ -1699,8 +1708,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"set" | "s" => { "set" | "s" => {
check_length(self.current_span(), &invocation.arguments, 3)?; let mut args = args!(3);
let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
let device = literal_or_variable!(tmp); let device = literal_or_variable!(tmp);
@@ -1720,8 +1728,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"setBatched" | "sb" => { "setBatched" | "sb" => {
check_length(self.current_span(), &invocation.arguments, 3)?; let mut args = args!(3);
let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
let device_hash = literal_or_variable!(tmp); let device_hash = literal_or_variable!(tmp);
@@ -1739,8 +1746,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"setBatchedNamed" | "sbn" => { "setBatchedNamed" | "sbn" => {
check_length(self.current_span(), &invocation.arguments, 4)?; let mut args = args!(4);
let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
let device_hash = literal_or_variable!(tmp); let device_hash = literal_or_variable!(tmp);
@@ -1760,30 +1766,110 @@ impl<'a> Parser<'a> {
expr, 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),
)))
}
// Math SysCalls // Math SysCalls
"acos" => { "acos" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let tmp = args.next().ok_or(Error::UnexpectedEOF)?; let tmp = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Acos(boxed!(tmp)))) Ok(SysCall::Math(Math::Acos(boxed!(tmp))))
} }
"asin" => { "asin" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let tmp = args.next().ok_or(Error::UnexpectedEOF)?; let tmp = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Asin(boxed!(tmp)))) Ok(SysCall::Math(Math::Asin(boxed!(tmp))))
} }
"atan" => { "atan" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let expr = args.next().ok_or(Error::UnexpectedEOF)?; let expr = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Atan(boxed!(expr)))) Ok(SysCall::Math(Math::Atan(boxed!(expr))))
} }
"atan2" => { "atan2" => {
check_length(self.current_span(), &invocation.arguments, 2)?; check_length(2)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg1 = args.next().ok_or(Error::UnexpectedEOF)?;
let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?;
@@ -1791,42 +1877,42 @@ impl<'a> Parser<'a> {
Ok(SysCall::Math(Math::Atan2(boxed!(arg1), boxed!(arg2)))) Ok(SysCall::Math(Math::Atan2(boxed!(arg1), boxed!(arg2))))
} }
"abs" => { "abs" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let expr = args.next().ok_or(Error::UnexpectedEOF)?; let expr = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Abs(boxed!(expr)))) Ok(SysCall::Math(Math::Abs(boxed!(expr))))
} }
"ceil" => { "ceil" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Ceil(boxed!(arg)))) Ok(SysCall::Math(Math::Ceil(boxed!(arg))))
} }
"cos" => { "cos" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Cos(boxed!(arg)))) Ok(SysCall::Math(Math::Cos(boxed!(arg))))
} }
"floor" => { "floor" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Floor(boxed!(arg)))) Ok(SysCall::Math(Math::Floor(boxed!(arg))))
} }
"log" => { "log" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Log(boxed!(arg)))) Ok(SysCall::Math(Math::Log(boxed!(arg))))
} }
"max" => { "max" => {
check_length(self.current_span(), &invocation.arguments, 2)?; check_length(2)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg1 = args.next().ok_or(Error::UnexpectedEOF)?;
let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?;
@@ -1834,7 +1920,7 @@ impl<'a> Parser<'a> {
Ok(SysCall::Math(Math::Max(boxed!(arg1), boxed!(arg2)))) Ok(SysCall::Math(Math::Max(boxed!(arg1), boxed!(arg2))))
} }
"min" => { "min" => {
check_length(self.current_span(), &invocation.arguments, 2)?; check_length(2)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg1 = args.next().ok_or(Error::UnexpectedEOF)?;
let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?;
@@ -1842,32 +1928,32 @@ impl<'a> Parser<'a> {
Ok(SysCall::Math(Math::Min(boxed!(arg1), boxed!(arg2)))) Ok(SysCall::Math(Math::Min(boxed!(arg1), boxed!(arg2))))
} }
"rand" => { "rand" => {
check_length(self.current_span(), &invocation.arguments, 0)?; check_length(0)?;
Ok(SysCall::Math(Math::Rand)) Ok(SysCall::Math(Math::Rand))
} }
"sin" => { "sin" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Sin(boxed!(arg)))) Ok(SysCall::Math(Math::Sin(boxed!(arg))))
} }
"sqrt" => { "sqrt" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Sqrt(boxed!(arg)))) Ok(SysCall::Math(Math::Sqrt(boxed!(arg))))
} }
"tan" => { "tan" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Tan(boxed!(arg)))) Ok(SysCall::Math(Math::Tan(boxed!(arg))))
} }
"trunc" => { "trunc" => {
check_length(self.current_span(), &invocation.arguments, 1)?; check_length(1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?; let arg = args.next().ok_or(Error::UnexpectedEOF)?;

View File

@@ -214,6 +214,30 @@ documented! {
Spanned<Literal<'a>>, Spanned<Literal<'a>>,
Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>,
), ),
/// Loads slot LogicSlotType from device into a variable
///
/// ## IC10
/// `ls r0 d0 2 Occupied`
/// ## Slang
/// `let isOccupied = loadSlot(deviceHash, 2, "Occupied");`
/// `let isOccupied = ls(deviceHash, 2, "Occupied");`
LoadSlot(
Spanned<LiteralOrVariable<'a>>,
Spanned<Literal<'a>>,
Spanned<Literal<'a>>
),
/// Stores a value of LogicType on a device by the index value
/// ## IC10
/// `ss d0 0 "Open" 1`
/// ## Slang
/// `setSlot(deviceHash, 0, "Open", true);`
/// `ss(deviceHash, 0, "Open", true);`
SetSlot(
Spanned<LiteralOrVariable<'a>>,
Spanned<Literal<'a>>,
Spanned<Literal<'a>>,
Box<Spanned<Expression<'a>>>
)
} }
} }
@@ -235,6 +259,8 @@ impl<'a> std::fmt::Display for System<'a> {
System::SetOnDeviceBatchedNamed(a, b, c, d) => { System::SetOnDeviceBatchedNamed(a, b, c, d) => {
write!(f, "setOnDeviceBatchedNamed({}, {}, {}, {})", a, b, c, d) write!(f, "setOnDeviceBatchedNamed({}, {}, {}, {})", a, b, c, d)
} }
System::LoadSlot(a, b, c) => write!(f, "loadSlot({}, {}, {})", a, b, c),
System::SetSlot(a, b, c, d) => write!(f, "setSlot({}, {}, {}, {})", a, b, c, d),
} }
} }
} }

View File

@@ -39,7 +39,7 @@ impl From<LexError> for Diagnostic {
..Default::default() ..Default::default()
} }
} }
_ => todo!(), _ => Diagnostic::default(),
} }
} }
} }