Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a8f0e84af | |||
| 5dbb0ee2d7 | |||
|
6b18489f54
|
|||
|
ecfed65221
|
|||
|
ed5ea9f6eb
|
|||
|
0b354d4ec0
|
|||
| 6c11c0e6e5 | |||
|
88b6571659
|
|||
|
477c2b1aef
|
|||
| 941e81a3e5 | |||
|
b98817c8a0
|
|||
|
6d5c179eac
|
|||
|
b7fbc499b6
|
19
Changelog.md
19
Changelog.md
@@ -1,5 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
[0.3.4]
|
||||||
|
|
||||||
|
- Added support for `loadReagent`, which maps to the `lr` IC10 instruction
|
||||||
|
- Shorthand is `lr`
|
||||||
|
- Longform is `loadReagent`
|
||||||
|
- Update various Rust dependencies
|
||||||
|
- Added more optimizations, prioritizing `pop` instead of `get` when available
|
||||||
|
when backing up / restoring registers for function invocations. This should
|
||||||
|
save approximately 2 lines per backed up register
|
||||||
|
|
||||||
|
[0.3.3]
|
||||||
|
|
||||||
|
- Fixed bug where negative temperature literals were converted to Kelvin
|
||||||
|
first before applying the negative
|
||||||
|
|
||||||
|
[0.3.2]
|
||||||
|
|
||||||
|
- Fixed stack overflow due to incorrect optimization of 'leaf' functions
|
||||||
|
|
||||||
[0.3.1]
|
[0.3.1]
|
||||||
|
|
||||||
- Fixed possible `KeyNotFoundException` in C# code due to invalid
|
- Fixed possible `KeyNotFoundException` in C# code due to invalid
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<Name>Slang</Name>
|
<Name>Slang</Name>
|
||||||
<Author>JoeDiertay</Author>
|
<Author>JoeDiertay</Author>
|
||||||
<Version>0.3.1</Version>
|
<Version>0.3.4</Version>
|
||||||
<Description>
|
<Description>
|
||||||
[h1]Slang: High-Level Programming for Stationeers[/h1]
|
[h1]Slang: High-Level Programming for Stationeers[/h1]
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Slang
|
|||||||
{
|
{
|
||||||
public const string PluginGuid = "com.biddydev.slang";
|
public const string PluginGuid = "com.biddydev.slang";
|
||||||
public const string PluginName = "Slang";
|
public const string PluginName = "Slang";
|
||||||
public const string PluginVersion = "0.3.1";
|
public const string PluginVersion = "0.3.4";
|
||||||
|
|
||||||
public static Mod MOD = new Mod(PluginName, PluginVersion);
|
public static Mod MOD = new Mod(PluginName, PluginVersion);
|
||||||
|
|
||||||
|
|||||||
@@ -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.3.1</Version>
|
<Version>0.3.2</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
18
rust_compiler/Cargo.lock
generated
18
rust_compiler/Cargo.lock
generated
@@ -172,9 +172,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecheck"
|
name = "bytecheck"
|
||||||
@@ -930,7 +930,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slang"
|
name = "slang"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -1063,18 +1063,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.7.3"
|
version = "0.7.4+spec-1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.23.7"
|
version = "0.23.10+spec-1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
|
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@@ -1084,9 +1084,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_parser"
|
name = "toml_parser"
|
||||||
version = "1.0.4"
|
version = "1.0.5+spec-1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "slang"
|
name = "slang"
|
||||||
version = "0.3.1"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ lsp-types = { workspace = true }
|
|||||||
rust_decimal = { workspace = true }
|
rust_decimal = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = { version = "1.0" }
|
anyhow = { workspace = true }
|
||||||
indoc = { version = "2.0" }
|
indoc = { version = "2.0" }
|
||||||
pretty_assertions = "1"
|
pretty_assertions = "1"
|
||||||
|
|||||||
@@ -52,11 +52,9 @@ fn nested_binary_expressions() -> Result<()> {
|
|||||||
add r1 r10 r9
|
add r1 r10 r9
|
||||||
mul r2 r1 r8
|
mul r2 r1 r8
|
||||||
move r15 r2
|
move r15 r2
|
||||||
j L1
|
j __internal_L1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
push 10
|
push 10
|
||||||
@@ -175,3 +173,52 @@ fn test_ternary_expression_assignment() -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative_literals() -> Result<()> {
|
||||||
|
let compiled = compile!(
|
||||||
|
debug
|
||||||
|
r#"
|
||||||
|
let item = -10c - 20c;
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 243.15
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mismatched_temperature_literals() -> Result<()> {
|
||||||
|
let compiled = compile!(
|
||||||
|
debug
|
||||||
|
r#"
|
||||||
|
let item = -10c - 100k;
|
||||||
|
let item2 = item + 500c;
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 163.15
|
||||||
|
add r1 r8 773.15
|
||||||
|
move r9 r1
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ fn test_if_statement() -> anyhow::Result<()> {
|
|||||||
main:
|
main:
|
||||||
move r8 10
|
move r8 10
|
||||||
sgt r1 r8 5
|
sgt r1 r8 5
|
||||||
beqz r1 L1
|
beqz r1 __internal_L1
|
||||||
move r8 20
|
move r8 20
|
||||||
L1:
|
__internal_L1:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -54,12 +54,12 @@ fn test_if_else_statement() -> anyhow::Result<()> {
|
|||||||
main:
|
main:
|
||||||
move r8 0
|
move r8 0
|
||||||
sgt r1 10 5
|
sgt r1 10 5
|
||||||
beqz r1 L2
|
beqz r1 __internal_L2
|
||||||
move r8 1
|
move r8 1
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
move r8 2
|
move r8 2
|
||||||
L1:
|
__internal_L1:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -91,18 +91,18 @@ fn test_if_else_if_statement() -> anyhow::Result<()> {
|
|||||||
main:
|
main:
|
||||||
move r8 0
|
move r8 0
|
||||||
seq r1 r8 1
|
seq r1 r8 1
|
||||||
beqz r1 L2
|
beqz r1 __internal_L2
|
||||||
move r8 10
|
move r8 10
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
seq r2 r8 2
|
seq r2 r8 2
|
||||||
beqz r2 L4
|
beqz r2 __internal_L4
|
||||||
move r8 20
|
move r8 20
|
||||||
j L3
|
j __internal_L3
|
||||||
L4:
|
__internal_L4:
|
||||||
move r8 30
|
move r8 30
|
||||||
L3:
|
__internal_L3:
|
||||||
L1:
|
__internal_L1:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -145,10 +145,10 @@ fn test_spilled_variable_update_in_branch() -> anyhow::Result<()> {
|
|||||||
move r14 7
|
move r14 7
|
||||||
push 8
|
push 8
|
||||||
seq r1 r8 1
|
seq r1 r8 1
|
||||||
beqz r1 L1
|
beqz r1 __internal_L1
|
||||||
sub r0 sp 1
|
sub r0 sp 1
|
||||||
put db r0 99
|
put db r0 99
|
||||||
L1:
|
__internal_L1:
|
||||||
sub sp sp 1
|
sub sp sp 1
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ fn no_arguments() -> anyhow::Result<()> {
|
|||||||
j main
|
j main
|
||||||
doSomething:
|
doSomething:
|
||||||
push ra
|
push ra
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
jal doSomething
|
jal doSomething
|
||||||
@@ -59,26 +57,22 @@ fn let_var_args() -> anyhow::Result<()> {
|
|||||||
push ra
|
push ra
|
||||||
mul r1 r8 2
|
mul r1 r8 2
|
||||||
move r15 r1
|
move r15 r1
|
||||||
j L1
|
j __internal_L1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
L2:
|
__internal_L2:
|
||||||
move r8 123
|
move r8 123
|
||||||
push r8
|
push r8
|
||||||
push r8
|
push r8
|
||||||
jal mul2
|
jal mul2
|
||||||
sub r0 sp 1
|
pop r8
|
||||||
get r8 db r0
|
|
||||||
sub sp sp 1
|
|
||||||
move r9 r15
|
move r9 r15
|
||||||
pow r1 r9 2
|
pow r1 r9 2
|
||||||
move r9 r1
|
move r9 r1
|
||||||
j L2
|
j __internal_L2
|
||||||
L3:
|
__internal_L3:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -127,11 +121,9 @@ fn inline_literal_args() -> anyhow::Result<()> {
|
|||||||
pop r9
|
pop r9
|
||||||
push ra
|
push ra
|
||||||
move r15 5
|
move r15 5
|
||||||
j L1
|
j __internal_L1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
move r8 123
|
move r8 123
|
||||||
@@ -139,9 +131,7 @@ fn inline_literal_args() -> anyhow::Result<()> {
|
|||||||
push 12
|
push 12
|
||||||
push 34
|
push 34
|
||||||
jal doSomething
|
jal doSomething
|
||||||
sub r0 sp 1
|
pop r8
|
||||||
get r8 db r0
|
|
||||||
sub sp sp 1
|
|
||||||
move r9 r15
|
move r9 r15
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
@@ -170,10 +160,8 @@ fn mixed_args() -> anyhow::Result<()> {
|
|||||||
pop r8
|
pop r8
|
||||||
pop r9
|
pop r9
|
||||||
push ra
|
push ra
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
move r8 123
|
move r8 123
|
||||||
@@ -181,9 +169,7 @@ fn mixed_args() -> anyhow::Result<()> {
|
|||||||
push r8
|
push r8
|
||||||
push 456
|
push 456
|
||||||
jal doSomething
|
jal doSomething
|
||||||
sub r0 sp 1
|
pop r8
|
||||||
get r8 db r0
|
|
||||||
sub sp sp 1
|
|
||||||
move r9 r15
|
move r9 r15
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
@@ -214,11 +200,9 @@ fn with_return_statement() -> anyhow::Result<()> {
|
|||||||
pop r8
|
pop r8
|
||||||
push ra
|
push ra
|
||||||
move r15 456
|
move r15 456
|
||||||
j L1
|
j __internal_L1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
push 123
|
push 123
|
||||||
@@ -251,10 +235,8 @@ fn with_negative_return_literal() -> anyhow::Result<()> {
|
|||||||
doSomething:
|
doSomething:
|
||||||
push ra
|
push ra
|
||||||
move r15 -1
|
move r15 -1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
jal doSomething
|
jal doSomething
|
||||||
|
|||||||
@@ -133,11 +133,9 @@ fn test_boolean_return() -> anyhow::Result<()> {
|
|||||||
getTrue:
|
getTrue:
|
||||||
push ra
|
push ra
|
||||||
move r15 1
|
move r15 1
|
||||||
j L1
|
j __internal_L1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
jal getTrue
|
jal getTrue
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ use pretty_assertions::assert_eq;
|
|||||||
fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
|
fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
|
||||||
let compiled = compile!(debug r#"
|
let compiled = compile!(debug r#"
|
||||||
// we need more than 4 params to 'spill' into a stack var
|
// we need more than 4 params to 'spill' into a stack var
|
||||||
fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {};
|
fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
|
||||||
|
return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7 + arg8 + arg9;
|
||||||
|
};
|
||||||
|
|
||||||
|
let item1 = 1;
|
||||||
|
let returned = doSomething(item1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
"#);
|
"#);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -21,11 +26,39 @@ fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
|
|||||||
pop r13
|
pop r13
|
||||||
pop r14
|
pop r14
|
||||||
push ra
|
push ra
|
||||||
L1:
|
sub r0 sp 3
|
||||||
sub r0 sp 1
|
get r1 db r0
|
||||||
get ra db r0
|
sub r0 sp 2
|
||||||
sub sp sp 3
|
get r2 db r0
|
||||||
|
add r3 r1 r2
|
||||||
|
add r4 r3 r14
|
||||||
|
add r5 r4 r13
|
||||||
|
add r6 r5 r12
|
||||||
|
add r7 r6 r11
|
||||||
|
add r1 r7 r10
|
||||||
|
add r2 r1 r9
|
||||||
|
add r3 r2 r8
|
||||||
|
move r15 r3
|
||||||
|
j __internal_L1
|
||||||
|
__internal_L1:
|
||||||
|
pop ra
|
||||||
|
sub sp sp 2
|
||||||
j ra
|
j ra
|
||||||
|
main:
|
||||||
|
move r8 1
|
||||||
|
push r8
|
||||||
|
push r8
|
||||||
|
push 2
|
||||||
|
push 3
|
||||||
|
push 4
|
||||||
|
push 5
|
||||||
|
push 6
|
||||||
|
push 7
|
||||||
|
push 8
|
||||||
|
push 9
|
||||||
|
jal doSomething
|
||||||
|
pop r8
|
||||||
|
move r9 r15
|
||||||
"}
|
"}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -54,15 +87,13 @@ fn test_early_return() -> anyhow::Result<()> {
|
|||||||
doSomething:
|
doSomething:
|
||||||
push ra
|
push ra
|
||||||
seq r1 1 1
|
seq r1 1 1
|
||||||
beqz r1 L2
|
beqz r1 __internal_L2
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
move r8 3
|
move r8 3
|
||||||
j L1
|
j __internal_L1
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
main:
|
main:
|
||||||
jal doSomething
|
jal doSomething
|
||||||
@@ -90,10 +121,8 @@ fn test_function_declaration_with_register_params() -> anyhow::Result<()> {
|
|||||||
pop r8
|
pop r8
|
||||||
pop r9
|
pop r9
|
||||||
push ra
|
push ra
|
||||||
L1:
|
__internal_L1:
|
||||||
sub r0 sp 1
|
pop ra
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
j ra
|
||||||
"}
|
"}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ fn test_infinite_loop() -> anyhow::Result<()> {
|
|||||||
"
|
"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Labels: L1 (start), L2 (end)
|
// __internal_Labels: L1 (start), L2 (end)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compiled,
|
compiled,
|
||||||
indoc! {
|
indoc! {
|
||||||
@@ -22,11 +22,11 @@ fn test_infinite_loop() -> anyhow::Result<()> {
|
|||||||
j main
|
j main
|
||||||
main:
|
main:
|
||||||
move r8 0
|
move r8 0
|
||||||
L1:
|
__internal_L1:
|
||||||
add r1 r8 1
|
add r1 r8 1
|
||||||
move r8 r1
|
move r8 r1
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -49,7 +49,7 @@ fn test_loop_break() -> anyhow::Result<()> {
|
|||||||
"
|
"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Labels: L1 (start), L2 (end), L3 (if end - implicit else label)
|
// __internal_Labels: L1 (start), L2 (end), L3 (if end - implicit else label)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compiled,
|
compiled,
|
||||||
indoc! {
|
indoc! {
|
||||||
@@ -57,15 +57,15 @@ fn test_loop_break() -> anyhow::Result<()> {
|
|||||||
j main
|
j main
|
||||||
main:
|
main:
|
||||||
move r8 0
|
move r8 0
|
||||||
L1:
|
__internal_L1:
|
||||||
add r1 r8 1
|
add r1 r8 1
|
||||||
move r8 r1
|
move r8 r1
|
||||||
sgt r2 r8 10
|
sgt r2 r8 10
|
||||||
beqz r2 L3
|
beqz r2 __internal_L3
|
||||||
j L2
|
j __internal_L2
|
||||||
L3:
|
__internal_L3:
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -85,7 +85,7 @@ fn test_while_loop() -> anyhow::Result<()> {
|
|||||||
"
|
"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Labels: L1 (start), L2 (end)
|
// __internal_Labels: L1 (start), L2 (end)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compiled,
|
compiled,
|
||||||
indoc! {
|
indoc! {
|
||||||
@@ -93,13 +93,13 @@ fn test_while_loop() -> anyhow::Result<()> {
|
|||||||
j main
|
j main
|
||||||
main:
|
main:
|
||||||
move r8 0
|
move r8 0
|
||||||
L1:
|
__internal_L1:
|
||||||
slt r1 r8 10
|
slt r1 r8 10
|
||||||
beqz r1 L2
|
beqz r1 __internal_L2
|
||||||
add r2 r8 1
|
add r2 r8 1
|
||||||
move r8 r2
|
move r8 r2
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -123,7 +123,7 @@ fn test_loop_continue() -> anyhow::Result<()> {
|
|||||||
"#
|
"#
|
||||||
};
|
};
|
||||||
|
|
||||||
// Labels: L1 (start), L2 (end), L3 (if end)
|
// __internal_Labels: L1 (start), L2 (end), L3 (if end)
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
compiled,
|
compiled,
|
||||||
indoc! {
|
indoc! {
|
||||||
@@ -131,16 +131,16 @@ fn test_loop_continue() -> anyhow::Result<()> {
|
|||||||
j main
|
j main
|
||||||
main:
|
main:
|
||||||
move r8 0
|
move r8 0
|
||||||
L1:
|
__internal_L1:
|
||||||
add r1 r8 1
|
add r1 r8 1
|
||||||
move r8 r1
|
move r8 r1
|
||||||
slt r2 r8 5
|
slt r2 r8 5
|
||||||
beqz r2 L3
|
beqz r2 __internal_L3
|
||||||
j L1
|
j __internal_L1
|
||||||
L3:
|
__internal_L3:
|
||||||
j L2
|
j __internal_L2
|
||||||
j L1
|
j __internal_L1
|
||||||
L2:
|
__internal_L2:
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -208,3 +208,29 @@ fn test_set_slot() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_reagent() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
r#"
|
||||||
|
device thingy = "d0";
|
||||||
|
|
||||||
|
let something = lr(thingy, "Contents", hash("Iron"));
|
||||||
|
"#
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
lr r15 d0 Contents -666742878
|
||||||
|
move r8 r15
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use parser::{
|
|||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use std::{borrow::Cow, collections::HashMap};
|
use std::{borrow::Cow, collections::HashMap};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokenizer::token::Number;
|
use tokenizer::token::{Number, Unit};
|
||||||
|
|
||||||
fn extract_literal<'a>(
|
fn extract_literal<'a>(
|
||||||
literal: Literal<'a>,
|
literal: Literal<'a>,
|
||||||
@@ -264,7 +264,7 @@ impl<'a> Compiler<'a> {
|
|||||||
|
|
||||||
fn next_label_name(&mut self) -> Cow<'a, str> {
|
fn next_label_name(&mut self) -> Cow<'a, str> {
|
||||||
self.label_counter += 1;
|
self.label_counter += 1;
|
||||||
Cow::from(format!("L{}", self.label_counter))
|
Cow::from(format!("__internal_L{}", self.label_counter))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(
|
fn expression(
|
||||||
@@ -811,7 +811,7 @@ impl<'a> Compiler<'a> {
|
|||||||
..
|
..
|
||||||
})),
|
})),
|
||||||
..
|
..
|
||||||
}) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash))),
|
}) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash), Unit::None)),
|
||||||
LiteralOr::Or(Spanned { span, .. }) => {
|
LiteralOr::Or(Spanned { span, .. }) => {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
"hash only supports string literals in this context.".into(),
|
"hash only supports string literals in this context.".into(),
|
||||||
@@ -1115,43 +1115,26 @@ impl<'a> Compiler<'a> {
|
|||||||
Some(name.span),
|
Some(name.span),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
for register in active_registers {
|
// cleanup spilled temporary variables
|
||||||
let VariableLocation::Stack(stack_offset) = stack
|
let total_stack_usage = stack.stack_offset();
|
||||||
.get_location_of(&Cow::from(format!("temp_{register}")), None)
|
let saved_regs_count = active_registers.len() as u16;
|
||||||
.map_err(Error::Scope)?
|
|
||||||
else {
|
if total_stack_usage > saved_regs_count {
|
||||||
// This shouldn't happen if we just added it
|
let spill_amount = total_stack_usage - saved_regs_count;
|
||||||
return Err(Error::Unknown(
|
|
||||||
format!("Failed to recover temp_{register}"),
|
|
||||||
Some(name.span),
|
|
||||||
));
|
|
||||||
};
|
|
||||||
self.write_instruction(
|
self.write_instruction(
|
||||||
Instruction::Sub(
|
Instruction::Sub(
|
||||||
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
|
|
||||||
Operand::StackPointer,
|
Operand::StackPointer,
|
||||||
Operand::Number(stack_offset.into()),
|
Operand::StackPointer,
|
||||||
),
|
Operand::Number(spill_amount.into()),
|
||||||
Some(name.span),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.write_instruction(
|
|
||||||
Instruction::Get(
|
|
||||||
Operand::Register(register),
|
|
||||||
Operand::Device(Cow::from("db")),
|
|
||||||
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
|
|
||||||
),
|
),
|
||||||
Some(name.span),
|
Some(name.span),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if stack.stack_offset() > 0 {
|
// restore the registers in reverse order from the stack, now using `pop`
|
||||||
|
for register in active_registers.iter().rev() {
|
||||||
self.write_instruction(
|
self.write_instruction(
|
||||||
Instruction::Sub(
|
Instruction::Pop(Operand::Register(*register)),
|
||||||
Operand::StackPointer,
|
|
||||||
Operand::StackPointer,
|
|
||||||
Operand::Number(Decimal::from(stack.stack_offset())),
|
|
||||||
),
|
|
||||||
Some(name.span),
|
Some(name.span),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -2022,6 +2005,7 @@ impl<'a> Compiler<'a> {
|
|||||||
|
|
||||||
let loc = VariableLocation::Constant(Literal::Number(Number::Integer(
|
let loc = VariableLocation::Constant(Literal::Number(Number::Integer(
|
||||||
crc_hash_signed(&str_lit),
|
crc_hash_signed(&str_lit),
|
||||||
|
Unit::None,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Ok(Some(CompileLocation {
|
Ok(Some(CompileLocation {
|
||||||
@@ -2295,6 +2279,48 @@ impl<'a> Compiler<'a> {
|
|||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
System::LoadReagent(device, reagent_mode, reagent_hash) => {
|
||||||
|
let Spanned {
|
||||||
|
node: LiteralOrVariable::Variable(device_spanned),
|
||||||
|
..
|
||||||
|
} = device
|
||||||
|
else {
|
||||||
|
return Err(Error::AgrumentMismatch(
|
||||||
|
"Arg1 expected to be a variable".into(),
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let (device, device_cleanup) = self.compile_literal_or_variable(
|
||||||
|
LiteralOrVariable::Variable(device_spanned),
|
||||||
|
scope,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (reagent_mode, reagent_cleanup) = self.compile_literal_or_variable(
|
||||||
|
LiteralOrVariable::Literal(reagent_mode.node),
|
||||||
|
scope,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (reagent_hash, reagent_hash_cleanup) =
|
||||||
|
self.compile_operand(*reagent_hash, scope)?;
|
||||||
|
|
||||||
|
self.write_instruction(
|
||||||
|
Instruction::LoadReagent(
|
||||||
|
Operand::Register(VariableScope::RETURN_REGISTER),
|
||||||
|
device,
|
||||||
|
reagent_mode,
|
||||||
|
reagent_hash,
|
||||||
|
),
|
||||||
|
Some(span),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
cleanup!(reagent_cleanup, reagent_hash_cleanup, device_cleanup);
|
||||||
|
|
||||||
|
Ok(Some(CompileLocation {
|
||||||
|
location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
|
||||||
|
temp_name: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2692,33 +2718,49 @@ impl<'a> Compiler<'a> {
|
|||||||
|
|
||||||
self.write_instruction(Instruction::LabelDef(return_label.clone()), Some(span))?;
|
self.write_instruction(Instruction::LabelDef(return_label.clone()), Some(span))?;
|
||||||
|
|
||||||
self.write_instruction(
|
if ra_stack_offset == 1 {
|
||||||
Instruction::Sub(
|
self.write_instruction(Instruction::Pop(Operand::ReturnAddress), Some(span))?;
|
||||||
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
|
|
||||||
Operand::StackPointer,
|
|
||||||
Operand::Number(ra_stack_offset.into()),
|
|
||||||
),
|
|
||||||
Some(span),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.write_instruction(
|
let remaining_cleanup = block_scope.stack_offset() - 1;
|
||||||
Instruction::Get(
|
if remaining_cleanup > 0 {
|
||||||
Operand::ReturnAddress,
|
self.write_instruction(
|
||||||
Operand::Device(Cow::from("db")),
|
Instruction::Sub(
|
||||||
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
|
Operand::StackPointer,
|
||||||
),
|
Operand::StackPointer,
|
||||||
Some(span),
|
Operand::Number(remaining_cleanup.into()),
|
||||||
)?;
|
),
|
||||||
|
Some(span),
|
||||||
if block_scope.stack_offset() > 0 {
|
)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
self.write_instruction(
|
self.write_instruction(
|
||||||
Instruction::Sub(
|
Instruction::Sub(
|
||||||
|
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
|
||||||
Operand::StackPointer,
|
Operand::StackPointer,
|
||||||
Operand::StackPointer,
|
Operand::Number(ra_stack_offset.into()),
|
||||||
Operand::Number(block_scope.stack_offset().into()),
|
|
||||||
),
|
),
|
||||||
Some(span),
|
Some(span),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.write_instruction(
|
||||||
|
Instruction::Get(
|
||||||
|
Operand::ReturnAddress,
|
||||||
|
Operand::Device(Cow::from("db")),
|
||||||
|
Operand::Register(VariableScope::TEMP_STACK_REGISTER),
|
||||||
|
),
|
||||||
|
Some(span),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if block_scope.stack_offset() > 0 {
|
||||||
|
self.write_instruction(
|
||||||
|
Instruction::Sub(
|
||||||
|
Operand::StackPointer,
|
||||||
|
Operand::StackPointer,
|
||||||
|
Operand::Number(block_scope.stack_offset().into()),
|
||||||
|
),
|
||||||
|
Some(span),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.write_instruction(Instruction::Jump(Operand::ReturnAddress), Some(span))?;
|
self.write_instruction(Instruction::Jump(Operand::ReturnAddress), Some(span))?;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ macro_rules! with_syscalls {
|
|||||||
"loadBatched",
|
"loadBatched",
|
||||||
"loadBatchedNamed",
|
"loadBatchedNamed",
|
||||||
"loadSlot",
|
"loadSlot",
|
||||||
|
"loadReagent",
|
||||||
"set",
|
"set",
|
||||||
"setBatched",
|
"setBatched",
|
||||||
"setBatchedNamed",
|
"setBatchedNamed",
|
||||||
@@ -35,6 +36,7 @@ macro_rules! with_syscalls {
|
|||||||
"lb",
|
"lb",
|
||||||
"lbn",
|
"lbn",
|
||||||
"ls",
|
"ls",
|
||||||
|
"lr",
|
||||||
"s",
|
"s",
|
||||||
"sb",
|
"sb",
|
||||||
"sbn",
|
"sbn",
|
||||||
|
|||||||
@@ -191,6 +191,9 @@ pub enum Instruction<'a> {
|
|||||||
/// `sbn deviceHash nameHash type value` - Set Batch Named
|
/// `sbn deviceHash nameHash type value` - Set Batch Named
|
||||||
StoreBatchNamed(Operand<'a>, Operand<'a>, Operand<'a>, Operand<'a>),
|
StoreBatchNamed(Operand<'a>, Operand<'a>, Operand<'a>, Operand<'a>),
|
||||||
|
|
||||||
|
/// `lr register device reagentMode int`
|
||||||
|
LoadReagent(Operand<'a>, Operand<'a>, Operand<'a>, Operand<'a>),
|
||||||
|
|
||||||
/// `j label` - Unconditional Jump
|
/// `j label` - Unconditional Jump
|
||||||
Jump(Operand<'a>),
|
Jump(Operand<'a>),
|
||||||
/// `jal label` - Jump and Link (Function Call)
|
/// `jal label` - Jump and Link (Function Call)
|
||||||
@@ -311,6 +314,9 @@ impl<'a> fmt::Display for Instruction<'a> {
|
|||||||
Instruction::StoreBatchNamed(d_hash, n_hash, typ, val) => {
|
Instruction::StoreBatchNamed(d_hash, n_hash, typ, val) => {
|
||||||
write!(f, "sbn {} {} {} {}", d_hash, n_hash, typ, val)
|
write!(f, "sbn {} {} {} {}", d_hash, n_hash, typ, val)
|
||||||
}
|
}
|
||||||
|
Instruction::LoadReagent(reg, device, reagent_mode, reagent_hash) => {
|
||||||
|
write!(f, "lr {} {} {} {}", reg, device, reagent_mode, reagent_hash)
|
||||||
|
}
|
||||||
Instruction::Jump(lbl) => write!(f, "j {}", lbl),
|
Instruction::Jump(lbl) => write!(f, "j {}", lbl),
|
||||||
Instruction::JumpAndLink(lbl) => write!(f, "jal {}", lbl),
|
Instruction::JumpAndLink(lbl) => write!(f, "jal {}", lbl),
|
||||||
Instruction::JumpRelative(off) => write!(f, "jr {}", off),
|
Instruction::JumpRelative(off) => write!(f, "jr {}", off),
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ pub fn find_leaf_functions(instructions: &[InstructionNode]) -> HashSet<String>
|
|||||||
for node in instructions {
|
for node in instructions {
|
||||||
match &node.instruction {
|
match &node.instruction {
|
||||||
Instruction::LabelDef(label) => {
|
Instruction::LabelDef(label) => {
|
||||||
|
if label.starts_with("__internal_L") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// If we were tracking a function, and it remained a leaf until now, save it.
|
// If we were tracking a function, and it remained a leaf until now, save it.
|
||||||
if let Some(name) = current_label.take()
|
if let Some(name) = current_label.take()
|
||||||
&& is_current_leaf
|
&& is_current_leaf
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ fn optimize_leaf_functions<'a>(
|
|||||||
// First scan: Identify instructions to remove and capture RA offsets
|
// First scan: Identify instructions to remove and capture RA offsets
|
||||||
for (i, node) in input.iter().enumerate() {
|
for (i, node) in input.iter().enumerate() {
|
||||||
match &node.instruction {
|
match &node.instruction {
|
||||||
Instruction::LabelDef(label) => {
|
Instruction::LabelDef(label) if !label.starts_with("__internal_L") => {
|
||||||
current_function = Some(label.to_string());
|
current_function = Some(label.to_string());
|
||||||
function_start_indices.insert(label.to_string(), i);
|
function_start_indices.insert(label.to_string(), i);
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,9 @@ fn optimize_leaf_functions<'a>(
|
|||||||
continue; // SKIP (Remove)
|
continue; // SKIP (Remove)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Instruction::LabelDef(l) = &node.instruction {
|
if let Instruction::LabelDef(l) = &node.instruction
|
||||||
|
&& !l.starts_with("__internal_L")
|
||||||
|
{
|
||||||
processing_function = Some(l.to_string());
|
processing_function = Some(l.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,6 +565,7 @@ fn get_destination_reg(instr: &Instruction) -> Option<u8> {
|
|||||||
| Instruction::Sqrt(Operand::Register(r), _)
|
| Instruction::Sqrt(Operand::Register(r), _)
|
||||||
| Instruction::Tan(Operand::Register(r), _)
|
| Instruction::Tan(Operand::Register(r), _)
|
||||||
| Instruction::Trunc(Operand::Register(r), _)
|
| Instruction::Trunc(Operand::Register(r), _)
|
||||||
|
| Instruction::LoadReagent(Operand::Register(r), _, _, _)
|
||||||
| Instruction::Pop(Operand::Register(r)) => Some(*r),
|
| Instruction::Pop(Operand::Register(r)) => Some(*r),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -593,6 +596,9 @@ fn set_destination_reg<'a>(instr: &Instruction<'a>, new_reg: u8) -> Option<Instr
|
|||||||
c.clone(),
|
c.clone(),
|
||||||
d.clone(),
|
d.clone(),
|
||||||
)),
|
)),
|
||||||
|
Instruction::LoadReagent(_, b, c, d) => {
|
||||||
|
Some(Instruction::LoadReagent(r, b.clone(), c.clone(), d.clone()))
|
||||||
|
}
|
||||||
Instruction::SetEq(_, a, b) => Some(Instruction::SetEq(r, a.clone(), b.clone())),
|
Instruction::SetEq(_, a, b) => Some(Instruction::SetEq(r, a.clone(), b.clone())),
|
||||||
Instruction::SetNe(_, a, b) => Some(Instruction::SetNe(r, a.clone(), b.clone())),
|
Instruction::SetNe(_, a, b) => Some(Instruction::SetNe(r, a.clone(), b.clone())),
|
||||||
Instruction::SetGt(_, a, b) => Some(Instruction::SetGt(r, a.clone(), b.clone())),
|
Instruction::SetGt(_, a, b) => Some(Instruction::SetGt(r, a.clone(), b.clone())),
|
||||||
@@ -655,6 +661,14 @@ fn reg_is_read(instr: &Instruction, reg: u8) -> bool {
|
|||||||
|
|
||||||
Instruction::BranchEqZero(a, _) | Instruction::BranchNeZero(a, _) => check(a),
|
Instruction::BranchEqZero(a, _) | Instruction::BranchNeZero(a, _) => check(a),
|
||||||
|
|
||||||
|
Instruction::LoadReagent(_, device, _, item_hash) => check(device) || check(item_hash),
|
||||||
|
|
||||||
|
Instruction::LoadSlot(_, dev, slot, _) => check(dev) || check(slot),
|
||||||
|
Instruction::LoadBatch(_, dev, _, mode) => check(dev) || check(mode),
|
||||||
|
Instruction::LoadBatchNamed(_, d_hash, n_hash, _, mode) => {
|
||||||
|
check(d_hash) || check(n_hash) || check(mode)
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::SetEq(_, a, b)
|
Instruction::SetEq(_, a, b)
|
||||||
| Instruction::SetNe(_, a, b)
|
| Instruction::SetNe(_, a, b)
|
||||||
| Instruction::SetGt(_, a, b)
|
| Instruction::SetGt(_, a, b)
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(Some(lhs))
|
Ok(Some(lhs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles dot notation chains: x.y.z()
|
/// Handles dot notation chains (x.y.z()) and array indexing (x[0])
|
||||||
fn parse_postfix(
|
fn parse_postfix(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut lhs: Spanned<Expression<'a>>,
|
mut lhs: Spanned<Expression<'a>>,
|
||||||
@@ -411,6 +411,9 @@ impl<'a> Parser<'a> {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else if self_matches_peek!(self, TokenType::Symbol(Symbol::LBracket)) {
|
||||||
|
// consume the `[` token
|
||||||
|
self.assign_next()?;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1909,6 +1912,20 @@ impl<'a> Parser<'a> {
|
|||||||
Box::new(expr),
|
Box::new(expr),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
"loadReagent" | "lr" => {
|
||||||
|
let mut args = args!(3);
|
||||||
|
let next = args.next();
|
||||||
|
let device = literal_or_variable!(next);
|
||||||
|
let next = args.next();
|
||||||
|
let reagent_mode = get_arg!(Literal, literal_or_variable!(next));
|
||||||
|
let reagent_hash = args.next().ok_or(Error::UnexpectedEOF)?;
|
||||||
|
|
||||||
|
Ok(SysCall::System(System::LoadReagent(
|
||||||
|
device,
|
||||||
|
reagent_mode,
|
||||||
|
Box::new(reagent_hash),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
// Math SysCalls
|
// Math SysCalls
|
||||||
"acos" => {
|
"acos" => {
|
||||||
|
|||||||
@@ -237,6 +237,18 @@ documented! {
|
|||||||
Spanned<Literal<'a>>,
|
Spanned<Literal<'a>>,
|
||||||
Spanned<Literal<'a>>,
|
Spanned<Literal<'a>>,
|
||||||
Box<Spanned<Expression<'a>>>
|
Box<Spanned<Expression<'a>>>
|
||||||
|
),
|
||||||
|
/// Loads reagent of device's ReagentMode where a hash of the reagent type to check for
|
||||||
|
///
|
||||||
|
/// ## IC10
|
||||||
|
/// `lr r? device(d?|r?|id) reagentMode int`
|
||||||
|
/// ## Slang
|
||||||
|
/// `let result = loadReagent(deviceHash, "ReagentMode", reagentHash);`
|
||||||
|
/// `let result = lr(deviceHash, "ReagentMode", reagentHash);`
|
||||||
|
LoadReagent(
|
||||||
|
Spanned<LiteralOrVariable<'a>>,
|
||||||
|
Spanned<Literal<'a>>,
|
||||||
|
Box<Spanned<Expression<'a>>>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,6 +273,7 @@ impl<'a> std::fmt::Display for System<'a> {
|
|||||||
}
|
}
|
||||||
System::LoadSlot(a, b, c) => write!(f, "loadSlot({}, {}, {})", a, b, c),
|
System::LoadSlot(a, b, c) => write!(f, "loadSlot({}, {}, {})", a, b, c),
|
||||||
System::SetSlot(a, b, c, d) => write!(f, "setSlot({}, {}, {}, {})", a, b, c, d),
|
System::SetSlot(a, b, c, d) => write!(f, "setSlot({}, {}, {}, {})", a, b, c, d),
|
||||||
|
System::LoadReagent(a, b, c) => write!(f, "loadReagent({}, {}, {})", a, b, c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use tokenizer::Tokenizer;
|
|
||||||
|
|
||||||
use crate::Parser;
|
use crate::Parser;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use tokenizer::Tokenizer;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_block() -> anyhow::Result<()> {
|
fn test_block() -> anyhow::Result<()> {
|
||||||
|
|||||||
@@ -54,10 +54,7 @@ fn test_const_declaration() -> Result<()> {
|
|||||||
let tokenizer = Tokenizer::from(input);
|
let tokenizer = Tokenizer::from(input);
|
||||||
let mut parser = Parser::new(tokenizer);
|
let mut parser = Parser::new(tokenizer);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!("(const item = 20c)", parser.parse()?.unwrap().to_string());
|
||||||
"(const item = 293.15)",
|
|
||||||
parser.parse()?.unwrap().to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"(const decimal = 200.15)",
|
"(const decimal = 200.15)",
|
||||||
|
|||||||
@@ -174,6 +174,18 @@ impl<'a> std::fmt::Display for MemberAccessExpression<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct MemberIndexingExpression<'a> {
|
||||||
|
pub object: LiteralOrVariable<'a>,
|
||||||
|
pub index_of: Box<Spanned<Expression<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::fmt::Display for MemberIndexingExpression<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}[{}]", self.object, self.index_of)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct MethodCallExpression<'a> {
|
pub struct MethodCallExpression<'a> {
|
||||||
pub object: Box<Spanned<Expression<'a>>>,
|
pub object: Box<Spanned<Expression<'a>>>,
|
||||||
|
|||||||
@@ -102,48 +102,6 @@ impl<'a> Token<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
|
|
||||||
pub enum Temperature {
|
|
||||||
Celsius(Number),
|
|
||||||
Fahrenheit(Number),
|
|
||||||
Kelvin(Number),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Temperature {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Temperature::Celsius(n) => write!(f, "{}°C", n),
|
|
||||||
Temperature::Fahrenheit(n) => write!(f, "{}°F", n),
|
|
||||||
Temperature::Kelvin(n) => write!(f, "{}°K", n),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Temperature {
|
|
||||||
pub fn to_kelvin(self) -> Number {
|
|
||||||
match self {
|
|
||||||
Temperature::Celsius(n) => {
|
|
||||||
let n = match n {
|
|
||||||
Number::Integer(i) => Decimal::new(i as i64, 0),
|
|
||||||
Number::Decimal(d) => d,
|
|
||||||
};
|
|
||||||
Number::Decimal(n + Decimal::new(27315, 2))
|
|
||||||
}
|
|
||||||
Temperature::Fahrenheit(n) => {
|
|
||||||
let n = match n {
|
|
||||||
Number::Integer(i) => Decimal::new(i as i64, 0),
|
|
||||||
Number::Decimal(d) => d,
|
|
||||||
};
|
|
||||||
|
|
||||||
let a = n - Decimal::new(32, 0);
|
|
||||||
let b = Decimal::new(5, 0) / Decimal::new(9, 0);
|
|
||||||
Number::Decimal(a * b + Decimal::new(27315, 2))
|
|
||||||
}
|
|
||||||
Temperature::Kelvin(n) => n,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! symbol {
|
macro_rules! symbol {
|
||||||
($var:ident) => {
|
($var:ident) => {
|
||||||
|_| Symbol::$var
|
|_| Symbol::$var
|
||||||
@@ -280,30 +238,27 @@ fn parse_number<'a>(lexer: &mut Lexer<'a, TokenType<'a>>) -> Result<Number, LexE
|
|||||||
span.end -= lexer.extras.line_start_index;
|
span.end -= lexer.extras.line_start_index;
|
||||||
span.start -= lexer.extras.line_start_index;
|
span.start -= lexer.extras.line_start_index;
|
||||||
|
|
||||||
let num = if clean_str.contains('.') {
|
let unit = match suffix {
|
||||||
Number::Decimal(
|
Some('c') => Unit::Celsius,
|
||||||
|
Some('f') => Unit::Fahrenheit,
|
||||||
|
Some('k') => Unit::Kelvin,
|
||||||
|
_ => Unit::None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if clean_str.contains('.') {
|
||||||
|
Ok(Number::Decimal(
|
||||||
clean_str
|
clean_str
|
||||||
.parse::<Decimal>()
|
.parse::<Decimal>()
|
||||||
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
|
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
|
||||||
)
|
unit,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Number::Integer(
|
Ok(Number::Integer(
|
||||||
clean_str
|
clean_str
|
||||||
.parse::<i128>()
|
.parse::<i128>()
|
||||||
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
|
.map_err(|_| LexError::NumberParse(line, span, slice.to_string()))?,
|
||||||
)
|
unit,
|
||||||
};
|
))
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,25 +350,55 @@ impl<'a> std::fmt::Display for TokenType<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
|
||||||
|
pub enum Unit {
|
||||||
|
None,
|
||||||
|
Celsius,
|
||||||
|
Fahrenheit,
|
||||||
|
Kelvin,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Hash, Eq, Clone, Copy)]
|
||||||
pub enum Number {
|
pub enum Number {
|
||||||
/// Represents an integer number
|
/// Represents an integer number
|
||||||
Integer(i128),
|
Integer(i128, Unit),
|
||||||
/// Represents a decimal type number with a precision of 64 bits
|
/// Represents a decimal type number with a precision of 64 bits
|
||||||
Decimal(Decimal),
|
Decimal(Decimal, Unit),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Number {
|
||||||
|
pub fn unit(&self) -> Unit {
|
||||||
|
match self {
|
||||||
|
Number::Integer(_, u) => *u,
|
||||||
|
Number::Decimal(_, u) => *u,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_unit(&self) -> bool {
|
||||||
|
self.unit() != Unit::None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for Number {
|
impl From<bool> for Number {
|
||||||
fn from(value: bool) -> Self {
|
fn from(value: bool) -> Self {
|
||||||
Self::Integer(if value { 1 } else { 0 })
|
Self::Integer(if value { 1 } else { 0 }, Unit::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Number> for Decimal {
|
impl From<Number> for Decimal {
|
||||||
fn from(value: Number) -> Self {
|
fn from(value: Number) -> Self {
|
||||||
match value {
|
let (val, unit) = match value {
|
||||||
Number::Decimal(d) => d,
|
Number::Decimal(d, u) => (d, u),
|
||||||
Number::Integer(i) => Decimal::from(i),
|
Number::Integer(i, u) => (Decimal::from(i), u),
|
||||||
|
};
|
||||||
|
|
||||||
|
match unit {
|
||||||
|
Unit::None | Unit::Kelvin => val,
|
||||||
|
Unit::Celsius => val + Decimal::new(27315, 2),
|
||||||
|
Unit::Fahrenheit => {
|
||||||
|
(val - Decimal::new(32, 0)) * Decimal::new(5, 0) / Decimal::new(9, 0)
|
||||||
|
+ Decimal::new(27315, 2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,22 +408,48 @@ impl std::ops::Neg for Number {
|
|||||||
|
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
match self {
|
match self {
|
||||||
Self::Integer(i) => Self::Integer(-i),
|
Self::Integer(i, u) => Self::Integer(-i, u),
|
||||||
Self::Decimal(d) => Self::Decimal(-d),
|
Self::Decimal(d, u) => Self::Decimal(-d, u),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn determine_target_unit(lhs_unit: Unit, rhs_unit: Unit) -> Option<Unit> {
|
||||||
|
if lhs_unit == rhs_unit {
|
||||||
|
return Some(lhs_unit);
|
||||||
|
}
|
||||||
|
if lhs_unit != Unit::None && rhs_unit == Unit::None {
|
||||||
|
return Some(lhs_unit);
|
||||||
|
}
|
||||||
|
if lhs_unit == Unit::None && rhs_unit != Unit::None {
|
||||||
|
return Some(rhs_unit);
|
||||||
|
}
|
||||||
|
// Mismatched units (C + F) -> Fallback to Kelvin/None
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Add for Number {
|
impl std::ops::Add for Number {
|
||||||
type Output = Number;
|
type Output = Number;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
match (self, rhs) {
|
// If we can determine a common target unit (e.g. C + C = C, or C + Scalar = C),
|
||||||
(Self::Integer(l), Self::Integer(r)) => Number::Integer(l + r),
|
// we preserve that unit. Otherwise, we convert to Kelvin (Decimal) and return Unit::None.
|
||||||
(Self::Decimal(l), Self::Decimal(r)) => Number::Decimal(l + r),
|
if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
|
||||||
(Self::Integer(l), Self::Decimal(r)) => Number::Decimal(Decimal::from(l) + r),
|
return match (self, rhs) {
|
||||||
(Self::Decimal(l), Self::Integer(r)) => Number::Decimal(l + Decimal::from(r)),
|
(Self::Integer(l, _), Self::Integer(r, _)) => Number::Integer(l + r, target_unit),
|
||||||
|
(Self::Decimal(l, _), Self::Decimal(r, _)) => Number::Decimal(l + r, target_unit),
|
||||||
|
(Self::Integer(l, _), Self::Decimal(r, _)) => {
|
||||||
|
Number::Decimal(Decimal::from(l) + r, target_unit)
|
||||||
|
}
|
||||||
|
(Self::Decimal(l, _), Self::Integer(r, _)) => {
|
||||||
|
Number::Decimal(l + Decimal::from(r), target_unit)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let l: Decimal = self.into();
|
||||||
|
let r: Decimal = rhs.into();
|
||||||
|
Number::Decimal(l + r, Unit::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,12 +457,22 @@ impl std::ops::Sub for Number {
|
|||||||
type Output = Number;
|
type Output = Number;
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
match (self, rhs) {
|
if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
|
||||||
(Self::Integer(l), Self::Integer(r)) => Self::Integer(l - r),
|
return match (self, rhs) {
|
||||||
(Self::Decimal(l), Self::Integer(r)) => Self::Decimal(l - Decimal::from(r)),
|
(Self::Integer(l, _), Self::Integer(r, _)) => Number::Integer(l - r, target_unit),
|
||||||
(Self::Integer(l), Self::Decimal(r)) => Self::Decimal(Decimal::from(l) - r),
|
(Self::Decimal(l, _), Self::Decimal(r, _)) => Number::Decimal(l - r, target_unit),
|
||||||
(Self::Decimal(l), Self::Decimal(r)) => Self::Decimal(l - r),
|
(Self::Integer(l, _), Self::Decimal(r, _)) => {
|
||||||
|
Number::Decimal(Decimal::from(l) - r, target_unit)
|
||||||
|
}
|
||||||
|
(Self::Decimal(l, _), Self::Integer(r, _)) => {
|
||||||
|
Number::Decimal(l - Decimal::from(r), target_unit)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let l: Decimal = self.into();
|
||||||
|
let r: Decimal = rhs.into();
|
||||||
|
Number::Decimal(l - r, Unit::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,12 +480,26 @@ impl std::ops::Mul for Number {
|
|||||||
type Output = Number;
|
type Output = Number;
|
||||||
|
|
||||||
fn mul(self, rhs: Self) -> Self::Output {
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
match (self, rhs) {
|
if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
|
||||||
(Number::Integer(l), Number::Integer(r)) => Number::Integer(l * r),
|
return match (self, rhs) {
|
||||||
(Number::Integer(l), Number::Decimal(r)) => Number::Decimal(Decimal::from(l) * r),
|
(Number::Integer(l, _), Number::Integer(r, _)) => {
|
||||||
(Number::Decimal(l), Number::Integer(r)) => Number::Decimal(l * Decimal::from(r)),
|
Number::Integer(l * r, target_unit)
|
||||||
(Number::Decimal(l), Number::Decimal(r)) => Number::Decimal(l * r),
|
}
|
||||||
|
(Number::Integer(l, _), Number::Decimal(r, _)) => {
|
||||||
|
Number::Decimal(Decimal::from(l) * r, target_unit)
|
||||||
|
}
|
||||||
|
(Number::Decimal(l, _), Number::Integer(r, _)) => {
|
||||||
|
Number::Decimal(l * Decimal::from(r), target_unit)
|
||||||
|
}
|
||||||
|
(Number::Decimal(l, _), Number::Decimal(r, _)) => {
|
||||||
|
Number::Decimal(l * r, target_unit)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let l: Decimal = self.into();
|
||||||
|
let r: Decimal = rhs.into();
|
||||||
|
Number::Decimal(l * r, Unit::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,7 +507,22 @@ impl std::ops::Div for Number {
|
|||||||
type Output = Number;
|
type Output = Number;
|
||||||
|
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
Number::Decimal(Decimal::from(self) / Decimal::from(rhs))
|
if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
|
||||||
|
// Division always promotes to Decimal
|
||||||
|
let l_val = match self {
|
||||||
|
Self::Integer(i, _) => Decimal::from(i),
|
||||||
|
Self::Decimal(d, _) => d,
|
||||||
|
};
|
||||||
|
let r_val = match rhs {
|
||||||
|
Self::Integer(i, _) => Decimal::from(i),
|
||||||
|
Self::Decimal(d, _) => d,
|
||||||
|
};
|
||||||
|
return Number::Decimal(l_val / r_val, target_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
let l: Decimal = self.into();
|
||||||
|
let r: Decimal = rhs.into();
|
||||||
|
Number::Decimal(l / r, Unit::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,15 +530,36 @@ impl std::ops::Rem for Number {
|
|||||||
type Output = Number;
|
type Output = Number;
|
||||||
|
|
||||||
fn rem(self, rhs: Self) -> Self::Output {
|
fn rem(self, rhs: Self) -> Self::Output {
|
||||||
Number::Decimal(Decimal::from(self) % Decimal::from(rhs))
|
if let Some(target_unit) = determine_target_unit(self.unit(), rhs.unit()) {
|
||||||
|
let l_val = match self {
|
||||||
|
Self::Integer(i, _) => Decimal::from(i),
|
||||||
|
Self::Decimal(d, _) => d,
|
||||||
|
};
|
||||||
|
let r_val = match rhs {
|
||||||
|
Self::Integer(i, _) => Decimal::from(i),
|
||||||
|
Self::Decimal(d, _) => d,
|
||||||
|
};
|
||||||
|
return Number::Decimal(l_val % r_val, target_unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
let l: Decimal = self.into();
|
||||||
|
let r: Decimal = rhs.into();
|
||||||
|
Number::Decimal(l % r, Unit::None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Number {
|
impl std::fmt::Display for Number {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
let (val, unit) = match self {
|
||||||
Number::Integer(i) => write!(f, "{}", i),
|
Number::Integer(i, u) => (i.to_string(), u),
|
||||||
Number::Decimal(d) => write!(f, "{}", d),
|
Number::Decimal(d, u) => (d.to_string(), u),
|
||||||
|
};
|
||||||
|
|
||||||
|
match unit {
|
||||||
|
Unit::None => write!(f, "{}", val),
|
||||||
|
Unit::Celsius => write!(f, "{}c", val),
|
||||||
|
Unit::Fahrenheit => write!(f, "{}f", val),
|
||||||
|
Unit::Kelvin => write!(f, "{}k", val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -771,3 +842,4 @@ documented! {
|
|||||||
While,
|
While,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ struct Args {
|
|||||||
/// The output file for the compiled program. If not set, output will go to stdout.
|
/// The output file for the compiled program. If not set, output will go to stdout.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
output_file: Option<PathBuf>,
|
output_file: Option<PathBuf>,
|
||||||
|
/// Should Slang attempt to optimize the output?
|
||||||
|
#[arg(short = 'z', long)]
|
||||||
|
optimize: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_logic<'a>() -> Result<(), Error<'a>> {
|
fn run_logic<'a>() -> Result<(), Error<'a>> {
|
||||||
@@ -107,7 +110,11 @@ fn run_logic<'a>() -> Result<(), Error<'a>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optimizer::optimize(instructions).write(&mut writer)?;
|
if args.optimize {
|
||||||
|
optimizer::optimize(instructions).write(&mut writer)?;
|
||||||
|
} else {
|
||||||
|
instructions.write(&mut writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
writer.flush()?;
|
writer.flush()?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user