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