20 Commits
0.1.1 ... 0.2.0

Author SHA1 Message Date
46500a456a Add support for colorized comments 2025-12-09 17:38:35 -07:00
f60c9b32a8 minor version bump 2025-12-09 16:50:23 -07:00
0cddb3e8c8 The Cows are all working. Moo. 2025-12-09 16:31:24 -07:00
c3986ab4d9 VariableManager lifetime errors 2025-12-09 16:05:40 -07:00
f54214acb9 Most of all the errors are gone 2025-12-09 13:59:54 -07:00
d9a7a31306 Lifetimes are declared, now I gotta fix the lifetime issues 2025-12-09 13:51:54 -07:00
d40b759442 TEST -- use Cow instead of String for tokens 2025-12-09 13:17:35 -07:00
080b5320f7 Removed off-by-one calculations in the C# mod 2025-12-09 12:24:29 -07:00
a50a45f0b4 More cleanup 2025-12-09 12:12:28 -07:00
c531f673a5 Remove quickerror in favor of thiserror 2025-12-09 11:32:14 -07:00
23c2ba4134 Fix visual bugs with new span logic 2025-12-09 02:21:56 -07:00
7b7c1f7d29 Logos plugged into Parser 2025-12-09 02:15:43 -07:00
72cf9ea042 wip 2025-12-09 01:43:12 -07:00
fac36c756b Lexer impl done 2025-12-08 23:19:23 -07:00
115a57128c Before error type refactor 2025-12-08 22:50:20 -07:00
6afeec6da2 First pass getting a logos tokenizer up and running 2025-12-08 21:06:42 -07:00
b6123219f8 Merge pull request #21 from dbidwell94/cysharp-removal
Updates for Stationeers Beta branch
2025-12-08 15:26:22 -07:00
f38c15da9c Remove references to Unitask and Cysharp 2025-12-07 23:00:45 -07:00
fdd1a311d5 Merge pull request #20 from dbidwell94/release
Release
2025-12-07 21:46:05 -07:00
fb7ca0d4fd Update About.xml again 2025-12-07 21:45:07 -07:00
22 changed files with 1212 additions and 1652 deletions

11
Changelog.md Normal file
View File

@@ -0,0 +1,11 @@
# 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]
- Removed references to `Unitask`

View File

@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<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>StationeersSlang</Name> <Name>Slang</Name>
<Author>JoeDiertay</Author> <Author>JoeDiertay</Author>
<Version>0.1.1</Version> <Version>0.2.0</Version>
<Description> <Description>
[h1]Slang: High-Level Programming for Stationeers[/h1] [h1]Slang: High-Level Programming for Stationeers[/h1]
Stop writing assembly. Start writing code. Stop writing assembly. Start writing code.
Slang (Stationeers Language) brings modern programming to Stationeers. It allows you to write scripts using a familiar C-style syntax (variables, functions, if/else, loops) directly in the in-game editor. When you hit confirm, Slang compiles your code into optimized IC10 MIPS assembly instantly. Slang (Stationeers Language) brings modern programming to Stationeers. It allows you to write scripts using a familiar C-style syntax (variables, functions, if/else, loops) directly in the in-game editor. When you hit confirm, Slang compiles your code into IC10 instantly.
[b]NOTE: This project is in BETA. Expect updates and changes![/b] [b]NOTE: This project is in BETA. Expect updates and changes![/b]
@@ -91,7 +91,7 @@ A: Yes! Slang does not modify any existing IC10 code, it is only a compiler. As
<OrderBefore WorkshopHandle="3592775931" /> <OrderBefore WorkshopHandle="3592775931" />
<InGameDescription><![CDATA[ <InGameDescription><![CDATA[
<size=30><color=#ffff00>Slang - High Level Language Compiler</color></size> <size=30><color=#ffff00>Slang - High Level Language Compiler</color></size>
A modern programming experience for Stationeers. Write C-style code that compiles to MIPS assembly instantly. A modern programming experience for Stationeers. Write C-style code that compiles to IC10 instantly.
<color=#ffa500><b>Features</b></color> <color=#ffa500><b>Features</b></color>
- <b>In-Game Compilation:</b> Write high-level logic directly in the chip editor. - <b>In-Game Compilation:</b> Write high-level logic directly in the chip editor.

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

@@ -4,7 +4,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Cysharp.Threading.Tasks; using System.Threading.Tasks;
using StationeersIC10Editor; using StationeersIC10Editor;
public class SlangFormatter : ICodeFormatter public class SlangFormatter : ICodeFormatter
@@ -98,29 +98,32 @@ public class SlangFormatter : ICodeFormatter
inputSrc = this.RawText; inputSrc = this.RawText;
} }
HandleLsp(inputSrc, token).Forget(); _ = HandleLsp(inputSrc, token);
} }
private async UniTaskVoid HandleLsp(string inputSrc, CancellationToken cancellationToken) private async Task HandleLsp(string inputSrc, CancellationToken cancellationToken)
{ {
try try
{ {
await UniTask.SwitchToThreadPool(); if (cancellationToken.IsCancellationRequested)
return;
await Task.Delay(200, cancellationToken: cancellationToken);
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
return; return;
await System.Threading.Tasks.Task.Delay(200, cancellationToken: cancellationToken); // Running this potentially CPU intensive work on a background thread.
var dict = await Task.Run(
if (cancellationToken.IsCancellationRequested) () =>
return; {
return Marshal
var dict = Marshal .DiagnoseSource(inputSrc)
.DiagnoseSource(inputSrc) .GroupBy(d => d.Range.StartLine)
.GroupBy(d => d.Range.StartLine) .ToDictionary(g => g.Key);
.ToDictionary(g => g.Key); },
cancellationToken
await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken); );
ApplyDiagnostics(dict); ApplyDiagnostics(dict);
} }
@@ -136,7 +139,6 @@ public class SlangFormatter : ICodeFormatter
{ {
HashSet<uint> linesToRefresh; HashSet<uint> linesToRefresh;
// CRITICAL FIX FOR LINE SHIFTS:
// If the line count has changed (lines added/deleted), indices have shifted. // If the line count has changed (lines added/deleted), indices have shifted.
// We must refresh ALL lines to ensure any line that shifted into a new position // We must refresh ALL lines to ensure any line that shifted into a new position
// gets scrubbed of its old visual state. // gets scrubbed of its old visual state.

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.1</Version> <Version>0.2.0</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
@@ -39,10 +39,6 @@
<HintPath>./ref/Assembly-CSharp.dll</HintPath> <HintPath>./ref/Assembly-CSharp.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UniTask">
<HintPath>./ref/UniTask.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IC10Editor.dll"> <Reference Include="IC10Editor.dll">
<HintPath>./ref/IC10Editor.dll</HintPath> <HintPath>./ref/IC10Editor.dll</HintPath>

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.1" 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.1" 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,15 +931,14 @@ 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 .push(Error::UnknownIdentifier(var_name.node, var_name.span));
.push(Error::UnknownIdentifier(var_name.node, var_name.span)); VariableLocation::Temporary(0)
VariableLocation::Temporary(0) }
} };
};
match loc { match loc {
VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => { VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => {
@@ -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>),
ParserError(err: parser::Error) {
from() #[error(transparent)]
display("Parser error: {}", err) Compile(compiler::Error<'a>),
}
CompileError(err: compiler::Error) { #[error(transparent)]
from() IO(#[from] std::io::Error),
display("Compile error: {}", err) }
}
IoError(err: std::io::Error) { impl<'a> From<parser::Error<'a>> for Error<'a> {
from() fn from(value: parser::Error<'a>) -> Self {
display("IO error: {}", err) Self::Parser(value)
} }
}
impl<'a> From<compiler::Error<'a>> for Error<'a> {
fn from(value: compiler::Error<'a>) -> Self {
Self::Compile(value)
}
}
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(())