5 Commits

Author SHA1 Message Date
d8fe9a0d7d Merge pull request 'optimizer-bug' (#14) from optimizer-bug into master
All checks were successful
CI/CD Pipeline / test (push) Successful in 36s
CI/CD Pipeline / build (push) Successful in 1m44s
CI/CD Pipeline / release (push) Successful in 4s
Reviewed-on: #14
2026-01-01 12:41:11 -07:00
089fe46d36 update changelog and version bump
All checks were successful
CI/CD Pipeline / test (pull_request) Successful in 37s
CI/CD Pipeline / build (pull_request) Has been skipped
CI/CD Pipeline / release (pull_request) Has been skipped
2026-01-01 12:38:53 -07:00
14c641797a Fixed optimizer bug where certain syscalls were not included in register liveness analysis 2026-01-01 12:35:57 -07:00
fb5eacea02 Merge pull request 'Updated auto-doc formatting for markdown files' (#13) from doc-formatting-fixes into master
All checks were successful
CI/CD Pipeline / test (push) Successful in 36s
CI/CD Pipeline / build (push) Successful in 1m44s
CI/CD Pipeline / release (push) Has been skipped
Reviewed-on: #13
2026-01-01 02:43:50 -07:00
9fd3a55182 Updated auto-doc formatting for markdown files
All checks were successful
CI/CD Pipeline / test (pull_request) Successful in 37s
CI/CD Pipeline / build (pull_request) Has been skipped
CI/CD Pipeline / release (pull_request) Has been skipped
2026-01-01 02:43:07 -07:00
13 changed files with 152 additions and 38 deletions

View File

@@ -1,5 +1,13 @@
# Changelog # Changelog
[0.5.1]
- Fixed optimizer bug where `StoreBatch` and `StoreBatchNamed` instructions
were not recognized as reading operands, causing incorrect elimination of
necessary device property loads
- Added comprehensive register read tracking for `StoreSlot`, `JumpRelative`,
and `Alias` instructions in the optimizer
[0.5.0] [0.5.0]
- Added full tuple support: declarations, assignments, and returns - Added full tuple support: declarations, assignments, and returns

View File

@@ -2,7 +2,7 @@
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Slang</Name> <Name>Slang</Name>
<Author>JoeDiertay</Author> <Author>JoeDiertay</Author>
<Version>0.5.0</Version> <Version>0.5.1</Version>
<Description> <Description>
[h1]Slang: High-Level Programming for Stationeers[/h1] [h1]Slang: High-Level Programming for Stationeers[/h1]

View File

@@ -39,7 +39,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.5.0"; public const string PluginVersion = "0.5.1";
private static Harmony? _harmony; private static Harmony? _harmony;

View File

@@ -1,59 +1,73 @@
using System;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Slang; namespace Slang;
public static class TextMeshProFormatter public static class TextMeshProFormatter
{ {
private const string CODE_COLOR = "#FFD700"; private const string CODE_COLOR = "#FFD700"; // Gold
private const string LINK_COLOR = "#0099FF"; // Blue
private const string QUOTE_COLOR = "#90EE90"; // Light Green
public static string FromMarkdown(string markdown) public static string FromMarkdown(string markdown)
{ {
if (string.IsNullOrEmpty(markdown)) if (string.IsNullOrEmpty(markdown))
return ""; return "";
// 1. Normalize Line Endings // Normalize Line Endings
string text = markdown.Replace("\r\n", "\n"); string text = markdown.Replace("\r\n", "\n");
// 2. Handle Code Blocks (```) // Process code blocks FIRST (``` ... ```)
text = Regex.Replace( text = Regex.Replace(
text, text,
@"```\s*(.*?)\s*```", @"```[^\n]*\n(.*?)\n```",
match => match =>
{ {
var codeContent = match.Groups[1].Value; var codeContent = match.Groups[1].Value;
return $"<color={CODE_COLOR}>{codeContent}</color>"; // Gold color for code return $"<color={CODE_COLOR}>{codeContent}</color>";
}, },
RegexOptions.Singleline RegexOptions.Singleline
); );
// Process headers - check for 1-6 hashes
text = Regex.Replace(text, @"^#{1}\s+(.+)$", "<size=120%><b>$1</b></size>", RegexOptions.Multiline);
text = Regex.Replace(text, @"^#{2}\s+(.+)$", "<size=110%><b>$1</b></size>", RegexOptions.Multiline);
text = Regex.Replace(text, @"^#{3}\s+(.+)$", "<size=100%><b>$1</b></size>", RegexOptions.Multiline);
text = Regex.Replace(text, @"^#{4}\s+(.+)$", "<size=90%><b>$1</b></size>", RegexOptions.Multiline);
text = Regex.Replace(text, @"^#{5}\s+(.+)$", "<size=80%><b>$1</b></size>", RegexOptions.Multiline);
text = Regex.Replace(text, @"^#{6}\s+(.+)$", "<size=70%><b>$1</b></size>", RegexOptions.Multiline);
// Process markdown links [text](url)
text = Regex.Replace( text = Regex.Replace(
text, text,
@"^\s*##\s+(.+)$", @"\[([^\]]+)\]\(([^\)]+)\)",
"<size=110%><color=#ffffff><b>$1</b></color></size>", $"<color={LINK_COLOR}><u>$1</u></color>"
RegexOptions.Multiline
); );
// 3. Handle # Headers SECOND (General) // Process inline code (`code`)
text = Regex.Replace(
text,
@"^\s*#\s+(.+)$",
"<size=120%><color=#ffffff><b>$1</b></color></size>",
RegexOptions.Multiline
);
// 4. Handle Inline Code (`code`)
text = Regex.Replace(text, @"`([^`]+)`", $"<color={CODE_COLOR}>$1</color>"); text = Regex.Replace(text, @"`([^`]+)`", $"<color={CODE_COLOR}>$1</color>");
// 5. Handle Bold (**text**) // Process bold (**text**)
text = Regex.Replace(text, @"\*\*(.+?)\*\*", "<b>$1</b>"); text = Regex.Replace(text, @"\*\*(.+?)\*\*", "<b>$1</b>");
// 6. Handle Italics (*text*) // Process italics (*text*)
text = Regex.Replace(text, @"\*(.+?)\*", "<i>$1</i>"); text = Regex.Replace(text, @"(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)", "<i>$1</i>");
// 7. Convert Newlines to TMP Line Breaks // Process block quotes (> text)
// Stationpedia needs <br> or explicit newlines. text = Regex.Replace(
// Often just ensuring \n is preserved is enough, but <br> is safer for HTML-like parsers. text,
text = text.Replace("\n", "<br>"); @"^>\s+(.+)$",
$"<color={QUOTE_COLOR}><i>$1</i></color>",
RegexOptions.Multiline
);
// Process unordered lists (- items)
text = Regex.Replace(
text,
@"^-\s+(.+)$",
" • $1",
RegexOptions.Multiline
);
return text; return text;
} }

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyName>StationeersSlang</AssemblyName> <AssemblyName>StationeersSlang</AssemblyName>
<Description>Slang Compiler Bridge</Description> <Description>Slang Compiler Bridge</Description>
<Version>0.5.0</Version> <Version>0.5.1</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>

View File

@@ -1039,7 +1039,7 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]] [[package]]
name = "slang" name = "slang"
version = "0.5.0" version = "0.5.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "slang" name = "slang"
version = "0.5.0" version = "0.5.1"
edition = "2021" edition = "2021"
[workspace] [workspace]

View File

@@ -1,4 +1,5 @@
use crc32fast::hash as crc32_hash; use crc32fast::hash as crc32_hash;
/// This function takes an input which is meant to be hashed via the CRC32 algorithm, but it then /// This function takes an input which is meant to be hashed via the CRC32 algorithm, but it then
/// converts the generated UNSIGNED number into it's SIGNED counterpart. /// converts the generated UNSIGNED number into it's SIGNED counterpart.
pub fn crc_hash_signed(input: &str) -> i128 { pub fn crc_hash_signed(input: &str) -> i128 {
@@ -9,3 +10,38 @@ pub fn crc_hash_signed(input: &str) -> i128 {
hash_value_i32 as i128 hash_value_i32 as i128
} }
/// Removes common leading whitespace from all lines in a string (dedent).
/// This is useful for cleaning up documentation strings that have uniform indentation.
pub fn dedent(text: &str) -> String {
let lines: Vec<&str> = text.lines().collect();
// Find minimum indentation (excluding empty lines)
let mut min_indent = usize::MAX;
for line in &lines {
if !line.trim().is_empty() {
let indent = line.len() - line.trim_start().len();
min_indent = min_indent.min(indent);
}
}
// If no lines or all empty, return as-is
if min_indent == usize::MAX {
return text.to_string();
}
// Remove the common indentation
lines
.iter()
.map(|line| {
if line.trim().is_empty() {
""
} else if line.len() >= min_indent {
&line[min_indent..]
} else {
line
}
})
.collect::<Vec<_>>()
.join("\n")
}

View File

@@ -1,4 +1,5 @@
mod helper_funcs; mod helper_funcs;
pub use helper_funcs::dedent;
mod macros; mod macros;
mod syscall; mod syscall;

View File

@@ -99,12 +99,12 @@ macro_rules! documented {
),* ),*
]; ];
doc_lines.iter() let combined = doc_lines.iter()
.filter_map(|&d| d) .filter_map(|&d| d)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join("\n");
.trim()
.to_string() $crate::dedent(&combined).trim().to_string()
} }
)* )*
} }
@@ -122,12 +122,13 @@ macro_rules! documented {
documented!(@doc_filter #[ $($variant_attr)* ]) documented!(@doc_filter #[ $($variant_attr)* ])
),* ),*
]; ];
doc_lines.iter()
let combined = doc_lines.iter()
.filter_map(|&d| d) .filter_map(|&d| d)
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join("\n");
.trim()
.to_string() $crate::dedent(&combined).trim().to_string()
} }
) )
),* ),*
@@ -136,4 +137,3 @@ macro_rules! documented {
} }
}; };
} }

View File

@@ -18,6 +18,12 @@ mod tests {
let compiler = Compiler::new(parser, None); let compiler = Compiler::new(parser, None);
let result = compiler.compile(); let result = compiler.compile();
assert!(
result.errors.is_empty(),
"Compilation errors: {:?}",
result.errors
);
// Get unoptimized output // Get unoptimized output
let mut unoptimized_writer = std::io::BufWriter::new(Vec::new()); let mut unoptimized_writer = std::io::BufWriter::new(Vec::new());
result result
@@ -219,4 +225,20 @@ mod tests {
let output = compile_with_and_without_optimization(source); let output = compile_with_and_without_optimization(source);
insta::assert_snapshot!(output); insta::assert_snapshot!(output);
} }
#[test]
fn test_setbatched_with_member_access() {
let source = indoc! {r#"
const SENSOR = 20088;
const PANELS = hash("StructureSolarPanelDual");
loop {
setBatched(PANELS, "Horizontal", SENSOR.Horizontal);
setBatched(PANELS, "Vertical", SENSOR.Vertical + 90);
yield();
}
"#};
let output = compile_with_and_without_optimization(source);
insta::assert_snapshot!(output);
}
} }

View File

@@ -0,0 +1,28 @@
---
source: libs/integration_tests/src/lib.rs
assertion_line: 242
expression: output
---
## Unoptimized Output
j main
main:
__internal_L1:
l r1 20088 Horizontal
sb -539224550 Horizontal r1
l r2 20088 Vertical
add r3 r2 90
sb -539224550 Vertical r3
yield
j __internal_L1
__internal_L2:
## Optimized Output
l r1 20088 Horizontal
sb -539224550 Horizontal r1
l r2 20088 Vertical
add r3 r2 90
sb -539224550 Vertical r3
yield
j 0

View File

@@ -125,6 +125,9 @@ pub fn reg_is_read(instr: &Instruction, reg: u8) -> bool {
| Instruction::Pow(_, a, b) => check(a) || check(b), | Instruction::Pow(_, a, b) => check(a) || check(b),
Instruction::Load(_, a, _) => check(a), Instruction::Load(_, a, _) => check(a),
Instruction::Store(a, _, b) => check(a) || check(b), Instruction::Store(a, _, b) => check(a) || check(b),
Instruction::StoreBatch(a, _, b) => check(a) || check(b),
Instruction::StoreBatchNamed(a, b, _, c) => check(a) || check(b) || check(c),
Instruction::StoreSlot(a, b, _, c) => check(a) || check(b) || check(c),
Instruction::BranchEq(a, b, _) Instruction::BranchEq(a, b, _)
| Instruction::BranchNe(a, b, _) | Instruction::BranchNe(a, b, _)
| Instruction::BranchGt(a, b, _) | Instruction::BranchGt(a, b, _)
@@ -167,6 +170,8 @@ pub fn reg_is_read(instr: &Instruction, reg: u8) -> bool {
Instruction::Atan2(_, a, b) | Instruction::Max(_, a, b) | Instruction::Min(_, a, b) => { Instruction::Atan2(_, a, b) | Instruction::Max(_, a, b) | Instruction::Min(_, a, b) => {
check(a) || check(b) check(a) || check(b)
} }
Instruction::JumpRelative(a) => check(a),
Instruction::Alias(_, a) => check(a),
_ => false, _ => false,
} }
} }