Merge pull request #22 from dbidwell94/logos

Tech Debt Cleanup
This commit is contained in:
2025-12-09 17:41:02 -07:00
committed by GitHub
21 changed files with 1186 additions and 1629 deletions

View File

@@ -1,5 +1,11 @@
# Changelog # Changelog
[0.2.0]
- Completely re-wrote the tokenizer to use `logos`
- Changed AST and Token data structures to use `Cow` instead of `String`
- Updated error reporting to use `thiserror` instead of `quickerror`
[0.1.2] [0.1.2]
- Removed references to `Unitask` - Removed references to `Unitask`

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.1.2</Version> <Version>0.2.0</Version>
<Description> <Description>
[h1]Slang: High-Level Programming for Stationeers[/h1] [h1]Slang: High-Level Programming for Stationeers[/h1]

View File

@@ -55,7 +55,7 @@ public static unsafe class SlangExtensions
var color = GetColorForKind(token.token_kind); var color = GetColorForKind(token.token_kind);
int colIndex = token.column - 1; int colIndex = token.column;
if (colIndex < 0) if (colIndex < 0)
colIndex = 0; colIndex = 0;
@@ -100,10 +100,10 @@ public static unsafe class SlangExtensions
Severity = item.severity, Severity = item.severity,
Range = new Slang.Range Range = new Slang.Range
{ {
EndCol = Math.Max(item.range.end_col - 2, 0), EndCol = Math.Max(item.range.end_col, 0),
EndLine = item.range.end_line - 1, EndLine = item.range.end_line,
StartCol = Math.Max(item.range.start_col - 2, 0), StartCol = Math.Max(item.range.start_col, 0),
StartLine = item.range.end_line - 1, StartLine = item.range.start_line,
}, },
} }
); );
@@ -134,6 +134,9 @@ public static unsafe class SlangExtensions
case 7: // (punctuation) case 7: // (punctuation)
return SlangFormatter.ColorDefault; return SlangFormatter.ColorDefault;
case 8: // Comments
return SlangFormatter.ColorComment;
case 10: // (syscalls) case 10: // (syscalls)
return SlangFormatter.ColorFunction; return SlangFormatter.ColorFunction;

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.1.2</Version> <Version>0.2.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

109
rust_compiler/Cargo.lock generated
View File

@@ -28,6 +28,15 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.21" version = "0.6.21"
@@ -114,6 +123,12 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "beef"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@@ -257,8 +272,8 @@ dependencies = [
"lsp-types", "lsp-types",
"parser", "parser",
"pretty_assertions", "pretty_assertions",
"quick-error",
"rust_decimal", "rust_decimal",
"thiserror",
"tokenizer", "tokenizer",
] ]
@@ -327,6 +342,12 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "funty" name = "funty"
version = "2.0.0" version = "2.0.0"
@@ -434,6 +455,40 @@ version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "logos"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a790d11254054e5dc83902dba85d253ff06ceb0cfafb12be8773435cb9dfb4f4"
dependencies = [
"logos-derive",
]
[[package]]
name = "logos-codegen"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60337c43a38313b58871f8d5d76872b8e17aa9d51fad494b5e76092c0ce05f5"
dependencies = [
"beef",
"fnv",
"proc-macro2",
"quote",
"regex-automata",
"regex-syntax",
"rustc_version",
"syn 2.0.111",
]
[[package]]
name = "logos-derive"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d151b2ae667f69e10b8738f5cac0c746faa22b2e15ea7e83b55476afec3767dc"
dependencies = [
"logos-codegen",
]
[[package]] [[package]]
name = "lsp-types" name = "lsp-types"
version = "0.97.0" version = "0.97.0"
@@ -516,7 +571,7 @@ dependencies = [
"helpers", "helpers",
"lsp-types", "lsp-types",
"pretty_assertions", "pretty_assertions",
"quick-error", "thiserror",
"tokenizer", "tokenizer",
] ]
@@ -593,12 +648,6 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.42" version = "1.0.42"
@@ -644,6 +693,23 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]] [[package]]
name = "rend" name = "rend"
version = "0.4.2" version = "0.4.2"
@@ -843,7 +909,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]] [[package]]
name = "slang" name = "slang"
version = "0.1.2" version = "0.2.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -851,9 +917,9 @@ dependencies = [
"helpers", "helpers",
"lsp-types", "lsp-types",
"parser", "parser",
"quick-error",
"rust_decimal", "rust_decimal",
"safer-ffi", "safer-ffi",
"thiserror",
"tokenizer", "tokenizer",
] ]
@@ -926,6 +992,26 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "thiserror"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.10.0" version = "1.10.0"
@@ -947,9 +1033,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"helpers", "helpers",
"logos",
"lsp-types", "lsp-types",
"quick-error",
"rust_decimal", "rust_decimal",
"thiserror",
] ]
[[package]] [[package]]

View File

@@ -1,13 +1,13 @@
[package] [package]
name = "slang" name = "slang"
version = "0.1.2" version = "0.2.0"
edition = "2021" edition = "2021"
[workspace] [workspace]
members = ["libs/*"] members = ["libs/*"]
[workspace.dependencies] [workspace.dependencies]
quick-error = "2" thiserror = "2"
rust_decimal = "1" rust_decimal = "1"
safer-ffi = { version = "0.1" } # Safely share structs in memory between C# and Rust safer-ffi = { version = "0.1" } # Safely share structs in memory between C# and Rust
lsp-types = { version = "0.97" } # Allows for LSP style reporting to the frontend lsp-types = { version = "0.97" } # Allows for LSP style reporting to the frontend
@@ -36,13 +36,13 @@ crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
clap = { version = "^4.5", features = ["derive"] } clap = { version = "^4.5", features = ["derive"] }
lsp-types = { workspace = true } lsp-types = { workspace = true }
quick-error = { workspace = true } thiserror = { workspace = true }
rust_decimal = { workspace = true } rust_decimal = { workspace = true }
tokenizer = { path = "libs/tokenizer" } tokenizer = { path = "libs/tokenizer" }
parser = { path = "libs/parser" } parser = { path = "libs/parser" }
compiler = { path = "libs/compiler" } compiler = { path = "libs/compiler" }
helpers = { path = "libs/helpers" } helpers = { path = "libs/helpers" }
safer-ffi = { workspace = true } safer-ffi = { workspace = true }
anyhow = { version = "^1.0", features = ["backtrace"] }
[dev-dependencies] [dev-dependencies]
anyhow = { version = "^1.0", features = ["backtrace"] }

View File

@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
quick-error = { workspace = true } thiserror = { workspace = true }
parser = { path = "../parser" } parser = { path = "../parser" }
tokenizer = { path = "../tokenizer" } tokenizer = { path = "../tokenizer" }
helpers = { path = "../helpers" } helpers = { path = "../helpers" }

View File

@@ -22,7 +22,7 @@ macro_rules! compile {
(result $source:expr) => {{ (result $source:expr) => {{
let mut writer = std::io::BufWriter::new(Vec::new()); let mut writer = std::io::BufWriter::new(Vec::new());
let compiler = crate::Compiler::new( let compiler = crate::Compiler::new(
parser::Parser::new(tokenizer::Tokenizer::from(String::from($source))), parser::Parser::new(tokenizer::Tokenizer::from($source)),
&mut writer, &mut writer,
Some(crate::CompilerConfig { debug: true }), Some(crate::CompilerConfig { debug: true }),
); );
@@ -32,7 +32,7 @@ macro_rules! compile {
(debug $source:expr) => {{ (debug $source:expr) => {{
let mut writer = std::io::BufWriter::new(Vec::new()); let mut writer = std::io::BufWriter::new(Vec::new());
let compiler = crate::Compiler::new( let compiler = crate::Compiler::new(
parser::Parser::new(tokenizer::Tokenizer::from(String::from($source))), parser::Parser::new(tokenizer::Tokenizer::from($source)),
&mut writer, &mut writer,
Some(crate::CompilerConfig { debug: true }), Some(crate::CompilerConfig { debug: true }),
); );

View File

@@ -11,11 +11,12 @@ use parser::{
LoopExpression, MemberAccessExpression, Span, Spanned, WhileExpression, LoopExpression, MemberAccessExpression, Span, Spanned, WhileExpression,
}, },
}; };
use quick_error::quick_error;
use std::{ use std::{
borrow::Cow,
collections::HashMap, collections::HashMap,
io::{BufWriter, Write}, io::{BufWriter, Write},
}; };
use thiserror::Error;
use tokenizer::token::Number; use tokenizer::token::Number;
macro_rules! debug { macro_rules! debug {
@@ -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(_)) { if !allow_strings && matches!(literal, Literal::String(_)) {
return Err(Error::Unknown( return Err(Error::Unknown(
"Literal strings are not allowed in this context".to_string(), "Literal strings are not allowed in this context".to_string(),
@@ -45,59 +49,56 @@ fn extract_literal(literal: Literal, allow_strings: bool) -> Result<String, Erro
} }
Ok(match literal { Ok(match literal {
Literal::String(s) => s, Literal::String(s) => s,
Literal::Number(n) => n.to_string(), Literal::Number(n) => Cow::from(n.to_string()),
Literal::Boolean(b) => if b { "1" } else { "0" }.into(), Literal::Boolean(b) => Cow::from(if b { "1" } else { "0" }),
}) })
} }
quick_error! { #[derive(Error, Debug)]
#[derive(Debug)] pub enum Error<'a> {
pub enum Error { #[error("{0}")]
ParseError(error: parser::Error) { Parse(parser::Error<'a>),
from()
} #[error("{0}")]
IoError(error: String) { Scope(variable_manager::Error<'a>),
display("IO Error: {}", error)
} #[error("IO Error: {0}")]
ScopeError(error: variable_manager::Error) { IO(String),
from()
} #[error("`{0}` has already been defined.")]
DuplicateIdentifier(func_name: String, span: Span) { DuplicateIdentifier(Cow<'a, str>, Span),
display("`{func_name}` has already been defined")
} #[error("`{0}` is not found in the current scope.")]
UnknownIdentifier(ident: String, span: Span) { UnknownIdentifier(Cow<'a, str>, Span),
display("`{ident}` is not found in the current scope.")
} #[error("`{0}` is not valid.")]
InvalidDevice(device: String, span: Span) { InvalidDevice(Cow<'a, str>, Span),
display("`{device}` is not valid")
} #[error("Incorrent number of arguments passed into `{0}`")]
AgrumentMismatch(func_name: String, span: Span) { AgrumentMismatch(Cow<'a, str>, Span),
display("Incorrect number of arguments passed into `{func_name}`")
} #[error("Attempted to re-assign a value to const variable `{0}`")]
ConstAssignment(ident: String, span: Span) { ConstAssignment(Cow<'a, str>, Span),
display("Attempted to re-assign a value to const variable `{ident}`")
} #[error("Attempted to re-assign a value to a device const `{0}`")]
DeviceAssignment(ident: String, span: Span) { DeviceAssignment(Cow<'a, str>, Span),
display("Attempted to re-assign a value to a device const `{ident}`")
} #[error("{0}")]
Unknown(reason: String, span: Option<Span>) { Unknown(String, Option<Span>),
display("{reason}")
}
}
} }
impl From<Error> for lsp_types::Diagnostic { impl<'a> From<Error<'a>> for lsp_types::Diagnostic {
fn from(value: Error) -> Self { fn from(value: Error) -> Self {
use Error::*; use Error::*;
use lsp_types::*; use lsp_types::*;
match value { match value {
ParseError(e) => e.into(), Parse(e) => e.into(),
IoError(e) => Diagnostic { IO(e) => Diagnostic {
message: e.to_string(), message: e.to_string(),
severity: Some(DiagnosticSeverity::ERROR), severity: Some(DiagnosticSeverity::ERROR),
..Default::default() ..Default::default()
}, },
ScopeError(e) => e.into(), Scope(e) => e.into(),
DuplicateIdentifier(_, span) DuplicateIdentifier(_, span)
| UnknownIdentifier(_, span) | UnknownIdentifier(_, span)
| InvalidDevice(_, span) | InvalidDevice(_, span)
@@ -119,10 +120,22 @@ 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 // 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 { fn from(err: std::io::Error) -> Self {
Error::IoError(err.to_string()) Error::IO(err.to_string())
} }
} }
@@ -132,32 +145,32 @@ pub struct CompilerConfig {
pub debug: bool, pub debug: bool,
} }
struct CompilationResult { struct CompilationResult<'a> {
location: VariableLocation, location: VariableLocation<'a>,
/// If Some, this is the name of the temporary variable that holds the result. /// If Some, this is the name of the temporary variable that holds the result.
/// It must be freed by the caller when done. /// 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 struct Compiler<'a, 'w, W: std::io::Write> {
pub parser: ASTParser<'a>, pub parser: ASTParser<'a>,
function_locations: HashMap<String, usize>, function_locations: HashMap<Cow<'a, str>, usize>,
function_metadata: HashMap<String, Vec<String>>, function_metadata: HashMap<Cow<'a, str>, Vec<Cow<'a, str>>>,
devices: HashMap<String, String>, devices: HashMap<Cow<'a, str>, Cow<'a, str>>,
output: &'a mut BufWriter<W>, output: &'w mut BufWriter<W>,
current_line: usize, current_line: usize,
declared_main: bool, declared_main: bool,
config: CompilerConfig, config: CompilerConfig,
temp_counter: usize, temp_counter: usize,
label_counter: usize, label_counter: usize,
loop_stack: Vec<(String, String)>, // Stores (start_label, end_label) loop_stack: Vec<(Cow<'a, str>, Cow<'a, str>)>, // Stores (start_label, end_label)
pub errors: Vec<Error>, pub errors: Vec<Error<'a>>,
} }
impl<'a, W: std::io::Write> Compiler<'a, W> { impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> {
pub fn new( pub fn new(
parser: ASTParser<'a>, parser: ASTParser<'a>,
writer: &'a mut BufWriter<W>, writer: &'w mut BufWriter<W>,
config: Option<CompilerConfig>, config: Option<CompilerConfig>,
) -> Self { ) -> Self {
Self { Self {
@@ -176,12 +189,12 @@ 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(); let expr = self.parser.parse_all();
// Copy errors from parser // Copy errors from parser
for e in std::mem::take(&mut self.parser.errors) { for e in std::mem::take(&mut self.parser.errors) {
self.errors.push(Error::ParseError(e)); self.errors.push(Error::Parse(e));
} }
// We treat parse_all result as potentially partial // We treat parse_all result as potentially partial
@@ -190,7 +203,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(None) => return self.errors, Ok(None) => return self.errors,
Err(e) => { Err(e) => {
// Should be covered by parser.errors, but just in case // Should be covered by parser.errors, but just in case
self.errors.push(Error::ParseError(e)); self.errors.push(Error::Parse(e));
return self.errors; return self.errors;
} }
}; };
@@ -214,15 +227,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
return self.errors; return self.errors;
} }
let mut scope = VariableScope::default();
// We ignore the result of the root expression (usually a block) // 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.push(e);
} }
self.errors 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(output.into().as_bytes())?;
self.output.write_all(b"\n")?; self.output.write_all(b"\n")?;
self.current_line += 1; self.current_line += 1;
@@ -230,21 +245,21 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) Ok(())
} }
fn next_temp_name(&mut self) -> String { fn next_temp_name(&mut self) -> Cow<'a, str> {
self.temp_counter += 1; 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; self.label_counter += 1;
format!("L{}", self.label_counter) Cow::from(format!("L{}", self.label_counter))
} }
fn expression<'v>( fn expression(
&mut self, &mut self,
expr: Spanned<Expression>, expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<Option<CompilationResult>, Error> { ) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
match expr.node { match expr.node {
Expression::Function(expr_func) => { Expression::Function(expr_func) => {
self.expression_function(expr_func, scope)?; self.expression_function(expr_func, scope)?;
@@ -303,11 +318,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Invocation returns result in r15 (RETURN_REGISTER). // Invocation returns result in r15 (RETURN_REGISTER).
// If used as an expression, we must move it to a temp to avoid overwrite. // If used as an expression, we must move it to a temp to avoid overwrite.
let temp_name = self.next_temp_name(); 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( self.emit_variable_assignment(
&temp_name, temp_name.clone(),
&temp_loc, &temp_loc,
format!("r{}", VariableScope::RETURN_REGISTER), Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)),
)?; )?;
Ok(Some(CompilationResult { Ok(Some(CompilationResult {
location: temp_loc, location: temp_loc,
@@ -325,8 +341,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Literal(spanned_lit) => match spanned_lit.node { Expression::Literal(spanned_lit) => match spanned_lit.node {
Literal::Number(num) => { Literal::Number(num) => {
let temp_name = self.next_temp_name(); let temp_name = self.next_temp_name();
let loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?; let loc = scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?;
self.emit_variable_assignment(&temp_name, &loc, num.to_string())?; self.emit_variable_assignment(
temp_name.clone(),
&loc,
Cow::from(num.to_string()),
)?;
Ok(Some(CompilationResult { Ok(Some(CompilationResult {
location: loc, location: loc,
temp_name: Some(temp_name), temp_name: Some(temp_name),
@@ -335,8 +355,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Literal::Boolean(b) => { Literal::Boolean(b) => {
let val = if b { "1" } else { "0" }; let val = if b { "1" } else { "0" };
let temp_name = self.next_temp_name(); let temp_name = self.next_temp_name();
let loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?; let loc = scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?;
self.emit_variable_assignment(&temp_name, &loc, val)?; self.emit_variable_assignment(temp_name.clone(), &loc, Cow::from(val))?;
Ok(Some(CompilationResult { Ok(Some(CompilationResult {
location: loc, location: loc,
temp_name: Some(temp_name), temp_name: Some(temp_name),
@@ -377,7 +397,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// 2. Allocate a temp register for the result // 2. Allocate a temp register for the result
let result_name = self.next_temp_name(); 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)?; let reg = self.resolve_register(&loc)?;
// 3. Emit load instruction: l rX device member // 3. Emit load instruction: l rX device member
@@ -409,7 +429,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Compile negation as 0 - inner // Compile negation as 0 - inner
let (inner_str, cleanup) = self.compile_operand(*inner_expr, scope)?; let (inner_str, cleanup) = self.compile_operand(*inner_expr, scope)?;
let result_name = self.next_temp_name(); 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)?; let result_reg = self.resolve_register(&result_loc)?;
self.write_output(format!("sub {result_reg} 0 {inner_str}"))?; self.write_output(format!("sub {result_reg} 0 {inner_str}"))?;
@@ -435,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`. /// Resolves an expression to a device identifier string for use in instructions like `s` or `l`.
/// Returns (device_string, optional_cleanup_temp_name). /// Returns (device_string, optional_cleanup_temp_name).
fn resolve_device<'v>( fn resolve_device(
&mut self, &mut self,
expr: Spanned<Expression>, expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<(String, Option<String>), Error> { ) -> 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 it's a direct variable reference, check if it's a known device alias first
if let Expression::Variable(ref name) = expr.node if let Expression::Variable(ref name) = expr.node
&& let Some(device_id) = self.devices.get(&name.node) && let Some(device_id) = self.devices.get(&name.node)
@@ -453,10 +474,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn emit_variable_assignment( fn emit_variable_assignment(
&mut self, &mut self,
var_name: &str, var_name: Cow<'a, str>,
location: &VariableLocation, location: &VariableLocation<'a>,
source_value: impl Into<String>, source_value: Cow<'a, str>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let debug_tag = if self.config.debug { let debug_tag = if self.config.debug {
format!(" #{var_name}") format!(" #{var_name}")
} else { } else {
@@ -465,10 +486,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
match location { match location {
VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) => { 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(_) => { VariableLocation::Stack(_) => {
self.write_output(format!("push {}{debug_tag}", source_value.into()))?; self.write_output(format!("push {}{debug_tag}", source_value))?;
} }
VariableLocation::Constant(_) => { VariableLocation::Constant(_) => {
return Err(Error::Unknown( return Err(Error::Unknown(
@@ -491,12 +512,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) Ok(())
} }
fn expression_declaration<'v>( fn expression_declaration(
&mut self, &mut self,
var_name: Spanned<String>, var_name: Spanned<Cow<'a, str>>,
expr: Spanned<Expression>, expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<Option<CompilationResult>, Error> { ) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
let name_str = var_name.node; let name_str = var_name.node;
let name_span = var_name.span; let name_span = var_name.span;
@@ -505,8 +526,13 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
&& let Expression::Literal(spanned_lit) = &box_expr.node && let Expression::Literal(spanned_lit) = &box_expr.node
&& let Literal::Number(neg_num) = &spanned_lit.node && let Literal::Number(neg_num) = &spanned_lit.node
{ {
let loc = scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; let loc =
self.emit_variable_assignment(&name_str, &loc, format!("-{neg_num}"))?; 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 { return Ok(Some(CompilationResult {
location: loc, location: loc,
temp_name: None, temp_name: None,
@@ -522,7 +548,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Some(name_span), 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) (var_location, None)
} }
Literal::Boolean(b) => { Literal::Boolean(b) => {
@@ -533,7 +563,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Some(name_span), 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) (var_location, None)
} }
_ => return Ok(None), _ => return Ok(None),
@@ -541,12 +571,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Invocation(invoke_expr) => { Expression::Invocation(invoke_expr) => {
self.expression_function_invocation(invoke_expr, scope)?; self.expression_function_invocation(invoke_expr, scope)?;
let loc = let loc = scope.add_variable(
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
self.emit_variable_assignment( self.emit_variable_assignment(
&name_str, name_str,
&loc, &loc,
format!("r{}", VariableScope::RETURN_REGISTER), Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)),
)?; )?;
(loc, None) (loc, None)
} }
@@ -566,12 +599,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
)); ));
}; };
let loc = let loc = scope.add_variable(
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
self.emit_variable_assignment( self.emit_variable_assignment(
&name_str, name_str,
&loc, &loc,
format!("r{}", VariableScope::RETURN_REGISTER), Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)),
)?; )?;
(loc, None) (loc, None)
@@ -579,20 +615,23 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Support assigning binary expressions to variables directly // Support assigning binary expressions to variables directly
Expression::Binary(bin_expr) => { Expression::Binary(bin_expr) => {
let result = self.expression_binary(bin_expr, scope)?; let result = self.expression_binary(bin_expr, scope)?;
let var_loc = let var_loc = scope.add_variable(
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
if let CompilationResult { if let CompilationResult {
location: VariableLocation::Constant(Literal::Number(num)), location: VariableLocation::Constant(Literal::Number(num)),
.. ..
} = result } = 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) (var_loc, None)
} else { } else {
// Move result from temp to new persistent variable // Move result from temp to new persistent variable
let result_reg = self.resolve_register(&result.location)?; 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 // Free the temp result
if let Some(name) = result.temp_name { if let Some(name) = result.temp_name {
@@ -603,12 +642,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
Expression::Logical(log_expr) => { Expression::Logical(log_expr) => {
let result = self.expression_logical(log_expr, scope)?; let result = self.expression_logical(log_expr, scope)?;
let var_loc = let var_loc = scope.add_variable(
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
// Move result from temp to new persistent variable // Move result from temp to new persistent variable
let result_reg = self.resolve_register(&result.location)?; 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 // Free the temp result
if let Some(name) = result.temp_name { if let Some(name) = result.temp_name {
@@ -628,8 +670,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
}; };
let var_loc = let var_loc = scope.add_variable(
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
// Handle loading from stack if necessary // Handle loading from stack if necessary
let src_str = match src_loc { let src_str = match src_loc {
@@ -649,7 +694,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
VariableLocation::Constant(_) | VariableLocation::Device(_) => unreachable!(), 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) (var_loc, None)
} }
Expression::Priority(inner) => { Expression::Priority(inner) => {
@@ -680,11 +725,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
)); ));
}; };
let var_loc = let var_loc = scope.add_variable(
scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; name_str.clone(),
LocationRequest::Persist,
Some(name_span),
)?;
let result_reg = self.resolve_register(&comp_res.location)?; 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 { if let Some(temp) = comp_res.temp_name {
scope.free_temp(temp, None)?; scope.free_temp(temp, None)?;
@@ -706,11 +754,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
})) }))
} }
fn expression_const_declaration<'v>( fn expression_const_declaration(
&mut self, &mut self,
expr: ConstDeclarationExpression, expr: ConstDeclarationExpression<'a>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<CompilationResult, Error> { ) -> Result<CompilationResult<'a>, Error<'a>> {
let ConstDeclarationExpression { let ConstDeclarationExpression {
name: const_name, name: const_name,
value: const_value, value: const_value,
@@ -741,11 +789,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}) })
} }
fn expression_assignment<'v>( fn expression_assignment(
&mut self, &mut self,
expr: AssignmentExpression, expr: AssignmentExpression<'a>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let AssignmentExpression { let AssignmentExpression {
assignee, assignee,
expression, expression,
@@ -831,9 +879,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn expression_function_invocation( fn expression_function_invocation(
&mut self, &mut self,
invoke_expr: Spanned<InvocationExpression>, invoke_expr: Spanned<InvocationExpression<'a>>,
stack: &mut VariableScope, stack: &mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let InvocationExpression { name, arguments } = invoke_expr.node; let InvocationExpression { name, arguments } = invoke_expr.node;
if !self.function_locations.contains_key(&name.node) { if !self.function_locations.contains_key(&name.node) {
@@ -848,12 +896,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
let Some(args) = self.function_metadata.get(&name.node) else { let Some(args) = self.function_metadata.get(&name.node) else {
// Should be covered by check above // 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() { if args.len() != arguments.len() {
self.errors 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. // Proceed anyway? The assembly will likely crash or act weird.
// Best to skip generation of this call to prevent bad IC10 // Best to skip generation of this call to prevent bad IC10
return Ok(()); return Ok(());
@@ -862,7 +910,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// backup all used registers to the stack // backup all used registers to the stack
let active_registers = stack.registers().cloned().collect::<Vec<_>>(); let active_registers = stack.registers().cloned().collect::<Vec<_>>();
for register in &active_registers { 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}"))?; self.write_output(format!("push r{register}"))?;
} }
for arg in arguments { for arg in arguments {
@@ -879,8 +931,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
_ => {} _ => {}
}, },
Expression::Variable(var_name) => { Expression::Variable(var_name) => {
let loc = let loc = match stack.get_location_of(&var_name.node, Some(var_name.span)) {
match stack.get_location_of(var_name.node.clone(), Some(var_name.span)) {
Ok(l) => l, Ok(l) => l,
Err(_) => { Err(_) => {
self.errors self.errors
@@ -978,8 +1029,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
for register in active_registers { for register in active_registers {
let VariableLocation::Stack(stack_offset) = stack 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::ScopeError)? .map_err(Error::Scope)?
else { else {
// This shouldn't happen if we just added it // This shouldn't happen if we just added it
return Err(Error::Unknown( return Err(Error::Unknown(
@@ -1004,7 +1055,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) 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) { if self.devices.contains_key(&expr.name.node) {
self.errors.push(Error::DuplicateIdentifier( self.errors.push(Error::DuplicateIdentifier(
expr.name.node.clone(), expr.name.node.clone(),
@@ -1020,11 +1074,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) Ok(())
} }
fn expression_if<'v>( fn expression_if(
&mut self, &mut self,
expr: IfExpression, expr: IfExpression<'a>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let end_label = self.next_label_name(); let end_label = self.next_label_name();
let else_label = if expr.else_branch.is_some() { let else_label = if expr.else_branch.is_some() {
self.next_label_name() self.next_label_name()
@@ -1067,11 +1121,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) Ok(())
} }
fn expression_loop<'v>( fn expression_loop(
&mut self, &mut self,
expr: LoopExpression, expr: LoopExpression<'a>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let start_label = self.next_label_name(); let start_label = self.next_label_name();
let end_label = self.next_label_name(); let end_label = self.next_label_name();
@@ -1093,11 +1147,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) Ok(())
} }
fn expression_while<'v>( fn expression_while(
&mut self, &mut self,
expr: WhileExpression, expr: WhileExpression<'a>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let start_label = self.next_label_name(); let start_label = self.next_label_name();
let end_label = self.next_label_name(); let end_label = self.next_label_name();
@@ -1129,7 +1183,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(()) 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() { if let Some((_, end_label)) = self.loop_stack.last() {
self.write_output(format!("j {end_label}"))?; self.write_output(format!("j {end_label}"))?;
Ok(()) Ok(())
@@ -1141,7 +1195,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() { if let Some((start_label, _)) = self.loop_stack.last() {
self.write_output(format!("j {start_label}"))?; self.write_output(format!("j {start_label}"))?;
Ok(()) Ok(())
@@ -1156,9 +1210,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// Helper to resolve a location to a register string (e.g., "r0"). /// Helper to resolve a location to a register string (e.g., "r0").
/// Note: This does not handle Stack locations automatically, as they require /// Note: This does not handle Stack locations automatically, as they require
/// instruction emission to load. Use `compile_operand` for general handling. /// 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 { 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( VariableLocation::Constant(_) => Err(Error::Unknown(
"Cannot resolve a constant value to register".into(), "Cannot resolve a constant value to register".into(),
None, None,
@@ -1180,19 +1236,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// so the caller can free it. /// so the caller can free it.
fn compile_operand( fn compile_operand(
&mut self, &mut self,
expr: Spanned<Expression>, expr: Spanned<Expression<'a>>,
scope: &mut VariableScope, scope: &mut VariableScope<'a, '_>,
) -> Result<(String, Option<String>), Error> { ) -> Result<(Cow<'a, str>, Option<Cow<'a, str>>), Error<'a>> {
// Optimization for literals // Optimization for literals
if let Expression::Literal(spanned_lit) = &expr.node { if let Expression::Literal(spanned_lit) = &expr.node {
if let Literal::Number(n) = spanned_lit.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 { 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 { if let Literal::String(ref s) = spanned_lit.node {
return Ok((s.to_string(), None)); return Ok((s.clone(), None));
} }
} }
@@ -1202,7 +1258,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
&& let Expression::Literal(spanned_lit) = &inner.node && let Expression::Literal(spanned_lit) = &inner.node
&& let Literal::Number(n) = spanned_lit.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)?; let result_opt = self.expression(expr, scope)?;
@@ -1211,23 +1267,24 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Some(r) => r, Some(r) => r,
None => { None => {
// Expression failed or returned void. Recover with dummy. // Expression failed or returned void. Recover with dummy.
return Ok(("r0".to_string(), None)); return Ok((Cow::from("r0"), None));
} }
}; };
match result.location { match result.location {
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => { 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 { VariableLocation::Constant(lit) => match lit {
Literal::Number(n) => Ok((n.to_string(), None)), Literal::Number(n) => Ok((Cow::from(n.to_string()), None)),
Literal::Boolean(b) => Ok((if b { "1" } else { "0" }.to_string(), None)), Literal::Boolean(b) => Ok((Cow::from(if b { "1" } else { "0" }), None)),
Literal::String(s) => Ok((s, None)), Literal::String(s) => Ok((s, None)),
}, },
VariableLocation::Stack(offset) => { VariableLocation::Stack(offset) => {
// If it's on the stack, we must load it into a temp to use it as an operand // 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_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)?; let temp_reg = self.resolve_register(&temp_loc)?;
self.write_output(format!( self.write_output(format!(
@@ -1250,9 +1307,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn compile_literal_or_variable( fn compile_literal_or_variable(
&mut self, &mut self,
val: LiteralOrVariable, val: LiteralOrVariable<'a>,
scope: &mut VariableScope, scope: &mut VariableScope<'a, '_>,
) -> Result<(String, Option<String>), Error> { ) -> Result<(Cow<'a, str>, Option<Cow<'a, str>>), Error<'a>> {
let dummy_span = Span { let dummy_span = Span {
start_line: 0, start_line: 0,
start_col: 0, start_col: 0,
@@ -1276,12 +1333,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
) )
} }
fn expression_binary<'v>( fn expression_binary(
&mut self, &mut self,
expr: Spanned<BinaryExpression>, expr: Spanned<BinaryExpression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<CompilationResult, Error> { ) -> Result<CompilationResult<'a>, Error<'a>> {
fn fold_binary_expression(expr: &BinaryExpression) -> Option<Number> { fn fold_binary_expression<'a>(expr: &BinaryExpression<'a>) -> Option<Number> {
let (lhs, rhs) = match &expr { let (lhs, rhs) = match &expr {
BinaryExpression::Add(l, r) BinaryExpression::Add(l, r)
| BinaryExpression::Subtract(l, r) | BinaryExpression::Subtract(l, r)
@@ -1301,7 +1358,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 { match expr {
// 1. Base Case: It's already a number // 1. Base Case: It's already a number
Expression::Literal(lit) => match lit.node { Expression::Literal(lit) => match lit.node {
@@ -1349,7 +1406,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Allocate result register // Allocate result register
let result_name = self.next_temp_name(); 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)?; let result_reg = self.resolve_register(&result_loc)?;
// Emit instruction: op result lhs rhs // Emit instruction: op result lhs rhs
@@ -1369,17 +1426,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}) })
} }
fn expression_logical<'v>( fn expression_logical(
&mut self, &mut self,
expr: Spanned<LogicalExpression>, expr: Spanned<LogicalExpression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<CompilationResult, Error> { ) -> Result<CompilationResult<'a>, Error<'a>> {
match expr.node { match expr.node {
LogicalExpression::Not(inner) => { LogicalExpression::Not(inner) => {
let (inner_str, cleanup) = self.compile_operand(*inner, scope)?; let (inner_str, cleanup) = self.compile_operand(*inner, scope)?;
let result_name = self.next_temp_name(); 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)?; let result_reg = self.resolve_register(&result_loc)?;
// seq rX rY 0 => if rY == 0 set rX = 1 else rX = 0 // seq rX rY 0 => if rY == 0 set rX = 1 else rX = 0
@@ -1414,7 +1472,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// Allocate result register // Allocate result register
let result_name = self.next_temp_name(); 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)?; let result_reg = self.resolve_register(&result_loc)?;
// Emit instruction: op result lhs rhs // Emit instruction: op result lhs rhs
@@ -1438,9 +1497,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
fn expression_block<'v>( fn expression_block<'v>(
&mut self, &mut self,
mut expr: BlockExpression, mut expr: BlockExpression<'a>,
parent_scope: &mut VariableScope<'v>, parent_scope: &'v mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
// First, sort the expressions to ensure functions are hoisted // First, sort the expressions to ensure functions are hoisted
expr.0.sort_by(|a, b| { expr.0.sort_by(|a, b| {
if matches!( if matches!(
@@ -1502,17 +1561,21 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
/// Takes the result of the expression and stores it in VariableScope::RETURN_REGISTER /// Takes the result of the expression and stores it in VariableScope::RETURN_REGISTER
fn expression_return<'v>( fn expression_return(
&mut self, &mut self,
expr: Spanned<Expression>, expr: Spanned<Expression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<VariableLocation, Error> { ) -> Result<VariableLocation<'a>, Error<'a>> {
if let Expression::Negation(neg_expr) = &expr.node if let Expression::Negation(neg_expr) = &expr.node
&& let Expression::Literal(spanned_lit) = &neg_expr.node && let Expression::Literal(spanned_lit) = &neg_expr.node
&& let Literal::Number(neg_num) = &spanned_lit.node && let Literal::Number(neg_num) = &spanned_lit.node
{ {
let loc = VariableLocation::Persistant(VariableScope::RETURN_REGISTER); 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); return Ok(loc);
}; };
@@ -1565,17 +1628,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Expression::Literal(spanned_lit) => match spanned_lit.node { Expression::Literal(spanned_lit) => match spanned_lit.node {
Literal::Number(num) => { Literal::Number(num) => {
self.emit_variable_assignment( self.emit_variable_assignment(
"returnValue", Cow::from("returnValue"),
&VariableLocation::Persistant(VariableScope::RETURN_REGISTER), &VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
num, Cow::from(num.to_string()),
)?; )?;
} }
Literal::Boolean(b) => { Literal::Boolean(b) => {
let val = if b { "1" } else { "0" }; let val = if b { "1" } else { "0" };
self.emit_variable_assignment( self.emit_variable_assignment(
"returnValue", Cow::from("returnValue"),
&VariableLocation::Persistant(VariableScope::RETURN_REGISTER), &VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
val, Cow::from(val.to_string()),
)?; )?;
} }
_ => {} _ => {}
@@ -1634,12 +1697,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// syscalls that return values will be stored in the VariableScope::RETURN_REGISTER // syscalls that return values will be stored in the VariableScope::RETURN_REGISTER
// register // register
fn expression_syscall_system<'v>( fn expression_syscall_system(
&mut self, &mut self,
expr: System, expr: System<'a>,
span: Span, span: Span,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<Option<CompilationResult>, Error> { ) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
macro_rules! cleanup { macro_rules! cleanup {
($($to_clean:expr),*) => { ($($to_clean:expr),*) => {
$( $(
@@ -1710,7 +1773,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
.devices .devices
.get(&device_name) .get(&device_name)
.cloned() .cloned()
.unwrap_or("d0".to_string()); .unwrap_or(Cow::from("d0"));
let Spanned { let Spanned {
node: Literal::String(logic_type), node: Literal::String(logic_type),
@@ -1802,7 +1865,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
.devices .devices
.get(&device_name) .get(&device_name)
.cloned() .cloned()
.unwrap_or("d0".to_string()); .unwrap_or(Cow::from("d0"));
let Spanned { let Spanned {
node: Literal::String(logic_type), node: Literal::String(logic_type),
@@ -1892,11 +1955,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
} }
fn expression_syscall_math<'v>( fn expression_syscall_math(
&mut self, &mut self,
expr: Math, expr: Math<'a>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<Option<CompilationResult>, Error> { ) -> Result<Option<CompilationResult<'a>>, Error<'a>> {
macro_rules! cleanup { macro_rules! cleanup {
($($to_clean:expr),*) => { ($($to_clean:expr),*) => {
$( $(
@@ -2088,11 +2151,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
/// Compile a function declaration. /// Compile a function declaration.
/// Calees are responsible for backing up any registers they wish to use. /// Calees are responsible for backing up any registers they wish to use.
fn expression_function<'v>( fn expression_function(
&mut self, &mut self,
expr: Spanned<FunctionExpression>, expr: Spanned<FunctionExpression<'a>>,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'a, '_>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let FunctionExpression { let FunctionExpression {
name, name,
arguments, arguments,
@@ -2174,7 +2237,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
self.write_output("push ra")?; self.write_output("push ra")?;
block_scope.add_variable( block_scope.add_variable(
format!("{}_ra", name.node), Cow::from(format!("{}_ra", name.node)),
LocationRequest::Stack, LocationRequest::Stack,
Some(name.span), Some(name.span),
)?; )?;
@@ -2201,7 +2264,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
// Get the saved return address and save it back into `ra` // Get the saved return address and save it back into `ra`
let ra_res = block_scope.get_location_of(format!("{}_ra", name.node), Some(name.span)); let ra_res =
block_scope.get_location_of(&Cow::from(format!("{}_ra", name.node)), Some(name.span));
let ra_stack_offset = match ra_res { let ra_stack_offset = match ra_res {
Ok(VariableLocation::Stack(offset)) => offset, Ok(VariableLocation::Stack(offset)) => offset,

View File

@@ -5,28 +5,28 @@
use lsp_types::{Diagnostic, DiagnosticSeverity}; use lsp_types::{Diagnostic, DiagnosticSeverity};
use parser::tree_node::{Literal, Span}; use parser::tree_node::{Literal, Span};
use quick_error::quick_error; use std::{
use std::collections::{HashMap, VecDeque}; borrow::Cow,
collections::{HashMap, VecDeque},
};
use thiserror::Error;
const TEMP: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; const TEMP: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
const PERSIST: [u8; 7] = [8, 9, 10, 11, 12, 13, 14]; const PERSIST: [u8; 7] = [8, 9, 10, 11, 12, 13, 14];
quick_error! { #[derive(Error, Debug)]
#[derive(Debug)] pub enum Error<'a> {
pub enum Error { #[error("{0} already exists.")]
DuplicateVariable(var: String, span: Option<Span>) { DuplicateVariable(Cow<'a, str>, Option<Span>),
display("{var} already exists.")
} #[error("{0} does not exist.")]
UnknownVariable(var: String, span: Option<Span>) { UnknownVariable(Cow<'a, str>, Option<Span>),
display("{var} does not exist.")
} #[error("{0}")]
Unknown(reason: String, span: Option<Span>) { Unknown(Cow<'a, str>, Option<Span>),
display("{reason}")
}
}
} }
impl From<Error> for lsp_types::Diagnostic { impl<'a> From<Error<'a>> for lsp_types::Diagnostic {
fn from(value: Error) -> Self { fn from(value: Error) -> Self {
match value { match value {
Error::DuplicateVariable(_, span) Error::DuplicateVariable(_, span)
@@ -53,7 +53,7 @@ pub enum LocationRequest {
} }
#[derive(Clone)] #[derive(Clone)]
pub enum VariableLocation { pub enum VariableLocation<'a> {
/// Represents a temporary register (r1 - r7) /// Represents a temporary register (r1 - r7)
Temporary(u8), Temporary(u8),
/// Represents a persistant register (r8 - r14) /// Represents a persistant register (r8 - r14)
@@ -61,20 +61,22 @@ pub enum VariableLocation {
/// Represents a a stack offset (current stack - offset = variable loc) /// Represents a a stack offset (current stack - offset = variable loc)
Stack(u16), Stack(u16),
/// Represents a constant value and should be directly substituted as such. /// 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 /// Represents a device pin. This will contain the exact `d0-d5` string
Device(String), Device(Cow<'a, str>),
} }
pub struct VariableScope<'a> { // FIX: Added 'b lifetime for the parent reference
pub struct VariableScope<'a, 'b> {
temporary_vars: VecDeque<u8>, temporary_vars: VecDeque<u8>,
persistant_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, stack_offset: u16,
parent: Option<&'a VariableScope<'a>>, parent: Option<&'b VariableScope<'a, 'b>>,
} }
impl<'a> Default for VariableScope<'a> { // FIX: Updated Default impl to include 'b
impl<'a, 'b> Default for VariableScope<'a, 'b> {
fn default() -> Self { fn default() -> Self {
Self { Self {
parent: None, parent: None,
@@ -86,7 +88,8 @@ impl<'a> Default for VariableScope<'a> {
} }
} }
impl<'a> VariableScope<'a> { // FIX: Updated impl block to include '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;
pub const PERSIST_REGISTER_COUNT: u8 = 7; pub const PERSIST_REGISTER_COUNT: u8 = 7;
@@ -109,7 +112,8 @@ impl<'a> VariableScope<'a> {
}) })
} }
pub fn scoped(parent: &'a VariableScope<'a>) -> Self { // FIX: parent is now &'b VariableScope<'a, 'b>
pub fn scoped(parent: &'b VariableScope<'a, 'b>) -> Self {
Self { Self {
parent: Option::Some(parent), parent: Option::Some(parent),
temporary_vars: parent.temporary_vars.clone(), temporary_vars: parent.temporary_vars.clone(),
@@ -126,12 +130,11 @@ impl<'a> VariableScope<'a> {
/// to the stack. /// to the stack.
pub fn add_variable( pub fn add_variable(
&mut self, &mut self,
var_name: impl Into<String>, var_name: Cow<'a, str>,
location: LocationRequest, location: LocationRequest,
span: Option<Span>, span: Option<Span>,
) -> Result<VariableLocation, Error> { ) -> Result<VariableLocation<'a>, Error<'a>> {
let var_name = var_name.into(); if self.var_lookup_table.contains_key(&var_name) {
if self.var_lookup_table.contains_key(var_name.as_str()) {
return Err(Error::DuplicateVariable(var_name, span)); return Err(Error::DuplicateVariable(var_name, span));
} }
let var_location = match location { let var_location = match location {
@@ -166,11 +169,10 @@ impl<'a> VariableScope<'a> {
pub fn define_const( pub fn define_const(
&mut self, &mut self,
var_name: impl Into<String>, var_name: Cow<'a, str>,
value: Literal, value: Literal<'a>,
span: Option<Span>, span: Option<Span>,
) -> Result<VariableLocation, Error> { ) -> Result<VariableLocation<'a>, Error<'a>> {
let var_name = var_name.into();
if self.var_lookup_table.contains_key(&var_name) { if self.var_lookup_table.contains_key(&var_name) {
return Err(Error::DuplicateVariable(var_name, span)); return Err(Error::DuplicateVariable(var_name, span));
} }
@@ -183,13 +185,11 @@ impl<'a> VariableScope<'a> {
pub fn get_location_of( pub fn get_location_of(
&self, &self,
var_name: impl Into<String>, var_name: &Cow<'a, str>,
span: Option<Span>, span: Option<Span>,
) -> Result<VariableLocation, Error> { ) -> Result<VariableLocation<'a>, Error<'a>> {
let var_name = var_name.into();
// 1. Check this scope // 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 { if let VariableLocation::Stack(inserted_at_offset) = var {
// Return offset relative to CURRENT sp // Return offset relative to CURRENT sp
return Ok(VariableLocation::Stack( return Ok(VariableLocation::Stack(
@@ -210,7 +210,7 @@ impl<'a> VariableScope<'a> {
return Ok(loc); return Ok(loc);
} }
Err(Error::UnknownVariable(var_name, span)) Err(Error::UnknownVariable(var_name.clone(), span))
} }
pub fn has_parent(&self) -> bool { pub fn has_parent(&self) -> bool {
@@ -220,11 +220,10 @@ impl<'a> VariableScope<'a> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn free_temp( pub fn free_temp(
&mut self, &mut self,
var_name: impl Into<String>, var_name: Cow<'a, str>,
span: Option<Span>, span: Option<Span>,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
let var_name = var_name.into(); let Some(location) = self.var_lookup_table.remove(&var_name) else {
let Some(location) = self.var_lookup_table.remove(var_name.as_str()) else {
return Err(Error::UnknownVariable(var_name, span)); return Err(Error::UnknownVariable(var_name, span));
}; };
@@ -234,7 +233,7 @@ impl<'a> VariableScope<'a> {
} }
VariableLocation::Persistant(_) => { VariableLocation::Persistant(_) => {
return Err(Error::UnknownVariable( return Err(Error::UnknownVariable(
String::from("Attempted to free a `let` variable."), Cow::from("Attempted to free a `let` variable."),
span, span,
)); ));
} }

View File

@@ -0,0 +1,41 @@
// Pressure numbers are in KPa
device self = "db";
device emergencyRelief = "d0";
device greenhouseSensor = "d1";
device recycleValve = "d2";
const MAX_INTERIOR_PRESSURE = 80;
const MAX_INTERIOR_TEMP = 28c;
const MIN_INTERIOR_PRESSURE = 75;
const MIN_INTERIOR_TEMP = 25c;
const daylightSensor = 1076425094;
const growLight = hash("StructureGrowLight");
const wallLight = hash("StructureLightLong");
const lightRound = hash("StructureLightRound");
let shouldPurge = false;
loop {
let interiorPress = greenhouseSensor.Pressure;
let interiorTemp = greenhouseSensor.Temperature;
shouldPurge = (
interiorPress > MAX_INTERIOR_PRESSURE ||
interiorTemp > MAX_INTERIOR_TEMP
) || shouldPurge;
emergencyRelief.On = shouldPurge;
recycleValve.On = !shouldPurge;
if (shouldPurge && (interiorPress < MIN_INTERIOR_PRESSURE && interiorTemp < MIN_INTERIOR_TEMP)) {
shouldPurge = false;
}
let solarAngle = lb(daylightSensor, "SolarAngle", "Average");
let isDaylight = solarAngle < 90;
sb(growLight, "On", isDaylight);
sb(wallLight, "On", !isDaylight);
sb(lightRound, "On", !isDaylight);
}

View File

@@ -3,15 +3,10 @@ macro_rules! documented {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Internal Helper: Filter doc comments // Internal Helper: Filter doc comments
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Case 1: Doc comment. Return Some("string").
// We match the specific structure of a doc attribute.
(@doc_filter #[doc = $doc:expr]) => { (@doc_filter #[doc = $doc:expr]) => {
Some($doc) Some($doc)
}; };
// Case 2: Other attributes (derives, etc.). Return None.
// We catch any other token sequence inside the brackets.
(@doc_filter #[$($attr:tt)*]) => { (@doc_filter #[$($attr:tt)*]) => {
None None
}; };
@@ -30,23 +25,59 @@ macro_rules! documented {
}; };
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Main Macro Entry Point // Entry Point 1: Enum with a single Lifetime (e.g. enum Foo<'a>)
// -------------------------------------------------------------------------
(
$(#[$enum_attr:meta])* $vis:vis enum $name:ident < $lt:lifetime > {
$($body:tt)*
}
) => {
documented!(@generate
meta: [$(#[$enum_attr])*],
vis: [$vis],
name: [$name],
generics: [<$lt>],
body: [$($body)*]
);
};
// -------------------------------------------------------------------------
// Entry Point 2: Regular Enum (No Generics)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
( (
$(#[$enum_attr:meta])* $vis:vis enum $name:ident { $(#[$enum_attr:meta])* $vis:vis enum $name:ident {
$($body:tt)*
}
) => {
documented!(@generate
meta: [$(#[$enum_attr])*],
vis: [$vis],
name: [$name],
generics: [],
body: [$($body)*]
);
};
// -------------------------------------------------------------------------
// Code Generator (Shared Logic)
// -------------------------------------------------------------------------
(@generate
meta: [$(#[$enum_attr:meta])*],
vis: [$vis:vis],
name: [$name:ident],
generics: [$($generics:tt)*],
body: [
$( $(
// Capture attributes as a sequence of token trees inside brackets
// to avoid "local ambiguity" and handle multi-token attributes (like doc="...").
$(#[ $($variant_attr:tt)* ])* $(#[ $($variant_attr:tt)* ])*
$variant:ident $variant:ident
$( ($($tuple:tt)*) )? $( ($($tuple:tt)*) )?
$( {$($structure:tt)*} )? $( {$($structure:tt)*} )?
),* $(,)? ),* $(,)?
} ]
) => { ) => {
// 1. Generate the actual Enum definition // 1. Generate the Enum Definition
$(#[$enum_attr])* $(#[$enum_attr])*
$vis enum $name { $vis enum $name $($generics)* {
$( $(
$(#[ $($variant_attr)* ])* $(#[ $($variant_attr)* ])*
$variant $variant
@@ -55,20 +86,19 @@ macro_rules! documented {
)* )*
} }
// 2. Implement the Documentation Trait // 2. Implement Documentation Trait
impl Documentation for $name { // We apply the captured generics (e.g., <'a>) to both the impl and the type
impl $($generics)* Documentation for $name $($generics)* {
fn docs(&self) -> String { fn docs(&self) -> String {
match self { match self {
$( $(
documented!(@arm $name $variant $( ($($tuple)*) )? $( {$($structure)*} )? ) => { documented!(@arm $name $variant $( ($($tuple)*) )? $( {$($structure)*} )? ) => {
// Create a temporary array of Option<&str> for all attributes
let doc_lines: &[Option<&str>] = &[ let doc_lines: &[Option<&str>] = &[
$( $(
documented!(@doc_filter #[ $($variant_attr)* ]) documented!(@doc_filter #[ $($variant_attr)* ])
),* ),*
]; ];
// Filter out the Nones (non-doc attributes), join, and return
doc_lines.iter() doc_lines.iter()
.filter_map(|&d| d) .filter_map(|&d| d)
.collect::<Vec<_>>() .collect::<Vec<_>>()
@@ -80,7 +110,6 @@ macro_rules! documented {
} }
} }
// 3. Implement Static Documentation Provider
#[allow(dead_code)] #[allow(dead_code)]
fn get_all_documentation() -> Vec<(&'static str, String)> { fn get_all_documentation() -> Vec<(&'static str, String)> {
vec![ vec![
@@ -88,7 +117,6 @@ macro_rules! documented {
( (
stringify!($variant), stringify!($variant),
{ {
// Re-use the same extraction logic
let doc_lines: &[Option<&str>] = &[ let doc_lines: &[Option<&str>] = &[
$( $(
documented!(@doc_filter #[ $($variant_attr)* ]) documented!(@doc_filter #[ $($variant_attr)* ])

View File

@@ -4,10 +4,10 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
quick-error = { workspace = true }
tokenizer = { path = "../tokenizer" } tokenizer = { path = "../tokenizer" }
helpers = { path = "../helpers" } helpers = { path = "../helpers" }
lsp-types = { workspace = true } lsp-types = { workspace = true }
thiserror = { workspace = true }
[dev-dependencies] [dev-dependencies]

View File

@@ -1,13 +1,12 @@
pub mod sys_call;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
pub mod sys_call;
pub mod tree_node; pub mod tree_node;
use crate::sys_call::{Math, System}; use crate::sys_call::{Math, System};
use quick_error::quick_error; use std::{borrow::Cow, io::SeekFrom};
use std::io::SeekFrom;
use sys_call::SysCall; use sys_call::SysCall;
use thiserror::Error;
use tokenizer::{ use tokenizer::{
self, Tokenizer, TokenizerBuffer, self, Tokenizer, TokenizerBuffer,
token::{Keyword, Symbol, Token, TokenType}, token::{Keyword, Symbol, Token, TokenType},
@@ -26,38 +25,33 @@ macro_rules! boxed {
}; };
} }
quick_error! { #[derive(Error, Debug)]
#[derive(Debug)] pub enum Error<'a> {
pub enum Error { #[error(transparent)]
TokenizerError(err: tokenizer::Error) { Tokenizer(#[from] tokenizer::Error),
from()
display("Tokenizer Error: {}", err) #[error("Unexpected token: {1}")]
source(err) UnexpectedToken(Span, Token<'a>),
}
UnexpectedToken(span: Span, token: Token) { #[error("Duplicate identifier: {1}")]
display("Unexpected token: {}", token.token_type) DuplicateIdentifier(Span, Token<'a>),
}
DuplicateIdentifier(span: Span, token: Token) { #[error("Invalid Syntax: {1}")]
display("Duplicate identifier: {}", token.token_type) InvalidSyntax(Span, String),
}
InvalidSyntax(span: Span, reason: String) { #[error("Unsupported Keyword: {1}")]
display("Invalid syntax: {}", reason) UnsupportedKeyword(Span, Token<'a>),
}
UnsupportedKeyword(span: Span, token: Token) { #[error("Unexpected End of File")]
display("Unsupported keyword: {}", token.token_type) UnexpectedEOF,
}
UnexpectedEOF {
display("Unexpected EOF")
}
}
} }
impl From<Error> for lsp_types::Diagnostic { impl<'a> From<Error<'a>> for lsp_types::Diagnostic {
fn from(value: Error) -> Self { fn from(value: Error) -> Self {
use Error::*; use Error::*;
use lsp_types::*; use lsp_types::*;
match value { match value {
TokenizerError(e) => e.into(), Tokenizer(e) => e.into(),
UnexpectedToken(span, _) UnexpectedToken(span, _)
| DuplicateIdentifier(span, _) | DuplicateIdentifier(span, _)
| InvalidSyntax(span, _) | InvalidSyntax(span, _)
@@ -111,8 +105,8 @@ macro_rules! self_matches_current {
pub struct Parser<'a> { pub struct Parser<'a> {
tokenizer: TokenizerBuffer<'a>, tokenizer: TokenizerBuffer<'a>,
current_token: Option<Token>, current_token: Option<Token<'a>>,
pub errors: Vec<Error>, pub errors: Vec<Error<'a>>,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@@ -125,13 +119,12 @@ impl<'a> Parser<'a> {
} }
/// Calculates a Span from a given Token reference. /// Calculates a Span from a given Token reference.
fn token_to_span(t: &Token) -> Span { fn token_to_span(t: &Token<'a>) -> Span {
let len = t.original_string.as_ref().map(|s| s.len()).unwrap_or(0);
Span { Span {
start_line: t.line, start_line: t.line,
start_col: t.column, start_col: t.span.start,
end_line: t.line, end_line: t.line,
end_col: t.column + len, end_col: t.span.end,
} }
} }
@@ -148,9 +141,9 @@ impl<'a> Parser<'a> {
} }
/// Helper to run a parsing closure and wrap the result in a Spanned struct /// Helper to run a parsing closure and wrap the result in a Spanned struct
fn spanned<F, T>(&mut self, parser: F) -> Result<Spanned<T>, Error> fn spanned<F, T>(&mut self, parser: F) -> Result<Spanned<T>, Error<'a>>
where where
F: FnOnce(&mut Self) -> Result<T, Error>, F: FnOnce(&mut Self) -> Result<T, Error<'a>>,
{ {
let start_token = if self.current_token.is_some() { let start_token = if self.current_token.is_some() {
self.current_token.clone() self.current_token.clone()
@@ -160,18 +153,16 @@ impl<'a> Parser<'a> {
let (start_line, start_col) = start_token let (start_line, start_col) = start_token
.as_ref() .as_ref()
.map(|t| (t.line, t.column)) .map(|t| (t.line, t.span.start))
.unwrap_or((1, 1)); .unwrap_or((0, 0));
let node = parser(self)?; let node = parser(self)?;
let end_token = self.current_token.as_ref(); let end_token = &self.current_token;
let (end_line, end_col) = end_token let (end_line, end_col) = end_token
.map(|t| { .clone()
let len = t.original_string.as_ref().map(|s| s.len()).unwrap_or(0); .map(|t| (t.line, t.span.end))
(t.line, t.column + len)
})
.unwrap_or((start_line, start_col)); .unwrap_or((start_line, start_col));
Ok(Spanned { Ok(Spanned {
@@ -185,7 +176,7 @@ impl<'a> Parser<'a> {
}) })
} }
fn synchronize(&mut self) -> Result<(), Error> { fn synchronize(&mut self) -> Result<(), Error<'a>> {
self.assign_next()?; self.assign_next()?;
while let Some(token) = &self.current_token { while let Some(token) = &self.current_token {
@@ -211,20 +202,20 @@ impl<'a> Parser<'a> {
Ok(()) Ok(())
} }
pub fn parse_all(&mut self) -> Result<Option<tree_node::Expression>, Error> { pub fn parse_all(&mut self) -> Result<Option<tree_node::Expression<'a>>, Error<'a>> {
let first_token = self.tokenizer.peek().unwrap_or(None); let first_token = self.tokenizer.peek().unwrap_or(None);
let (start_line, start_col) = first_token let (start_line, start_col) = first_token
.as_ref() .as_ref()
.map(|tok| (tok.line, tok.column)) .map(|tok| (tok.line, tok.span.start))
.unwrap_or((1, 1)); .unwrap_or((0, 0));
let mut expressions = Vec::<Spanned<Expression>>::new(); let mut expressions = Vec::<Spanned<Expression<'a>>>::new();
loop { loop {
match self.tokenizer.peek() { match self.tokenizer.peek() {
Ok(None) => break, Ok(None) => break,
Err(e) => { Err(e) => {
self.errors.push(Error::TokenizerError(e)); self.errors.push(Error::Tokenizer(e));
break; break;
} }
_ => {} _ => {}
@@ -246,10 +237,7 @@ impl<'a> Parser<'a> {
let end_token_opt = self.tokenizer.peek().unwrap_or(None); let end_token_opt = self.tokenizer.peek().unwrap_or(None);
let (end_line, end_col) = end_token_opt let (end_line, end_col) = end_token_opt
.map(|tok| { .map(|tok| (tok.line, tok.span.end))
let len = tok.original_string.as_ref().map(|s| s.len()).unwrap_or(0);
(tok.line, tok.column + len)
})
.unwrap_or((start_line, start_col)); .unwrap_or((start_line, start_col));
let span = Span { let span = Span {
@@ -265,7 +253,7 @@ impl<'a> Parser<'a> {
}))) })))
} }
pub fn parse(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> { pub fn parse(&mut self) -> Result<Option<Spanned<tree_node::Expression<'a>>>, Error<'a>> {
self.assign_next()?; self.assign_next()?;
if self.current_token.is_none() { if self.current_token.is_none() {
@@ -281,17 +269,17 @@ impl<'a> Parser<'a> {
Ok(expr) Ok(expr)
} }
fn assign_next(&mut self) -> Result<(), Error> { fn assign_next(&mut self) -> Result<(), Error<'a>> {
self.current_token = self.tokenizer.next_token()?; self.current_token = self.tokenizer.next_token()?;
Ok(()) Ok(())
} }
fn get_next(&mut self) -> Result<Option<&Token>, Error> { fn get_next(&mut self) -> Result<Option<Token<'a>>, Error<'a>> {
self.assign_next()?; self.assign_next()?;
Ok(self.current_token.as_ref()) Ok(self.current_token.clone())
} }
fn expression(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> { fn expression(&mut self) -> Result<Option<Spanned<tree_node::Expression<'a>>>, Error<'a>> {
// Parse the Left Hand Side (unary/primary expression) // Parse the Left Hand Side (unary/primary expression)
let lhs = self.unary()?; let lhs = self.unary()?;
@@ -322,14 +310,14 @@ impl<'a> Parser<'a> {
/// Handles dot notation chains: x.y.z() /// Handles dot notation chains: x.y.z()
fn parse_postfix( fn parse_postfix(
&mut self, &mut self,
mut lhs: Spanned<Expression>, mut lhs: Spanned<Expression<'a>>,
) -> Result<Spanned<Expression>, Error> { ) -> Result<Spanned<Expression<'a>>, Error<'a>> {
loop { loop {
if self_matches_peek!(self, TokenType::Symbol(Symbol::Dot)) { if self_matches_peek!(self, TokenType::Symbol(Symbol::Dot)) {
self.assign_next()?; // consume Dot self.assign_next()?; // consume Dot
let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
let identifier_span = Self::token_to_span(identifier_token); let identifier_span = Self::token_to_span(&identifier_token);
let identifier = match identifier_token.token_type { let identifier = match identifier_token.token_type {
TokenType::Identifier(ref id) => id.clone(), TokenType::Identifier(ref id) => id.clone(),
_ => { _ => {
@@ -344,7 +332,7 @@ impl<'a> Parser<'a> {
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) { if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) {
// Method Call // Method Call
self.assign_next()?; // consume '(' self.assign_next()?; // consume '('
let mut arguments = Vec::<Spanned<Expression>>::new(); let mut arguments = Vec::<Spanned<Expression<'a>>>::new();
while !token_matches!( while !token_matches!(
self.get_next()?.ok_or(Error::UnexpectedEOF)?, self.get_next()?.ok_or(Error::UnexpectedEOF)?,
@@ -366,8 +354,8 @@ impl<'a> Parser<'a> {
{ {
let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(next_token), Self::token_to_span(&next_token),
next_token.clone(), next_token,
)); ));
} }
@@ -429,7 +417,7 @@ impl<'a> Parser<'a> {
Ok(lhs) Ok(lhs)
} }
fn unary(&mut self) -> Result<Option<Spanned<tree_node::Expression>>, Error> { fn unary(&mut self) -> Result<Option<Spanned<tree_node::Expression<'a>>>, Error<'a>> {
macro_rules! matches_keyword { macro_rules! matches_keyword {
($keyword:expr, $($pattern:pat),+) => { ($keyword:expr, $($pattern:pat),+) => {
matches!($keyword, $($pattern)|+) matches!($keyword, $($pattern)|+)
@@ -507,10 +495,7 @@ impl<'a> Parser<'a> {
let span = self.current_span(); let span = self.current_span();
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
Some(Spanned { Some(Spanned {
span, span,
@@ -522,10 +507,7 @@ impl<'a> Parser<'a> {
let span = self.current_span(); let span = self.current_span();
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
Some(Spanned { Some(Spanned {
span, span,
@@ -586,17 +568,6 @@ impl<'a> Parser<'a> {
let start_span = self.current_span(); let start_span = self.current_span();
self.assign_next()?; self.assign_next()?;
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?; let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
// NOTE: Unary negation can also have postfix applied to the inner expression
// But generally -a.b parses as -(a.b), which is what parse_postfix ensures if called here.
// However, we call parse_postfix on the RESULT of unary in expression(), so
// `expression` sees `Negation`. `parse_postfix` doesn't apply to Negation node unless we allow it?
// Actually, `x.y` binds tighter than `-`. `postfix` logic belongs inside `unary` logic or
// `expression` logic.
// If I have `-x.y`, standard precedence says `-(x.y)`.
// `unary` returns `Negation(x)`. Then `expression` calls `postfix` on `Negation(x)`.
// `postfix` loop runs on `Negation`. This implies `(-x).y`. This is usually WRONG.
// `.` binds tighter than `-`.
// So `unary` must call `postfix` on the *operand* of the negation.
let inner_with_postfix = self.parse_postfix(inner_expr)?; let inner_with_postfix = self.parse_postfix(inner_expr)?;
@@ -643,7 +614,7 @@ impl<'a> Parser<'a> {
Ok(expr) Ok(expr)
} }
fn get_infix_child_node(&mut self) -> Result<Spanned<tree_node::Expression>, Error> { fn get_infix_child_node(&mut self) -> Result<Spanned<tree_node::Expression<'a>>, Error<'a>> {
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
let start_span = self.current_span(); let start_span = self.current_span();
@@ -727,7 +698,7 @@ impl<'a> Parser<'a> {
self.parse_postfix(expr) self.parse_postfix(expr)
} }
fn device(&mut self) -> Result<DeviceDeclarationExpression, Error> { fn device(&mut self) -> Result<DeviceDeclarationExpression<'a>, Error<'a>> {
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
if !self_matches_current!(self, TokenType::Keyword(Keyword::Device)) { if !self_matches_current!(self, TokenType::Keyword(Keyword::Device)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
@@ -737,12 +708,12 @@ impl<'a> Parser<'a> {
} }
let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
let identifier_span = Self::token_to_span(identifier_token); let identifier_span = Self::token_to_span(&identifier_token);
let identifier = match identifier_token.token_type { let identifier = match identifier_token.token_type {
TokenType::Identifier(ref id) => id.clone(), TokenType::Identifier(ref id) => id.clone(),
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(identifier_token), Self::token_to_span(&identifier_token),
identifier_token.clone(), identifier_token.clone(),
)); ));
} }
@@ -751,7 +722,7 @@ impl<'a> Parser<'a> {
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::Assign)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::Assign)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(current_token), Self::token_to_span(&current_token),
current_token.clone(), current_token.clone(),
)); ));
} }
@@ -761,7 +732,7 @@ impl<'a> Parser<'a> {
TokenType::String(ref id) => id.clone(), TokenType::String(ref id) => id.clone(),
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(device_token), Self::token_to_span(&device_token),
device_token.clone(), device_token.clone(),
)); ));
} }
@@ -776,7 +747,10 @@ impl<'a> Parser<'a> {
}) })
} }
fn infix(&mut self, previous: Spanned<Expression>) -> Result<Spanned<Expression>, Error> { fn infix(
&mut self,
previous: Spanned<Expression<'a>>,
) -> Result<Spanned<Expression<'a>>, Error<'a>> {
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone(); let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?.clone();
match previous.node { match previous.node {
@@ -1084,7 +1058,7 @@ impl<'a> Parser<'a> {
expressions.pop().ok_or(Error::UnexpectedEOF) expressions.pop().ok_or(Error::UnexpectedEOF)
} }
fn priority(&mut self) -> Result<Option<Box<Spanned<Expression>>>, Error> { fn priority(&mut self) -> Result<Option<Box<Spanned<Expression<'a>>>>, Error<'a>> {
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
@@ -1099,15 +1073,15 @@ impl<'a> Parser<'a> {
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::RParen)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::RParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(current_token), Self::token_to_span(&current_token),
current_token.clone(), current_token,
)); ));
} }
Ok(Some(boxed!(expression))) Ok(Some(boxed!(expression)))
} }
fn invocation(&mut self) -> Result<InvocationExpression, Error> { fn invocation(&mut self) -> Result<InvocationExpression<'a>, Error<'a>> {
let identifier_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let identifier_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
let identifier_span = Self::token_to_span(identifier_token); let identifier_span = Self::token_to_span(identifier_token);
let identifier = match identifier_token.token_type { let identifier = match identifier_token.token_type {
@@ -1123,8 +1097,8 @@ impl<'a> Parser<'a> {
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(current_token), Self::token_to_span(&current_token),
current_token.clone(), current_token,
)); ));
} }
@@ -1150,8 +1124,8 @@ impl<'a> Parser<'a> {
{ {
let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(next_token), Self::token_to_span(&next_token),
next_token.clone(), next_token,
)); ));
} }
@@ -1169,7 +1143,7 @@ impl<'a> Parser<'a> {
}) })
} }
fn block(&mut self) -> Result<BlockExpression, Error> { fn block(&mut self) -> Result<BlockExpression<'a>, Error<'a>> {
let mut expressions = Vec::<Spanned<Expression>>::new(); let mut expressions = Vec::<Spanned<Expression>>::new();
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
@@ -1192,7 +1166,7 @@ impl<'a> Parser<'a> {
if token_matches!(current_token, TokenType::Keyword(Keyword::Return)) { if token_matches!(current_token, TokenType::Keyword(Keyword::Return)) {
// Need to capture return span // Need to capture return span
let ret_start_span = Self::token_to_span(current_token); let ret_start_span = Self::token_to_span(&current_token);
self.assign_next()?; self.assign_next()?;
let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?; let expression = self.expression()?.ok_or(Error::UnexpectedEOF)?;
@@ -1211,25 +1185,19 @@ impl<'a> Parser<'a> {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) { if !token_matches!(next, TokenType::Symbol(Symbol::Semicolon)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::RBrace)) { if !token_matches!(next, TokenType::Symbol(Symbol::RBrace)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
} }
Ok(BlockExpression(expressions)) Ok(BlockExpression(expressions))
} }
fn const_declaration(&mut self) -> Result<ConstDeclarationExpression, Error> { fn const_declaration(&mut self) -> Result<ConstDeclarationExpression<'a>, Error<'a>> {
// const // const
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
if !self_matches_current!(self, TokenType::Keyword(Keyword::Const)) { if !self_matches_current!(self, TokenType::Keyword(Keyword::Const)) {
@@ -1241,7 +1209,7 @@ impl<'a> Parser<'a> {
// variable_name // variable_name
let ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
let ident_span = Self::token_to_span(ident_token); let ident_span = Self::token_to_span(&ident_token);
let ident = match ident_token.token_type { let ident = match ident_token.token_type {
TokenType::Identifier(ref id) => id.clone(), TokenType::Identifier(ref id) => id.clone(),
_ => return Err(Error::UnexpectedToken(ident_span, ident_token.clone())), _ => return Err(Error::UnexpectedToken(ident_span, ident_token.clone())),
@@ -1299,7 +1267,7 @@ impl<'a> Parser<'a> {
} }
} }
fn declaration(&mut self) -> Result<Expression, Error> { fn declaration(&mut self) -> Result<Expression<'a>, Error<'a>> {
let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.as_ref().ok_or(Error::UnexpectedEOF)?;
if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) { if !self_matches_current!(self, TokenType::Keyword(Keyword::Let)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
@@ -1308,13 +1276,13 @@ impl<'a> Parser<'a> {
)); ));
} }
let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let identifier_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
let identifier_span = Self::token_to_span(identifier_token); let identifier_span = Self::token_to_span(&identifier_token);
let identifier = match identifier_token.token_type { let identifier = match identifier_token.token_type {
TokenType::Identifier(ref id) => id.clone(), TokenType::Identifier(ref id) => id.clone(),
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(identifier_token), Self::token_to_span(&identifier_token),
identifier_token.clone(), identifier_token,
)); ));
} }
}; };
@@ -1334,8 +1302,8 @@ impl<'a> Parser<'a> {
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::Semicolon)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::Semicolon)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(current_token), Self::token_to_span(&current_token),
current_token.clone(), current_token,
)); ));
} }
@@ -1348,7 +1316,7 @@ impl<'a> Parser<'a> {
)) ))
} }
fn literal(&mut self) -> Result<Literal, Error> { fn literal(&mut self) -> Result<Literal<'a>, Error<'a>> {
let current_token = self.current_token.clone().ok_or(Error::UnexpectedEOF)?; let current_token = self.current_token.clone().ok_or(Error::UnexpectedEOF)?;
let literal = match current_token.token_type { let literal = match current_token.token_type {
TokenType::Number(num) => Literal::Number(num), TokenType::Number(num) => Literal::Number(num),
@@ -1358,11 +1326,11 @@ impl<'a> Parser<'a> {
Some(Token { Some(Token {
token_type: TokenType::Number(num), token_type: TokenType::Number(num),
.. ..
}) => Literal::Number(-*num), }) => Literal::Number(-num),
Some(wrong_token) => { Some(wrong_token) => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(wrong_token), Self::token_to_span(&wrong_token),
wrong_token.clone(), wrong_token,
)); ));
} }
None => return Err(Error::UnexpectedEOF), None => return Err(Error::UnexpectedEOF),
@@ -1378,14 +1346,11 @@ impl<'a> Parser<'a> {
Ok(literal) Ok(literal)
} }
fn if_expression(&mut self) -> Result<IfExpression, Error> { fn if_expression(&mut self) -> Result<IfExpression<'a>, Error<'a>> {
// 'if' is current // 'if' is current
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) { if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
self.assign_next()?; self.assign_next()?;
@@ -1393,18 +1358,12 @@ impl<'a> Parser<'a> {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) { if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) { if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
let body = self.spanned(|p| p.block())?; let body = self.spanned(|p| p.block())?;
@@ -1429,10 +1388,7 @@ impl<'a> Parser<'a> {
})) }))
} else { } else {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
} else { } else {
None None
@@ -1445,13 +1401,10 @@ impl<'a> Parser<'a> {
}) })
} }
fn loop_expression(&mut self) -> Result<LoopExpression, Error> { fn loop_expression(&mut self) -> Result<LoopExpression<'a>, Error<'a>> {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) { if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
let body = self.spanned(|p| p.block())?; let body = self.spanned(|p| p.block())?;
@@ -1459,13 +1412,10 @@ impl<'a> Parser<'a> {
Ok(LoopExpression { body }) Ok(LoopExpression { body })
} }
fn while_expression(&mut self) -> Result<WhileExpression, Error> { fn while_expression(&mut self) -> Result<WhileExpression<'a>, Error<'a>> {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) { if !token_matches!(next, TokenType::Symbol(Symbol::LParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
self.assign_next()?; self.assign_next()?;
@@ -1473,18 +1423,12 @@ impl<'a> Parser<'a> {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) { if !token_matches!(next, TokenType::Symbol(Symbol::RParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) { if !token_matches!(next, TokenType::Symbol(Symbol::LBrace)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
let body = self.block()?; let body = self.block()?;
@@ -1495,29 +1439,26 @@ impl<'a> Parser<'a> {
}) })
} }
fn function(&mut self) -> Result<FunctionExpression, Error> { fn function(&mut self) -> Result<FunctionExpression<'a>, Error<'a>> {
// 'fn' is current // 'fn' is current
let fn_ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let fn_ident_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
let fn_ident_span = Self::token_to_span(fn_ident_token); let fn_ident_span = Self::token_to_span(&fn_ident_token);
let fn_ident = match fn_ident_token.token_type { let fn_ident = match fn_ident_token.token_type {
TokenType::Identifier(ref id) => id.clone(), TokenType::Identifier(ref id) => id.clone(),
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(fn_ident_span, fn_ident_token));
Self::token_to_span(fn_ident_token),
fn_ident_token.clone(),
));
} }
}; };
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::LParen)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(current_token), Self::token_to_span(&current_token),
current_token.clone(), current_token,
)); ));
} }
let mut arguments = Vec::<Spanned<String>>::new(); let mut arguments = Vec::<Spanned<Cow<'a, str>>>::new();
while !token_matches!( while !token_matches!(
self.get_next()?.ok_or(Error::UnexpectedEOF)?, self.get_next()?.ok_or(Error::UnexpectedEOF)?,
@@ -1528,10 +1469,7 @@ impl<'a> Parser<'a> {
let argument = match current_token.token_type { let argument = match current_token.token_type {
TokenType::Identifier(ref id) => id.clone(), TokenType::Identifier(ref id) => id.clone(),
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(arg_span, current_token.clone()));
Self::token_to_span(current_token),
current_token.clone(),
));
} }
}; };
@@ -1553,10 +1491,7 @@ impl<'a> Parser<'a> {
&& !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) && !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen))
{ {
let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let next = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(Self::token_to_span(&next), next));
Self::token_to_span(next),
next.clone(),
));
} }
if !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) { if !self_matches_peek!(self, TokenType::Symbol(Symbol::RParen)) {
@@ -1567,8 +1502,8 @@ impl<'a> Parser<'a> {
let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?; let current_token = self.get_next()?.ok_or(Error::UnexpectedEOF)?;
if !token_matches!(current_token, TokenType::Symbol(Symbol::LBrace)) { if !token_matches!(current_token, TokenType::Symbol(Symbol::LBrace)) {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
Self::token_to_span(current_token), Self::token_to_span(&current_token),
current_token.clone(), current_token,
)); ));
}; };
@@ -1582,15 +1517,15 @@ impl<'a> Parser<'a> {
}) })
} }
fn syscall(&mut self) -> Result<SysCall, Error> { fn syscall(&mut self) -> Result<SysCall<'a>, Error<'a>> {
fn check_length( fn check_length<'a>(
parser: &Parser, span: Span,
arguments: &[Spanned<Expression>], arguments: &[Spanned<Expression<'a>>],
length: usize, length: usize,
) -> Result<(), Error> { ) -> Result<(), Error<'a>> {
if arguments.len() != length { if arguments.len() != length {
return Err(Error::InvalidSyntax( return Err(Error::InvalidSyntax(
parser.current_span(), span,
format!("Expected {} arguments", length), format!("Expected {} arguments", length),
)); ));
} }
@@ -1648,20 +1583,20 @@ impl<'a> Parser<'a> {
let invocation = self.invocation()?; let invocation = self.invocation()?;
match invocation.name.node.as_str() { match invocation.name.node.as_ref() {
// System SysCalls // System SysCalls
"yield" => { "yield" => {
check_length(self, &invocation.arguments, 0)?; check_length(self.current_span(), &invocation.arguments, 0)?;
Ok(SysCall::System(sys_call::System::Yield)) Ok(SysCall::System(sys_call::System::Yield))
} }
"sleep" => { "sleep" => {
check_length(self, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 1)?;
let mut arg = invocation.arguments.into_iter(); let mut arg = invocation.arguments.into_iter();
let expr = arg.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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let lit_str = literal_or_variable!(args.next()); let lit_str = literal_or_variable!(args.next());
@@ -1682,7 +1617,7 @@ impl<'a> Parser<'a> {
}))) })))
} }
"load" | "l" => { "load" | "l" => {
check_length(self, &invocation.arguments, 2)?; check_length(self.current_span(), &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let tmp = args.next(); let tmp = args.next();
@@ -1727,7 +1662,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"loadBatched" | "lb" => { "loadBatched" | "lb" => {
check_length(self, &invocation.arguments, 3)?; check_length(self.current_span(), &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); 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);
@@ -1745,7 +1680,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"loadBatchedNamed" | "lbn" => { "loadBatchedNamed" | "lbn" => {
check_length(self, &invocation.arguments, 4)?; check_length(self.current_span(), &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter(); 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);
@@ -1764,7 +1699,7 @@ impl<'a> Parser<'a> {
))) )))
} }
"set" | "s" => { "set" | "s" => {
check_length(self, &invocation.arguments, 3)?; check_length(self.current_span(), &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); 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);
@@ -1776,14 +1711,16 @@ impl<'a> Parser<'a> {
Ok(SysCall::System(sys_call::System::SetOnDevice( Ok(SysCall::System(sys_call::System::SetOnDevice(
device, device,
Spanned { Spanned {
node: Literal::String(logic_type.node.to_string().replace("\"", "")), node: Literal::String(Cow::from(
logic_type.node.to_string().replace("\"", ""),
)),
span: logic_type.span, span: logic_type.span,
}, },
boxed!(variable), boxed!(variable),
))) )))
} }
"setBatched" | "sb" => { "setBatched" | "sb" => {
check_length(self, &invocation.arguments, 3)?; check_length(self.current_span(), &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); 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);
@@ -1795,14 +1732,14 @@ impl<'a> Parser<'a> {
Ok(SysCall::System(sys_call::System::SetOnDeviceBatched( Ok(SysCall::System(sys_call::System::SetOnDeviceBatched(
device_hash, device_hash,
Spanned { Spanned {
node: Literal::String(logic_type.to_string().replace("\"", "")), node: Literal::String(Cow::from(logic_type.to_string().replace("\"", ""))),
span: logic_type.span, span: logic_type.span,
}, },
boxed!(variable), boxed!(variable),
))) )))
} }
"setBatchedNamed" | "sbn" => { "setBatchedNamed" | "sbn" => {
check_length(self, &invocation.arguments, 4)?; check_length(self.current_span(), &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter(); 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);
@@ -1825,28 +1762,28 @@ impl<'a> Parser<'a> {
} }
// Math SysCalls // Math SysCalls
"acos" => { "acos" => {
check_length(self, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 2)?; check_length(self.current_span(), &invocation.arguments, 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)?;
@@ -1854,42 +1791,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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 2)?; check_length(self.current_span(), &invocation.arguments, 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)?;
@@ -1897,7 +1834,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, &invocation.arguments, 2)?; check_length(self.current_span(), &invocation.arguments, 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)?;
@@ -1905,32 +1842,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, &invocation.arguments, 0)?; check_length(self.current_span(), &invocation.arguments, 0)?;
Ok(SysCall::Math(Math::Rand)) Ok(SysCall::Math(Math::Rand))
} }
"sin" => { "sin" => {
check_length(self, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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, &invocation.arguments, 1)?; check_length(self.current_span(), &invocation.arguments, 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

@@ -4,73 +4,73 @@ use helpers::prelude::*;
documented! { documented! {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum Math { pub enum Math<'a> {
/// Returns the angle in radians whose cosine is the specified number. /// Returns the angle in radians whose cosine is the specified number.
/// ## IC10 /// ## IC10
/// `acos r? a(r?|num)` /// `acos r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = acos(number|var|expression);` /// `let item = acos(number|var|expression);`
Acos(Box<Spanned<Expression>>), Acos(Box<Spanned<Expression<'a>>>),
/// Returns the angle in radians whose sine is the specified number. /// Returns the angle in radians whose sine is the specified number.
/// ## IC10 /// ## IC10
/// `asin r? a(r?|num)` /// `asin r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = asin(number|var|expression);` /// `let item = asin(number|var|expression);`
Asin(Box<Spanned<Expression>>), Asin(Box<Spanned<Expression<'a>>>),
/// Returns the angle in radians whose tangent is the specified number. /// Returns the angle in radians whose tangent is the specified number.
/// ## IC10 /// ## IC10
/// `atan r? a(r?|num)` /// `atan r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = atan(number|var|expression);` /// `let item = atan(number|var|expression);`
Atan(Box<Spanned<Expression>>), Atan(Box<Spanned<Expression<'a>>>),
/// Returns the angle in radians whose tangent is the quotient of the specified numbers. /// Returns the angle in radians whose tangent is the quotient of the specified numbers.
/// ## IC10 /// ## IC10
/// `atan2 r? a(r?|num) b(r?|num)` /// `atan2 r? a(r?|num) b(r?|num)`
/// ## Slang /// ## Slang
/// `let item = atan2((number|var|expression), (number|var|expression));` /// `let item = atan2((number|var|expression), (number|var|expression));`
Atan2(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Atan2(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
/// Gets the absolute value of a number. /// Gets the absolute value of a number.
/// ## IC10 /// ## IC10
/// `abs r? a(r?|num)` /// `abs r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = abs((number|var|expression));` /// `let item = abs((number|var|expression));`
Abs(Box<Spanned<Expression>>), Abs(Box<Spanned<Expression<'a>>>),
/// Rounds a number up to the nearest whole number. /// Rounds a number up to the nearest whole number.
/// ## IC10 /// ## IC10
/// `ceil r? a(r?|num)` /// `ceil r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = ceil((number|var|expression));` /// `let item = ceil((number|var|expression));`
Ceil(Box<Spanned<Expression>>), Ceil(Box<Spanned<Expression<'a>>>),
/// Returns the cosine of the specified angle in radians. /// Returns the cosine of the specified angle in radians.
/// ## IC10 /// ## IC10
/// `cos r? a(r?|num)` /// `cos r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = cos((number|var|expression));` /// `let item = cos((number|var|expression));`
Cos(Box<Spanned<Expression>>), Cos(Box<Spanned<Expression<'a>>>),
/// Rounds a number down to the nearest whole number. /// Rounds a number down to the nearest whole number.
/// ## IC10 /// ## IC10
/// `floor r? a(r?|num)` /// `floor r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = floor((number|var|expression));` /// `let item = floor((number|var|expression));`
Floor(Box<Spanned<Expression>>), Floor(Box<Spanned<Expression<'a>>>),
/// Computes the natural logarithm of a number. /// Computes the natural logarithm of a number.
/// ## IC10 /// ## IC10
/// `log r? a(r?|num)` /// `log r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = log((number|var|expression));` /// `let item = log((number|var|expression));`
Log(Box<Spanned<Expression>>), Log(Box<Spanned<Expression<'a>>>),
/// Computes the maximum of two numbers. /// Computes the maximum of two numbers.
/// ## IC10 /// ## IC10
/// `max r? a(r?|num) b(r?|num)` /// `max r? a(r?|num) b(r?|num)`
/// ## Slang /// ## Slang
/// `let item = max((number|var|expression), (number|var|expression));` /// `let item = max((number|var|expression), (number|var|expression));`
Max(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Max(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
/// Computes the minimum of two numbers. /// Computes the minimum of two numbers.
/// ## IC10 /// ## IC10
/// `min r? a(r?|num) b(r?|num)` /// `min r? a(r?|num) b(r?|num)`
/// ## Slang /// ## Slang
/// `let item = min((number|var|expression), (number|var|expression));` /// `let item = min((number|var|expression), (number|var|expression));`
Min(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Min(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
/// Gets a random number between 0 and 1. /// Gets a random number between 0 and 1.
/// ## IC10 /// ## IC10
/// `rand r?` /// `rand r?`
@@ -82,29 +82,29 @@ documented! {
/// `sin r? a(r?|num)` /// `sin r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = sin((number|var|expression));` /// `let item = sin((number|var|expression));`
Sin(Box<Spanned<Expression>>), Sin(Box<Spanned<Expression<'a>>>),
/// Computes the square root of a number. /// Computes the square root of a number.
/// ## IC10 /// ## IC10
/// `sqrt r? a(r?|num)` /// `sqrt r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = sqrt((number|var|expression));` /// `let item = sqrt((number|var|expression));`
Sqrt(Box<Spanned<Expression>>), Sqrt(Box<Spanned<Expression<'a>>>),
/// Returns the tangent of the specified angle in radians. /// Returns the tangent of the specified angle in radians.
/// ## IC10 /// ## IC10
/// `tan r? a(r?|num)` /// `tan r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = tan((number|var|expression));` /// `let item = tan((number|var|expression));`
Tan(Box<Spanned<Expression>>), Tan(Box<Spanned<Expression<'a>>>),
/// Truncates a number by removing the decimal portion. /// Truncates a number by removing the decimal portion.
/// ## IC10 /// ## IC10
/// `trunc r? a(r?|num)` /// `trunc r? a(r?|num)`
/// ## Slang /// ## Slang
/// `let item = trunc((number|var|expression));` /// `let item = trunc((number|var|expression));`
Trunc(Box<Spanned<Expression>>), Trunc(Box<Spanned<Expression<'a>>>),
} }
} }
impl std::fmt::Display for Math { impl<'a> std::fmt::Display for Math<'a> {
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),
@@ -129,7 +129,7 @@ impl std::fmt::Display for Math {
documented! { documented! {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum System { pub enum System<'a> {
/// Pauses execution for exactly 1 tick and then resumes. /// Pauses execution for exactly 1 tick and then resumes.
/// ## IC10 /// ## IC10
/// `yield` /// `yield`
@@ -141,7 +141,7 @@ documented! {
/// `sleep a(r?|num)` /// `sleep a(r?|num)`
/// ## Slang /// ## Slang
/// `sleep(number|var);` /// `sleep(number|var);`
Sleep(Box<Spanned<Expression>>), Sleep(Box<Spanned<Expression<'a>>>),
/// Gets the in-game hash for a specific prefab name. NOTE! This call is COMPLETELY /// Gets the in-game hash for a specific prefab name. NOTE! This call is COMPLETELY
/// optimized away unless you bind it to a `let` variable. If you use a `const` variable /// optimized away unless you bind it to a `let` variable. If you use a `const` variable
/// however, the hash is correctly computed at compile time and substitued automatically. /// however, the hash is correctly computed at compile time and substitued automatically.
@@ -155,7 +155,7 @@ documented! {
/// const compDoor = hash("StructureCompositeDoor"); /// const compDoor = hash("StructureCompositeDoor");
/// setOnDeviceBatched(compDoor, "Lock", true); /// setOnDeviceBatched(compDoor, "Lock", true);
/// ``` /// ```
Hash(Spanned<Literal>), Hash(Spanned<Literal<'a>>),
/// Represents a function which loads a device variable into a register. /// Represents a function which loads a device variable into a register.
/// ## IC10 /// ## IC10
/// `l r? d? var` /// `l r? d? var`
@@ -163,7 +163,7 @@ documented! {
/// `let item = load(deviceHash, "LogicType");` /// `let item = load(deviceHash, "LogicType");`
/// `let item = l(deviceHash, "LogicType");` /// `let item = l(deviceHash, "LogicType");`
/// `let item = deviceAlias.LogicType;` /// `let item = deviceAlias.LogicType;`
LoadFromDevice(Spanned<LiteralOrVariable>, Spanned<Literal>), LoadFromDevice(Spanned<LiteralOrVariable<'a>>, Spanned<Literal<'a>>),
/// Function which gets a LogicType from all connected network devices that match /// Function which gets a LogicType from all connected network devices that match
/// the provided device hash and name, aggregating them via a batchMode /// the provided device hash and name, aggregating them via a batchMode
/// ## IC10 /// ## IC10
@@ -172,10 +172,10 @@ documented! {
/// `loadBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");` /// `loadBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");`
/// `lbn(deviceHash, deviceName, "LogicType", "BatchMode");` /// `lbn(deviceHash, deviceName, "LogicType", "BatchMode");`
LoadBatchNamed( LoadBatchNamed(
Spanned<LiteralOrVariable>, Spanned<LiteralOrVariable<'a>>,
Spanned<LiteralOrVariable>, Spanned<LiteralOrVariable<'a>>,
Spanned<Literal>, Spanned<Literal<'a>>,
Spanned<Literal>, Spanned<Literal<'a>>,
), ),
/// Loads a LogicType from all connected network devices, aggregating them via a /// Loads a LogicType from all connected network devices, aggregating them via a
/// BatchMode /// BatchMode
@@ -184,7 +184,7 @@ documented! {
/// ## Slang /// ## Slang
/// `loadBatched(deviceHash, "Variable", "LogicType");` /// `loadBatched(deviceHash, "Variable", "LogicType");`
/// `lb(deviceHash, "Variable", "LogicType");` /// `lb(deviceHash, "Variable", "LogicType");`
LoadBatch(Spanned<LiteralOrVariable>, Spanned<Literal>, Spanned<Literal>), LoadBatch(Spanned<LiteralOrVariable<'a>>, Spanned<Literal<'a>>, Spanned<Literal<'a>>),
/// Represents a function which stores a setting into a specific device. /// Represents a function which stores a setting into a specific device.
/// ## IC10 /// ## IC10
/// `s d? logicType r?` /// `s d? logicType r?`
@@ -192,7 +192,7 @@ documented! {
/// `set(deviceHash, "LogicType", (number|var));` /// `set(deviceHash, "LogicType", (number|var));`
/// `s(deviceHash, "LogicType", (number|var));` /// `s(deviceHash, "LogicType", (number|var));`
/// `deviceAlias.LogicType = (number|var);` /// `deviceAlias.LogicType = (number|var);`
SetOnDevice(Spanned<LiteralOrVariable>, Spanned<Literal>, Box<Spanned<Expression>>), SetOnDevice(Spanned<LiteralOrVariable<'a>>, Spanned<Literal<'a>>, Box<Spanned<Expression<'a>>>),
/// Represents a function which stores a setting to all devices that match /// Represents a function which stores a setting to all devices that match
/// the given deviceHash /// the given deviceHash
/// ## IC10 /// ## IC10
@@ -200,7 +200,7 @@ documented! {
/// ## Slang /// ## Slang
/// `setBatched(deviceHash, "LogicType", (number|var));` /// `setBatched(deviceHash, "LogicType", (number|var));`
/// `sb(deviceHash, "LogicType", (number|var));` /// `sb(deviceHash, "LogicType", (number|var));`
SetOnDeviceBatched(Spanned<LiteralOrVariable>, Spanned<Literal>, Box<Spanned<Expression>>), SetOnDeviceBatched(Spanned<LiteralOrVariable<'a>>, Spanned<Literal<'a>>, Box<Spanned<Expression<'a>>>),
/// Represents a function which stores a setting to all devices that match /// Represents a function which stores a setting to all devices that match
/// both the given deviceHash AND the given nameHash /// both the given deviceHash AND the given nameHash
/// ## IC10 /// ## IC10
@@ -209,15 +209,15 @@ documented! {
/// `setBatchedNamed(deviceHash, nameHash, "LogicType", (number|var));` /// `setBatchedNamed(deviceHash, nameHash, "LogicType", (number|var));`
/// `sbn(deviceHash, nameHash, "LogicType", (number|var));` /// `sbn(deviceHash, nameHash, "LogicType", (number|var));`
SetOnDeviceBatchedNamed( SetOnDeviceBatchedNamed(
Spanned<LiteralOrVariable>, Spanned<LiteralOrVariable<'a>>,
Spanned<LiteralOrVariable>, Spanned<LiteralOrVariable<'a>>,
Spanned<Literal>, Spanned<Literal<'a>>,
Box<Spanned<Expression>>, Box<Spanned<Expression<'a>>>,
), ),
} }
} }
impl std::fmt::Display for System { impl<'a> std::fmt::Display for System<'a> {
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()"),
@@ -242,13 +242,13 @@ impl std::fmt::Display for System {
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
/// This represents built in functions that cannot be overwritten, but can be invoked by the user as functions. /// This represents built in functions that cannot be overwritten, but can be invoked by the user as functions.
pub enum SysCall { pub enum SysCall<'a> {
System(System), System(System<'a>),
/// Represents any mathmatical function that can be called. /// Represents any mathmatical function that can be called.
Math(Math), Math(Math<'a>),
} }
impl Documentation for SysCall { impl<'a> Documentation for SysCall<'a> {
fn docs(&self) -> String { fn docs(&self) -> String {
match self { match self {
Self::System(s) => s.docs(), Self::System(s) => s.docs(),
@@ -264,7 +264,7 @@ impl Documentation for SysCall {
} }
} }
impl std::fmt::Display for SysCall { impl<'a> std::fmt::Display for SysCall<'a> {
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 {
SysCall::System(s) => write!(f, "{}", s), SysCall::System(s) => write!(f, "{}", s),
@@ -273,7 +273,7 @@ impl std::fmt::Display for SysCall {
} }
} }
impl SysCall { impl<'a> SysCall<'a> {
pub fn is_syscall(identifier: &str) -> bool { pub fn is_syscall(identifier: &str) -> bool {
tokenizer::token::is_syscall(identifier) tokenizer::token::is_syscall(identifier)
} }

View File

@@ -1,24 +1,22 @@
use std::ops::Deref;
use crate::sys_call;
use super::sys_call::SysCall; use super::sys_call::SysCall;
use crate::sys_call;
use std::{borrow::Cow, ops::Deref};
use tokenizer::token::Number; use tokenizer::token::Number;
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
pub enum Literal { pub enum Literal<'a> {
Number(Number), Number(Number),
String(String), String(Cow<'a, str>),
Boolean(bool), Boolean(bool),
} }
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
pub enum LiteralOr<T> { pub enum LiteralOr<'a, T> {
Literal(Spanned<Literal>), Literal(Spanned<Literal<'a>>),
Or(Spanned<T>), Or(Spanned<T>),
} }
impl<T: std::fmt::Display> std::fmt::Display for LiteralOr<T> { impl<'a, T: std::fmt::Display> std::fmt::Display for LiteralOr<'a, T> {
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 {
Self::Literal(l) => write!(f, "{l}"), Self::Literal(l) => write!(f, "{l}"),
@@ -27,7 +25,7 @@ impl<T: std::fmt::Display> std::fmt::Display for LiteralOr<T> {
} }
} }
impl std::fmt::Display for Literal { impl<'a> std::fmt::Display for Literal<'a> {
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 {
Literal::Number(n) => write!(f, "{}", n), Literal::Number(n) => write!(f, "{}", n),
@@ -38,16 +36,16 @@ impl std::fmt::Display for Literal {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum BinaryExpression { pub enum BinaryExpression<'a> {
Add(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Add(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Multiply(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Multiply(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Divide(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Divide(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Subtract(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Subtract(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Exponent(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Exponent(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Modulo(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Modulo(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
} }
impl std::fmt::Display for BinaryExpression { impl<'a> std::fmt::Display for BinaryExpression<'a> {
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 {
BinaryExpression::Add(l, r) => write!(f, "({} + {})", l, r), BinaryExpression::Add(l, r) => write!(f, "({} + {})", l, r),
@@ -61,19 +59,19 @@ impl std::fmt::Display for BinaryExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum LogicalExpression { pub enum LogicalExpression<'a> {
And(Box<Spanned<Expression>>, Box<Spanned<Expression>>), And(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Or(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Or(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
Not(Box<Spanned<Expression>>), Not(Box<Spanned<Expression<'a>>>),
Equal(Box<Spanned<Expression>>, Box<Spanned<Expression>>), Equal(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
NotEqual(Box<Spanned<Expression>>, Box<Spanned<Expression>>), NotEqual(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
GreaterThan(Box<Spanned<Expression>>, Box<Spanned<Expression>>), GreaterThan(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
GreaterThanOrEqual(Box<Spanned<Expression>>, Box<Spanned<Expression>>), GreaterThanOrEqual(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
LessThan(Box<Spanned<Expression>>, Box<Spanned<Expression>>), LessThan(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
LessThanOrEqual(Box<Spanned<Expression>>, Box<Spanned<Expression>>), LessThanOrEqual(Box<Spanned<Expression<'a>>>, Box<Spanned<Expression<'a>>>),
} }
impl std::fmt::Display for LogicalExpression { impl<'a> std::fmt::Display for LogicalExpression<'a> {
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 {
LogicalExpression::And(l, r) => write!(f, "({} && {})", l, r), LogicalExpression::And(l, r) => write!(f, "({} && {})", l, r),
@@ -90,25 +88,25 @@ impl std::fmt::Display for LogicalExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct AssignmentExpression { pub struct AssignmentExpression<'a> {
pub assignee: Box<Spanned<Expression>>, pub assignee: Box<Spanned<Expression<'a>>>,
pub expression: Box<Spanned<Expression>>, pub expression: Box<Spanned<Expression<'a>>>,
} }
impl std::fmt::Display for AssignmentExpression { impl<'a> std::fmt::Display for AssignmentExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({} = {})", self.assignee, self.expression) write!(f, "({} = {})", self.assignee, self.expression)
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct FunctionExpression { pub struct FunctionExpression<'a> {
pub name: Spanned<String>, pub name: Spanned<Cow<'a, str>>,
pub arguments: Vec<Spanned<String>>, pub arguments: Vec<Spanned<Cow<'a, str>>>,
pub body: BlockExpression, pub body: BlockExpression<'a>,
} }
impl std::fmt::Display for FunctionExpression { impl<'a> std::fmt::Display for FunctionExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@@ -125,9 +123,9 @@ impl std::fmt::Display for FunctionExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct BlockExpression(pub Vec<Spanned<Expression>>); pub struct BlockExpression<'a>(pub Vec<Spanned<Expression<'a>>>);
impl std::fmt::Display for BlockExpression { impl<'a> std::fmt::Display for BlockExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@@ -142,12 +140,12 @@ impl std::fmt::Display for BlockExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct InvocationExpression { pub struct InvocationExpression<'a> {
pub name: Spanned<String>, pub name: Spanned<Cow<'a, str>>,
pub arguments: Vec<Spanned<Expression>>, pub arguments: Vec<Spanned<Expression<'a>>>,
} }
impl std::fmt::Display for InvocationExpression { impl<'a> std::fmt::Display for InvocationExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@@ -163,25 +161,25 @@ impl std::fmt::Display for InvocationExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct MemberAccessExpression { pub struct MemberAccessExpression<'a> {
pub object: Box<Spanned<Expression>>, pub object: Box<Spanned<Expression<'a>>>,
pub member: Spanned<String>, pub member: Spanned<Cow<'a, str>>,
} }
impl std::fmt::Display for MemberAccessExpression { impl<'a> std::fmt::Display for MemberAccessExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}", self.object, self.member) write!(f, "{}.{}", self.object, self.member)
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct MethodCallExpression { pub struct MethodCallExpression<'a> {
pub object: Box<Spanned<Expression>>, pub object: Box<Spanned<Expression<'a>>>,
pub method: Spanned<String>, pub method: Spanned<Cow<'a, str>>,
pub arguments: Vec<Spanned<Expression>>, pub arguments: Vec<Spanned<Expression<'a>>>,
} }
impl std::fmt::Display for MethodCallExpression { impl<'a> std::fmt::Display for MethodCallExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(
f, f,
@@ -198,12 +196,12 @@ impl std::fmt::Display for MethodCallExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum LiteralOrVariable { pub enum LiteralOrVariable<'a> {
Literal(Literal), Literal(Literal<'a>),
Variable(Spanned<String>), Variable(Spanned<Cow<'a, str>>),
} }
impl std::fmt::Display for LiteralOrVariable { impl<'a> std::fmt::Display for LiteralOrVariable<'a> {
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 {
LiteralOrVariable::Literal(l) => write!(f, "{}", l), LiteralOrVariable::Literal(l) => write!(f, "{}", l),
@@ -213,46 +211,46 @@ impl std::fmt::Display for LiteralOrVariable {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct ConstDeclarationExpression { pub struct ConstDeclarationExpression<'a> {
pub name: Spanned<String>, pub name: Spanned<Cow<'a, str>>,
pub value: LiteralOr<SysCall>, pub value: LiteralOr<'a, SysCall<'a>>,
} }
impl ConstDeclarationExpression { impl<'a> ConstDeclarationExpression<'a> {
pub fn is_syscall_supported(call: &SysCall) -> bool { pub fn is_syscall_supported(call: &SysCall) -> bool {
use sys_call::System; use sys_call::System;
matches!(call, SysCall::System(sys) if matches!(sys, System::Hash(_))) matches!(call, SysCall::System(sys) if matches!(sys, System::Hash(_)))
} }
} }
impl std::fmt::Display for ConstDeclarationExpression { impl<'a> std::fmt::Display for ConstDeclarationExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(const {} = {})", self.name, self.value) write!(f, "(const {} = {})", self.name, self.value)
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct DeviceDeclarationExpression { pub struct DeviceDeclarationExpression<'a> {
/// any variable-like name /// any variable-like name
pub name: Spanned<String>, pub name: Spanned<Cow<'a, str>>,
/// The device port, ex. (db, d0, d1, d2, d3, d4, d5) /// The device port, ex. (db, d0, d1, d2, d3, d4, d5)
pub device: String, pub device: Cow<'a, str>,
} }
impl std::fmt::Display for DeviceDeclarationExpression { impl<'a> std::fmt::Display for DeviceDeclarationExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(device {} = {})", self.name, self.device) write!(f, "(device {} = {})", self.name, self.device)
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct IfExpression { pub struct IfExpression<'a> {
pub condition: Box<Spanned<Expression>>, pub condition: Box<Spanned<Expression<'a>>>,
pub body: Spanned<BlockExpression>, pub body: Spanned<BlockExpression<'a>>,
pub else_branch: Option<Box<Spanned<Expression>>>, pub else_branch: Option<Box<Spanned<Expression<'a>>>>,
} }
impl std::fmt::Display for IfExpression { impl<'a> std::fmt::Display for IfExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(if ({}) {}", self.condition, self.body)?; write!(f, "(if ({}) {}", self.condition, self.body)?;
if let Some(else_branch) = &self.else_branch { if let Some(else_branch) = &self.else_branch {
@@ -263,23 +261,23 @@ impl std::fmt::Display for IfExpression {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct LoopExpression { pub struct LoopExpression<'a> {
pub body: Spanned<BlockExpression>, pub body: Spanned<BlockExpression<'a>>,
} }
impl std::fmt::Display for LoopExpression { impl<'a> std::fmt::Display for LoopExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(loop {})", self.body) write!(f, "(loop {})", self.body)
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub struct WhileExpression { pub struct WhileExpression<'a> {
pub condition: Box<Spanned<Expression>>, pub condition: Box<Spanned<Expression<'a>>>,
pub body: BlockExpression, pub body: BlockExpression<'a>,
} }
impl std::fmt::Display for WhileExpression { impl<'a> std::fmt::Display for WhileExpression<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(while {} {})", self.condition, self.body) write!(f, "(while {} {})", self.condition, self.body)
} }
@@ -347,32 +345,32 @@ impl<T> Deref for Spanned<T> {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum Expression { pub enum Expression<'a> {
Assignment(Spanned<AssignmentExpression>), Assignment(Spanned<AssignmentExpression<'a>>),
Binary(Spanned<BinaryExpression>), Binary(Spanned<BinaryExpression<'a>>),
Block(Spanned<BlockExpression>), Block(Spanned<BlockExpression<'a>>),
Break(Span), Break(Span),
ConstDeclaration(Spanned<ConstDeclarationExpression>), ConstDeclaration(Spanned<ConstDeclarationExpression<'a>>),
Continue(Span), Continue(Span),
Declaration(Spanned<String>, Box<Spanned<Expression>>), Declaration(Spanned<Cow<'a, str>>, Box<Spanned<Expression<'a>>>),
DeviceDeclaration(Spanned<DeviceDeclarationExpression>), DeviceDeclaration(Spanned<DeviceDeclarationExpression<'a>>),
Function(Spanned<FunctionExpression>), Function(Spanned<FunctionExpression<'a>>),
If(Spanned<IfExpression>), If(Spanned<IfExpression<'a>>),
Invocation(Spanned<InvocationExpression>), Invocation(Spanned<InvocationExpression<'a>>),
Literal(Spanned<Literal>), Literal(Spanned<Literal<'a>>),
Logical(Spanned<LogicalExpression>), Logical(Spanned<LogicalExpression<'a>>),
Loop(Spanned<LoopExpression>), Loop(Spanned<LoopExpression<'a>>),
MemberAccess(Spanned<MemberAccessExpression>), MemberAccess(Spanned<MemberAccessExpression<'a>>),
MethodCall(Spanned<MethodCallExpression>), MethodCall(Spanned<MethodCallExpression<'a>>),
Negation(Box<Spanned<Expression>>), Negation(Box<Spanned<Expression<'a>>>),
Priority(Box<Spanned<Expression>>), Priority(Box<Spanned<Expression<'a>>>),
Return(Box<Spanned<Expression>>), Return(Box<Spanned<Expression<'a>>>),
Syscall(Spanned<SysCall>), Syscall(Spanned<SysCall<'a>>),
Variable(Spanned<String>), Variable(Spanned<Cow<'a, str>>),
While(Spanned<WhileExpression>), While(Spanned<WhileExpression<'a>>),
} }
impl std::fmt::Display for Expression { impl<'a> std::fmt::Display for Expression<'a> {
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 {
Expression::Assignment(e) => write!(f, "{}", e), Expression::Assignment(e) => write!(f, "{}", e),

View File

@@ -5,9 +5,10 @@ edition = "2024"
[dependencies] [dependencies]
rust_decimal = { workspace = true } rust_decimal = { workspace = true }
quick-error = { workspace = true }
lsp-types = { workspace = true } lsp-types = { workspace = true }
thiserror = { workspace = true }
helpers = { path = "../helpers" } helpers = { path = "../helpers" }
logos = "0.16"
[dev-dependencies] [dev-dependencies]
anyhow = { version = "^1" } anyhow = { version = "^1" }

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,59 @@
use std::borrow::Cow;
use helpers::prelude::*; use helpers::prelude::*;
use logos::{Lexer, Logos, Skip, Span};
use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
use rust_decimal::Decimal; use rust_decimal::Decimal;
use thiserror::Error;
#[derive(Debug, Error, Default, Clone, PartialEq)]
pub enum LexError {
#[error("Attempted to parse an invalid number: {2}")]
NumberParse(usize, Span, String),
#[error("An invalid character was found in token stream: {2}")]
InvalidInput(usize, Span, String),
#[default]
#[error("An unknown error occurred")]
Other,
}
impl From<LexError> for Diagnostic {
fn from(value: LexError) -> Self {
match value {
LexError::NumberParse(line, col, str) | LexError::InvalidInput(line, col, str) => {
Diagnostic {
range: Range {
start: Position {
character: col.start as u32,
line: line as u32,
},
end: Position {
line: line as u32,
character: col.end as u32,
},
},
severity: Some(DiagnosticSeverity::ERROR),
message: str,
..Default::default()
}
}
_ => todo!(),
}
}
}
impl LexError {
pub fn from_lexer<'a>(lex: &mut Lexer<'a, TokenType<'a>>) -> Self {
let mut span = lex.span();
let line = lex.extras.line_count;
span.start -= lex.extras.line_start_index;
span.end -= lex.extras.line_start_index;
Self::InvalidInput(line, span, lex.slice().chars().as_str().to_string())
}
}
// Define a local macro to consume the list // Define a local macro to consume the list
macro_rules! generate_check { macro_rules! generate_check {
@@ -10,29 +64,40 @@ macro_rules! generate_check {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Default)]
pub struct Token { pub struct Extras {
/// The type of the token pub line_count: usize,
pub token_type: TokenType, pub line_start_index: usize,
/// The line where the token was found
pub line: usize,
/// The column where the token was found
pub column: usize,
pub original_string: Option<String>,
} }
impl Token { fn update_line_index<'a>(lex: &mut Lexer<'a, TokenType<'a>>) -> Skip {
pub fn new( lex.extras.line_count += 1;
token_type: TokenType, lex.extras.line_start_index = lex.span().end;
line: usize, Skip
column: usize, }
original: Option<String>,
) -> Self { #[derive(Debug, PartialEq, Eq, Clone)]
pub struct Token<'a> {
/// The type of the token
pub token_type: TokenType<'a>,
/// The line where the token was found
pub line: usize,
/// The span where the token starts and ends
pub span: Span,
}
impl<'a> std::fmt::Display for Token<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.token_type)
}
}
impl<'a> Token<'a> {
pub fn new(token_type: TokenType<'a>, line: usize, span: Span) -> Self {
Self { Self {
token_type, token_type,
line, line,
column, span,
original_string: original,
} }
} }
} }
@@ -79,25 +144,186 @@ impl Temperature {
} }
} }
#[derive(Debug, PartialEq, Hash, Eq, Clone)] macro_rules! symbol {
pub enum TokenType { ($var:ident) => {
|_| Symbol::$var
};
}
macro_rules! keyword {
($var:ident) => {
|_| Keyword::$var
};
}
#[derive(Debug, PartialEq, Hash, Eq, Clone, Logos)]
#[logos(skip r"[ \t\f]+")]
#[logos(extras = Extras)]
#[logos(error(LexError, LexError::from_lexer))]
pub enum TokenType<'a> {
#[regex(r"\n", update_line_index)]
Newline,
// matches strings with double quotes
#[regex(r#""(?:[^"\\]|\\.)*""#, |v| {
let str = v.slice();
Cow::from(&str[1..str.len() - 1])
})]
// matches strings with single quotes
#[regex(r#"'(?:[^'\\]|\\.)*'"#, |v| {
let str = v.slice();
Cow::from(&str[1..str.len() - 1])
})]
/// Represents a string token /// Represents a string token
String(String), String(Cow<'a, str>),
#[regex(r"[0-9][0-9_]*(\.[0-9][0-9_]*)?([cfk])?", parse_number)]
/// Represents a number token /// Represents a number token
Number(Number), Number(Number),
#[token("true", |_| true)]
#[token("false", |_| false)]
/// Represents a boolean token /// Represents a boolean token
Boolean(bool), Boolean(bool),
#[token("continue", keyword!(Continue))]
#[token("const", keyword!(Const))]
#[token("let", keyword!(Let))]
#[token("fn", keyword!(Fn))]
#[token("if", keyword!(If))]
#[token("device", keyword!(Device))]
#[token("else", keyword!(Else))]
#[token("return", keyword!(Return))]
#[token("enum", keyword!(Enum))]
#[token("loop", keyword!(Loop))]
#[token("break", keyword!(Break))]
#[token("while", keyword!(While))]
/// Represents a keyword token /// Represents a keyword token
Keyword(Keyword), Keyword(Keyword),
#[regex(r"[a-zA-Z_][a-zA-Z0-9_]*", |v| Cow::from(v.slice()))]
/// Represents an identifier token /// Represents an identifier token
Identifier(String), Identifier(Cow<'a, str>),
#[token("(", symbol!(LParen))]
#[token(")", symbol!(RParen))]
#[token("{", symbol!(LBrace))]
#[token("}", symbol!(RBrace))]
#[token("[", symbol!(LBracket))]
#[token("]", symbol!(RBracket))]
#[token(";", symbol!(Semicolon))]
#[token(":", symbol!(Colon))]
#[token(",", symbol!(Comma))]
#[token("+", symbol!(Plus))]
#[token("-", symbol!(Minus))]
#[token("*", symbol!(Asterisk))]
#[token("/", symbol!(Slash))]
#[token("<", symbol!(LessThan))]
#[token(">", symbol!(GreaterThan))]
#[token("=", symbol!(Assign))]
#[token("!", symbol!(LogicalNot))]
#[token(".", symbol!(Dot))]
#[token("^", symbol!(Caret))]
#[token("%", symbol!(Percent))]
#[token("==", symbol!(Equal))]
#[token("!=", symbol!(NotEqual))]
#[token("&&", symbol!(LogicalAnd))]
#[token("||", symbol!(LogicalOr))]
#[token("<=", symbol!(LessThanOrEqual))]
#[token(">=", symbol!(GreaterThanOrEqual))]
#[token("**", symbol!(Exp))]
/// Represents a symbol token /// Represents a symbol token
Symbol(Symbol), Symbol(Symbol),
#[token("//", |lex| Comment::Line(read_line(lex)))]
#[token("///", |lex| Comment::Doc(read_line(lex)))]
/// Represents a comment, both a line comment and a doc comment
Comment(Comment<'a>),
#[end]
/// Represents an end of file token /// Represents an end of file token
EOF, EOF,
} }
impl Documentation for TokenType { fn read_line<'a>(lexer: &mut Lexer<'a, TokenType<'a>>) -> Cow<'a, str> {
let rem = lexer.remainder();
let len = rem.find('\n').unwrap_or(rem.len());
let content = rem[..len].trim().to_string();
lexer.bump(len);
Cow::from(content)
}
#[derive(Hash, Debug, Eq, PartialEq, Clone)]
pub enum Comment<'a> {
Line(Cow<'a, str>),
Doc(Cow<'a, str>),
}
fn parse_number<'a>(lexer: &mut Lexer<'a, TokenType<'a>>) -> Result<Number, LexError> {
let slice = lexer.slice();
let last_char = slice.chars().last().unwrap_or_default();
let (num_str, suffix) = match last_char {
'c' | 'k' | 'f' => (&slice[..slice.len() - 1], Some(last_char)),
_ => (slice, None),
};
let clean_str = if num_str.contains('_') {
num_str.replace('_', "")
} else {
num_str.to_string()
};
let line = lexer.extras.line_count;
let mut span = lexer.span();
span.end -= lexer.extras.line_start_index;
span.start -= lexer.extras.line_start_index;
let num = if clean_str.contains('.') {
Number::Decimal(
clean_str
.parse::<Decimal>()
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
)
} else {
Number::Integer(
clean_str
.parse::<i128>()
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
)
};
if let Some(suffix) = suffix {
Ok(match suffix {
'c' => Temperature::Celsius(num),
'f' => Temperature::Fahrenheit(num),
'k' => Temperature::Kelvin(num),
_ => unreachable!(),
}
.to_kelvin())
} else {
Ok(num)
}
}
impl<'a> std::fmt::Display for Comment<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Line(c) => write!(f, "// {}", c),
Self::Doc(d) => {
let lines = d
.split('\n')
.map(|s| format!("/// {s}"))
.collect::<Vec<_>>()
.join("\n");
write!(f, "{}", lines)
}
}
}
}
impl<'a> Documentation for TokenType<'a> {
fn docs(&self) -> String { fn docs(&self) -> String {
match self { match self {
Self::Keyword(k) => k.docs(), Self::Keyword(k) => k.docs(),
@@ -112,7 +338,7 @@ impl Documentation for TokenType {
helpers::with_syscalls!(generate_check); helpers::with_syscalls!(generate_check);
impl From<TokenType> for u32 { impl<'a> From<TokenType<'a>> for u32 {
fn from(value: TokenType) -> Self { fn from(value: TokenType) -> Self {
match value { match value {
TokenType::String(_) => 1, TokenType::String(_) => 1,
@@ -128,6 +354,7 @@ impl From<TokenType> for u32 {
| Keyword::Return => 4, | Keyword::Return => 4,
_ => 5, _ => 5,
}, },
TokenType::Comment(_) => 8,
TokenType::Identifier(s) => { TokenType::Identifier(s) => {
if is_syscall(&s) { if is_syscall(&s) {
10 10
@@ -146,12 +373,12 @@ impl From<TokenType> for u32 {
7 7
} }
} }
TokenType::EOF => 0, _ => 0,
} }
} }
} }
impl std::fmt::Display for TokenType { impl<'a> std::fmt::Display for TokenType<'a> {
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 {
TokenType::String(s) => write!(f, "{}", s), TokenType::String(s) => write!(f, "{}", s),
@@ -160,7 +387,9 @@ impl std::fmt::Display for TokenType {
TokenType::Keyword(k) => write!(f, "{:?}", k), TokenType::Keyword(k) => write!(f, "{:?}", k),
TokenType::Identifier(i) => write!(f, "{}", i), TokenType::Identifier(i) => write!(f, "{}", i),
TokenType::Symbol(s) => write!(f, "{}", s), TokenType::Symbol(s) => write!(f, "{}", s),
TokenType::Comment(c) => write!(f, "{}", c),
TokenType::EOF => write!(f, "EOF"), TokenType::EOF => write!(f, "EOF"),
_ => write!(f, ""),
} }
} }
} }

View File

@@ -96,9 +96,10 @@ pub fn free_docs_vec(v: safer_ffi::Vec<FfiDocumentedItem>) {
#[ffi_export] #[ffi_export]
pub fn compile_from_string(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::String { pub fn compile_from_string(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::String {
let res = std::panic::catch_unwind(|| { let res = std::panic::catch_unwind(|| {
let input = String::from_utf16_lossy(input.as_slice());
let mut writer = BufWriter::new(Vec::new()); let mut writer = BufWriter::new(Vec::new());
let tokenizer = Tokenizer::from(String::from_utf16_lossy(input.as_slice())); let tokenizer = Tokenizer::from(input.as_str());
let parser = Parser::new(tokenizer); let parser = Parser::new(tokenizer);
let compiler = Compiler::new(parser, &mut writer, None); let compiler = Compiler::new(parser, &mut writer, None);
@@ -120,7 +121,8 @@ pub fn compile_from_string(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::
#[ffi_export] #[ffi_export]
pub fn tokenize_line(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<FfiToken> { pub fn tokenize_line(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<FfiToken> {
let res = std::panic::catch_unwind(|| { let res = std::panic::catch_unwind(|| {
let tokenizer = Tokenizer::from(String::from_utf16_lossy(input.as_slice())); let input = String::from_utf16_lossy(input.as_slice());
let tokenizer = Tokenizer::from(input.as_str());
let mut tokens = Vec::new(); let mut tokens = Vec::new();
@@ -136,34 +138,31 @@ pub fn tokenize_line(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<Ff
} }
match token { match token {
Err(ref e) => { Err(ref e) => {
use tokenizer::token::LexError;
use tokenizer::Error::*; use tokenizer::Error::*;
let (err_str, col, og) = match e { let (err_str, _, span) = match e {
NumberParseError(_, _, col, og) LexError(LexError::NumberParse(line, span, err))
| DecimalParseError(_, _, col, og) | LexError(LexError::InvalidInput(line, span, err)) => {
| UnknownSymbolError(_, _, col, og) (err.to_string(), line, span)
| UnknownKeywordOrIdentifierError(_, _, col, og) => {
(e.to_string(), col, og)
} }
_ => continue, _ => continue,
}; };
tokens.push(FfiToken { tokens.push(FfiToken {
column: *col as i32, column: span.start as i32,
error: err_str.into(), error: err_str.into(),
tooltip: "".into(), tooltip: "".into(),
length: og.len() as i32, length: (span.end - span.start) as i32,
token_kind: 0, token_kind: 0,
}) })
} }
Ok(Token { Ok(Token {
column, span, token_type, ..
original_string,
token_type,
..
}) => tokens.push(FfiToken { }) => tokens.push(FfiToken {
column: column as i32, column: span.start as i32,
error: "".into(), error: "".into(),
length: (original_string.unwrap_or_default().len()) as i32, length: (span.end - span.start) as i32,
tooltip: token_type.docs().into(), tooltip: token_type.docs().into(),
token_kind: token_type.into(), token_kind: token_type.into(),
}), }),
@@ -179,8 +178,10 @@ pub fn tokenize_line(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<Ff
#[ffi_export] #[ffi_export]
pub fn diagnose_source(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<FfiDiagnostic> { pub fn diagnose_source(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec<FfiDiagnostic> {
let res = std::panic::catch_unwind(|| { let res = std::panic::catch_unwind(|| {
let input = String::from_utf16_lossy(input.as_slice());
let mut writer = BufWriter::new(Vec::new()); let mut writer = BufWriter::new(Vec::new());
let tokenizer = Tokenizer::from(String::from_utf16_lossy(input.as_slice())); let tokenizer = Tokenizer::from(input.as_str());
let compiler = Compiler::new(Parser::new(tokenizer), &mut writer, None); let compiler = Compiler::new(Parser::new(tokenizer), &mut writer, None);
let diagnosis = compiler.compile(); let diagnosis = compiler.compile();

View File

@@ -1,8 +1,5 @@
#![allow(clippy::result_large_err)] #![allow(clippy::result_large_err)]
#[macro_use]
extern crate quick_error;
use clap::Parser; use clap::Parser;
use compiler::Compiler; use compiler::Compiler;
use parser::Parser as ASTParser; use parser::Parser as ASTParser;
@@ -11,27 +8,39 @@ use std::{
io::{stderr, BufWriter, Read, Write}, io::{stderr, BufWriter, Read, Write},
path::PathBuf, path::PathBuf,
}; };
use thiserror::Error;
use tokenizer::{self, Tokenizer}; use tokenizer::{self, Tokenizer};
quick_error! { #[derive(Error, Debug)]
#[derive(Debug)] enum Error<'a> {
enum StationlangError { #[error(transparent)]
TokenizerError(err: tokenizer::Error) { Tokenizer(tokenizer::Error),
from()
display("Tokenizer error: {}", err) #[error(transparent)]
Parser(parser::Error<'a>),
#[error(transparent)]
Compile(compiler::Error<'a>),
#[error(transparent)]
IO(#[from] std::io::Error),
}
impl<'a> From<parser::Error<'a>> for Error<'a> {
fn from(value: parser::Error<'a>) -> Self {
Self::Parser(value)
} }
ParserError(err: parser::Error) { }
from()
display("Parser error: {}", err) impl<'a> From<compiler::Error<'a>> for Error<'a> {
} fn from(value: compiler::Error<'a>) -> Self {
CompileError(err: compiler::Error) { Self::Compile(value)
from()
display("Compile error: {}", err)
}
IoError(err: std::io::Error) {
from()
display("IO error: {}", err)
} }
}
impl<'a> From<tokenizer::Error> for Error<'a> {
fn from(value: tokenizer::Error) -> Self {
Self::Tokenizer(value)
} }
} }
@@ -46,12 +55,17 @@ struct Args {
output_file: Option<PathBuf>, output_file: Option<PathBuf>,
} }
fn run_logic() -> Result<(), StationlangError> { fn run_logic<'a>() -> Result<(), Error<'a>> {
let args = Args::parse(); let args = Args::parse();
let input_file = args.input_file; let input_file = args.input_file;
let tokenizer: Tokenizer = match input_file { let input_string = match input_file {
Some(input_file) => Tokenizer::from_path(&input_file)?, Some(input_path) => {
let mut buf = String::new();
let mut file = std::fs::File::open(input_path).unwrap();
file.read_to_string(&mut buf).unwrap();
buf
}
None => { None => {
let mut buf = String::new(); let mut buf = String::new();
let stdin = std::io::stdin(); let stdin = std::io::stdin();
@@ -62,10 +76,11 @@ fn run_logic() -> Result<(), StationlangError> {
return Ok(()); return Ok(());
} }
Tokenizer::from(buf) buf
} }
}; };
let tokenizer = Tokenizer::from(input_string.as_str());
let parser = ASTParser::new(tokenizer); let parser = ASTParser::new(tokenizer);
let mut writer: BufWriter<Box<dyn Write>> = match args.output_file { let mut writer: BufWriter<Box<dyn Write>> = match args.output_file {
@@ -75,20 +90,17 @@ fn run_logic() -> Result<(), StationlangError> {
let compiler = Compiler::new(parser, &mut writer, None); let compiler = Compiler::new(parser, &mut writer, None);
let mut errors = compiler.compile(); let errors = compiler.compile();
if !errors.is_empty() { if !errors.is_empty() {
let mut std_error = stderr(); let mut std_error = stderr();
let last = errors.pop(); let errors = errors.into_iter().map(Error::from);
let errors = errors.into_iter().map(StationlangError::from);
std_error.write_all(b"Compilation error:\n")?; std_error.write_all(b"Compilation error:\n")?;
for err in errors { for err in errors {
std_error.write_all(format!("{}\n", err).as_bytes())?; std_error.write_all(format!("{}\n", err).as_bytes())?;
} }
return Err(StationlangError::from(last.unwrap()));
} }
writer.flush()?; writer.flush()?;
@@ -96,7 +108,7 @@ fn run_logic() -> Result<(), StationlangError> {
Ok(()) Ok(())
} }
fn main() -> Result<(), StationlangError> { fn main() -> anyhow::Result<()> {
run_logic()?; run_logic()?;
Ok(()) Ok(())