1 Commits

Author SHA1 Message Date
3a8f0e84af wip -- start indexing work 2025-12-21 00:47:47 -07:00
10 changed files with 27 additions and 129 deletions

View File

@@ -1,11 +1,5 @@
# Changelog
[0.4.0]
- First pass getting compiled IC10 to output along side the Slang source code
- IC10 side is currently not scrollable, and text might be cut off from the bottom,
requiring newlines to be added to the bottom of the Slang source if needed
[0.3.4]
- Added support for `loadReagent`, which maps to the `lr` IC10 instruction

View File

@@ -2,7 +2,7 @@
<ModMetadata xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>Slang</Name>
<Author>JoeDiertay</Author>
<Version>0.4.0</Version>
<Version>0.3.4</Version>
<Description>
[h1]Slang: High-Level Programming for Stationeers[/h1]
@@ -23,8 +23,6 @@ Slang (Stationeers Language) brings modern programming to Stationeers. It allows
[*] [b]Optimizations:[/b] Features like Constant Folding calculate math at compile time to save instructions.
[*] [b]Device Aliasing:[/b] Simple mapping: device sensor = "d0".
[*] [b]Temperature Literals:[/b] Don't worry about converting Celsius to Kelvin anymore. Define your temperatures as whatever you want and append the proper suffix at the end (ex. 20c, 68f, 293.15k)
[*] [b]Side-by-side IC10 output:[/b] Preview the compiled IC10 alongside the Slang source code. What you see is what you get.
[*] [b]Compiler Optimizations:[/b] Slang now does its best to safely optimize the output IC10, removing labels, unnecessary moves, etc.
[/list]
[h2]Installation[/h2]
@@ -52,12 +50,16 @@ loop {
[h2]Known Issues (Beta)[/h2]
[list]
[*] [b]Stack Access:[/b] Direct stack memory access is disabled to prevent conflicts with the compiler's internal memory management. A workaround is being planned.
[*] [b]Code Size:[/b] Compiled output is currently more verbose than hand-optimized assembly. Optimization passes are planned.
[*] [b]Stack Access:[/b] Direct stack memory access is disabled to prevent conflicts with the compiler's internal memory management.
[*] [b]Documentation:[/b] In-game tooltips for syscalls (like load, set) are WIP. Check the "Slang" entry in the Stationpedia (F1) for help.
[*] [b]Debugging:[/b] Runtime errors currently point to the compiled IC10 line number, not your Slang source line. Source mapping is coming soon.
[/list]
[h2]Planned Features[/h2]
[list]
[*] Side-by-side view: Slang vs. Compiled IC10.
[*] Compiler optimizations (dead code elimination, smarter register allocation).
[*] Enhanced LSP features (Autocomplete, Go to Definition).
[*] Full feature parity with all IC10 instructions.
[*] Tutorials and beginner script examples.

View File

@@ -5,20 +5,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ImGuiNET;
using StationeersIC10Editor;
using StationeersIC10Editor.IC10;
using UnityEngine;
public class SlangFormatter : ICodeFormatter
{
private CancellationTokenSource? _lspCancellationToken;
private object _tokenLock = new();
private IC10CodeFormatter iC10CodeFormatter = new IC10CodeFormatter();
private string ic10CompilationResult = "";
private List<SourceMapEntry> ic10SourceMap = new();
// VS Code Dark Theme Palette
public static readonly uint ColorControl = ColorFromHTML("#C586C0"); // Pink (if, return, loop)
public static readonly uint ColorDeclaration = ColorFromHTML("#569CD6"); // Blue (let, device, fn)
@@ -40,7 +33,6 @@ public class SlangFormatter : ICodeFormatter
: base()
{
OnCodeChanged += HandleCodeChanged;
OnCaretMoved += UpdateIc10Formatter;
}
public static double MatchingScore(string input)
@@ -78,34 +70,6 @@ public class SlangFormatter : ICodeFormatter
return this.Lines.RawText;
}
public override void DrawLine(int lineIndex, TextRange selection, bool drawLineNumber = true)
{
Vector2 cursorPos = ImGui.GetCursorScreenPos();
Vector2 space = ImGui.GetContentRegionAvail();
base.DrawLine(lineIndex, selection, drawLineNumber);
var charWidth = Settings.CharWidth;
var width = Mathf.Max(Lines.Width + 10.0f + LineNumberOffset * charWidth, space.x / 2);
ImGui
.GetWindowDrawList()
.AddLine(
new Vector2(cursorPos.x + width + 4.5f * charWidth, cursorPos.y),
new Vector2(
cursorPos.x + width + 4.5f * charWidth,
cursorPos.y + space.y + Settings.LineHeight
),
ColorLineNumber,
1.0f
);
cursorPos.x += width;
ImGui.SetCursorScreenPos(cursorPos);
if (lineIndex < iC10CodeFormatter.Lines.Count)
iC10CodeFormatter.DrawLine(lineIndex, new TextRange(), true);
}
public override StyledLine ParseLine(string line)
{
// We create the line first
@@ -162,32 +126,6 @@ public class SlangFormatter : ICodeFormatter
);
ApplyDiagnostics(dict);
// If we have valid code, update the IC10 output
if (dict.Count > 0)
{
return;
}
var (compilationSuccess, compiled, sourceMap) = await Task.Run(
() =>
{
var successful = Marshal.CompileFromString(
inputSrc,
out var compiled,
out var sourceMap
);
return (successful, compiled, sourceMap);
},
cancellationToken
);
if (compilationSuccess)
{
ic10CompilationResult = compiled;
ic10SourceMap = sourceMap;
UpdateIc10Formatter();
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
@@ -196,53 +134,6 @@ public class SlangFormatter : ICodeFormatter
}
}
private void UpdateIc10Formatter()
{
iC10CodeFormatter.Editor = Editor;
var caretPos = Editor.CaretPos.Line;
// get the slang sourceMap at the current editor line
var lines = ic10SourceMap.FindAll(entry =>
entry.SlangSource.StartLine == caretPos || entry.SlangSource.EndLine == caretPos
);
// extract the current "context" of the ic10 compilation. The current Slang source line
// should be directly next to the compiled IC10 source line, and we should highlight the
// IC10 code that directly represents the Slang source
iC10CodeFormatter.ResetCode(ic10CompilationResult);
if (lines.Count() < 1)
{
return;
}
// get the total range of the IC10 source for the selected Slang line
var max = lines.Max(line => line.Ic10Line);
var min = lines.Min(line => line.Ic10Line);
// highlight all the IC10 lines that are within the specified range
foreach (var index in Enumerable.Range((int)min, (int)(max - min) + 1))
{
var lineText = iC10CodeFormatter.Lines[index].Text;
var newLine = new StyledLine(
lineText,
[
new SemanticToken
{
Column = 0,
Length = lineText.Length,
Line = index,
Background = ColorIdentifier,
Color = ColorFromHTML("black"),
},
]
);
iC10CodeFormatter.Lines[index] = newLine;
}
}
// This runs on the Main Thread
private void ApplyDiagnostics(Dictionary<uint, IGrouping<uint, Diagnostic>> dict)
{

View File

@@ -41,7 +41,7 @@ namespace Slang
{
public const string PluginGuid = "com.biddydev.slang";
public const string PluginName = "Slang";
public const string PluginVersion = "0.4.0";
public const string PluginVersion = "0.3.4";
public static Mod MOD = new Mod(PluginName, PluginVersion);

View File

@@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<AssemblyName>StationeersSlang</AssemblyName>
<Description>Slang Compiler Bridge</Description>
<Version>0.4.0</Version>
<Version>0.3.2</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@@ -39,10 +39,6 @@
<HintPath>./ref/Assembly-CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="RG.ImGui">
<HintPath>./ref/RG.ImGui.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IC10Editor.dll">
<HintPath>./ref/IC10Editor.dll</HintPath>

View File

@@ -930,7 +930,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "slang"
version = "0.4.0"
version = "0.3.4"
dependencies = [
"anyhow",
"clap",

View File

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

View File

@@ -13,6 +13,6 @@ lsp-types = { workspace = true }
rust_decimal = { workspace = true }
[dev-dependencies]
anyhow = { version = "1.0" }
anyhow = { workspace = true }
indoc = { version = "2.0" }
pretty_assertions = "1"

View File

@@ -308,7 +308,7 @@ impl<'a> Parser<'a> {
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(
&mut self,
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 {
break;
}

View File

@@ -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)]
pub struct MethodCallExpression<'a> {
pub object: Box<Spanned<Expression<'a>>>,