VariableManager lifetime errors

This commit is contained in:
2025-12-09 16:05:40 -07:00
parent f54214acb9
commit c3986ab4d9
3 changed files with 281 additions and 232 deletions

View File

@@ -12,6 +12,7 @@ use parser::{
},
};
use std::{
borrow::Cow,
collections::HashMap,
io::{BufWriter, Write},
};
@@ -36,7 +37,10 @@ macro_rules! debug {
};
}
fn extract_literal(literal: Literal, allow_strings: bool) -> Result<String, Error> {
fn extract_literal<'a>(
literal: Literal<'a>,
allow_strings: bool,
) -> Result<Cow<'a, str>, Error<'a>> {
if !allow_strings && matches!(literal, Literal::String(_)) {
return Err(Error::Unknown(
"Literal strings are not allowed in this context".to_string(),
@@ -45,45 +49,45 @@ fn extract_literal(literal: Literal, allow_strings: bool) -> Result<String, Erro
}
Ok(match literal {
Literal::String(s) => s,
Literal::Number(n) => n.to_string(),
Literal::Boolean(b) => if b { "1" } else { "0" }.into(),
Literal::Number(n) => Cow::from(n.to_string()),
Literal::Boolean(b) => Cow::from(if b { "1" } else { "0" }),
})
}
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Parse(#[from] parser::Error),
pub enum Error<'a> {
#[error("{0}")]
Parse(parser::Error<'a>),
#[error(transparent)]
Scope(#[from] variable_manager::Error),
#[error("{0}")]
Scope(variable_manager::Error<'a>),
#[error("IO Error: {0}")]
IO(String),
#[error("`{0}` has already been defined.")]
DuplicateIdentifier(String, Span),
DuplicateIdentifier(Cow<'a, str>, Span),
#[error("`{0}` is not found in the current scope.")]
UnknownIdentifier(String, Span),
UnknownIdentifier(Cow<'a, str>, Span),
#[error("`{0}` is not valid.")]
InvalidDevice(String, Span),
InvalidDevice(Cow<'a, str>, Span),
#[error("Incorrent number of arguments passed into `{0}`")]
AgrumentMismatch(String, Span),
AgrumentMismatch(Cow<'a, str>, Span),
#[error("Attempted to re-assign a value to const variable `{0}`")]
ConstAssignment(String, Span),
ConstAssignment(Cow<'a, str>, Span),
#[error("Attempted to re-assign a value to a device const `{0}`")]
DeviceAssignment(String, Span),
DeviceAssignment(Cow<'a, str>, Span),
#[error("{0}")]
Unknown(String, Option<Span>),
}
impl From<Error> for lsp_types::Diagnostic {
impl<'a> From<Error<'a>> for lsp_types::Diagnostic {
fn from(value: Error) -> Self {
use Error::*;
use lsp_types::*;
@@ -116,8 +120,20 @@ impl From<Error> for lsp_types::Diagnostic {
}
}
impl<'a> From<parser::Error<'a>> for Error<'a> {
fn from(value: parser::Error<'a>) -> Self {
Self::Parse(value)
}
}
impl<'a> From<variable_manager::Error<'a>> for Error<'a> {
fn from(value: variable_manager::Error<'a>) -> Self {
Self::Scope(value)
}
}
// Map io::Error to Error manually since we can't clone io::Error
impl From<std::io::Error> for Error {
impl<'a> From<std::io::Error> for Error<'a> {
fn from(err: std::io::Error) -> Self {
Error::IO(err.to_string())
}
@@ -129,26 +145,26 @@ pub struct CompilerConfig {
pub debug: bool,
}
struct CompilationResult {
location: VariableLocation,
struct CompilationResult<'a> {
location: VariableLocation<'a>,
/// If Some, this is the name of the temporary variable that holds the result.
/// It must be freed by the caller when done.
temp_name: Option<String>,
temp_name: Option<Cow<'a, str>>,
}
pub struct Compiler<'a, W: std::io::Write> {
pub parser: ASTParser<'a>,
function_locations: HashMap<String, usize>,
function_metadata: HashMap<String, Vec<String>>,
devices: HashMap<String, String>,
function_locations: HashMap<Cow<'a, str>, usize>,
function_metadata: HashMap<Cow<'a, str>, Vec<Cow<'a, str>>>,
devices: HashMap<Cow<'a, str>, Cow<'a, str>>,
output: &'a mut BufWriter<W>,
current_line: usize,
declared_main: bool,
config: CompilerConfig,
temp_counter: usize,
label_counter: usize,
loop_stack: Vec<(String, String)>, // Stores (start_label, end_label)
pub errors: Vec<Error>,
loop_stack: Vec<(Cow<'a, str>, Cow<'a, str>)>, // Stores (start_label, end_label)
pub errors: Vec<Error<'a>>,
}
impl<'a, W: std::io::Write> Compiler<'a, W> {
@@ -173,7 +189,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
}
pub fn compile(mut self) -> Vec<Error> {
pub fn compile(mut self) -> Vec<Error<'a>> {
let expr = self.parser.parse_all();
// Copy errors from parser
@@ -211,15 +227,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
return self.errors;
}
let mut scope = VariableScope::default();
// We ignore the result of the root expression (usually a block)
if let Err(e) = self.expression(spanned_root, &mut VariableScope::default()) {
if let Err(e) = self.expression(spanned_root, &mut scope) {
self.errors.push(e);
}
self.errors
}
fn write_output(&mut self, output: impl Into<String>) -> Result<(), Error> {
fn write_output(&mut self, output: impl Into<String>) -> Result<(), Error<'a>> {
self.output.write_all(output.into().as_bytes())?;
self.output.write_all(b"\n")?;
self.current_line += 1;
@@ -227,21 +245,21 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn next_temp_name(&mut self) -> String {
fn next_temp_name(&mut self) -> Cow<'a, str> {
self.temp_counter += 1;
format!("__binary_temp_{}", self.temp_counter)
Cow::from(format!("__binary_temp_{}", self.temp_counter))
}
fn next_label_name(&mut self) -> String {
fn next_label_name(&mut self) -> Cow<'a, str> {
self.label_counter += 1;
format!("L{}", self.label_counter)
Cow::from(format!("L{}", self.label_counter))
}
fn expression<'v>(
fn expression(
&mut self,
expr: Spanned<Expression>,
scope: &mut VariableScope<'v>,
) -> Result<Option<CompilationResult>, Error> {
expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
match expr.node {
Expression::Function(expr_func) => {
self.expression_function(expr_func, scope)?;
@@ -300,11 +318,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Invocation returns result in r15 (RETURN_REGISTER).
// If used as an expression, we must move it to a temp to avoid overwrite.
let temp_name = self.next_temp_name();
let temp_loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?;
let temp_loc =
scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?;
self.emit_variable_assignment(
&temp_name,
temp_name.clone(),
&temp_loc,
format!("r{}", VariableScope::RETURN_REGISTER),
Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)),
)?;
Ok(Some(CompilationResult {
location: temp_loc,
@@ -322,8 +341,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Literal(spanned_lit) => match spanned_lit.node {
Literal::Number(num) => {
let temp_name = self.next_temp_name();
let loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?;
self.emit_variable_assignment(&temp_name, &loc, num.to_string())?;
let loc = scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?;
self.emit_variable_assignment(
temp_name.clone(),
&loc,
Cow::from(num.to_string()),
)?;
Ok(Some(CompilationResult {
location: loc,
temp_name: Some(temp_name),
@@ -332,8 +355,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Literal::Boolean(b) => {
let val = if b { "1" } else { "0" };
let temp_name = self.next_temp_name();
let loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?;
self.emit_variable_assignment(&temp_name, &loc, val)?;
let loc = scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?;
self.emit_variable_assignment(temp_name.clone(), &loc, Cow::from(val))?;
Ok(Some(CompilationResult {
location: loc,
temp_name: Some(temp_name),
@@ -374,7 +397,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// 2. Allocate a temp register for the result
let result_name = self.next_temp_name();
let loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?;
let loc = scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?;
let reg = self.resolve_register(&loc)?;
// 3. Emit load instruction: l rX device member
@@ -406,7 +429,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Compile negation as 0 - inner
let (inner_str, cleanup) = self.compile_operand(*inner_expr, scope)?;
let result_name = self.next_temp_name();
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?;
let result_loc =
scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?;
let result_reg = self.resolve_register(&result_loc)?;
self.write_output(format!("sub {result_reg} 0 {inner_str}"))?;
@@ -432,11 +456,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// Resolves an expression to a device identifier string for use in instructions like `s` or `l`.
/// Returns (device_string, optional_cleanup_temp_name).
fn resolve_device<'v>(
fn resolve_device(
&mut self,
expr: Spanned<Expression>,
scope: &mut VariableScope<'v>,
) -> Result<(String, Option<String>), Error> {
expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<(Cow<'a, str>, Option<Cow<'a, str>>), Error<'a>> {
// If it's a direct variable reference, check if it's a known device alias first
if let Expression::Variable(ref name) = expr.node
&& let Some(device_id) = self.devices.get(&name.node)
@@ -450,10 +474,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn emit_variable_assignment(
&mut self,
var_name: &str,
location: &VariableLocation,
source_value: impl Into<String>,
) -> Result<(), Error> {
var_name: Cow<'a, str>,
location: &VariableLocation<'a>,
source_value: Cow<'a, str>,
) -> Result<(), Error<'a>> {
let debug_tag = if self.config.debug {
format!(" #{var_name}")
} else {
@@ -462,10 +486,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
match location {
VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) => {
self.write_output(format!("move r{reg} {}{debug_tag}", source_value.into()))?;
self.write_output(format!("move r{reg} {}{debug_tag}", source_value))?;
}
VariableLocation::Stack(_) => {
self.write_output(format!("push {}{debug_tag}", source_value.into()))?;
self.write_output(format!("push {}{debug_tag}", source_value))?;
}
VariableLocation::Constant(_) => {
return Err(Error::Unknown(
@@ -488,12 +512,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn expression_declaration<'v>(
fn expression_declaration(
&mut self,
var_name: Spanned<String>,
expr: Spanned<Expression>,
scope: &mut VariableScope<'v>,
) -> Result<Option<CompilationResult>, Error> {
var_name: Spanned<Cow<'a, str>>,
expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
let name_str = var_name.node;
let name_span = var_name.span;
@@ -502,8 +526,13 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
&& let Expression::Literal(spanned_lit) = &box_expr.node
&& let Literal::Number(neg_num) = &spanned_lit.node
{
let loc = scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
self.emit_variable_assignment(&name_str, &loc, format!("-{neg_num}"))?;
let loc =
scope.add_variable(name_str.clone(), LocationRequest::Persist, Some(name_span))?;
self.emit_variable_assignment(
name_str.clone(),
&loc,
Cow::from(format!("-{neg_num}")),
)?;
return Ok(Some(CompilationResult {
location: loc,
temp_name: None,
@@ -519,7 +548,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Some(name_span),
)?;
self.emit_variable_assignment(&name_str, &var_location, num)?;
self.emit_variable_assignment(
name_str.clone(),
&var_location,
Cow::from(num.to_string()),
)?;
(var_location, None)
}
Literal::Boolean(b) => {
@@ -530,7 +563,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Some(name_span),
)?;
self.emit_variable_assignment(&name_str, &var_location, val)?;
self.emit_variable_assignment(name_str, &var_location, Cow::from(val))?;
(var_location, None)
}
_ => return Ok(None),
@@ -538,12 +571,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Invocation(invoke_expr) => {
self.expression_function_invocation(invoke_expr, scope)?;
let loc =
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
let loc = scope.add_variable(
name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
self.emit_variable_assignment(
&name_str,
name_str,
&loc,
format!("r{}", VariableScope::RETURN_REGISTER),
Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)),
)?;
(loc, None)
}
@@ -564,11 +600,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
};
let loc =
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?;
self.emit_variable_assignment(
&name_str,
name_str,
&loc,
format!("r{}", VariableScope::RETURN_REGISTER),
Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)),
)?;
(loc, None)
@@ -577,19 +613,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Binary(bin_expr) => {
let result = self.expression_binary(bin_expr, scope)?;
let var_loc =
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?;
if let CompilationResult {
location: VariableLocation::Constant(Literal::Number(num)),
..
} = result
{
self.emit_variable_assignment(&name_str, &var_loc, num)?;
self.emit_variable_assignment(name_str, &var_loc, Cow::from(num.to_string()))?;
(var_loc, None)
} else {
// Move result from temp to new persistent variable
let result_reg = self.resolve_register(&result.location)?;
self.emit_variable_assignment(&name_str, &var_loc, result_reg)?;
self.emit_variable_assignment(name_str, &var_loc, result_reg)?;
// Free the temp result
if let Some(name) = result.temp_name {
@@ -601,11 +637,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Logical(log_expr) => {
let result = self.expression_logical(log_expr, scope)?;
let var_loc =
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?;
// Move result from temp to new persistent variable
let result_reg = self.resolve_register(&result.location)?;
self.emit_variable_assignment(&name_str, &var_loc, result_reg)?;
self.emit_variable_assignment(name_str, &var_loc, result_reg)?;
// Free the temp result
if let Some(name) = result.temp_name {
@@ -626,7 +662,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
};
let var_loc =
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?;
// Handle loading from stack if necessary
let src_str = match src_loc {
@@ -646,7 +682,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
VariableLocation::Constant(_) | VariableLocation::Device(_) => unreachable!(),
};
self.emit_variable_assignment(&name_str, &var_loc, src_str)?;
self.emit_variable_assignment(name_str, &var_loc, Cow::from(src_str))?;
(var_loc, None)
}
Expression::Priority(inner) => {
@@ -678,10 +714,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
};
let var_loc =
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?;
scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?;
let result_reg = self.resolve_register(&comp_res.location)?;
self.emit_variable_assignment(&name_str, &var_loc, result_reg)?;
self.emit_variable_assignment(name_str, &var_loc, result_reg)?;
if let Some(temp) = comp_res.temp_name {
scope.free_temp(temp, None)?;
@@ -703,11 +739,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}))
}
fn expression_const_declaration<'v>(
fn expression_const_declaration(
&mut self,
expr: ConstDeclarationExpression,
scope: &mut VariableScope<'v>,
) -> Result<CompilationResult, Error> {
expr: ConstDeclarationExpression<'a>,
scope: &mut VariableScope<'a>,
) -> Result<CompilationResult<'a>, Error<'a>> {
let ConstDeclarationExpression {
name: const_name,
value: const_value,
@@ -738,11 +774,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
})
}
fn expression_assignment<'v>(
fn expression_assignment(
&mut self,
expr: AssignmentExpression,
scope: &mut VariableScope<'v>,
) -> Result<(), Error> {
expr: AssignmentExpression<'a>,
scope: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
let AssignmentExpression {
assignee,
expression,
@@ -828,9 +864,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn expression_function_invocation(
&mut self,
invoke_expr: Spanned<InvocationExpression>,
stack: &mut VariableScope,
) -> Result<(), Error> {
invoke_expr: Spanned<InvocationExpression<'a>>,
stack: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
let InvocationExpression { name, arguments } = invoke_expr.node;
if !self.function_locations.contains_key(&name.node) {
@@ -845,12 +881,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
let Some(args) = self.function_metadata.get(&name.node) else {
// Should be covered by check above
return Err(Error::UnknownIdentifier(name.node.clone(), name.span));
return Err(Error::UnknownIdentifier(name.node, name.span));
};
if args.len() != arguments.len() {
self.errors
.push(Error::AgrumentMismatch(name.node.clone(), name.span));
.push(Error::AgrumentMismatch(name.node, name.span));
// Proceed anyway? The assembly will likely crash or act weird.
// Best to skip generation of this call to prevent bad IC10
return Ok(());
@@ -859,7 +895,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// backup all used registers to the stack
let active_registers = stack.registers().cloned().collect::<Vec<_>>();
for register in &active_registers {
stack.add_variable(format!("temp_{register}"), LocationRequest::Stack, None)?;
stack.add_variable(
Cow::from(format!("temp_{register}")),
LocationRequest::Stack,
None,
)?;
self.write_output(format!("push r{register}"))?;
}
for arg in arguments {
@@ -876,8 +916,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
_ => {}
},
Expression::Variable(var_name) => {
let loc =
match stack.get_location_of(var_name.node.clone(), Some(var_name.span)) {
let loc = match stack.get_location_of(&var_name.node, Some(var_name.span)) {
Ok(l) => l,
Err(_) => {
self.errors
@@ -975,7 +1014,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
for register in active_registers {
let VariableLocation::Stack(stack_offset) = stack
.get_location_of(format!("temp_{register}"), None)
.get_location_of(&Cow::from(format!("temp_{register}")), None)
.map_err(Error::Scope)?
else {
// This shouldn't happen if we just added it
@@ -1001,7 +1040,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn expression_device(&mut self, expr: DeviceDeclarationExpression) -> Result<(), Error> {
fn expression_device(
&mut self,
expr: DeviceDeclarationExpression<'a>,
) -> Result<(), Error<'a>> {
if self.devices.contains_key(&expr.name.node) {
self.errors.push(Error::DuplicateIdentifier(
expr.name.node.clone(),
@@ -1017,11 +1059,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn expression_if<'v>(
fn expression_if(
&mut self,
expr: IfExpression,
scope: &mut VariableScope<'v>,
) -> Result<(), Error> {
expr: IfExpression<'a>,
scope: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
let end_label = self.next_label_name();
let else_label = if expr.else_branch.is_some() {
self.next_label_name()
@@ -1064,11 +1106,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn expression_loop<'v>(
fn expression_loop(
&mut self,
expr: LoopExpression,
scope: &mut VariableScope<'v>,
) -> Result<(), Error> {
expr: LoopExpression<'a>,
scope: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
let start_label = self.next_label_name();
let end_label = self.next_label_name();
@@ -1090,11 +1132,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn expression_while<'v>(
fn expression_while(
&mut self,
expr: WhileExpression,
scope: &mut VariableScope<'v>,
) -> Result<(), Error> {
expr: WhileExpression<'a>,
scope: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
let start_label = self.next_label_name();
let end_label = self.next_label_name();
@@ -1126,7 +1168,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
fn expression_break(&mut self) -> Result<(), Error> {
fn expression_break(&mut self) -> Result<(), Error<'a>> {
if let Some((_, end_label)) = self.loop_stack.last() {
self.write_output(format!("j {end_label}"))?;
Ok(())
@@ -1138,7 +1180,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
}
fn expression_continue(&mut self) -> Result<(), Error> {
fn expression_continue(&mut self) -> Result<(), Error<'a>> {
if let Some((start_label, _)) = self.loop_stack.last() {
self.write_output(format!("j {start_label}"))?;
Ok(())
@@ -1153,9 +1195,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// Helper to resolve a location to a register string (e.g., "r0").
/// Note: This does not handle Stack locations automatically, as they require
/// instruction emission to load. Use `compile_operand` for general handling.
fn resolve_register(&self, loc: &VariableLocation) -> Result<String, Error> {
fn resolve_register(&self, loc: &VariableLocation) -> Result<Cow<'a, str>, Error<'a>> {
match loc {
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => Ok(format!("r{r}")),
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => {
Ok(Cow::from(format!("r{r}")))
}
VariableLocation::Constant(_) => Err(Error::Unknown(
"Cannot resolve a constant value to register".into(),
None,
@@ -1177,19 +1221,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// so the caller can free it.
fn compile_operand(
&mut self,
expr: Spanned<Expression>,
scope: &mut VariableScope,
) -> Result<(String, Option<String>), Error> {
expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<(Cow<'a, str>, Option<Cow<'a, str>>), Error<'a>> {
// Optimization for literals
if let Expression::Literal(spanned_lit) = &expr.node {
if let Literal::Number(n) = spanned_lit.node {
return Ok((n.to_string(), None));
return Ok((Cow::from(n.to_string()), None));
}
if let Literal::Boolean(b) = spanned_lit.node {
return Ok((if b { "1".to_string() } else { "0".to_string() }, None));
return Ok((Cow::from(if b { "1" } else { "0" }), None));
}
if let Literal::String(ref s) = spanned_lit.node {
return Ok((s.to_string(), None));
return Ok((s.clone(), None));
}
}
@@ -1199,7 +1243,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
&& let Expression::Literal(spanned_lit) = &inner.node
&& let Literal::Number(n) = spanned_lit.node
{
return Ok((format!("-{}", n), None));
return Ok((Cow::from(format!("-{}", n)), None));
}
let result_opt = self.expression(expr, scope)?;
@@ -1208,23 +1252,24 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Some(r) => r,
None => {
// Expression failed or returned void. Recover with dummy.
return Ok(("r0".to_string(), None));
return Ok((Cow::from("r0"), None));
}
};
match result.location {
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => {
Ok((format!("r{r}"), result.temp_name))
Ok((Cow::from(format!("r{r}")), result.temp_name))
}
VariableLocation::Constant(lit) => match lit {
Literal::Number(n) => Ok((n.to_string(), None)),
Literal::Boolean(b) => Ok((if b { "1" } else { "0" }.to_string(), None)),
Literal::Number(n) => Ok((Cow::from(n.to_string()), None)),
Literal::Boolean(b) => Ok((Cow::from(if b { "1" } else { "0" }), None)),
Literal::String(s) => Ok((s, None)),
},
VariableLocation::Stack(offset) => {
// If it's on the stack, we must load it into a temp to use it as an operand
let temp_name = self.next_temp_name();
let temp_loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?;
let temp_loc =
scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?;
let temp_reg = self.resolve_register(&temp_loc)?;
self.write_output(format!(
@@ -1247,9 +1292,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn compile_literal_or_variable(
&mut self,
val: LiteralOrVariable,
scope: &mut VariableScope,
) -> Result<(String, Option<String>), Error> {
val: LiteralOrVariable<'a>,
scope: &mut VariableScope<'a>,
) -> Result<(Cow<'a, str>, Option<Cow<'a, str>>), Error<'a>> {
let dummy_span = Span {
start_line: 0,
start_col: 0,
@@ -1273,12 +1318,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
)
}
fn expression_binary<'v>(
fn expression_binary(
&mut self,
expr: Spanned<BinaryExpression>,
scope: &mut VariableScope<'v>,
) -> Result<CompilationResult, Error> {
fn fold_binary_expression(expr: &BinaryExpression) -> Option<Number> {
expr: Spanned<BinaryExpression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<CompilationResult<'a>, Error<'a>> {
fn fold_binary_expression<'a>(expr: &BinaryExpression<'a>) -> Option<Number> {
let (lhs, rhs) = match &expr {
BinaryExpression::Add(l, r)
| BinaryExpression::Subtract(l, r)
@@ -1298,7 +1343,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
}
fn fold_expression(expr: &Expression) -> Option<Number> {
fn fold_expression<'a>(expr: &Expression<'a>) -> Option<Number> {
match expr {
// 1. Base Case: It's already a number
Expression::Literal(lit) => match lit.node {
@@ -1346,7 +1391,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Allocate result register
let result_name = self.next_temp_name();
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?;
let result_loc = scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?;
let result_reg = self.resolve_register(&result_loc)?;
// Emit instruction: op result lhs rhs
@@ -1366,17 +1411,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
})
}
fn expression_logical<'v>(
fn expression_logical(
&mut self,
expr: Spanned<LogicalExpression>,
scope: &mut VariableScope<'v>,
) -> Result<CompilationResult, Error> {
expr: Spanned<LogicalExpression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<CompilationResult<'a>, Error<'a>> {
match expr.node {
LogicalExpression::Not(inner) => {
let (inner_str, cleanup) = self.compile_operand(*inner, scope)?;
let result_name = self.next_temp_name();
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?;
let result_loc =
scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?;
let result_reg = self.resolve_register(&result_loc)?;
// seq rX rY 0 => if rY == 0 set rX = 1 else rX = 0
@@ -1411,7 +1457,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Allocate result register
let result_name = self.next_temp_name();
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?;
let result_loc =
scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?;
let result_reg = self.resolve_register(&result_loc)?;
// Emit instruction: op result lhs rhs
@@ -1433,11 +1480,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
}
fn expression_block<'v>(
fn expression_block(
&mut self,
mut expr: BlockExpression,
parent_scope: &mut VariableScope<'v>,
) -> Result<(), Error> {
mut expr: BlockExpression<'a>,
parent_scope: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
// First, sort the expressions to ensure functions are hoisted
expr.0.sort_by(|a, b| {
if matches!(
@@ -1499,17 +1546,21 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
/// Takes the result of the expression and stores it in VariableScope::RETURN_REGISTER
fn expression_return<'v>(
fn expression_return(
&mut self,
expr: Spanned<Expression>,
scope: &mut VariableScope<'v>,
) -> Result<VariableLocation, Error> {
expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<VariableLocation<'a>, Error<'a>> {
if let Expression::Negation(neg_expr) = &expr.node
&& let Expression::Literal(spanned_lit) = &neg_expr.node
&& let Literal::Number(neg_num) = &spanned_lit.node
{
let loc = VariableLocation::Persistant(VariableScope::RETURN_REGISTER);
self.emit_variable_assignment("returnValue", &loc, format!("-{neg_num}"))?;
self.emit_variable_assignment(
Cow::from("returnValue"),
&loc,
Cow::from(format!("-{neg_num}")),
)?;
return Ok(loc);
};
@@ -1562,17 +1613,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Literal(spanned_lit) => match spanned_lit.node {
Literal::Number(num) => {
self.emit_variable_assignment(
"returnValue",
Cow::from("returnValue"),
&VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
num,
Cow::from(num.to_string()),
)?;
}
Literal::Boolean(b) => {
let val = if b { "1" } else { "0" };
self.emit_variable_assignment(
"returnValue",
Cow::from("returnValue"),
&VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
val,
Cow::from(val.to_string()),
)?;
}
_ => {}
@@ -1631,12 +1682,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// syscalls that return values will be stored in the VariableScope::RETURN_REGISTER
// register
fn expression_syscall_system<'v>(
fn expression_syscall_system(
&mut self,
expr: System,
expr: System<'a>,
span: Span,
scope: &mut VariableScope<'v>,
) -> Result<Option<CompilationResult>, Error> {
scope: &mut VariableScope<'a>,
) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
macro_rules! cleanup {
($($to_clean:expr),*) => {
$(
@@ -1707,7 +1758,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
.devices
.get(&device_name)
.cloned()
.unwrap_or("d0".to_string());
.unwrap_or(Cow::from("d0"));
let Spanned {
node: Literal::String(logic_type),
@@ -1799,7 +1850,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
.devices
.get(&device_name)
.cloned()
.unwrap_or("d0".to_string());
.unwrap_or(Cow::from("d0"));
let Spanned {
node: Literal::String(logic_type),
@@ -1889,11 +1940,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
}
fn expression_syscall_math<'v>(
fn expression_syscall_math(
&mut self,
expr: Math,
scope: &mut VariableScope<'v>,
) -> Result<Option<CompilationResult>, Error> {
expr: Math<'a>,
scope: &mut VariableScope<'a>,
) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
macro_rules! cleanup {
($($to_clean:expr),*) => {
$(
@@ -2085,11 +2136,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// Compile a function declaration.
/// Calees are responsible for backing up any registers they wish to use.
fn expression_function<'v>(
fn expression_function(
&mut self,
expr: Spanned<FunctionExpression>,
scope: &mut VariableScope<'v>,
) -> Result<(), Error> {
expr: Spanned<FunctionExpression<'a>>,
scope: &mut VariableScope<'a>,
) -> Result<(), Error<'a>> {
let FunctionExpression {
name,
arguments,

View File

@@ -5,25 +5,28 @@
use lsp_types::{Diagnostic, DiagnosticSeverity};
use parser::tree_node::{Literal, Span};
use std::collections::{HashMap, VecDeque};
use std::{
borrow::Cow,
collections::{HashMap, VecDeque},
};
use thiserror::Error;
const TEMP: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
const PERSIST: [u8; 7] = [8, 9, 10, 11, 12, 13, 14];
#[derive(Error, Debug)]
pub enum Error {
pub enum Error<'a> {
#[error("{0} already exists.")]
DuplicateVariable(String, Option<Span>),
DuplicateVariable(Cow<'a, str>, Option<Span>),
#[error("{0} does not exist.")]
UnknownVariable(String, Option<Span>),
UnknownVariable(Cow<'a, str>, Option<Span>),
#[error("{0}")]
Unknown(String, Option<Span>),
Unknown(Cow<'a, str>, Option<Span>),
}
impl From<Error> for lsp_types::Diagnostic {
impl<'a> From<Error<'a>> for lsp_types::Diagnostic {
fn from(value: Error) -> Self {
match value {
Error::DuplicateVariable(_, span)
@@ -50,7 +53,7 @@ pub enum LocationRequest {
}
#[derive(Clone)]
pub enum VariableLocation {
pub enum VariableLocation<'a> {
/// Represents a temporary register (r1 - r7)
Temporary(u8),
/// Represents a persistant register (r8 - r14)
@@ -58,15 +61,15 @@ pub enum VariableLocation {
/// Represents a a stack offset (current stack - offset = variable loc)
Stack(u16),
/// Represents a constant value and should be directly substituted as such.
Constant(Literal),
Constant(Literal<'a>),
/// Represents a device pin. This will contain the exact `d0-d5` string
Device(String),
Device(Cow<'a, str>),
}
pub struct VariableScope<'a> {
temporary_vars: VecDeque<u8>,
persistant_vars: VecDeque<u8>,
var_lookup_table: HashMap<String, VariableLocation>,
var_lookup_table: HashMap<Cow<'a, str>, VariableLocation<'a>>,
stack_offset: u16,
parent: Option<&'a VariableScope<'a>>,
}
@@ -123,12 +126,11 @@ impl<'a> VariableScope<'a> {
/// to the stack.
pub fn add_variable(
&mut self,
var_name: impl Into<String>,
var_name: Cow<'a, str>,
location: LocationRequest,
span: Option<Span>,
) -> Result<VariableLocation, Error> {
let var_name = var_name.into();
if self.var_lookup_table.contains_key(var_name.as_str()) {
) -> Result<VariableLocation<'a>, Error<'a>> {
if self.var_lookup_table.contains_key(&var_name) {
return Err(Error::DuplicateVariable(var_name, span));
}
let var_location = match location {
@@ -163,11 +165,10 @@ impl<'a> VariableScope<'a> {
pub fn define_const(
&mut self,
var_name: impl Into<String>,
value: Literal,
var_name: Cow<'a, str>,
value: Literal<'a>,
span: Option<Span>,
) -> Result<VariableLocation, Error> {
let var_name = var_name.into();
) -> Result<VariableLocation<'a>, Error<'a>> {
if self.var_lookup_table.contains_key(&var_name) {
return Err(Error::DuplicateVariable(var_name, span));
}
@@ -180,13 +181,11 @@ impl<'a> VariableScope<'a> {
pub fn get_location_of(
&self,
var_name: impl Into<String>,
var_name: &Cow<'a, str>,
span: Option<Span>,
) -> Result<VariableLocation, Error> {
let var_name = var_name.into();
) -> Result<VariableLocation<'a>, Error<'a>> {
// 1. Check this scope
if let Some(var) = self.var_lookup_table.get(var_name.as_str()) {
if let Some(var) = self.var_lookup_table.get(var_name) {
if let VariableLocation::Stack(inserted_at_offset) = var {
// Return offset relative to CURRENT sp
return Ok(VariableLocation::Stack(
@@ -207,7 +206,7 @@ impl<'a> VariableScope<'a> {
return Ok(loc);
}
Err(Error::UnknownVariable(var_name, span))
Err(Error::UnknownVariable(Cow::from(var_name.to_owned()), span))
}
pub fn has_parent(&self) -> bool {
@@ -217,11 +216,10 @@ impl<'a> VariableScope<'a> {
#[allow(dead_code)]
pub fn free_temp(
&mut self,
var_name: impl Into<String>,
var_name: Cow<'a, str>,
span: Option<Span>,
) -> Result<(), Error> {
let var_name = var_name.into();
let Some(location) = self.var_lookup_table.remove(var_name.as_str()) else {
) -> Result<(), Error<'a>> {
let Some(location) = self.var_lookup_table.remove(&var_name) else {
return Err(Error::UnknownVariable(var_name, span));
};
@@ -231,7 +229,7 @@ impl<'a> VariableScope<'a> {
}
VariableLocation::Persistant(_) => {
return Err(Error::UnknownVariable(
String::from("Attempted to free a `let` variable."),
Cow::from("Attempted to free a `let` variable."),
span,
));
}

View File

@@ -1518,14 +1518,14 @@ impl<'a> Parser<'a> {
}
fn syscall(&mut self) -> Result<SysCall<'a>, Error<'a>> {
fn check_length<'a, 't: 'a>(
parser: &'t Parser,
fn check_length<'a>(
span: Span,
arguments: &[Spanned<Expression<'a>>],
length: usize,
) -> Result<(), Error<'a>> {
if arguments.len() != length {
return Err(Error::InvalidSyntax(
parser.current_span(),
span,
format!("Expected {} arguments", length),
));
}
@@ -1586,17 +1586,17 @@ impl<'a> Parser<'a> {
match invocation.name.node.as_ref() {
// System SysCalls
"yield" => {
check_length(self, &invocation.arguments, 0)?;
check_length(self.current_span(), &invocation.arguments, 0)?;
Ok(SysCall::System(sys_call::System::Yield))
}
"sleep" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut arg = invocation.arguments.into_iter();
let expr = arg.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::System(System::Sleep(boxed!(expr))))
}
"hash" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let lit_str = literal_or_variable!(args.next());
@@ -1617,7 +1617,7 @@ impl<'a> Parser<'a> {
})))
}
"load" | "l" => {
check_length(self, &invocation.arguments, 2)?;
check_length(self.current_span(), &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next();
@@ -1662,7 +1662,7 @@ impl<'a> Parser<'a> {
)))
}
"loadBatched" | "lb" => {
check_length(self, &invocation.arguments, 3)?;
check_length(self.current_span(), &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next();
let device_hash = literal_or_variable!(tmp);
@@ -1680,7 +1680,7 @@ impl<'a> Parser<'a> {
)))
}
"loadBatchedNamed" | "lbn" => {
check_length(self, &invocation.arguments, 4)?;
check_length(self.current_span(), &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next();
let dev_hash = literal_or_variable!(tmp);
@@ -1699,7 +1699,7 @@ impl<'a> Parser<'a> {
)))
}
"set" | "s" => {
check_length(self, &invocation.arguments, 3)?;
check_length(self.current_span(), &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next();
let device = literal_or_variable!(tmp);
@@ -1720,7 +1720,7 @@ impl<'a> Parser<'a> {
)))
}
"setBatched" | "sb" => {
check_length(self, &invocation.arguments, 3)?;
check_length(self.current_span(), &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next();
let device_hash = literal_or_variable!(tmp);
@@ -1739,7 +1739,7 @@ impl<'a> Parser<'a> {
)))
}
"setBatchedNamed" | "sbn" => {
check_length(self, &invocation.arguments, 4)?;
check_length(self.current_span(), &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next();
let device_hash = literal_or_variable!(tmp);
@@ -1762,28 +1762,28 @@ impl<'a> Parser<'a> {
}
// Math SysCalls
"acos" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Acos(boxed!(tmp))))
}
"asin" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let tmp = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Asin(boxed!(tmp))))
}
"atan" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let expr = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Atan(boxed!(expr))))
}
"atan2" => {
check_length(self, &invocation.arguments, 2)?;
check_length(self.current_span(), &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter();
let arg1 = args.next().ok_or(Error::UnexpectedEOF)?;
let arg2 = args.next().ok_or(Error::UnexpectedEOF)?;
@@ -1791,42 +1791,42 @@ impl<'a> Parser<'a> {
Ok(SysCall::Math(Math::Atan2(boxed!(arg1), boxed!(arg2))))
}
"abs" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let expr = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Abs(boxed!(expr))))
}
"ceil" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Ceil(boxed!(arg))))
}
"cos" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Cos(boxed!(arg))))
}
"floor" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Floor(boxed!(arg))))
}
"log" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Log(boxed!(arg))))
}
"max" => {
check_length(self, &invocation.arguments, 2)?;
check_length(self.current_span(), &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter();
let arg1 = args.next().ok_or(Error::UnexpectedEOF)?;
let arg2 = args.next().ok_or(Error::UnexpectedEOF)?;
@@ -1834,7 +1834,7 @@ impl<'a> Parser<'a> {
Ok(SysCall::Math(Math::Max(boxed!(arg1), boxed!(arg2))))
}
"min" => {
check_length(self, &invocation.arguments, 2)?;
check_length(self.current_span(), &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter();
let arg1 = args.next().ok_or(Error::UnexpectedEOF)?;
let arg2 = args.next().ok_or(Error::UnexpectedEOF)?;
@@ -1842,32 +1842,32 @@ impl<'a> Parser<'a> {
Ok(SysCall::Math(Math::Min(boxed!(arg1), boxed!(arg2))))
}
"rand" => {
check_length(self, &invocation.arguments, 0)?;
check_length(self.current_span(), &invocation.arguments, 0)?;
Ok(SysCall::Math(Math::Rand))
}
"sin" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Sin(boxed!(arg))))
}
"sqrt" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Sqrt(boxed!(arg))))
}
"tan" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::Math(Math::Tan(boxed!(arg))))
}
"trunc" => {
check_length(self, &invocation.arguments, 1)?;
check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let arg = args.next().ok_or(Error::UnexpectedEOF)?;