V2 #1

Open
martin wants to merge 0 commits from V2 into main
139 changed files with 3528 additions and 740 deletions

41
.vscode/launch.json vendored
View File

@ -1,25 +1,42 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": ".NET Core Launch (console)", "name": "Launch STCompiler.Compiler",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/Compiler.dll", "program": "${workspaceFolder}/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Compiler",
"args": [ "args": ["${workspaceFolder}/STCompiler.Compiler/input.st", "${workspaceFolder}/STCompiler.Compiler/output.bin"],
"input.st", "cwd": "${workspaceFolder}/STCompiler.Compiler",
"out.bin" "console": "integratedTerminal"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
}, },
{ {
"name": ".NET Core Attach", "name": "Launch STCompiler.Disassembler",
"type": "coreclr", "type": "coreclr",
"request": "attach" "request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Disassembler",
"args": ["${workspaceFolder}/STCompiler.Compiler/output.bin"],
"cwd": "${workspaceFolder}/STCompiler.Disassembler",
"console": "integratedTerminal"
},
{
"name": "Launch STCompiler.Simulator",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/STCompiler.Simulator/bin/Debug/net8.0/STCompiler.Simulator",
"args": ["${workspaceFolder}/STCompiler.Compiler/output.bin"],
"cwd": "${workspaceFolder}/STCompiler.Simulator",
"console": "integratedTerminal"
},
{
"name": "Attach to .NET",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
} }
] ]
} }

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"csharp.suppressDotnetRestoreNotification": true,
"dotnet.defaultSolution": "${workspaceFolder}/STCompiler.sln",
"docwriter.style": "Auto-detect"
}

48
.vscode/tasks.json vendored
View File

@ -5,39 +5,33 @@
"label": "build", "label": "build",
"command": "dotnet", "command": "dotnet",
"type": "process", "type": "process",
"args": [ "args": ["build", "${workspaceFolder}/STCompiler.sln"],
"build", "group": {
"${workspaceFolder}/Compiler.csproj", "kind": "build",
"/property:GenerateFullPaths=true", "isDefault": true
"/consoleloggerparameters:NoSummary" },
], "problemMatcher": ["$msCompile"]
"problemMatcher": "$msCompile"
}, },
{ {
"label": "publish", "label": "run:compiler",
"command": "dotnet",
"type": "process", "type": "process",
"args": [ "command": "dotnet",
"publish", "args": ["run", "--project", "${workspaceFolder}/STCompiler.Compiler/STCompiler.Compiler.csproj", "--", "${workspaceFolder}/STCompiler.Compiler/input.st", "${workspaceFolder}/STCompiler.Compiler/output.bin"],
"${workspaceFolder}/Compiler.csproj", "presentation": { "reveal": "always", "panel": "shared" }
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}, },
{ {
"label": "watch", "label": "run:disassembler",
"command": "dotnet",
"type": "process", "type": "process",
"args": [ "command": "dotnet",
"watch", "args": ["run", "--project", "${workspaceFolder}/STCompiler.Disassembler/STCompiler.Disassembler.csproj", "--", "${workspaceFolder}/STCompiler.Compiler/output.bin"],
"run", "presentation": { "reveal": "always", "panel": "shared" }
"--project", },
"${workspaceFolder}/Compiler.csproj", {
"input.st", "label": "run:simulator",
"out.bin" "type": "process",
], "command": "dotnet",
"problemMatcher": "$msCompile" "args": ["run", "--project", "${workspaceFolder}/STCompiler.Simulator/STCompiler.Simulator.csproj", "--", "${workspaceFolder}/STCompiler.Compiler/output.bin"],
"presentation": { "reveal": "always", "panel": "shared" }
} }
] ]
} }

View File

@ -1,24 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compiler", "Compiler.csproj", "{CBEA9558-E4AD-DA70-5319-E69E934D0501}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CBEA9558-E4AD-DA70-5319-E69E934D0501}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBEA9558-E4AD-DA70-5319-E69E934D0501}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBEA9558-E4AD-DA70-5319-E69E934D0501}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBEA9558-E4AD-DA70-5319-E69E934D0501}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {27C531AE-201E-4E9C-9ACC-BDCA8ADD46DC}
EndGlobalSection
EndGlobal

View File

@ -1,614 +0,0 @@
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
// === ENTRY POINT ===
class Program {
static int Main(string[] args) {
if (args.Length < 2) {
Console.WriteLine("Usage: StEmitter <input.st> <output.bin>");
return 1;
}
var input = File.ReadAllText(args[0]);
var parser = new StParser(input);
var prog = parser.ParseProgram();
if (parser.HasErrors) {
Console.WriteLine($"Compilation failed with {parser.Errors.Count} errors:");
foreach (var error in parser.Errors) {
Console.WriteLine($"Error at line {error.Line}: {error.Message}");
}
return 1;
}
if (prog == null) {
Console.WriteLine("Compilation failed: invalid program structure");
return 1;
}
try {
var emitter = new BytecodeEmitter();
emitter.Compile(prog);
File.WriteAllBytes(args[1], emitter.BuildBinary());
Console.WriteLine($"Wrote {args[1]}: consts={emitter.ConstantsCount}, vars={emitter.VarCount}, code={emitter.CodeLength}");
return 0;
} catch (Exception ex) {
Console.WriteLine($"Internal compiler error: {ex.Message}");
return 1;
}
}
}
// === AST ===
public enum VarType { Int8=1, Int16=2, Int32=3, Byte=4, Bool=5 }
public abstract class StNode{}
public class ProgramNode:StNode{ public List<VarDecl> Vars=new(); public List<Stmt> Stmts=new(); }
public class VarDecl:StNode{
required public string Name;
public VarType Type;
public Expr? Init;
}
public abstract class Stmt:StNode{}
public class AssignStmt:Stmt{
required public string Target;
required public Expr Expr;
}
public class IfStmt:Stmt{
required public Expr Cond;
public List<Stmt> ThenStmts=new();
public List<Stmt> ElseStmts=new();
}
public class WhileStmt:Stmt{
required public Expr Cond;
public List<Stmt> Body=new();
}
public class ForStmt:Stmt{
required public string Var;
required public Expr Start;
required public Expr End;
public Expr Step = new IntExpr(1);
public List<Stmt> Body=new();
}
public abstract class Expr:StNode{}
public class IntExpr:Expr{ public int Value; public IntExpr(int v){Value=v;} }
public class VarExpr:Expr{ public string Name; public VarExpr(string n){Name=n;} }
public class BinaryExpr:Expr{ public Expr L; public Expr R; public TokType Op; public BinaryExpr(Expr l,TokType op,Expr r){L=l;Op=op;R=r;} }
// === TOKENIZER ===
public enum TokType {
IDENT, INT, ASSIGN, SEMI, LPAREN, RPAREN,
PLUS, MINUS, MUL, DIV,
LT, GT, LE, GE, EQ, NEQ,
IF, THEN, ELSE, END_IF,
WHILE, DO, END_WHILE,
FOR, TO, BY, END_FOR,
PROGRAM, VAR, END_VAR, END_PROGRAM,
EOF
}
public class Token{
public TokType Type;
public string Text;
public int Line;
public Token(TokType t, string s, int line) { Type=t; Text=s; Line=line; }
}
public class CompileError {
public int Line;
public string Message;
public CompileError(int line, string msg) { Line=line; Message=msg; }
}
public class StLexer {
private readonly string src;
private int i;
private int currentLine = 1;
public List<CompileError> Errors = new();
public StLexer(string s){src=s;}
char Peek()=> i<src.Length?src[i]:'\0';
char Next(){
if (i >= src.Length) return '\0';
char c = src[i++];
if (c == '\n') currentLine++;
return c;
}
void AddError(string msg) => Errors.Add(new CompileError(currentLine, msg));
public Token NextToken() {
while (char.IsWhiteSpace(Peek())) Next();
if (Peek()=='\0') return new Token(TokType.EOF,"", currentLine);
if (char.IsLetter(Peek())||Peek()=='_'){
var sb=new StringBuilder();
int startLine = currentLine;
while (char.IsLetterOrDigit(Peek())||Peek()=='_') sb.Append(Next());
var s=sb.ToString().ToUpperInvariant();
return s switch {
"PROGRAM"=>new Token(TokType.PROGRAM,s,startLine),
"VAR"=>new Token(TokType.VAR,s,startLine),
"END_VAR"=>new Token(TokType.END_VAR,s,startLine),
"END_PROGRAM"=>new Token(TokType.END_PROGRAM,s,startLine),
"IF"=>new Token(TokType.IF,s,startLine),
"THEN"=>new Token(TokType.THEN,s,startLine),
"ELSE"=>new Token(TokType.ELSE,s,startLine),
"END_IF"=>new Token(TokType.END_IF,s,startLine),
"WHILE"=>new Token(TokType.WHILE,s,startLine),
"DO"=>new Token(TokType.DO,s,startLine),
"END_WHILE"=>new Token(TokType.END_WHILE,s,startLine),
"FOR"=>new Token(TokType.FOR,s,startLine),
"TO"=>new Token(TokType.TO,s,startLine),
"BY"=>new Token(TokType.BY,s,startLine),
"END_FOR"=>new Token(TokType.END_FOR,s,startLine),
_=>new Token(TokType.IDENT,s,startLine)
};
}
if (char.IsDigit(Peek())) {
var sb=new StringBuilder();
int startLine = currentLine;
while(char.IsDigit(Peek())) sb.Append(Next());
return new Token(TokType.INT,sb.ToString(),startLine);
}
int tokenLine = currentLine;
if (Peek()==':'){
Next();
if(Peek()=='='){
Next();
return new Token(TokType.ASSIGN,":=",tokenLine);
}
AddError("Expected '=' after ':' for assignment");
// Bei einem einzelnen ':' geben wir EOF zurück und stoppen das Parsen
i--; // Gehen einen Schritt zurück, damit der fehlerhafte ':' Token beim nächsten Mal neu gelesen wird
return new Token(TokType.EOF,"",tokenLine);
}
if (Peek()=='<'){
Next();
if (Peek()=='='){Next(); return new Token(TokType.LE,"<=",tokenLine);}
if (Peek()=='>'){Next(); return new Token(TokType.NEQ,"<>",tokenLine);}
return new Token(TokType.LT,"<",tokenLine);
}
if (Peek()=='>'){
Next();
if (Peek()=='='){Next(); return new Token(TokType.GE,">=",tokenLine);}
return new Token(TokType.GT,">",tokenLine);
}
if (Peek()=='='){Next();return new Token(TokType.EQ,"=",tokenLine);}
char c=Next();
if (c == ';') return new Token(TokType.SEMI,";",tokenLine);
if (c == '(') return new Token(TokType.LPAREN,"(",tokenLine);
if (c == ')') return new Token(TokType.RPAREN,")",tokenLine);
if (c == '+') return new Token(TokType.PLUS,"+",tokenLine);
if (c == '-') return new Token(TokType.MINUS,"-",tokenLine);
if (c == '*') return new Token(TokType.MUL,"*",tokenLine);
if (c == '/') return new Token(TokType.DIV,"/",tokenLine);
AddError($"Unexpected character '{c}'");
return new Token(TokType.EOF,"",tokenLine); // Skip invalid character
}
}
// === PARSER ===
public class StParser {
StLexer lex;
Token cur;
public List<CompileError> Errors => lex.Errors;
public bool HasErrors => Errors.Count > 0;
public StParser(string s){
lex=new StLexer(s);
cur=lex.NextToken();
}
void Next()=>cur=lex.NextToken();
void AddError(string msg) => lex.Errors.Add(new CompileError(cur.Line, msg));
bool Expect(TokType t){
if(cur.Type!=t) {
AddError($"Expected {t}, got {cur.Type}");
return false;
}
Next();
return true;
}
public ProgramNode? ParseProgram(){
var p=new ProgramNode();
if (!Expect(TokType.PROGRAM)) return null;
if(cur.Type==TokType.IDENT) Next();
if(cur.Type==TokType.VAR){
Next();
while(cur.Type==TokType.IDENT) {
var varDecl = ParseVarDecl();
if (varDecl == null) return null;
p.Vars.Add(varDecl);
}
if (!Expect(TokType.END_VAR)) return null;
}
while(cur.Type!=TokType.END_PROGRAM && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
p.Stmts.Add(stmt);
}
if (!Expect(TokType.END_PROGRAM)) return null;
return p;
}
VarDecl? ParseVarDecl(){
if (cur.Type != TokType.IDENT) {
AddError("Expected identifier for variable declaration");
return null;
}
string name=cur.Text;
Next();
if (cur.Type != TokType.IDENT) {
AddError("Expected type name");
return null;
}
string tname=cur.Text.ToLowerInvariant();
Next();
VarType? vt = tname switch {
"int8" => VarType.Int8,
"int16" => VarType.Int16,
"int32" => VarType.Int32,
"byte" => VarType.Byte,
"bool" => VarType.Bool,
_ => null
};
if (vt == null) {
AddError($"Unknown type '{tname}'");
return null;
}
Expr? init=null;
if(cur.Type==TokType.ASSIGN){
Next(); // consume :=
init=ParseExpr();
if (init == null) {
AddError($"Expected expression after ':=' in variable declaration");
return null;
}
}
if (!Expect(TokType.SEMI)) return null;
return new VarDecl{Name=name,Type=vt.Value,Init=init};
}
Stmt? ParseAssign() {
// Der Aufrufer hat bereits geprüft, dass wir bei einem IDENT sind
string target = cur.Text;
Next(); // consume identifier
if (cur.Type != TokType.ASSIGN) {
AddError($"Expected ':=' after identifier '{target}'");
return null;
}
Next(); // consume :=
var e = ParseExpr();
if (e == null) return null;
if (!Expect(TokType.SEMI)) return null;
return new AssignStmt{Target=target, Expr=e};
}
Stmt? ParseStmt(){
switch(cur.Type) {
case TokType.IF: return ParseIf();
case TokType.WHILE: return ParseWhile();
case TokType.FOR: return ParseFor();
case TokType.IDENT:
return ParseAssign();
default:
AddError($"Unexpected token {cur.Type} in statement");
return null;
}
}
IfStmt? ParseIf(){
Next(); // IF
var cond=ParseExpr();
if (cond == null) return null;
if (!Expect(TokType.THEN)) return null;
var node=new IfStmt{Cond=cond};
while(cur.Type!=TokType.ELSE && cur.Type!=TokType.END_IF && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
node.ThenStmts.Add(stmt);
}
if(cur.Type==TokType.ELSE){
Next();
while(cur.Type!=TokType.END_IF && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
node.ElseStmts.Add(stmt);
}
}
if (!Expect(TokType.END_IF)) return null;
if (!Expect(TokType.SEMI)) return null;
return node;
}
WhileStmt? ParseWhile(){
Next(); // WHILE
var cond=ParseExpr();
if (cond == null) return null;
if (!Expect(TokType.DO)) return null;
var ws=new WhileStmt{Cond=cond};
while(cur.Type!=TokType.END_WHILE && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
ws.Body.Add(stmt);
}
if (!Expect(TokType.END_WHILE)) return null;
if (!Expect(TokType.SEMI)) return null;
return ws;
}
ForStmt? ParseFor(){
Next(); // FOR
if (cur.Type != TokType.IDENT) {
AddError("Expected identifier for FOR loop variable");
return null;
}
string varName = cur.Text;
Next(); // consume identifier
if (cur.Type != TokType.ASSIGN) {
AddError($"Expected ':=' after identifier '{varName}'");
return null;
}
Next(); // consume :=
var start = ParseExpr();
if (start == null) return null;
if (!Expect(TokType.TO)) return null;
var end = ParseExpr();
if (end == null) return null;
Expr step = new IntExpr(1);
if(cur.Type==TokType.BY){
Next();
step = ParseExpr() ?? step;
}
if (!Expect(TokType.DO)) return null;
var fs = new ForStmt{Var=varName, Start=start, End=end, Step=step};
while(cur.Type!=TokType.END_FOR && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
fs.Body.Add(stmt);
}
if (!Expect(TokType.END_FOR)) return null;
if (!Expect(TokType.SEMI)) return null;
return fs;
}
Expr? ParseExpr() => ParseCompare();
Expr? ParseCompare(){
var l = ParseAddSub();
if (l == null) return null;
while(cur.Type is TokType.LT or TokType.GT or TokType.LE or TokType.GE or TokType.EQ or TokType.NEQ){
var op=cur.Type;
Next();
var r=ParseAddSub();
if (r == null) return null;
l=new BinaryExpr(l,op,r);
}
return l;
}
Expr? ParseAddSub(){
var l=ParseMulDiv();
if (l == null) return null;
while(cur.Type==TokType.PLUS||cur.Type==TokType.MINUS){
var op=cur.Type;
Next();
var r=ParseMulDiv();
if (r == null) return null;
l=new BinaryExpr(l,op,r);
}
return l;
}
Expr? ParseMulDiv(){
var l=ParsePrimary();
if (l == null) return null;
while(cur.Type==TokType.MUL||cur.Type==TokType.DIV){
var op=cur.Type;
Next();
var r=ParsePrimary();
if (r == null) return null;
l=new BinaryExpr(l,op,r);
}
return l;
}
Expr? ParsePrimary(){
int startLine = cur.Line;
switch(cur.Type) {
case TokType.INT:
if (!int.TryParse(cur.Text, out var v)) {
AddError($"Invalid integer literal '{cur.Text}'");
return null;
}
Next();
return new IntExpr(v);
case TokType.IDENT:
string n=cur.Text;
Next();
return new VarExpr(n);
case TokType.LPAREN:
Next();
var e=ParseExpr();
if (e == null) return null;
if (!Expect(TokType.RPAREN)) return null;
return e;
default:
AddError($"Unexpected token {cur.Type} in expression");
return null;
}
}
}
// === BYTECODE ===
public class BytecodeEmitter {
List<int> consts=new(); Dictionary<string,Symbol> syms=new(); List<byte> code=new();
class Symbol {
required public string Name;
public VarType Type;
public int Index;
}
public int ConstantsCount=>consts.Count; public int VarCount=>syms.Count; public int CodeLength=>code.Count;
public void Compile(ProgramNode p){
int idx=0;
foreach(var v in p.Vars)
syms[v.Name]=new Symbol{Name=v.Name,Type=v.Type,Index=idx++};
foreach(var v in p.Vars)
if(v.Init!=null){EmitExpr(v.Init);EmitByte(0x03);EmitU16((ushort)syms[v.Name].Index);}
foreach(var s in p.Stmts)
EmitStmt(s);
EmitByte(0xF0); // Program End
}
Dictionary<TokType, byte> opCodes = new() {
{TokType.PLUS, 0x10}, {TokType.MINUS, 0x11}, {TokType.MUL, 0x12}, {TokType.DIV, 0x13},
{TokType.LT, 0x14}, {TokType.GT, 0x15}, {TokType.LE, 0x16}, {TokType.GE, 0x17},
{TokType.EQ, 0x18}, {TokType.NEQ, 0x19}
};
void EmitStmt(Stmt s){
switch(s){
case AssignStmt a:
Symbol? symbol = null;
if (!syms.TryGetValue(a.Target, out symbol)) {
throw new Exception($"Undeclared variable '{a.Target}'");
}
EmitExpr(a.Expr);
EmitByte(0x03); EmitU16((ushort)symbol.Index);
break;
case IfStmt iff:
EmitExpr(iff.Cond);
EmitByte(0x20); int jz=code.Count; EmitU16(0);
foreach(var st in iff.ThenStmts) EmitStmt(st);
if(iff.ElseStmts.Count>0){
EmitByte(0x21); int jmp=code.Count; EmitU16(0);
PatchJump(jz,code.Count);
foreach(var st in iff.ElseStmts) EmitStmt(st);
PatchJump(jmp,code.Count);
} else PatchJump(jz,code.Count);
break;
case WhileStmt w:
int loopStart=code.Count;
EmitExpr(w.Cond);
EmitByte(0x20); int jzpos=code.Count; EmitU16(0);
foreach(var st in w.Body) EmitStmt(st);
EmitByte(0x21); EmitU16((ushort)loopStart);
PatchJump(jzpos,code.Count);
break;
case ForStmt f:
Symbol? forSymbol = null;
if (!syms.TryGetValue(f.Var, out forSymbol)) {
throw new Exception($"Undeclared variable '{f.Var}'");
}
// Initialisierung: var := start
EmitExpr(f.Start);
EmitByte(0x03); EmitU16((ushort)forSymbol.Index);
int cmpPos = code.Count; // Position des Vergleichs
EmitExpr(new VarExpr(f.Var));
EmitExpr(f.End);
EmitByte(0x16); // LE
EmitByte(0x20); int jzFor = code.Count; EmitU16(0); // Jump zum Ende
// Body
foreach(var st in f.Body) EmitStmt(st);
// Inkrement: i := i + step
EmitExpr(new VarExpr(f.Var));
EmitExpr(f.Step);
EmitByte(0x10); // PLUS
EmitByte(0x03); EmitU16((ushort)syms[f.Var].Index);
// Vergleich erneut, aber wir merken uns den Sprung zum Vergleich
EmitByte(0x21); EmitU16((ushort)cmpPos);
// Patch Jump: Ende der Schleife springt hierhin
PatchJump(jzFor, code.Count);
// Korrektur: Schleifenvariable auf Endwert, falls sie überläuft
EmitExpr(f.End);
EmitByte(0x03); EmitU16((ushort)syms[f.Var].Index);
break;
}
}
void PatchJump(int pos,int target){code[pos]=(byte)(target&0xFF);code[pos+1]=(byte)(target>>8);}
void EmitExpr(Expr e){
switch(e){
case IntExpr ie:
int ci=AddConst(ie.Value);EmitByte(0x01);EmitU16((ushort)ci);break;
case VarExpr ve:
Symbol? symbol = null;
if (!syms.TryGetValue(ve.Name, out symbol)) {
throw new Exception($"Undeclared variable '{ve.Name}'");
}
EmitByte(0x02);EmitU16((ushort)symbol.Index);break;
case BinaryExpr be:
if (!opCodes.ContainsKey(be.Op)) {
throw new Exception($"Unknown operator '{be.Op}'");
}
EmitExpr(be.L);EmitExpr(be.R);
EmitByte(opCodes[be.Op]);
break;
}
}
int AddConst(int v){int i=consts.IndexOf(v);if(i>=0)return i;consts.Add(v);return consts.Count-1;}
void EmitByte(byte b)=>code.Add(b);
void EmitU16(ushort v){code.Add((byte)(v&0xFF));code.Add((byte)(v>>8));}
public byte[] BuildBinary(){
using var ms=new MemoryStream();var w=new BinaryWriter(ms);
w.Write(Encoding.ASCII.GetBytes("STBC"));
w.Write((ushort)1);
w.Write((ushort)consts.Count);foreach(var c in consts)w.Write(c);
w.Write((ushort)syms.Count);
var types=new byte[syms.Count];foreach(var kv in syms)types[kv.Value.Index]=(byte)kv.Value.Type;
foreach(var b in types)w.Write(b);
w.Write((ushort)code.Count);w.Write(code.ToArray());
return ms.ToArray();
}
}

View File

@ -0,0 +1,91 @@
namespace STCompiler.Common;
using System.Collections.Generic;
public static class Bytecode {
public const string Magic = "STBC";
public const ushort Version = 2;
public const ushort DefaultCycleTime = 100; // in milliseconds
public static class OpCodes {
public const byte NOP = 0x00;
public const byte PUSH_CONST = 0x01; // integer/long
public const byte PUSH_REAL_CONST = 0x02; // float/double
public const byte LOAD_VAR = 0x03;
public const byte STORE_VAR = 0x04;
// Signed integer arithmetic (SINT, INT, DINT, LINT)
public const byte ADD_SINT = 0x10; public const byte SUB_SINT = 0x11; public const byte MUL_SINT = 0x12; public const byte DIV_SINT = 0x13;
public const byte ADD_INT = 0x14; public const byte SUB_INT = 0x15; public const byte MUL_INT = 0x16; public const byte DIV_INT = 0x17;
public const byte ADD_DINT = 0x18; public const byte SUB_DINT = 0x19; public const byte MUL_DINT = 0x1A; public const byte DIV_DINT = 0x1B;
public const byte ADD_LINT = 0x1C; public const byte SUB_LINT = 0x1D; public const byte MUL_LINT = 0x1E; public const byte DIV_LINT = 0x1F;
// Unsigned integer arithmetic (USINT, UINT, UDINT, ULINT) - moved to avoid conflicts
public const byte ADD_USINT = 0x22; public const byte SUB_USINT = 0x23; public const byte MUL_USINT = 0x24; public const byte DIV_USINT = 0x25;
public const byte ADD_UINT = 0x26; public const byte SUB_UINT = 0x27; public const byte MUL_UINT = 0x28; public const byte DIV_UINT = 0x29;
public const byte ADD_UDINT = 0x2A; public const byte SUB_UDINT = 0x2B; public const byte MUL_UDINT = 0x2C; public const byte DIV_UDINT = 0x2D;
public const byte ADD_ULINT = 0x2E; public const byte SUB_ULINT = 0x2F; public const byte MUL_ULINT = 0x30; public const byte DIV_ULINT = 0x31;
// Floating point arithmetic
public const byte ADD_REAL = 0x40; public const byte SUB_REAL = 0x41; public const byte MUL_REAL = 0x42; public const byte DIV_REAL = 0x43;
public const byte ADD_LREAL = 0x44; public const byte SUB_LREAL = 0x45; public const byte MUL_LREAL = 0x46; public const byte DIV_LREAL = 0x47;
// Signed integer comparisons
public const byte LT_SINT = 0x50; public const byte GT_SINT = 0x51; public const byte LE_SINT = 0x52; public const byte GE_SINT = 0x53; public const byte EQ_SINT = 0x54; public const byte NEQ_SINT = 0x55;
public const byte LT_INT = 0x56; public const byte GT_INT = 0x57; public const byte LE_INT = 0x58; public const byte GE_INT = 0x59; public const byte EQ_INT = 0x5A; public const byte NEQ_INT = 0x5B;
public const byte LT_DINT = 0x5C; public const byte GT_DINT = 0x5D; public const byte LE_DINT = 0x5E; public const byte GE_DINT = 0x5F; public const byte EQ_DINT = 0x60; public const byte NEQ_DINT = 0x61;
public const byte LT_LINT = 0x62; public const byte GT_LINT = 0x63; public const byte LE_LINT = 0x64; public const byte GE_LINT = 0x65; public const byte EQ_LINT = 0x66; public const byte NEQ_LINT = 0x67;
// Unsigned integer comparisons
public const byte LT_USINT = 0x70; public const byte GT_USINT = 0x71; public const byte LE_USINT = 0x72; public const byte GE_USINT = 0x73; public const byte EQ_USINT = 0x74; public const byte NEQ_USINT = 0x75;
public const byte LT_UINT = 0x76; public const byte GT_UINT = 0x77; public const byte LE_UINT = 0x78; public const byte GE_UINT = 0x79; public const byte EQ_UINT = 0x7A; public const byte NEQ_UINT = 0x7B;
public const byte LT_UDINT = 0x7C; public const byte GT_UDINT = 0x7D; public const byte LE_UDINT = 0x7E; public const byte GE_UDINT = 0x7F; public const byte EQ_UDINT = 0x80; public const byte NEQ_UDINT = 0x81;
public const byte LT_ULINT = 0x82; public const byte GT_ULINT = 0x83; public const byte LE_ULINT = 0x84; public const byte GE_ULINT = 0x85; public const byte EQ_ULINT = 0x86; public const byte NEQ_ULINT = 0x87;
// Floating point comparisons
public const byte LT_REAL = 0x90; public const byte GT_REAL = 0x91; public const byte LE_REAL = 0x92; public const byte GE_REAL = 0x93; public const byte EQ_REAL = 0x94; public const byte NEQ_REAL = 0x95;
public const byte LT_LREAL = 0x96; public const byte GT_LREAL = 0x97; public const byte LE_LREAL = 0x98; public const byte GE_LREAL = 0x99; public const byte EQ_LREAL = 0x9A; public const byte NEQ_LREAL = 0x9B;
// Control flow
public const byte JZ = 0xA0;
public const byte JMP = 0xA1;
// Array operations
public const byte ARRAY_BOUNDS_CHECK = 0xE0;
public const byte HALT = 0xF0;
}
static readonly Dictionary<byte, string> names = new() {
{ OpCodes.NOP, "NOP" },
{ OpCodes.PUSH_CONST, "PUSH_CONST" },
{ OpCodes.PUSH_REAL_CONST, "PUSH_REAL_CONST" },
{ OpCodes.LOAD_VAR, "LOAD_VAR" },
{ OpCodes.STORE_VAR, "STORE_VAR" },
{ OpCodes.ADD_SINT, "ADD_SINT" }, { OpCodes.SUB_SINT, "SUB_SINT" }, { OpCodes.MUL_SINT, "MUL_SINT" }, { OpCodes.DIV_SINT, "DIV_SINT" },
{ OpCodes.ADD_INT, "ADD_INT" }, { OpCodes.SUB_INT, "SUB_INT" }, { OpCodes.MUL_INT, "MUL_INT" }, { OpCodes.DIV_INT, "DIV_INT" },
{ OpCodes.ADD_DINT, "ADD_DINT" }, { OpCodes.SUB_DINT, "SUB_DINT" }, { OpCodes.MUL_DINT, "MUL_DINT" }, { OpCodes.DIV_DINT, "DIV_DINT" },
{ OpCodes.ADD_LINT, "ADD_LINT" }, { OpCodes.SUB_LINT, "SUB_LINT" }, { OpCodes.MUL_LINT, "MUL_LINT" }, { OpCodes.DIV_LINT, "DIV_LINT" },
{ OpCodes.ADD_USINT, "ADD_USINT" }, { OpCodes.SUB_USINT, "SUB_USINT" }, { OpCodes.MUL_USINT, "MUL_USINT" }, { OpCodes.DIV_USINT, "DIV_USINT" },
{ OpCodes.ADD_UINT, "ADD_UINT" }, { OpCodes.SUB_UINT, "SUB_UINT" }, { OpCodes.MUL_UINT, "MUL_UINT" }, { OpCodes.DIV_UINT, "DIV_UINT" },
{ OpCodes.ADD_UDINT, "ADD_UDINT" }, { OpCodes.SUB_UDINT, "SUB_UDINT" }, { OpCodes.MUL_UDINT, "MUL_UDINT" }, { OpCodes.DIV_UDINT, "DIV_UDINT" },
{ OpCodes.ADD_ULINT, "ADD_ULINT" }, { OpCodes.SUB_ULINT, "SUB_ULINT" }, { OpCodes.MUL_ULINT, "MUL_ULINT" }, { OpCodes.DIV_ULINT, "DIV_ULINT" },
{ OpCodes.ADD_REAL, "ADD_REAL" }, { OpCodes.SUB_REAL, "SUB_REAL" }, { OpCodes.MUL_REAL, "MUL_REAL" }, { OpCodes.DIV_REAL, "DIV_REAL" },
{ OpCodes.ADD_LREAL, "ADD_LREAL" }, { OpCodes.SUB_LREAL, "SUB_LREAL" }, { OpCodes.MUL_LREAL, "MUL_LREAL" }, { OpCodes.DIV_LREAL, "DIV_LREAL" },
{ OpCodes.LT_SINT, "LT_SINT" }, { OpCodes.GT_SINT, "GT_SINT" }, { OpCodes.LE_SINT, "LE_SINT" }, { OpCodes.GE_SINT, "GE_SINT" }, { OpCodes.EQ_SINT, "EQ_SINT" }, { OpCodes.NEQ_SINT, "NEQ_SINT" },
{ OpCodes.LT_INT, "LT_INT" }, { OpCodes.GT_INT, "GT_INT" }, { OpCodes.LE_INT, "LE_INT" }, { OpCodes.GE_INT, "GE_INT" }, { OpCodes.EQ_INT, "EQ_INT" }, { OpCodes.NEQ_INT, "NEQ_INT" },
{ OpCodes.LT_DINT, "LT_DINT" }, { OpCodes.GT_DINT, "GT_DINT" }, { OpCodes.LE_DINT, "LE_DINT" }, { OpCodes.GE_DINT, "GE_DINT" }, { OpCodes.EQ_DINT, "EQ_DINT" }, { OpCodes.NEQ_DINT, "NEQ_DINT" },
{ OpCodes.LT_LINT, "LT_LINT" }, { OpCodes.GT_LINT, "GT_LINT" }, { OpCodes.LE_LINT, "LE_LINT" }, { OpCodes.GE_LINT, "GE_LINT" }, { OpCodes.EQ_LINT, "EQ_LINT" }, { OpCodes.NEQ_LINT, "NEQ_LINT" },
{ OpCodes.LT_USINT, "LT_USINT" }, { OpCodes.GT_USINT, "GT_USINT" }, { OpCodes.LE_USINT, "LE_USINT" }, { OpCodes.GE_USINT, "GE_USINT" }, { OpCodes.EQ_USINT, "EQ_USINT" }, { OpCodes.NEQ_USINT, "NEQ_USINT" },
{ OpCodes.LT_UINT, "LT_UINT" }, { OpCodes.GT_UINT, "GT_UINT" }, { OpCodes.LE_UINT, "LE_UINT" }, { OpCodes.GE_UINT, "GE_UINT" }, { OpCodes.EQ_UINT, "EQ_UINT" }, { OpCodes.NEQ_UINT, "NEQ_UINT" },
{ OpCodes.LT_UDINT, "LT_UDINT" }, { OpCodes.GT_UDINT, "GT_UDINT" }, { OpCodes.LE_UDINT, "LE_UDINT" }, { OpCodes.GE_UDINT, "GE_UDINT" }, { OpCodes.EQ_UDINT, "EQ_UDINT" }, { OpCodes.NEQ_UDINT, "NEQ_UDINT" },
{ OpCodes.LT_ULINT, "LT_ULINT" }, { OpCodes.GT_ULINT, "GT_ULINT" }, { OpCodes.LE_ULINT, "LE_ULINT" }, { OpCodes.GE_ULINT, "GE_ULINT" }, { OpCodes.EQ_ULINT, "EQ_ULINT" }, { OpCodes.NEQ_ULINT, "NEQ_ULINT" },
{ OpCodes.LT_REAL, "LT_REAL" }, { OpCodes.GT_REAL, "GT_REAL" }, { OpCodes.LE_REAL, "LE_REAL" }, { OpCodes.GE_REAL, "GE_REAL" }, { OpCodes.EQ_REAL, "EQ_REAL" }, { OpCodes.NEQ_REAL, "NEQ_REAL" },
{ OpCodes.LT_LREAL, "LT_LREAL" }, { OpCodes.GT_LREAL, "GT_LREAL" }, { OpCodes.LE_LREAL, "LE_LREAL" }, { OpCodes.GE_LREAL, "GE_LREAL" }, { OpCodes.EQ_LREAL, "EQ_LREAL" }, { OpCodes.NEQ_LREAL, "NEQ_LREAL" },
{ OpCodes.JZ, "JZ" }, { OpCodes.JMP, "JMP" },
{ OpCodes.ARRAY_BOUNDS_CHECK, "ARRAY_BOUNDS_CHECK" },
{ OpCodes.HALT, "HALT" }
};
public static string OpName(byte op) => names.TryGetValue(op, out var s) ? s : ($"0x{op:X2}");
}

View File

@ -0,0 +1,6 @@
namespace STCompiler.Common;
public class Class1
{
}

View File

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>

View File

@ -0,0 +1,41 @@
namespace STCompiler.Common;
// Gemeinsame Variable types für Compiler, Disassembler und Simulator
public enum VarType {
// Boolean
BOOL = 1,
// Unsigned integers
BYTE = 2, WORD = 3, DWORD = 4, LWORD = 5,
// Signed integers
SINT = 6, INT = 7, DINT = 8, LINT = 9,
// Unsigned integers (alternative names)
USINT = 10, UINT = 11, UDINT = 12, ULINT = 13,
// Floating point
REAL = 14, LREAL = 15,
// Date/time types
TIME = 16, // Zeitdauer -> TimeSpan
DATE = 17, // Datum -> DateTime
TIME_OF_DAY = 18, // Tageszeit (TOD) -> TimeSpan / DateTime
TOD = TIME_OF_DAY,
DATE_AND_TIME = 19,// Datum + Uhrzeit (DT) -> DateTime
DT = DATE_AND_TIME,
// Array marker - actual array types use ArrayType class
ARRAY = 20
}
// Represents an array type in structured text
public class ArrayType {
public VarType ElementType { get; set; } // Type of array elements
public int Start { get; set; } // Start index
public int End { get; set; } // End index
public int Length => End - Start + 1; // Number of elements
public ArrayType(VarType elementType, int start, int end) {
ElementType = elementType;
Start = start;
End = end;
if (end < start) throw new System.ArgumentException("Array end index must be greater than or equal to start index");
}
public override string ToString() => $"ARRAY [{Start}..{End}] OF {ElementType}";
}

View File

@ -6,15 +6,15 @@
"compilationOptions": {}, "compilationOptions": {},
"targets": { "targets": {
".NETCoreApp,Version=v8.0": { ".NETCoreApp,Version=v8.0": {
"Compiler/1.0.0": { "STCompiler.Common/1.0.0": {
"runtime": { "runtime": {
"Compiler.dll": {} "STCompiler.Common.dll": {}
} }
} }
} }
}, },
"libraries": { "libraries": {
"Compiler/1.0.0": { "STCompiler.Common/1.0.0": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""

View File

@ -10,12 +10,12 @@
using System; using System;
using System.Reflection; using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Compiler")] [assembly: System.Reflection.AssemblyCompanyAttribute("STCompiler.Common")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+dbd7715193158898901ea48bb685edce0d11c047")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5a002806a9ee3a7389c5a14069c534271a20a9ac")]
[assembly: System.Reflection.AssemblyProductAttribute("Compiler")] [assembly: System.Reflection.AssemblyProductAttribute("STCompiler.Common")]
[assembly: System.Reflection.AssemblyTitleAttribute("Compiler")] [assembly: System.Reflection.AssemblyTitleAttribute("STCompiler.Common")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Von der MSBuild WriteCodeFragment-Klasse generiert. // Von der MSBuild WriteCodeFragment-Klasse generiert.

View File

@ -0,0 +1 @@
47a06821b3855b1302f973341d27a55ee26f60ac14ed88a286a0d4a4b2a812d1

View File

@ -7,7 +7,7 @@ build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly = build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules = build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = Compiler build_property.RootNamespace = STCompiler.Common
build_property.ProjectDir = /home/martin/Projekte/c#/Compiler/Compiler/ build_property.ProjectDir = /home/martin/Projects/STCompiler/STCompiler.Common/
build_property.EnableComHosting = build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop = build_property.EnableGeneratedComInterfaceComImportInterop =

View File

@ -0,0 +1 @@
1c21a086cd38f15730e5b6283c22981505869f5cda3497f26cde2d0f5d493189

View File

@ -0,0 +1,11 @@
/home/martin/Projects/STCompiler/STCompiler.Common/bin/Debug/net8.0/STCompiler.Common.deps.json
/home/martin/Projects/STCompiler/STCompiler.Common/bin/Debug/net8.0/STCompiler.Common.dll
/home/martin/Projects/STCompiler/STCompiler.Common/bin/Debug/net8.0/STCompiler.Common.pdb
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/STCompiler.Common.GeneratedMSBuildEditorConfig.editorconfig
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/STCompiler.Common.AssemblyInfoInputs.cache
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/STCompiler.Common.AssemblyInfo.cs
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/STCompiler.Common.csproj.CoreCompileInputs.cache
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/STCompiler.Common.dll
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/refint/STCompiler.Common.dll
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/STCompiler.Common.pdb
/home/martin/Projects/STCompiler/STCompiler.Common/obj/Debug/net8.0/ref/STCompiler.Common.dll

View File

@ -1,17 +1,17 @@
{ {
"format": 1, "format": 1,
"restore": { "restore": {
"/home/martin/Projekte/c#/Compiler/Compiler/Compiler.csproj": {} "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {}
}, },
"projects": { "projects": {
"/home/martin/Projekte/c#/Compiler/Compiler/Compiler.csproj": { "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"version": "1.0.0", "version": "1.0.0",
"restore": { "restore": {
"projectUniqueName": "/home/martin/Projekte/c#/Compiler/Compiler/Compiler.csproj", "projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"projectName": "Compiler", "projectName": "STCompiler.Common",
"projectPath": "/home/martin/Projekte/c#/Compiler/Compiler/Compiler.csproj", "projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"packagesPath": "/home/martin/.nuget/packages/", "packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projekte/c#/Compiler/Compiler/obj/", "outputPath": "/home/martin/Projects/STCompiler/STCompiler.Common/obj/",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"configFilePaths": [ "configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config" "/home/martin/.nuget/NuGet/NuGet.Config"

View File

@ -13,11 +13,11 @@
"project": { "project": {
"version": "1.0.0", "version": "1.0.0",
"restore": { "restore": {
"projectUniqueName": "/home/martin/Projekte/c#/Compiler/Compiler/Compiler.csproj", "projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"projectName": "Compiler", "projectName": "STCompiler.Common",
"projectPath": "/home/martin/Projekte/c#/Compiler/Compiler/Compiler.csproj", "projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"packagesPath": "/home/martin/.nuget/packages/", "packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projekte/c#/Compiler/Compiler/obj/", "outputPath": "/home/martin/Projects/STCompiler/STCompiler.Common/obj/",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"configFilePaths": [ "configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config" "/home/martin/.nuget/NuGet/NuGet.Config"

View File

@ -0,0 +1,10 @@
{
"version": 2,
"dgSpecHash": "jt2HMehc3BneQxy6gmHmXdh6VZS/3dtjkNjH2GR4yNPSBFcEqtHnBMxfL4LgGBL/hoFiIYvAdg6xhIGGboFt/w==",
"success": true,
"projectFilePath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"expectedPackageFiles": [
"/home/martin/.nuget/packages/microsoft.aspnetcore.app.ref/8.0.20/microsoft.aspnetcore.app.ref.8.0.20.nupkg.sha512"
],
"logs": []
}

View File

@ -0,0 +1,94 @@
namespace STCompiler.Compiler;
using System.Collections.Generic;
using STCompiler.Common;
public abstract class StNode {}
public class ProgramNode:StNode{ public List<VarDecl> Vars=new(); public List<Stmt> Stmts=new(); }
public class VarDecl:StNode{
required public string Name;
public VarType Type;
public Expr? Init;
}
public abstract class Stmt:StNode{}
public class AssignStmt:Stmt{
required public string Target; // Variable name
public Expr? Index; // Array index if this is an array assignment
required public Expr Expr; // Value to assign
}
public class IfStmt:Stmt{
required public Expr Cond;
public List<Stmt> ThenStmts=new();
public List<Stmt> ElseStmts=new();
}
public class WhileStmt:Stmt{
required public Expr Cond;
public List<Stmt> Body=new();
}
public class ForStmt:Stmt{
required public string Var;
required public Expr Start;
required public Expr End;
public Expr Step = new IntExpr(1);
public List<Stmt> Body=new();
}
public abstract class Expr:StNode {
public VarType Type; // Speichert den Typ des Ausdrucks
}
public class IntExpr:Expr {
public long Value;
public IntExpr(long v, VarType type = VarType.DINT) {
Value = v;
Type = type;
}
}
public class RealExpr:Expr {
public double Value;
public RealExpr(double v, VarType type = VarType.REAL) {
Value = v;
Type = type;
}
}
public class VarExpr:Expr {
public string Name;
public VarExpr(string n, VarType type) {
Name = n;
Type = type;
}
}
public class BinaryExpr:Expr {
public Expr L;
public Expr R;
public TokType Op;
public BinaryExpr(Expr l, TokType op, Expr r) {
L = l;
Op = op;
R = r;
Type = DetermineResultType(l.Type, r.Type);
}
private static VarType DetermineResultType(VarType left, VarType right) {
// Wenn einer der Operanden LREAL ist, ist das Ergebnis LREAL
if (left == VarType.LREAL || right == VarType.LREAL)
return VarType.LREAL;
// Wenn einer der Operanden REAL ist, ist das Ergebnis REAL
if (left == VarType.REAL || right == VarType.REAL)
return VarType.REAL;
// Bei gemischten Integer-Typen nehmen wir den größeren
if ((int)left > (int)right)
return left;
return right;
}
}
public class ArrayAccessExpr : Expr {
public string ArrayName;
public Expr Index;
public ArrayAccessExpr(string name, Expr idx, VarType elementType) {
ArrayName = name;
Index = idx;
Type = elementType;
}
}

View File

@ -0,0 +1,182 @@
namespace STCompiler.Compiler;
using System;
using System.Text;
using System.Collections.Generic;
using STCompiler.Common;
public enum TokType {
IDENT, INT, REAL, ASSIGN, SEMI, LPAREN, RPAREN,
PLUS, MINUS, MUL, DIV,
LT, GT, LE, GE, EQ, NEQ,
IF, THEN, ELSE, END_IF,
WHILE, DO, END_WHILE,
FOR, TO, BY, END_FOR,
PROGRAM, VAR, END_VAR, END_PROGRAM,
ARRAY, OF, LBRACKET, RBRACKET, DOTS,
EOF
}
public class Token{
public TokType Type;
public string Text;
public int Line;
public Token(TokType t, string s, int line) { Type=t; Text=s; Line=line; }
}
public class CompileError {
public int Line;
public string Message;
public CompileError(int line, string msg) { Line=line; Message=msg; }
}
public class StLexer {
private readonly string src;
private int i;
private int currentLine = 1;
public List<CompileError> Errors = new();
public StLexer(string s){src=s;}
char Peek()=> i<src.Length?src[i]:'\0';
char Peek2()=> i+1<src.Length?src[i+1]:'\0';
char Next(){
if (i >= src.Length) return '\0';
char c = src[i++];
if (c == '\n') currentLine++;
return c;
}
void AddError(string msg) => Errors.Add(new CompileError(currentLine, msg));
public Token NextToken() {
while (char.IsWhiteSpace(Peek())) Next();
if (Peek()=='\0') return new Token(TokType.EOF,"", currentLine);
// Skip line comments starting with '//'
if (Peek() == '/' && Peek2() == '/') {
// consume '//'
Next(); Next();
// skip until end of line or EOF
while (Peek() != '\0' && Peek() != '\n') Next();
// consume newline if present
if (Peek() == '\n') Next();
// restart tokenization after the comment
return NextToken();
}
if (char.IsLetter(Peek())||Peek()=='_'){
var sb=new StringBuilder();
int startLine = currentLine;
while (char.IsLetterOrDigit(Peek())||Peek()=='_') sb.Append(Next());
var s=sb.ToString().ToUpperInvariant();
return s switch {
"PROGRAM"=>new Token(TokType.PROGRAM,s,startLine),
"VAR"=>new Token(TokType.VAR,s,startLine),
"END_VAR"=>new Token(TokType.END_VAR,s,startLine),
"END_PROGRAM"=>new Token(TokType.END_PROGRAM,s,startLine),
"IF"=>new Token(TokType.IF,s,startLine),
"THEN"=>new Token(TokType.THEN,s,startLine),
"ELSE"=>new Token(TokType.ELSE,s,startLine),
"END_IF"=>new Token(TokType.END_IF,s,startLine),
"WHILE"=>new Token(TokType.WHILE,s,startLine),
"DO"=>new Token(TokType.DO,s,startLine),
"END_WHILE"=>new Token(TokType.END_WHILE,s,startLine),
"FOR"=>new Token(TokType.FOR,s,startLine),
"TO"=>new Token(TokType.TO,s,startLine),
"BY"=>new Token(TokType.BY,s,startLine),
"END_FOR"=>new Token(TokType.END_FOR,s,startLine),
"ARRAY"=>new Token(TokType.ARRAY,s,startLine),
"OF"=>new Token(TokType.OF,s,startLine),
_=>new Token(TokType.IDENT,s,startLine)
};
}
if (char.IsDigit(Peek())) {
var sb = new StringBuilder();
int startLine = currentLine;
bool isFloat = false;
// Ganze Zahl vor dem Dezimalpunkt oder Bereichsoperator
while(char.IsDigit(Peek()))
sb.Append(Next());
// Prüfe auf Bereichsoperator (..) oder Dezimalpunkt (.)
if (Peek() == '.') {
if (Peek2() == '.') {
// Es ist ein Bereichsoperator (..)
// Don't consume the dots, just return the number
return new Token(TokType.INT, sb.ToString(), startLine);
} else {
// Es ist ein Dezimalpunkt
isFloat = true;
sb.Append(Next());
while(char.IsDigit(Peek()))
sb.Append(Next());
}
}
// Optional: Exponentialdarstellung
if (Peek() == 'E' || Peek() == 'e') {
isFloat = true;
sb.Append(Next());
if (Peek() == '+' || Peek() == '-')
sb.Append(Next());
if (!char.IsDigit(Peek())) {
AddError("Expected digits after exponent");
return new Token(TokType.EOF, "", startLine);
}
while(char.IsDigit(Peek()))
sb.Append(Next());
}
return new Token(isFloat ? TokType.REAL : TokType.INT, sb.ToString(), startLine);
}
int tokenLine = currentLine;
if (Peek()==':'){
Next();
if(Peek()=='='){
Next();
return new Token(TokType.ASSIGN,":=",tokenLine);
}
AddError("Expected '=' after ':' for assignment");
// Bei einem einzelnen ':' geben wir EOF zurück und stoppen das Parsen
i--; // Gehen einen Schritt zurück, damit der fehlerhafte ':' Token beim nächsten Mal neu gelesen wird
return new Token(TokType.EOF,"",tokenLine);
}
if (Peek()=='<'){
Next();
if (Peek()=='='){Next(); return new Token(TokType.LE,"<=",tokenLine);}
if (Peek()=='>'){Next(); return new Token(TokType.NEQ,"<>",tokenLine);}
return new Token(TokType.LT,"<",tokenLine);
}
if (Peek()=='>'){
Next();
if (Peek()=='='){Next(); return new Token(TokType.GE,">=",tokenLine);}
return new Token(TokType.GT,">",tokenLine);
}
if (Peek()=='='){Next();return new Token(TokType.EQ,"=",tokenLine);}
char c=Next();
if (c == ';') return new Token(TokType.SEMI,";",tokenLine);
if (c == '(') return new Token(TokType.LPAREN,"(",tokenLine);
if (c == ')') return new Token(TokType.RPAREN,")",tokenLine);
if (c == '[') return new Token(TokType.LBRACKET,"[",tokenLine);
if (c == ']') return new Token(TokType.RBRACKET,"]",tokenLine);
if (c == '+') return new Token(TokType.PLUS,"+",tokenLine);
if (c == '-') return new Token(TokType.MINUS,"-",tokenLine);
if (c == '*') return new Token(TokType.MUL,"*",tokenLine);
if (c == '/') return new Token(TokType.DIV,"/",tokenLine);
if (c == '.') {
if (Peek() == '.') {
Next();
return new Token(TokType.DOTS,"..",tokenLine);
}
// Put back the dot for floating point numbers
i--;
return NextToken();
}
AddError($"Unexpected character '{c}'");
return new Token(TokType.EOF,"",tokenLine); // Skip invalid character
}
}

View File

@ -0,0 +1,438 @@
namespace STCompiler.Compiler;
using System;
using System.Collections.Generic;
using STCompiler.Common;
public record Symbol {
required public string Name;
public VarType Type;
public int Index;
}
public class StParser {
StLexer lex;
Token cur;
Dictionary<string,Symbol> syms = new();
public Dictionary<string,ArrayType> arrayTypes = new(); // Add dictionary for array types
public List<CompileError> Errors => lex.Errors;
public bool HasErrors => Errors.Count > 0;
public StParser(string s){
lex=new StLexer(s);
cur=lex.NextToken();
}
void Next()=>cur=lex.NextToken();
void AddError(string msg) => lex.Errors.Add(new CompileError(cur.Line, msg));
bool Expect(TokType t){
if(cur.Type!=t) {
AddError($"Expected {t}, got {cur.Type}");
return false;
}
Next();
return true;
}
public ProgramNode? ParseProgram(){
var p=new ProgramNode();
if (!Expect(TokType.PROGRAM)) return null;
if(cur.Type==TokType.IDENT) Next();
if(cur.Type==TokType.VAR){
Next();
while(cur.Type==TokType.IDENT) {
var varDecl = ParseVarDecl();
if (varDecl == null) return null;
p.Vars.Add(varDecl);
}
if (!Expect(TokType.END_VAR)) return null;
}
while(cur.Type!=TokType.END_PROGRAM && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
p.Stmts.Add(stmt);
}
if (!Expect(TokType.END_PROGRAM)) return null;
return p;
}
VarType? ParseType(string varName) {
if (cur.Type == TokType.ARRAY) {
Next(); // Skip ARRAY
if (!Expect(TokType.LBRACKET)) return null;
var start = ParseExpr();
if (start == null || start is not IntExpr startInt) {
AddError("Array start index must be an integer constant");
return null;
}
if (!Expect(TokType.DOTS)) return null;
var end = ParseExpr();
if (end == null || end is not IntExpr endInt) {
AddError("Array end index must be an integer constant");
return null;
}
if (!Expect(TokType.RBRACKET)) return null;
if (!Expect(TokType.OF)) return null;
if (cur.Type != TokType.IDENT) {
AddError("Expected element type name after OF");
return null;
}
string elemTypeName = cur.Text.ToLowerInvariant();
Next();
VarType? elementType = elemTypeName switch {
"bool" => VarType.BOOL,
"byte" => VarType.BYTE,
"word" => VarType.WORD,
"dword" => VarType.DWORD,
"lword" => VarType.LWORD,
"sint" => VarType.SINT,
"int" => VarType.INT,
"dint" => VarType.DINT,
"lint" => VarType.LINT,
"usint" => VarType.USINT,
"uint" => VarType.UINT,
"udint" => VarType.UDINT,
"ulint" => VarType.ULINT,
"real" => VarType.REAL,
"lreal" => VarType.LREAL,
_ => null
};
if (elementType == null) {
AddError($"Unknown element type '{elemTypeName}'");
return null;
}
arrayTypes[varName] = new ArrayType(elementType.Value, (int)startInt.Value, (int)endInt.Value);
return VarType.ARRAY;
}
else if (cur.Type == TokType.IDENT) {
string typeName = cur.Text.ToLowerInvariant();
Next();
return typeName switch {
"bool" => VarType.BOOL,
"byte" => VarType.BYTE,
"word" => VarType.WORD,
"dword" => VarType.DWORD,
"lword" => VarType.LWORD,
"sint" => VarType.SINT,
"int" => VarType.INT,
"dint" => VarType.DINT,
"lint" => VarType.LINT,
"usint" => VarType.USINT,
"uint" => VarType.UINT,
"udint" => VarType.UDINT,
"ulint" => VarType.ULINT,
"real" => VarType.REAL,
"lreal" => VarType.LREAL,
_ => null
};
}
AddError("Expected type name or ARRAY");
return null;
}
VarDecl? ParseVarDecl(){
if (cur.Type != TokType.IDENT) {
AddError("Expected identifier for variable declaration");
return null;
}
string name = cur.Text.ToUpperInvariant(); // Variablennamen in Großbuchstaben
Next();
var vt = ParseType(name);
if (vt == null) {
AddError($"Invalid type for variable '{name}'");
return null;
}
Expr? init=null;
if(cur.Type==TokType.ASSIGN){
Next(); // consume :=
init=ParseExpr();
if (init == null) {
AddError($"Expected expression after ':=' in variable declaration");
return null;
}
}
if (!Expect(TokType.SEMI)) return null;
// Füge Variable zur Symboltabelle hinzu
syms[name] = new Symbol { Name = name, Type = vt.Value };
return new VarDecl{Name=name,Type=vt.Value,Init=init};
}
Stmt? ParseAssign() {
string target = cur.Text.ToUpperInvariant();
Next(); // consume identifier
// Check if this is an array assignment
Expr? index = null;
if (cur.Type == TokType.LBRACKET) {
Symbol? sym;
if (!syms.TryGetValue(target, out sym)) {
AddError($"Undeclared variable '{target}'");
return null;
}
if (sym.Type != VarType.ARRAY) {
AddError($"Cannot use array indexing on non-array variable '{target}'");
return null;
}
Next(); // consume [
index = ParseExpr();
if (index == null) return null;
if (!Expect(TokType.RBRACKET)) return null;
}
if (cur.Type != TokType.ASSIGN) {
AddError($"Expected ':=' after identifier '{target}'");
return null;
}
Next(); // consume :=
var e = ParseExpr();
if (e == null) return null;
if (!Expect(TokType.SEMI)) return null;
return new AssignStmt{Target=target, Index=index, Expr=e};
}
Stmt? ParseStmt(){
switch(cur.Type) {
case TokType.IF: return ParseIf();
case TokType.WHILE: return ParseWhile();
case TokType.FOR: return ParseFor();
case TokType.IDENT:
return ParseAssign();
default:
AddError($"Unexpected token {cur.Type} in statement");
return null;
}
}
IfStmt? ParseIf(){
Next(); // IF
var cond=ParseExpr();
if (cond == null) return null;
if (!Expect(TokType.THEN)) return null;
var node=new IfStmt{Cond=cond};
while(cur.Type!=TokType.ELSE && cur.Type!=TokType.END_IF && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
node.ThenStmts.Add(stmt);
}
if(cur.Type==TokType.ELSE){
Next();
while(cur.Type!=TokType.END_IF && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
node.ElseStmts.Add(stmt);
}
}
if (!Expect(TokType.END_IF)) return null;
if (!Expect(TokType.SEMI)) return null;
return node;
}
WhileStmt? ParseWhile(){
Next(); // WHILE
var cond=ParseExpr();
if (cond == null) return null;
if (!Expect(TokType.DO)) return null;
var ws=new WhileStmt{Cond=cond};
while(cur.Type!=TokType.END_WHILE && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
ws.Body.Add(stmt);
}
if (!Expect(TokType.END_WHILE)) return null;
if (!Expect(TokType.SEMI)) return null;
return ws;
}
ForStmt? ParseFor(){
Next(); // FOR
if (cur.Type != TokType.IDENT) {
AddError("Expected identifier for FOR loop variable");
return null;
}
string varName = cur.Text.ToUpperInvariant(); // Variablennamen in Großbuchstaben
Next(); // consume identifier
if (cur.Type != TokType.ASSIGN) {
AddError($"Expected ':=' after identifier '{varName}'");
return null;
}
Next(); // consume :=
var start = ParseExpr();
if (start == null) return null;
if (!Expect(TokType.TO)) return null;
var end = ParseExpr();
if (end == null) return null;
Expr step = new IntExpr(1);
if(cur.Type==TokType.BY){
Next();
step = ParseExpr() ?? step;
}
if (!Expect(TokType.DO)) return null;
var fs = new ForStmt{Var=varName, Start=start, End=end, Step=step};
while(cur.Type!=TokType.END_FOR && cur.Type!=TokType.EOF) {
var stmt = ParseStmt();
if (stmt == null) return null;
fs.Body.Add(stmt);
}
if (!Expect(TokType.END_FOR)) return null;
if (!Expect(TokType.SEMI)) return null;
return fs;
}
Expr? ParseExpr() => ParseCompare();
Expr? ParseCompare(){
var l = ParseAddSub();
if (l == null) return null;
while(cur.Type is TokType.LT or TokType.GT or TokType.LE or TokType.GE or TokType.EQ or TokType.NEQ){
var op=cur.Type;
Next();
var r=ParseAddSub();
if (r == null) return null;
l=new BinaryExpr(l,op,r);
}
return l;
}
Expr? ParseAddSub(){
var l=ParseMulDiv();
if (l == null) return null;
while(cur.Type==TokType.PLUS||cur.Type==TokType.MINUS){
var op=cur.Type;
Next();
var r=ParseMulDiv();
if (r == null) return null;
l=new BinaryExpr(l,op,r);
}
return l;
}
Expr? ParseMulDiv(){
var l=ParsePrimary();
if (l == null) return null;
while(cur.Type==TokType.MUL||cur.Type==TokType.DIV){
var op=cur.Type;
Next();
var r=ParsePrimary();
if (r == null) return null;
l=new BinaryExpr(l,op,r);
}
return l;
}
Expr? ParsePrimary(){
int startLine = cur.Line;
// Support unary + and -
if (cur.Type == TokType.PLUS || cur.Type == TokType.MINUS) {
var sign = cur.Type;
Next();
var p = ParsePrimary();
if (p == null) return null;
Expr zero;
if (p.Type == VarType.REAL || p.Type == VarType.LREAL) {
zero = new RealExpr(0.0, p.Type);
} else {
zero = new IntExpr(0, p.Type);
}
var op = sign == TokType.PLUS ? TokType.PLUS : TokType.MINUS;
return new BinaryExpr(zero, op, p);
}
switch(cur.Type) {
case TokType.INT:
if (!long.TryParse(cur.Text, out var v)) {
AddError($"Invalid integer literal '{cur.Text}'");
return null;
}
Next();
return new IntExpr(v, VarType.DINT);
case TokType.REAL:
if (!double.TryParse(cur.Text, out var d)) {
AddError($"Invalid floating point literal '{cur.Text}'");
return null;
}
Next();
return new RealExpr(d);
case TokType.IDENT:
string n = cur.Text.ToUpperInvariant(); // Variablennamen in Großbuchstaben
Next();
Symbol? sym;
if (!syms.TryGetValue(n, out sym)) {
AddError($"Undeclared variable '{n}'");
return null;
}
// Check for array access
if (cur.Type == TokType.LBRACKET) {
if (sym.Type != VarType.ARRAY) {
AddError($"Cannot index non-array variable '{n}'");
return null;
}
Next(); // consume [
var idx = ParseExpr();
if (idx == null) return null;
if (!Expect(TokType.RBRACKET)) return null;
ArrayType? arrayType;
if (!arrayTypes.TryGetValue(n, out arrayType)) {
AddError($"Internal error: Array type information missing for '{n}'");
return null;
}
return new ArrayAccessExpr(n, idx, arrayType.ElementType);
}
return new VarExpr(n, sym.Type);
case TokType.LPAREN:
Next();
var e = ParseExpr();
if (e == null) return null;
if (!Expect(TokType.RPAREN)) return null;
return e;
default:
AddError($"Unexpected token {cur.Type} in expression");
return null;
}
}
public ArrayType? GetArrayType(string name) {
ArrayType? type;
return arrayTypes.TryGetValue(name, out type) ? type : null;
}
}

View File

@ -0,0 +1,426 @@
namespace STCompiler.Compiler;
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using STCompiler.Common;
// === ENTRY POINT ===
class Program {
static int Main(string[] args) {
if (args.Length < 2) {
Console.WriteLine("Usage: StEmitter <input.st> <output.bin>");
return 1;
}
var input = File.ReadAllText(args[0]);
var parser = new StParser(input);
var prog = parser.ParseProgram();
if (parser.HasErrors) {
Console.WriteLine($"Compilation failed with {parser.Errors.Count} errors:");
foreach (var error in parser.Errors) {
Console.WriteLine($"Error at line {error.Line}: {error.Message}");
}
return 1;
}
if (prog == null) {
Console.WriteLine("Compilation failed: invalid program structure");
return 1;
}
try {
var emitter = new BytecodeEmitter();
// Register array types from parser
foreach (var (name, arrayType) in parser.arrayTypes) {
emitter.RegisterArrayType(name, arrayType);
}
emitter.Compile(prog);
File.WriteAllBytes(args[1], emitter.BuildBinary());
Console.WriteLine($"Wrote {args[1]}: consts={emitter.ConstantsCount}, vars={emitter.VarCount}, code={emitter.CodeLength}");
return 0;
} catch (Exception ex) {
Console.WriteLine($"Internal compiler error: {ex.Message}");
return 1;
}
}
}
// Parser, Lexer und AST wurden ausgelagert nach Parser.cs, Lexer.cs und Ast.cs
// Die Implementierungen dort werden vom Entry-Point genutzt.
// === BYTECODE ===
public class BytecodeEmitter {
List<object> consts = new(); // Kann nun int, long, float oder double speichern
Dictionary<string,Symbol> syms = new();
List<byte> code = new();
Dictionary<string,ArrayType> arrayTypes = new();
class Symbol {
required public string Name;
public VarType Type;
public int Index;
public ArrayType? ArrayInfo; // Null for non-array types
}
public void RegisterArrayType(string name, ArrayType type) {
arrayTypes[name] = type;
}
public int ConstantsCount => consts.Count;
public int VarCount => syms.Count;
public int CodeLength => code.Count;
public void Compile(ProgramNode p){
int idx=0;
foreach(var v in p.Vars) {
var symbol = new Symbol{Name=v.Name, Type=v.Type, Index=idx};
if (v.Type == VarType.ARRAY) {
var arrayInfo = arrayTypes[v.Name];
symbol.ArrayInfo = arrayInfo;
// For arrays, increment the index by array size
idx += arrayInfo.Length;
} else {
// For regular variables, increment by 1
idx++;
}
syms[v.Name] = symbol;
}
foreach(var v in p.Vars)
if(v.Init!=null){EmitExpr(v.Init);EmitByte(Bytecode.OpCodes.STORE_VAR);EmitU16((ushort)syms[v.Name].Index);}
foreach(var s in p.Stmts)
EmitStmt(s);
EmitByte(Bytecode.OpCodes.HALT); // Program End
}
// Operationscodes für verschiedene Datentypen
Dictionary<(TokType, VarType), byte> opCodes = new() {
// Signed integer arithmetic
{(TokType.PLUS, VarType.SINT), Bytecode.OpCodes.ADD_SINT}, {(TokType.MINUS, VarType.SINT), Bytecode.OpCodes.SUB_SINT},
{(TokType.MUL, VarType.SINT), Bytecode.OpCodes.MUL_SINT}, {(TokType.DIV, VarType.SINT), Bytecode.OpCodes.DIV_SINT},
{(TokType.PLUS, VarType.INT), Bytecode.OpCodes.ADD_INT}, {(TokType.MINUS, VarType.INT), Bytecode.OpCodes.SUB_INT},
{(TokType.MUL, VarType.INT), Bytecode.OpCodes.MUL_INT}, {(TokType.DIV, VarType.INT), Bytecode.OpCodes.DIV_INT},
{(TokType.PLUS, VarType.DINT), Bytecode.OpCodes.ADD_DINT}, {(TokType.MINUS, VarType.DINT), Bytecode.OpCodes.SUB_DINT},
{(TokType.MUL, VarType.DINT), Bytecode.OpCodes.MUL_DINT}, {(TokType.DIV, VarType.DINT), Bytecode.OpCodes.DIV_DINT},
{(TokType.PLUS, VarType.LINT), Bytecode.OpCodes.ADD_LINT}, {(TokType.MINUS, VarType.LINT), Bytecode.OpCodes.SUB_LINT},
{(TokType.MUL, VarType.LINT), Bytecode.OpCodes.MUL_LINT}, {(TokType.DIV, VarType.LINT), Bytecode.OpCodes.DIV_LINT},
// Unsigned integer arithmetic
{(TokType.PLUS, VarType.USINT), Bytecode.OpCodes.ADD_USINT}, {(TokType.MINUS, VarType.USINT), Bytecode.OpCodes.SUB_USINT},
{(TokType.MUL, VarType.USINT), Bytecode.OpCodes.MUL_USINT}, {(TokType.DIV, VarType.USINT), Bytecode.OpCodes.DIV_USINT},
{(TokType.PLUS, VarType.UINT), Bytecode.OpCodes.ADD_UINT}, {(TokType.MINUS, VarType.UINT), Bytecode.OpCodes.SUB_UINT},
{(TokType.MUL, VarType.UINT), Bytecode.OpCodes.MUL_UINT}, {(TokType.DIV, VarType.UINT), Bytecode.OpCodes.DIV_UINT},
{(TokType.PLUS, VarType.UDINT), Bytecode.OpCodes.ADD_UDINT}, {(TokType.MINUS, VarType.UDINT), Bytecode.OpCodes.SUB_UDINT},
{(TokType.MUL, VarType.UDINT), Bytecode.OpCodes.MUL_UDINT}, {(TokType.DIV, VarType.UDINT), Bytecode.OpCodes.DIV_UDINT},
{(TokType.PLUS, VarType.ULINT), Bytecode.OpCodes.ADD_ULINT}, {(TokType.MINUS, VarType.ULINT), Bytecode.OpCodes.SUB_ULINT},
{(TokType.MUL, VarType.ULINT), Bytecode.OpCodes.MUL_ULINT}, {(TokType.DIV, VarType.ULINT), Bytecode.OpCodes.DIV_ULINT},
// Floating point arithmetic
{(TokType.PLUS, VarType.REAL), Bytecode.OpCodes.ADD_REAL}, {(TokType.MINUS, VarType.REAL), Bytecode.OpCodes.SUB_REAL},
{(TokType.MUL, VarType.REAL), Bytecode.OpCodes.MUL_REAL}, {(TokType.DIV, VarType.REAL), Bytecode.OpCodes.DIV_REAL},
{(TokType.PLUS, VarType.LREAL), Bytecode.OpCodes.ADD_LREAL}, {(TokType.MINUS, VarType.LREAL), Bytecode.OpCodes.SUB_LREAL},
{(TokType.MUL, VarType.LREAL), Bytecode.OpCodes.MUL_LREAL}, {(TokType.DIV, VarType.LREAL), Bytecode.OpCodes.DIV_LREAL},
// Comparisons signed
{(TokType.LT, VarType.SINT), Bytecode.OpCodes.LT_SINT}, {(TokType.GT, VarType.SINT), Bytecode.OpCodes.GT_SINT},
{(TokType.LE, VarType.SINT), Bytecode.OpCodes.LE_SINT}, {(TokType.GE, VarType.SINT), Bytecode.OpCodes.GE_SINT},
{(TokType.EQ, VarType.SINT), Bytecode.OpCodes.EQ_SINT}, {(TokType.NEQ, VarType.SINT), Bytecode.OpCodes.NEQ_SINT},
{(TokType.LT, VarType.INT), Bytecode.OpCodes.LT_INT}, {(TokType.GT, VarType.INT), Bytecode.OpCodes.GT_INT},
{(TokType.LE, VarType.INT), Bytecode.OpCodes.LE_INT}, {(TokType.GE, VarType.INT), Bytecode.OpCodes.GE_INT},
{(TokType.EQ, VarType.INT), Bytecode.OpCodes.EQ_INT}, {(TokType.NEQ, VarType.INT), Bytecode.OpCodes.NEQ_INT},
{(TokType.LT, VarType.DINT), Bytecode.OpCodes.LT_DINT}, {(TokType.GT, VarType.DINT), Bytecode.OpCodes.GT_DINT},
{(TokType.LE, VarType.DINT), Bytecode.OpCodes.LE_DINT}, {(TokType.GE, VarType.DINT), Bytecode.OpCodes.GE_DINT},
{(TokType.EQ, VarType.DINT), Bytecode.OpCodes.EQ_DINT}, {(TokType.NEQ, VarType.DINT), Bytecode.OpCodes.NEQ_DINT},
{(TokType.LT, VarType.LINT), Bytecode.OpCodes.LT_LINT}, {(TokType.GT, VarType.LINT), Bytecode.OpCodes.GT_LINT},
{(TokType.LE, VarType.LINT), Bytecode.OpCodes.LE_LINT}, {(TokType.GE, VarType.LINT), Bytecode.OpCodes.GE_LINT},
{(TokType.EQ, VarType.LINT), Bytecode.OpCodes.EQ_LINT}, {(TokType.NEQ, VarType.LINT), Bytecode.OpCodes.NEQ_LINT},
// Comparisons unsigned
{(TokType.LT, VarType.USINT), Bytecode.OpCodes.LT_USINT}, {(TokType.GT, VarType.USINT), Bytecode.OpCodes.GT_USINT},
{(TokType.LE, VarType.USINT), Bytecode.OpCodes.LE_USINT}, {(TokType.GE, VarType.USINT), Bytecode.OpCodes.GE_USINT},
{(TokType.EQ, VarType.USINT), Bytecode.OpCodes.EQ_USINT}, {(TokType.NEQ, VarType.USINT), Bytecode.OpCodes.NEQ_USINT},
{(TokType.LT, VarType.UINT), Bytecode.OpCodes.LT_UINT}, {(TokType.GT, VarType.UINT), Bytecode.OpCodes.GT_UINT},
{(TokType.LE, VarType.UINT), Bytecode.OpCodes.LE_UINT}, {(TokType.GE, VarType.UINT), Bytecode.OpCodes.GE_UINT},
{(TokType.EQ, VarType.UINT), Bytecode.OpCodes.EQ_UINT}, {(TokType.NEQ, VarType.UINT), Bytecode.OpCodes.NEQ_UINT},
{(TokType.LT, VarType.UDINT), Bytecode.OpCodes.LT_UDINT}, {(TokType.GT, VarType.UDINT), Bytecode.OpCodes.GT_UDINT},
{(TokType.LE, VarType.UDINT), Bytecode.OpCodes.LE_UDINT}, {(TokType.GE, VarType.UDINT), Bytecode.OpCodes.GE_UDINT},
{(TokType.EQ, VarType.UDINT), Bytecode.OpCodes.EQ_UDINT}, {(TokType.NEQ, VarType.UDINT), Bytecode.OpCodes.NEQ_UDINT},
{(TokType.LT, VarType.ULINT), Bytecode.OpCodes.LT_ULINT}, {(TokType.GT, VarType.ULINT), Bytecode.OpCodes.GT_ULINT},
{(TokType.LE, VarType.ULINT), Bytecode.OpCodes.LE_ULINT}, {(TokType.GE, VarType.ULINT), Bytecode.OpCodes.GE_ULINT},
{(TokType.EQ, VarType.ULINT), Bytecode.OpCodes.EQ_ULINT}, {(TokType.NEQ, VarType.ULINT), Bytecode.OpCodes.NEQ_ULINT},
// Comparisons floating
{(TokType.LT, VarType.REAL), Bytecode.OpCodes.LT_REAL}, {(TokType.GT, VarType.REAL), Bytecode.OpCodes.GT_REAL},
{(TokType.LE, VarType.REAL), Bytecode.OpCodes.LE_REAL}, {(TokType.GE, VarType.REAL), Bytecode.OpCodes.GE_REAL},
{(TokType.EQ, VarType.REAL), Bytecode.OpCodes.EQ_REAL}, {(TokType.NEQ, VarType.REAL), Bytecode.OpCodes.NEQ_REAL},
{(TokType.LT, VarType.LREAL), Bytecode.OpCodes.LT_LREAL}, {(TokType.GT, VarType.LREAL), Bytecode.OpCodes.GT_LREAL},
{(TokType.LE, VarType.LREAL), Bytecode.OpCodes.LE_LREAL}, {(TokType.GE, VarType.LREAL), Bytecode.OpCodes.GE_LREAL},
{(TokType.EQ, VarType.LREAL), Bytecode.OpCodes.EQ_LREAL}, {(TokType.NEQ, VarType.LREAL), Bytecode.OpCodes.NEQ_LREAL}
};
void EmitStmt(Stmt s){
switch(s){
case AssignStmt a:
Symbol? symbol = null;
if (!syms.TryGetValue(a.Target, out symbol)) {
throw new Exception($"Undeclared variable '{a.Target}'");
}
// If this is an array assignment, we need to evaluate the index and adjust the target address
if (symbol.Type == VarType.ARRAY && a.Index != null) {
// Evaluate array index
EmitExpr(a.Index);
// Bounds check
if (symbol.ArrayInfo == null)
throw new Exception($"Internal error: Array info missing for '{a.Target}'");
EmitByte(Bytecode.OpCodes.ARRAY_BOUNDS_CHECK);
EmitU16((ushort)symbol.ArrayInfo.Start);
EmitU16((ushort)symbol.ArrayInfo.End);
// Convert to zero-based index
if (symbol.ArrayInfo.Start != 0) {
EmitByte(Bytecode.OpCodes.PUSH_CONST);
EmitU16((ushort)AddConst(symbol.ArrayInfo.Start));
EmitByte(Bytecode.OpCodes.SUB_INT);
}
// Add base address
EmitByte(Bytecode.OpCodes.PUSH_CONST);
EmitU16((ushort)symbol.Index);
EmitByte(Bytecode.OpCodes.ADD_INT);
// Now evaluate and store the value
EmitExpr(a.Expr);
EmitByte(Bytecode.OpCodes.STORE_VAR);
EmitU16(0); // Index is already on stack
}
else {
// Regular variable assignment
EmitExpr(a.Expr);
EmitByte(Bytecode.OpCodes.STORE_VAR);
EmitU16((ushort)symbol.Index);
}
break;
case IfStmt iff:
EmitExpr(iff.Cond);
EmitByte(Bytecode.OpCodes.JZ); int jz=code.Count; EmitU16(0);
foreach(var st in iff.ThenStmts) EmitStmt(st);
if(iff.ElseStmts.Count>0){
EmitByte(Bytecode.OpCodes.JMP); int jmp=code.Count; EmitU16(0);
PatchJump(jz,code.Count);
foreach(var st in iff.ElseStmts) EmitStmt(st);
PatchJump(jmp,code.Count);
} else PatchJump(jz,code.Count);
break;
case WhileStmt w:
int loopStart=code.Count;
EmitExpr(w.Cond);
EmitByte(Bytecode.OpCodes.JZ); int jzpos=code.Count; EmitU16(0);
foreach(var st in w.Body) EmitStmt(st);
EmitByte(Bytecode.OpCodes.JMP); EmitU16((ushort)loopStart);
PatchJump(jzpos,code.Count);
break;
case ForStmt f:
Symbol? forSymbol = null;
if (!syms.TryGetValue(f.Var, out forSymbol)) {
throw new Exception($"Undeclared variable '{f.Var}'");
}
// Initialisierung: var := start
EmitExpr(f.Start);
EmitByte(Bytecode.OpCodes.STORE_VAR); EmitU16((ushort)forSymbol.Index);
int cmpPos = code.Count; // Position des Vergleichs
EmitExpr(new VarExpr(f.Var, forSymbol.Type));
EmitExpr(f.End);
var key = (TokType.LE, forSymbol.Type);
if (!opCodes.ContainsKey(key)) {
throw new Exception($"Comparison not supported for type {forSymbol.Type}");
}
EmitByte(opCodes[key]);
EmitByte(Bytecode.OpCodes.JZ); int jzFor = code.Count; EmitU16(0); // Jump zum Ende
// Body
foreach(var st in f.Body) EmitStmt(st);
// Inkrement: i := i + step
EmitExpr(new VarExpr(f.Var, forSymbol.Type));
EmitExpr(f.Step);
var addKey = (TokType.PLUS, forSymbol.Type);
if (!opCodes.ContainsKey(addKey)) {
throw new Exception($"Addition not supported for type {forSymbol.Type}");
}
EmitByte(opCodes[addKey]);
EmitByte(Bytecode.OpCodes.STORE_VAR); EmitU16((ushort)forSymbol.Index);
// Vergleich erneut, aber wir merken uns den Sprung zum Vergleich
EmitByte(Bytecode.OpCodes.JMP); EmitU16((ushort)cmpPos);
// Patch Jump: Ende der Schleife springt hierhin
PatchJump(jzFor, code.Count);
// Korrektur: Schleifenvariable auf Endwert, falls sie überläuft
EmitExpr(f.End);
EmitByte(Bytecode.OpCodes.STORE_VAR); EmitU16((ushort)syms[f.Var].Index);
break;
}
}
void PatchJump(int pos,int target){code[pos]=(byte)(target&0xFF);code[pos+1]=(byte)(target>>8);}
void EmitExpr(Expr e){
switch(e){
case IntExpr ie:
int ci = AddConst(ie.Value);
EmitByte(Bytecode.OpCodes.PUSH_CONST); // PUSH_CONST
EmitU16((ushort)ci);
break;
case RealExpr re:
int cri = AddConst(re.Value);
EmitByte(Bytecode.OpCodes.PUSH_REAL_CONST); // PUSH_REAL_CONST
EmitU16((ushort)cri);
break;
case VarExpr ve:
Symbol? symbol = null;
if (!syms.TryGetValue(ve.Name, out symbol)) {
throw new Exception($"Undeclared variable '{ve.Name}'");
}
EmitByte(Bytecode.OpCodes.LOAD_VAR); // LOAD_VAR
EmitU16((ushort)symbol.Index);
break;
case ArrayAccessExpr ae:
Symbol? arraySymbol = null;
if (!syms.TryGetValue(ae.ArrayName, out arraySymbol)) {
throw new Exception($"Undeclared array '{ae.ArrayName}'");
}
if (arraySymbol.Type != VarType.ARRAY || arraySymbol.ArrayInfo == null) {
throw new Exception($"Variable '{ae.ArrayName}' is not an array");
}
// Emit the index expression first
EmitExpr(ae.Index);
// Emit array bounds check
int start = arraySymbol.ArrayInfo.Start;
int end = arraySymbol.ArrayInfo.End;
EmitByte(Bytecode.OpCodes.ARRAY_BOUNDS_CHECK);
EmitU16((ushort)start);
EmitU16((ushort)end);
// Convert index to zero-based
if (start != 0) {
EmitByte(Bytecode.OpCodes.PUSH_CONST);
EmitU16((ushort)AddConst(start));
EmitByte(Bytecode.OpCodes.SUB_INT);
}
// Load array base address
EmitByte(Bytecode.OpCodes.PUSH_CONST);
EmitU16((ushort)arraySymbol.Index);
EmitByte(Bytecode.OpCodes.ADD_INT);
// Load array element
EmitByte(Bytecode.OpCodes.LOAD_VAR);
EmitU16(0); // Index is already on stack
break;
case BinaryExpr be:
EmitExpr(be.L);
EmitExpr(be.R);
var key = (be.Op, be.Type);
if (!opCodes.ContainsKey(key)) {
throw new Exception($"Unknown operator '{be.Op}' for type {be.Type}");
}
EmitByte(opCodes[key]);
break;
}
}
int AddConst(object v) {
int i = consts.FindIndex(c => c.Equals(v));
if (i >= 0) return i;
consts.Add(v);
return consts.Count - 1;
}
void EmitByte(byte b) => code.Add(b);
void EmitU16(ushort v) {code.Add((byte)(v&0xFF));code.Add((byte)(v>>8));}
public byte[] BuildBinary() {
using var ms = new MemoryStream();
var w = new BinaryWriter(ms);
// Header
w.Write(Encoding.ASCII.GetBytes(Bytecode.Magic));
w.Write(Bytecode.Version);
w.Write(Bytecode.DefaultCycleTime);
// Konstanten
w.Write((ushort)consts.Count);
foreach(var c in consts) {
if (c is long l) {
w.Write((byte)1); // Long type marker
w.Write(l);
}
else if (c is double d) {
w.Write((byte)2); // Double type marker
w.Write(d);
}
else if (c is float f) {
w.Write((byte)3); // Float type marker
w.Write(f);
}
else if (c is int i) {
w.Write((byte)4); // Int type marker
w.Write(i);
}
else {
throw new Exception($"Unsupported constant type: {c.GetType()}");
}
}
// Calculate total variable space needed (accounting for arrays)
int totalVarSpace = 0;
foreach(var kv in syms) {
if (kv.Value.Type == VarType.ARRAY && kv.Value.ArrayInfo != null) {
totalVarSpace = Math.Max(totalVarSpace, kv.Value.Index + kv.Value.ArrayInfo.Length);
} else {
totalVarSpace = Math.Max(totalVarSpace, kv.Value.Index + 1);
}
}
// Variablen
w.Write((ushort)totalVarSpace);
var types = new byte[totalVarSpace];
foreach(var kv in syms) {
if (kv.Value.Type == VarType.ARRAY && kv.Value.ArrayInfo != null) {
// Fill array elements with their element type
for (int i = 0; i < kv.Value.ArrayInfo.Length; i++) {
types[kv.Value.Index + i] = (byte)kv.Value.ArrayInfo.ElementType;
}
} else {
types[kv.Value.Index] = (byte)kv.Value.Type;
}
}
foreach(var b in types)
w.Write(b);
// Code
w.Write((ushort)code.Count);
w.Write(code.ToArray());
return ms.ToArray();
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\STCompiler.Common\STCompiler.Common.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,36 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"STCompiler.Compiler/1.0.0": {
"dependencies": {
"STCompiler.Common": "1.0.0"
},
"runtime": {
"STCompiler.Compiler.dll": {}
}
},
"STCompiler.Common/1.0.0": {
"runtime": {
"STCompiler.Common.dll": {}
}
}
}
},
"libraries": {
"STCompiler.Compiler/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"STCompiler.Common/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

View File

@ -0,0 +1,62 @@
PROGRAM Demo
VAR
a UINT := 0;
b UINT := 0;
i UINT := 0;
sum DINT := 0;
flag BOOL := 0; // FALSE -> 0
x REAL := 1.5;
y LREAL := 2.5;
cnt INT := 0;
// numbers ARRAY [1..10] OF INT;
// matrix ARRAY [0..3] OF REAL;
END_VAR
// numbers[1] := 42;
// numbers[2] := numbers[1] * 2;
// matrix[0] := 3.14;
// Einfacher If-Test
IF a = 0 THEN
a := 1;
END_IF;
// While-Schleife mit arithmetischen Ausdrücken
WHILE cnt < 3 DO
cnt := cnt + 1;
b := b + cnt * 2;
END_WHILE;
// For-Schleife mit BY und verschachteltem IF
FOR i := 1 TO 10 BY 2 DO
sum := sum + i;
IF i <> 5 THEN
flag := 1; // TRUE -> 1
ELSE
flag := 0; // FALSE -> 0
END_IF;
END_FOR;
// Abwärts-Schleife (negativer BY-Wert)
FOR i := 5 TO 1 BY -1 DO
sum := sum + i;
b := b + (i * (cnt + 1));
END_FOR;
// Rechnen mit Gleitkommazahlen
x := x + y / 2.0;
END_PROGRAM
//=== Variable summary ===
//Index Type Value
//0 UINT 1
//1 UINT 12
//2 UINT 1
//3 DINT 25
//4 BOOL 1
//5 REAL 16,25
//6 LREAL 25
//7 INT 3

View File

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("STCompiler.Compiler")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5a002806a9ee3a7389c5a14069c534271a20a9ac")]
[assembly: System.Reflection.AssemblyProductAttribute("STCompiler.Compiler")]
[assembly: System.Reflection.AssemblyTitleAttribute("STCompiler.Compiler")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Von der MSBuild WriteCodeFragment-Klasse generiert.

View File

@ -0,0 +1 @@
d47a514fbef951d9157ec77bff6b43f7e588943a6cbc9cdecac26e64e892fcea

View File

@ -0,0 +1,13 @@
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = STCompiler.Compiler
build_property.ProjectDir = /home/martin/Projects/STCompiler/STCompiler.Compiler/
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =

View File

@ -0,0 +1,8 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@ -0,0 +1 @@
ef34cbbbf6d7558fb23c1b07b1bdfb963a1168dbf40a10e84ba2675b09fc9880

View File

@ -0,0 +1,18 @@
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Compiler
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Compiler.deps.json
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Compiler.runtimeconfig.json
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Compiler.dll
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Compiler.pdb
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Common.dll
/home/martin/Projects/STCompiler/STCompiler.Compiler/bin/Debug/net8.0/STCompiler.Common.pdb
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.csproj.AssemblyReference.cache
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.GeneratedMSBuildEditorConfig.editorconfig
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.AssemblyInfoInputs.cache
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.AssemblyInfo.cs
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.csproj.CoreCompileInputs.cache
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.csproj.CopyComplete
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.dll
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/refint/STCompiler.Compiler.dll
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.pdb
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/STCompiler.Compiler.genruntimeconfig.cache
/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/Debug/net8.0/ref/STCompiler.Compiler.dll

View File

@ -0,0 +1 @@
a4bc37825fdc00aadca1794cf7a7ec833f4b0bab9ce25c074c60fc7b293e6157

View File

@ -0,0 +1,130 @@
{
"format": 1,
"restore": {
"/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj": {}
},
"projects": {
"/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"projectName": "STCompiler.Common",
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projects/STCompiler/STCompiler.Common/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"downloadDependencies": [
{
"name": "Microsoft.AspNetCore.App.Ref",
"version": "[8.0.20, 8.0.20]"
}
],
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.120/PortableRuntimeIdentifierGraph.json"
}
}
},
"/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj",
"projectName": "STCompiler.Compiler",
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj",
"packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {
"/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"downloadDependencies": [
{
"name": "Microsoft.AspNetCore.App.Ref",
"version": "[8.0.20, 8.0.20]"
}
],
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.120/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/home/martin/.nuget/packages/</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/home/martin/.nuget/packages/</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.8.1</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="/home/martin/.nuget/packages/" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@ -0,0 +1,95 @@
{
"version": 3,
"targets": {
"net8.0": {
"STCompiler.Common/1.0.0": {
"type": "project",
"framework": ".NETCoreApp,Version=v8.0",
"compile": {
"bin/placeholder/STCompiler.Common.dll": {}
},
"runtime": {
"bin/placeholder/STCompiler.Common.dll": {}
}
}
}
},
"libraries": {
"STCompiler.Common/1.0.0": {
"type": "project",
"path": "../STCompiler.Common/STCompiler.Common.csproj",
"msbuildProject": "../STCompiler.Common/STCompiler.Common.csproj"
}
},
"projectFileDependencyGroups": {
"net8.0": [
"STCompiler.Common >= 1.0.0"
]
},
"packageFolders": {
"/home/martin/.nuget/packages/": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj",
"projectName": "STCompiler.Compiler",
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj",
"packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projects/STCompiler/STCompiler.Compiler/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {
"/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"downloadDependencies": [
{
"name": "Microsoft.AspNetCore.App.Ref",
"version": "[8.0.20, 8.0.20]"
}
],
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.120/PortableRuntimeIdentifierGraph.json"
}
}
}
}

View File

@ -0,0 +1,10 @@
{
"version": 2,
"dgSpecHash": "pOc/XBic+bS/nY5zfA5/C00qF5czDCXpa+QmG0s/xXRdiSa+OYPNVVz01R8yAbKlACRDVmGtIdQwJXDTjGwwYg==",
"success": true,
"projectFilePath": "/home/martin/Projects/STCompiler/STCompiler.Compiler/STCompiler.Compiler.csproj",
"expectedPackageFiles": [
"/home/martin/.nuget/packages/microsoft.aspnetcore.app.ref/8.0.20/microsoft.aspnetcore.app.ref.8.0.20.nupkg.sha512"
],
"logs": []
}

Binary file not shown.

View File

@ -0,0 +1,217 @@
using System;
using System.IO;
using System.Text;
using STCompiler.Common;
using System.Collections.Generic;
class Program {
static int Main(string[] args) {
if (args.Length < 1) {
Console.WriteLine("Usage: StDisasm <file.stbc>");
return 1;
}
var path = args[0];
if (!File.Exists(path)) { Console.WriteLine("File not found: " + path); return 2; }
var data = File.ReadAllBytes(path);
try { Disasm(data); } catch(Exception ex) { Console.WriteLine("Error: " + ex.Message); return 3; }
return 0;
}
static void Disasm(byte[] data) {
using var ms = new MemoryStream(data);
using var r = new BinaryReader(ms);
string magic = Encoding.ASCII.GetString(r.ReadBytes(4));
Console.WriteLine($"Magic: {magic}");
ushort ver = r.ReadUInt16();
Console.WriteLine($"Version: {ver}");
ushort cycletime = r.ReadUInt16();
Console.WriteLine($"Cycle: {cycletime} ms");
ushort nConsts = r.ReadUInt16();
Console.WriteLine($"Consts: {nConsts}");
var consts = new List<object>();
for (int i = 0; i < nConsts; i++) {
byte t = r.ReadByte();
switch(t) {
case 1: { long v = r.ReadInt64(); consts.Add(v); Console.WriteLine($" [{i}] (long) = {v}"); break; }
case 2: { double v = r.ReadDouble(); consts.Add(v); Console.WriteLine($" [{i}] (double) = {v}"); break; }
case 3: { float v = r.ReadSingle(); consts.Add(v); Console.WriteLine($" [{i}] (float) = {v}"); break; }
case 4: { int v = r.ReadInt32(); consts.Add(v); Console.WriteLine($" [{i}] (int) = {v}"); break; }
default: { Console.WriteLine($" [{i}] Unknown const type {t}"); break; }
}
}
ushort nVars = r.ReadUInt16();
Console.WriteLine($"Vars: {nVars}");
var varTypes = new VarType[nVars];
for (int i = 0; i < nVars; i++) {
byte tb = r.ReadByte();
var vt = (VarType)tb;
varTypes[i] = vt;
if (vt == VarType.ARRAY) {
// Look ahead to find array length and element type
int arrayStart = i;
int length = 1;
VarType elementType = 0; // Initialize to INT as default
if (i + 1 < nVars) {
elementType = (VarType)varTypes[i + 1];
while (i + length < nVars && varTypes[i + length] == varTypes[i + 1]) {
length++;
}
}
Console.WriteLine($" Var[{i}] type = ARRAY[{length}] OF {elementType}");
i += length - 1; // Skip array elements
}
else if (Enum.IsDefined(typeof(VarType), vt)) {
Console.WriteLine($" Var[{i}] type = {vt}");
}
else {
Console.WriteLine($" Var[{i}] type = {tb} (unknown)");
}
}
ushort codeLen = r.ReadUInt16();
Console.WriteLine($"CodeLen: {codeLen} bytes");
var code = r.ReadBytes(codeLen);
Console.WriteLine("\n--- Disassembly ---");
int ip = 0;
while (ip < code.Length) {
int addr = ip;
byte op = code[ip++];
Console.Write($"{addr:0000}: 0x{op:X2} ");
switch (op) {
case Bytecode.OpCodes.NOP: Console.WriteLine("NOP"); break;
case Bytecode.OpCodes.PUSH_CONST: { ushort ci = ReadU16(code, ref ip); Console.WriteLine($"PUSH_CONST {ci} ({consts[ci]})"); break; }
case Bytecode.OpCodes.PUSH_REAL_CONST: { ushort ci = ReadU16(code, ref ip); Console.WriteLine($"PUSH_REAL_CONST {ci} ({consts[ci]})"); break; }
case Bytecode.OpCodes.LOAD_VAR: { ushort vi = ReadU16(code, ref ip); Console.WriteLine($"LOAD_VAR {vi}"); break; }
case Bytecode.OpCodes.STORE_VAR: { ushort vi = ReadU16(code, ref ip); Console.WriteLine($"STORE_VAR {vi}"); break; }
case Bytecode.OpCodes.JZ: { ushort target = ReadU16(code, ref ip); Console.WriteLine($"JZ addr={target:0000}"); break; }
case Bytecode.OpCodes.JMP: { ushort target = ReadU16(code, ref ip); Console.WriteLine($"JMP addr={target:0000}"); break; }
case Bytecode.OpCodes.HALT: Console.WriteLine("HALT"); break;
case Bytecode.OpCodes.ARRAY_BOUNDS_CHECK: {
Console.WriteLine("ARRAY_BOUNDS_CHECK");
// Skip the next byte as it's part of the array bounds check instruction
if (ip < code.Length) ip++;
break;
}
// Signed integer arithmetic
case Bytecode.OpCodes.ADD_SINT: Console.WriteLine("ADD_SINT"); break;
case Bytecode.OpCodes.SUB_SINT: Console.WriteLine("SUB_SINT"); break;
case Bytecode.OpCodes.MUL_SINT: Console.WriteLine("MUL_SINT"); break;
case Bytecode.OpCodes.DIV_SINT: Console.WriteLine("DIV_SINT"); break;
case Bytecode.OpCodes.ADD_INT: Console.WriteLine("ADD_INT"); break;
case Bytecode.OpCodes.SUB_INT: Console.WriteLine("SUB_INT"); break;
case Bytecode.OpCodes.MUL_INT: Console.WriteLine("MUL_INT"); break;
case Bytecode.OpCodes.DIV_INT: Console.WriteLine("DIV_INT"); break;
case Bytecode.OpCodes.ADD_DINT: Console.WriteLine("ADD_DINT"); break;
case Bytecode.OpCodes.SUB_DINT: Console.WriteLine("SUB_DINT"); break;
case Bytecode.OpCodes.MUL_DINT: Console.WriteLine("MUL_DINT"); break;
case Bytecode.OpCodes.DIV_DINT: Console.WriteLine("DIV_DINT"); break;
case Bytecode.OpCodes.ADD_LINT: Console.WriteLine("ADD_LINT"); break;
case Bytecode.OpCodes.SUB_LINT: Console.WriteLine("SUB_LINT"); break;
case Bytecode.OpCodes.MUL_LINT: Console.WriteLine("MUL_LINT"); break;
case Bytecode.OpCodes.DIV_LINT: Console.WriteLine("DIV_LINT"); break;
// Unsigned integer arithmetic
case Bytecode.OpCodes.ADD_USINT: Console.WriteLine("ADD_USINT"); break;
case Bytecode.OpCodes.SUB_USINT: Console.WriteLine("SUB_USINT"); break;
case Bytecode.OpCodes.MUL_USINT: Console.WriteLine("MUL_USINT"); break;
case Bytecode.OpCodes.DIV_USINT: Console.WriteLine("DIV_USINT"); break;
case Bytecode.OpCodes.ADD_UINT: Console.WriteLine("ADD_UINT"); break;
case Bytecode.OpCodes.SUB_UINT: Console.WriteLine("SUB_UINT"); break;
case Bytecode.OpCodes.MUL_UINT: Console.WriteLine("MUL_UINT"); break;
case Bytecode.OpCodes.DIV_UINT: Console.WriteLine("DIV_UINT"); break;
case Bytecode.OpCodes.ADD_UDINT: Console.WriteLine("ADD_UDINT"); break;
case Bytecode.OpCodes.SUB_UDINT: Console.WriteLine("SUB_UDINT"); break;
case Bytecode.OpCodes.MUL_UDINT: Console.WriteLine("MUL_UDINT"); break;
case Bytecode.OpCodes.DIV_UDINT: Console.WriteLine("DIV_UDINT"); break;
case Bytecode.OpCodes.ADD_ULINT: Console.WriteLine("ADD_ULINT"); break;
case Bytecode.OpCodes.SUB_ULINT: Console.WriteLine("SUB_ULINT"); break;
case Bytecode.OpCodes.MUL_ULINT: Console.WriteLine("MUL_ULINT"); break;
case Bytecode.OpCodes.DIV_ULINT: Console.WriteLine("DIV_ULINT"); break;
// Floating point arithmetic
case Bytecode.OpCodes.ADD_REAL: Console.WriteLine("ADD_REAL"); break;
case Bytecode.OpCodes.SUB_REAL: Console.WriteLine("SUB_REAL"); break;
case Bytecode.OpCodes.MUL_REAL: Console.WriteLine("MUL_REAL"); break;
case Bytecode.OpCodes.DIV_REAL: Console.WriteLine("DIV_REAL"); break;
case Bytecode.OpCodes.ADD_LREAL: Console.WriteLine("ADD_LREAL"); break;
case Bytecode.OpCodes.SUB_LREAL: Console.WriteLine("SUB_LREAL"); break;
case Bytecode.OpCodes.MUL_LREAL: Console.WriteLine("MUL_LREAL"); break;
case Bytecode.OpCodes.DIV_LREAL: Console.WriteLine("DIV_LREAL"); break;
// Signed integer comparisons
case Bytecode.OpCodes.LT_SINT: Console.WriteLine("LT_SINT"); break;
case Bytecode.OpCodes.GT_SINT: Console.WriteLine("GT_SINT"); break;
case Bytecode.OpCodes.LE_SINT: Console.WriteLine("LE_SINT"); break;
case Bytecode.OpCodes.GE_SINT: Console.WriteLine("GE_SINT"); break;
case Bytecode.OpCodes.EQ_SINT: Console.WriteLine("EQ_SINT"); break;
case Bytecode.OpCodes.NEQ_SINT: Console.WriteLine("NEQ_SINT"); break;
case Bytecode.OpCodes.LT_INT: Console.WriteLine("LT_INT"); break;
case Bytecode.OpCodes.GT_INT: Console.WriteLine("GT_INT"); break;
case Bytecode.OpCodes.LE_INT: Console.WriteLine("LE_INT"); break;
case Bytecode.OpCodes.GE_INT: Console.WriteLine("GE_INT"); break;
case Bytecode.OpCodes.EQ_INT: Console.WriteLine("EQ_INT"); break;
case Bytecode.OpCodes.NEQ_INT: Console.WriteLine("NEQ_INT"); break;
case Bytecode.OpCodes.LT_DINT: Console.WriteLine("LT_DINT"); break;
case Bytecode.OpCodes.GT_DINT: Console.WriteLine("GT_DINT"); break;
case Bytecode.OpCodes.LE_DINT: Console.WriteLine("LE_DINT"); break;
case Bytecode.OpCodes.GE_DINT: Console.WriteLine("GE_DINT"); break;
case Bytecode.OpCodes.EQ_DINT: Console.WriteLine("EQ_DINT"); break;
case Bytecode.OpCodes.NEQ_DINT: Console.WriteLine("NEQ_DINT"); break;
case Bytecode.OpCodes.LT_LINT: Console.WriteLine("LT_LINT"); break;
case Bytecode.OpCodes.GT_LINT: Console.WriteLine("GT_LINT"); break;
case Bytecode.OpCodes.LE_LINT: Console.WriteLine("LE_LINT"); break;
case Bytecode.OpCodes.GE_LINT: Console.WriteLine("GE_LINT"); break;
case Bytecode.OpCodes.EQ_LINT: Console.WriteLine("EQ_LINT"); break;
case Bytecode.OpCodes.NEQ_LINT: Console.WriteLine("NEQ_LINT"); break;
// Unsigned integer comparisons
case Bytecode.OpCodes.LT_USINT: Console.WriteLine("LT_USINT"); break;
case Bytecode.OpCodes.GT_USINT: Console.WriteLine("GT_USINT"); break;
case Bytecode.OpCodes.LE_USINT: Console.WriteLine("LE_USINT"); break;
case Bytecode.OpCodes.GE_USINT: Console.WriteLine("GE_USINT"); break;
case Bytecode.OpCodes.EQ_USINT: Console.WriteLine("EQ_USINT"); break;
case Bytecode.OpCodes.NEQ_USINT: Console.WriteLine("NEQ_USINT"); break;
case Bytecode.OpCodes.LT_UINT: Console.WriteLine("LT_UINT"); break;
case Bytecode.OpCodes.GT_UINT: Console.WriteLine("GT_UINT"); break;
case Bytecode.OpCodes.LE_UINT: Console.WriteLine("LE_UINT"); break;
case Bytecode.OpCodes.GE_UINT: Console.WriteLine("GE_UINT"); break;
case Bytecode.OpCodes.EQ_UINT: Console.WriteLine("EQ_UINT"); break;
case Bytecode.OpCodes.NEQ_UINT: Console.WriteLine("NEQ_UINT"); break;
case Bytecode.OpCodes.LT_UDINT: Console.WriteLine("LT_UDINT"); break;
case Bytecode.OpCodes.GT_UDINT: Console.WriteLine("GT_UDINT"); break;
case Bytecode.OpCodes.LE_UDINT: Console.WriteLine("LE_UDINT"); break;
case Bytecode.OpCodes.GE_UDINT: Console.WriteLine("GE_UDINT"); break;
case Bytecode.OpCodes.EQ_UDINT: Console.WriteLine("EQ_UDINT"); break;
case Bytecode.OpCodes.NEQ_UDINT: Console.WriteLine("NEQ_UDINT"); break;
case Bytecode.OpCodes.LT_ULINT: Console.WriteLine("LT_ULINT"); break;
case Bytecode.OpCodes.GT_ULINT: Console.WriteLine("GT_ULINT"); break;
case Bytecode.OpCodes.LE_ULINT: Console.WriteLine("LE_ULINT"); break;
case Bytecode.OpCodes.GE_ULINT: Console.WriteLine("GE_ULINT"); break;
case Bytecode.OpCodes.EQ_ULINT: Console.WriteLine("EQ_ULINT"); break;
case Bytecode.OpCodes.NEQ_ULINT: Console.WriteLine("NEQ_ULINT"); break;
// Floating point comparisons
case Bytecode.OpCodes.LT_REAL: Console.WriteLine("LT_REAL"); break;
case Bytecode.OpCodes.GT_REAL: Console.WriteLine("GT_REAL"); break;
case Bytecode.OpCodes.LE_REAL: Console.WriteLine("LE_REAL"); break;
case Bytecode.OpCodes.GE_REAL: Console.WriteLine("GE_REAL"); break;
case Bytecode.OpCodes.EQ_REAL: Console.WriteLine("EQ_REAL"); break;
case Bytecode.OpCodes.NEQ_REAL: Console.WriteLine("NEQ_REAL"); break;
case Bytecode.OpCodes.LT_LREAL: Console.WriteLine("LT_LREAL"); break;
case Bytecode.OpCodes.GT_LREAL: Console.WriteLine("GT_LREAL"); break;
case Bytecode.OpCodes.LE_LREAL: Console.WriteLine("LE_LREAL"); break;
case Bytecode.OpCodes.GE_LREAL: Console.WriteLine("GE_LREAL"); break;
case Bytecode.OpCodes.EQ_LREAL: Console.WriteLine("EQ_LREAL"); break;
case Bytecode.OpCodes.NEQ_LREAL: Console.WriteLine("NEQ_LREAL"); break;
default: Console.WriteLine($"{Bytecode.OpName(op)}"); break;
}
}
}
static ushort ReadU16(byte[] a, ref int ip) {
ushort v = (ushort)(a[ip] | (a[ip+1] << 8));
ip += 2;
return v;
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\STCompiler.Common\STCompiler.Common.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,36 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"STCompiler.Disassembler/1.0.0": {
"dependencies": {
"STCompiler.Common": "1.0.0"
},
"runtime": {
"STCompiler.Disassembler.dll": {}
}
},
"STCompiler.Common/1.0.0": {
"runtime": {
"STCompiler.Common.dll": {}
}
}
}
},
"libraries": {
"STCompiler.Disassembler/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"STCompiler.Common/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

View File

@ -0,0 +1,12 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
"configProperties": {
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("STCompiler.Disassembler")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5a002806a9ee3a7389c5a14069c534271a20a9ac")]
[assembly: System.Reflection.AssemblyProductAttribute("STCompiler.Disassembler")]
[assembly: System.Reflection.AssemblyTitleAttribute("STCompiler.Disassembler")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Von der MSBuild WriteCodeFragment-Klasse generiert.

View File

@ -0,0 +1 @@
127117f0342214ac98d3bad7b812bb24f45bbb46074eff037772675e34d8b027

View File

@ -0,0 +1,13 @@
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = STCompiler.Disassembler
build_property.ProjectDir = /home/martin/Projects/STCompiler/STCompiler.Disassembler/
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =

View File

@ -0,0 +1,8 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@ -0,0 +1 @@
fed524f70924a7a6242fb1ec88f1925ce6879f83c899f8ed3dffe3ffd15c88b0

View File

@ -0,0 +1,18 @@
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Disassembler
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Disassembler.deps.json
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Disassembler.runtimeconfig.json
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Disassembler.dll
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Disassembler.pdb
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Common.dll
/home/martin/Projects/STCompiler/STCompiler.Disassembler/bin/Debug/net8.0/STCompiler.Common.pdb
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.csproj.AssemblyReference.cache
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.GeneratedMSBuildEditorConfig.editorconfig
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.AssemblyInfoInputs.cache
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.AssemblyInfo.cs
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.csproj.CoreCompileInputs.cache
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.csproj.CopyComplete
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.dll
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/refint/STCompiler.Disassembler.dll
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.pdb
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/STCompiler.Disassembler.genruntimeconfig.cache
/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/Debug/net8.0/ref/STCompiler.Disassembler.dll

View File

@ -0,0 +1 @@
0f919aa6f48480752187acc6336f3dd954d81be64b7f977f5e7455370c6b0417

Binary file not shown.

View File

@ -0,0 +1,130 @@
{
"format": 1,
"restore": {
"/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj": {}
},
"projects": {
"/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"projectName": "STCompiler.Common",
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj",
"packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projects/STCompiler/STCompiler.Common/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"downloadDependencies": [
{
"name": "Microsoft.AspNetCore.App.Ref",
"version": "[8.0.20, 8.0.20]"
}
],
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.120/PortableRuntimeIdentifierGraph.json"
}
}
},
"/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj",
"projectName": "STCompiler.Disassembler",
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj",
"packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {
"/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"downloadDependencies": [
{
"name": "Microsoft.AspNetCore.App.Ref",
"version": "[8.0.20, 8.0.20]"
}
],
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.120/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/home/martin/.nuget/packages/</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/home/martin/.nuget/packages/</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.8.1</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="/home/martin/.nuget/packages/" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />

View File

@ -0,0 +1,95 @@
{
"version": 3,
"targets": {
"net8.0": {
"STCompiler.Common/1.0.0": {
"type": "project",
"framework": ".NETCoreApp,Version=v8.0",
"compile": {
"bin/placeholder/STCompiler.Common.dll": {}
},
"runtime": {
"bin/placeholder/STCompiler.Common.dll": {}
}
}
}
},
"libraries": {
"STCompiler.Common/1.0.0": {
"type": "project",
"path": "../STCompiler.Common/STCompiler.Common.csproj",
"msbuildProject": "../STCompiler.Common/STCompiler.Common.csproj"
}
},
"projectFileDependencyGroups": {
"net8.0": [
"STCompiler.Common >= 1.0.0"
]
},
"packageFolders": {
"/home/martin/.nuget/packages/": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj",
"projectName": "STCompiler.Disassembler",
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj",
"packagesPath": "/home/martin/.nuget/packages/",
"outputPath": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/obj/",
"projectStyle": "PackageReference",
"configFilePaths": [
"/home/martin/.nuget/NuGet/NuGet.Config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {
"/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj": {
"projectPath": "/home/martin/Projects/STCompiler/STCompiler.Common/STCompiler.Common.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"downloadDependencies": [
{
"name": "Microsoft.AspNetCore.App.Ref",
"version": "[8.0.20, 8.0.20]"
}
],
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.120/PortableRuntimeIdentifierGraph.json"
}
}
}
}

View File

@ -0,0 +1,10 @@
{
"version": 2,
"dgSpecHash": "s0tj8ngaSHf4PEU0x7K1E9bEHGayTmYwySGUMOEav8+YNokCEe1HNBdMoO+cEMLuxPCm3+TQcBxCGTSvXq6UBQ==",
"success": true,
"projectFilePath": "/home/martin/Projects/STCompiler/STCompiler.Disassembler/STCompiler.Disassembler.csproj",
"expectedPackageFiles": [
"/home/martin/.nuget/packages/microsoft.aspnetcore.app.ref/8.0.20/microsoft.aspnetcore.app.ref.8.0.20.nupkg.sha512"
],
"logs": []
}

View File

@ -0,0 +1,704 @@
using System;
using System.IO;
using STCompiler.Common;
using System.Collections.Generic;
using System.Diagnostics;
class Program {
static int Main(string[] args) {
bool verbose = true; // Default to verbose mode
if (args.Length > 0 && args[0] == "--verbose") {
verbose = true;
args = args[1..]; // Remove the --verbose argument from further processing
} else {
verbose = false;
}
if (args.Length < 1) {
Console.WriteLine("Usage: StSim [--verbose|--quiet] <file.stbc>");
return 1;
}
var path = args[0];
if (!File.Exists(path)) { Console.WriteLine("File not found: " + path); return 2; }
var data = File.ReadAllBytes(path);
try { Simulate(data, verbose); } catch(Exception ex) { Console.WriteLine("Error: " + ex.Message); return 3; }
return 0;
}
static void Simulate(byte[] data, bool verbose) {
using var ms = new MemoryStream(data);
using var r = new BinaryReader(ms);
string magic = System.Text.Encoding.ASCII.GetString(r.ReadBytes(4));
if (magic != Bytecode.Magic) throw new Exception("Invalid magic");
ushort ver = r.ReadUInt16();
if (verbose) Console.WriteLine($"Version: {ver}");
bool oldFormat = ver < Bytecode.Version;
ushort cycletime = r.ReadUInt16();
if (verbose) Console.WriteLine($"Cycle: {cycletime} ms");
ushort nConsts = r.ReadUInt16();
if (verbose) Console.WriteLine($"Consts: {nConsts}");
var consts = new List<object>();
if (oldFormat) {
// old format: constants stored as 4-byte ints
for (int i = 0; i < nConsts; i++) { int v = r.ReadInt32(); consts.Add(v); if (verbose) Console.WriteLine($" [{i}] = {v}"); }
} else {
for (int i = 0; i < nConsts; i++) {
byte t = r.ReadByte();
switch(t) {
case 1: { long v = r.ReadInt64(); consts.Add(v); if (verbose) Console.WriteLine($" [{i}] (long) = {v}"); break; }
case 2: { double v = r.ReadDouble(); consts.Add(v); if (verbose) Console.WriteLine($" [{i}] (double) = {v}"); break; }
case 3: { float v = r.ReadSingle(); consts.Add(v); if (verbose) Console.WriteLine($" [{i}] (float) = {v}"); break; }
case 4: { int v = r.ReadInt32(); consts.Add(v); if (verbose) Console.WriteLine($" [{i}] (int) = {v}"); break; }
default: { if (verbose) Console.WriteLine($" [{i}] Unknown const type {t}"); break; }
}
}
}
ushort nVars = r.ReadUInt16();
if (verbose) Console.WriteLine($"Vars: {nVars}");
var varTypes = new byte[nVars];
for (int i = 0; i < nVars; i++) { varTypes[i] = r.ReadByte(); if (verbose) Console.WriteLine($" Var[{i}] type = {varTypes[i]}"); }
ushort codeLen = r.ReadUInt16();
if (verbose) Console.WriteLine($"CodeLen: {codeLen} bytes");
var code = r.ReadBytes(codeLen);
// Initialize arrays for element type tracking
var arrayRanges = new Dictionary<int, (int start, int length, VarType elementType)>();
if (verbose) Console.WriteLine("\nAnalyzing variable types:");
for (int i = 0; i < nVars; i++) {
if (verbose) Console.WriteLine($" [{i}] = {(VarType)varTypes[i]}");
}
if (verbose) Console.WriteLine("\nDetecting arrays:");
for (int i = 0; i < nVars; i++) {
if (varTypes[i] == (byte)VarType.ARRAY && i + 2 <= nVars) {
// An array consists of:
// - ARRAY marker at position i
// - ElementType at position i+1
// - Array elements starting at position i+2
int arrayStart = i;
VarType elementType = (VarType)varTypes[i + 1];
int startOfElements = i + 2;
// Count elements until we hit either end of vars or a different type
int length = 0;
for (int j = startOfElements; j < nVars; j++) {
if (varTypes[j] != (byte)elementType) break;
length++;
}
arrayRanges[arrayStart] = (startOfElements, length, elementType);
if (verbose) {
Console.WriteLine($"Found array at index {arrayStart}:");
Console.WriteLine($" - Element type: {elementType}");
Console.WriteLine($" - Elements start at: {startOfElements}");
Console.WriteLine($" - Length: {length}");
}
// Skip past the array definition (ARRAY marker + element type)
i = startOfElements - 1;
}
}
var stack = new Stack<object>();
var vars = new object[nVars];
int ip = 0;
var opcodeExecutionTimes = new Dictionary<byte, long>();
var opcodeCounts = new Dictionary<byte, int>();
var stopwatch = new Stopwatch();
if (verbose) Console.WriteLine("\n--- Disassembly / Simulation ---");
while (ip < code.Length) {
int addr = ip;
byte op = code[ip++];
if (verbose) Console.Write($"{addr:0000}: 0x{op:X2} ");
// Track opcode execution time
stopwatch.Restart();
switch (op) {
case Bytecode.OpCodes.NOP: if (verbose) Console.WriteLine("NOP"); break;
case Bytecode.OpCodes.PUSH_CONST: {
ushort ci = (ushort)(code[ip++] | (code[ip++] << 8));
if (oldFormat) { byte typeMarker = code[ip++]; /* skip legacy type byte */ }
if (verbose) Console.WriteLine($"PUSH_CONST {ci} ({consts[ci]})");
stack.Push(consts[ci]);
break; }
case Bytecode.OpCodes.PUSH_REAL_CONST: {
ushort ci = (ushort)(code[ip++] | (code[ip++] << 8));
if (oldFormat) { byte typeMarker = code[ip++]; /* skip legacy type byte */ }
if (verbose) Console.WriteLine($"PUSH_REAL_CONST {ci} ({consts[ci]})");
stack.Push(consts[ci]);
break; }
case Bytecode.OpCodes.LOAD_VAR: {
ushort vi = (ushort)(code[ip++] | (code[ip++] << 8));
if (verbose) Console.WriteLine($"LOAD_VAR {vi}");
// Check if the variable index is within bounds
if (vi >= vars.Length) {
throw new Exception($"Variable index {vi} is out of bounds (max: {vars.Length - 1})");
}
if (arrayRanges.ContainsKey(vi)) {
var (start, arrayLength, arrayElementType) = arrayRanges[vi];
var arrayData = new object[arrayLength];
Array.Copy(vars, start, arrayData, 0, arrayLength);
stack.Push(arrayData);
} else {
stack.Push(vars[vi]);
}
break;
}
case Bytecode.OpCodes.STORE_VAR: {
ushort vi = (ushort)(code[ip++] | (code[ip++] << 8));
if (verbose) Console.WriteLine($"STORE_VAR {vi}");
// Check if the variable index is within bounds
if (vi >= vars.Length) {
throw new Exception($"Variable index {vi} is out of bounds (max: {vars.Length - 1})");
}
var value = stack.Pop();
if (arrayRanges.ContainsKey(vi)) {
var (start, arrayLength, arrayElementType) = arrayRanges[vi];
if (value is object[] arr) {
Array.Copy(arr, 0, vars, start, Math.Min(arr.Length, arrayLength));
} else {
if (verbose) Console.WriteLine($"Warning: Attempting to store non-array value in array variable at index {vi}");
vars[vi] = value;
}
} else {
vars[vi] = value;
}
break;
}
case Bytecode.OpCodes.JZ: { ushort target = (ushort)(code[ip++] | (code[ip++] << 8)); if (verbose) Console.WriteLine($"JZ addr={target:0000}"); var cond = stack.Pop(); bool isFalse = cond is int ci ? ci == 0 : cond is long cl ? cl == 0L : cond is double cd ? cd == 0.0 : cond == null; if (isFalse) ip = target; break; }
case Bytecode.OpCodes.JMP: {
ushort target = (ushort)(code[ip++] | (code[ip++] << 8));
if (verbose) Console.WriteLine($"JMP addr={target:0000}");
ip = target;
break;
}
case Bytecode.OpCodes.HALT:
if (verbose) Console.WriteLine("HALT");
ip = code.Length;
break;
case Bytecode.OpCodes.ARRAY_BOUNDS_CHECK:
// Extract the array index from the instruction
byte arrayIndex = 0;
if (ip < code.Length) {
arrayIndex = code[ip];
ip++;
}
// Check if this variable is actually an array
if (!arrayRanges.ContainsKey(arrayIndex)) {
if (verbose) Console.WriteLine($"Warning: ARRAY_BOUNDS_CHECK instruction for non-array variable {arrayIndex}");
continue; // Skip this instruction
}
if (verbose) Console.WriteLine($"ARRAY_BOUNDS_CHECK for array at index {arrayIndex}");
if (stack.Count < 1) {
throw new Exception("Stack underflow during array bounds check");
}
// Convert index value safely
var indexObj = stack.Pop();
int index = Convert.ToInt32(indexObj);
// The arrayStart from the instruction should be the array marker directly
if (!arrayRanges.TryGetValue(arrayIndex, out var arrayInfo)) {
// This should not happen since we checked above
throw new Exception($"Array metadata missing for index {arrayIndex}");
}
var (elementsStart, length, elementType) = arrayInfo;
// Adjust for IEC array indexing (1-based)
int adjustedIndex = index - 1;
if (adjustedIndex < 0 || adjustedIndex >= length) {
throw new Exception($"Array index {index} out of bounds [1..{length}]");
}
// Calculate the actual element address
stack.Push(elementsStart + adjustedIndex);
if (verbose) Console.WriteLine($"Accessing element {index} of array, mapped to variable index {elementsStart + adjustedIndex}");
break;
default:
// fallback handlers
if (Bytecode.OpName(op).StartsWith("ADD_") || Bytecode.OpName(op).StartsWith("SUB_") || Bytecode.OpName(op).StartsWith("MUL_") || Bytecode.OpName(op).StartsWith("DIV_")) {
dynamic b = stack.Pop(); dynamic a = stack.Pop();
if (Bytecode.OpName(op).StartsWith("ADD_")) stack.Push(a + b);
else if (Bytecode.OpName(op).StartsWith("SUB_")) stack.Push(a - b);
else if (Bytecode.OpName(op).StartsWith("MUL_")) stack.Push(a * b);
else stack.Push(a / b);
break;
}
if (Bytecode.OpName(op).StartsWith("LT_") || Bytecode.OpName(op).StartsWith("GT_") || Bytecode.OpName(op).StartsWith("LE_") || Bytecode.OpName(op).StartsWith("GE_") || Bytecode.OpName(op).StartsWith("EQ_") || Bytecode.OpName(op).StartsWith("NEQ_")) {
dynamic rVal = stack.Pop(); dynamic lVal = stack.Pop();
// Optimized comparison handler with type-specific operations
switch (op) {
// Signed comparisons
case Bytecode.OpCodes.LT_SINT:
if (lVal is byte lb_s && rVal is byte rb_s) stack.Push(lb_s < rb_s ? 1 : 0);
else if (lVal is short ls_s && rVal is short rs_s) stack.Push(ls_s < rs_s ? 1 : 0);
else if (lVal is int li1_s && rVal is int ri1_s) stack.Push(li1_s < ri1_s ? 1 : 0);
else if (lVal is long ll_s && rVal is long rl_s) stack.Push(ll_s < rl_s ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) < Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_SINT:
if (lVal is byte lb_gt && rVal is byte rb_gt) stack.Push(lb_gt > rb_gt ? 1 : 0);
else if (lVal is short ls_gt && rVal is short rs_gt) stack.Push(ls_gt > rs_gt ? 1 : 0);
else if (lVal is int li2_gt && rVal is int ri2_gt) stack.Push(li2_gt > ri2_gt ? 1 : 0);
else if (lVal is long ll_gt && rVal is long rl_gt) stack.Push(ll_gt > rl_gt ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) > Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_SINT:
if (lVal is byte lb_le && rVal is byte rb_le) stack.Push(lb_le <= rb_le ? 1 : 0);
else if (lVal is short ls_le && rVal is short rs_le) stack.Push(ls_le <= rs_le ? 1 : 0);
else if (lVal is int li3_le && rVal is int ri3_le) stack.Push(li3_le <= ri3_le ? 1 : 0);
else if (lVal is long ll_le && rVal is long rl_le) stack.Push(ll_le <= rl_le ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) <= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_SINT:
if (lVal is byte lb_ge && rVal is byte rb_ge) stack.Push(lb_ge >= rb_ge ? 1 : 0);
else if (lVal is short ls_ge && rVal is short rs_ge) stack.Push(ls_ge >= rs_ge ? 1 : 0);
else if (lVal is int li4_ge && rVal is int ri4_ge) stack.Push(li4_ge >= ri4_ge ? 1 : 0);
else if (lVal is long ll_ge && rVal is long rl_ge) stack.Push(ll_ge >= rl_ge ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) >= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_SINT:
if (lVal is byte lb_eq && rVal is byte rb_eq) stack.Push(lb_eq == rb_eq ? 1 : 0);
else if (lVal is short ls_eq && rVal is short rs_eq) stack.Push(ls_eq == rs_eq ? 1 : 0);
else if (lVal is int li5_eq && rVal is int ri5_eq) stack.Push(li5_eq == ri5_eq ? 1 : 0);
else if (lVal is long ll_eq && rVal is long rl_eq) stack.Push(ll_eq == rl_eq ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_SINT:
if (lVal is byte lb_ne && rVal is byte rb_ne) stack.Push(lb_ne != rb_ne ? 1 : 0);
else if (lVal is short ls_ne && rVal is short rs_ne) stack.Push(ls_ne != rs_ne ? 1 : 0);
else if (lVal is int li6_ne && rVal is int ri6_ne) stack.Push(li6_ne != ri6_ne ? 1 : 0);
else if (lVal is long ll_ne && rVal is long rl_ne) stack.Push(ll_ne != rl_ne ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_INT:
if (lVal is byte lb_lt && rVal is byte rb_lt) stack.Push(lb_lt < rb_lt ? 1 : 0);
else if (lVal is short ls_lt && rVal is short rs_lt) stack.Push(ls_lt < rs_lt ? 1 : 0);
else if (lVal is int li1_lt && rVal is int ri1_lt) stack.Push(li1_lt < ri1_lt ? 1 : 0);
else if (lVal is long ll_lt && rVal is long rl_lt) stack.Push(ll_lt < rl_lt ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) < Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_INT:
if (lVal is byte lb_gt2 && rVal is byte rb_gt2) stack.Push(lb_gt2 > rb_gt2 ? 1 : 0);
else if (lVal is short ls_gt2 && rVal is short rs_gt2) stack.Push(ls_gt2 > rs_gt2 ? 1 : 0);
else if (lVal is int li2_gt2 && rVal is int ri2_gt2) stack.Push(li2_gt2 > ri2_gt2 ? 1 : 0);
else if (lVal is long ll_gt2 && rVal is long rl_gt2) stack.Push(ll_gt2 > rl_gt2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) > Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_INT:
if (lVal is byte lb_le2 && rVal is byte rb_le2) stack.Push(lb_le2 <= rb_le2 ? 1 : 0);
else if (lVal is short ls_le2 && rVal is short rs_le2) stack.Push(ls_le2 <= rs_le2 ? 1 : 0);
else if (lVal is int li3_le2 && rVal is int ri3_le2) stack.Push(li3_le2 <= ri3_le2 ? 1 : 0);
else if (lVal is long ll_le2 && rVal is long rl_le2) stack.Push(ll_le2 <= rl_le2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) <= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_INT:
if (lVal is byte lb_ge2 && rVal is byte rb_ge2) stack.Push(lb_ge2 >= rb_ge2 ? 1 : 0);
else if (lVal is short ls_ge2 && rVal is short rs_ge2) stack.Push(ls_ge2 >= rs_ge2 ? 1 : 0);
else if (lVal is int li4_ge2 && rVal is int ri4_ge2) stack.Push(li4_ge2 >= ri4_ge2 ? 1 : 0);
else if (lVal is long ll_ge2 && rVal is long rl_ge2) stack.Push(ll_ge2 >= rl_ge2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) >= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_INT:
if (lVal is byte lb_eq2 && rVal is byte rb_eq2) stack.Push(lb_eq2 == rb_eq2 ? 1 : 0);
else if (lVal is short ls_eq2 && rVal is short rs_eq2) stack.Push(ls_eq2 == rs_eq2 ? 1 : 0);
else if (lVal is int li5_eq2 && rVal is int ri5_eq2) stack.Push(li5_eq2 == ri5_eq2 ? 1 : 0);
else if (lVal is long ll_eq2 && rVal is long rl_eq2) stack.Push(ll_eq2 == rl_eq2 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_INT:
if (lVal is byte lb_ne2 && rVal is byte rb_ne2) stack.Push(lb_ne2 != rb_ne2 ? 1 : 0);
else if (lVal is short ls_ne2 && rVal is short rs_ne2) stack.Push(ls_ne2 != rs_ne2 ? 1 : 0);
else if (lVal is int li6_ne2 && rVal is int ri6_ne2) stack.Push(li6_ne2 != ri6_ne2 ? 1 : 0);
else if (lVal is long ll_ne2 && rVal is long rl_ne2) stack.Push(ll_ne2 != rl_ne2 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_DINT:
if (lVal is byte lb_dint && rVal is byte rb_dint) stack.Push(lb_dint < rb_dint ? 1 : 0);
else if (lVal is short ls_dint && rVal is short rs_dint) stack.Push(ls_dint < rs_dint ? 1 : 0);
else if (lVal is int li1_dint && rVal is int ri1_dint) stack.Push(li1_dint < ri1_dint ? 1 : 0);
else if (lVal is long ll_dint && rVal is long rl_dint) stack.Push(ll_dint < rl_dint ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) < Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_DINT:
if (lVal is byte lb_dint2 && rVal is byte rb_dint2) stack.Push(lb_dint2 > rb_dint2 ? 1 : 0);
else if (lVal is short ls_dint2 && rVal is short rs_dint2) stack.Push(ls_dint2 > rs_dint2 ? 1 : 0);
else if (lVal is int li2_dint && rVal is int ri2_dint) stack.Push(li2_dint > ri2_dint ? 1 : 0);
else if (lVal is long ll_dint2 && rVal is long rl_dint2) stack.Push(ll_dint2 > rl_dint2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) > Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_DINT:
if (lVal is byte lb_dint3 && rVal is byte rb_dint3) stack.Push(lb_dint3 <= rb_dint3 ? 1 : 0);
else if (lVal is short ls_dint3 && rVal is short rs_dint3) stack.Push(ls_dint3 <= rs_dint3 ? 1 : 0);
else if (lVal is int li3_dint && rVal is int ri3_dint) stack.Push(li3_dint <= ri3_dint ? 1 : 0);
else if (lVal is long ll_dint3 && rVal is long rl_dint3) stack.Push(ll_dint3 <= rl_dint3 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) <= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_DINT:
if (lVal is byte lb_dint4 && rVal is byte rb_dint4) stack.Push(lb_dint4 >= rb_dint4 ? 1 : 0);
else if (lVal is short ls_dint4 && rVal is short rs_dint4) stack.Push(ls_dint4 >= rs_dint4 ? 1 : 0);
else if (lVal is int li4_dint && rVal is int ri4_dint) stack.Push(li4_dint >= ri4_dint ? 1 : 0);
else if (lVal is long ll_dint4 && rVal is long rl_dint4) stack.Push(ll_dint4 >= rl_dint4 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) >= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_DINT:
if (lVal is byte lb_dint5 && rVal is byte rb_dint5) stack.Push(lb_dint5 == rb_dint5 ? 1 : 0);
else if (lVal is short ls_dint5 && rVal is short rs_dint5) stack.Push(ls_dint5 == rs_dint5 ? 1 : 0);
else if (lVal is int li5_dint && rVal is int ri5_dint) stack.Push(li5_dint == ri5_dint ? 1 : 0);
else if (lVal is long ll_dint5 && rVal is long rl_dint5) stack.Push(ll_dint5 == rl_dint5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_DINT:
if (lVal is byte lb_dint6 && rVal is byte rb_dint6) stack.Push(lb_dint6 != rb_dint6 ? 1 : 0);
else if (lVal is short ls_dint6 && rVal is short rs_dint6) stack.Push(ls_dint6 != rs_dint6 ? 1 : 0);
else if (lVal is int li6_dint && rVal is int ri6_dint) stack.Push(li6_dint != ri6_dint ? 1 : 0);
else if (lVal is long ll_dint6 && rVal is long rl_dint6) stack.Push(ll_dint6 != rl_dint6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_LINT:
if (lVal is byte lb_lint && rVal is byte rb_lint) stack.Push(lb_lint < rb_lint ? 1 : 0);
else if (lVal is short ls_lint && rVal is short rs_lint) stack.Push(ls_lint < rs_lint ? 1 : 0);
else if (lVal is int li1_lint && rVal is int ri1_lint) stack.Push(li1_lint < ri1_lint ? 1 : 0);
else if (lVal is long ll_lint && rVal is long rl_lint) stack.Push(ll_lint < rl_lint ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) < Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_LINT:
if (lVal is byte lb_lint2 && rVal is byte rb_lint2) stack.Push(lb_lint2 > rb_lint2 ? 1 : 0);
else if (lVal is short ls_lint2 && rVal is short rs_lint2) stack.Push(ls_lint2 > rs_lint2 ? 1 : 0);
else if (lVal is int li2_lint && rVal is int ri2_lint) stack.Push(li2_lint > ri2_lint ? 1 : 0);
else if (lVal is long ll_lint2 && rVal is long rl_lint2) stack.Push(ll_lint2 > rl_lint2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) > Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_LINT:
if (lVal is byte lb_lint3 && rVal is byte rb_lint3) stack.Push(lb_lint3 <= rb_lint3 ? 1 : 0);
else if (lVal is short ls_lint3 && rVal is short rs_lint3) stack.Push(ls_lint3 <= rs_lint3 ? 1 : 0);
else if (lVal is int li3_lint && rVal is int ri3_lint) stack.Push(li3_lint <= ri3_lint ? 1 : 0);
else if (lVal is long ll_lint3 && rVal is long rl_lint3) stack.Push(ll_lint3 <= rl_lint3 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) <= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_LINT:
if (lVal is byte lb_lint4 && rVal is byte rb_lint4) stack.Push(lb_lint4 >= rb_lint4 ? 1 : 0);
else if (lVal is short ls_lint4 && rVal is short rs_lint4) stack.Push(ls_lint4 >= rs_lint4 ? 1 : 0);
else if (lVal is int li4_lint && rVal is int ri4_lint) stack.Push(li4_lint >= ri4_lint ? 1 : 0);
else if (lVal is long ll_lint4 && rVal is long rl_lint4) stack.Push(ll_lint4 >= rl_lint4 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) >= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_LINT:
if (lVal is byte lb_lint5 && rVal is byte rb_lint5) stack.Push(lb_lint5 == rb_lint5 ? 1 : 0);
else if (lVal is short ls_lint5 && rVal is short rs_lint5) stack.Push(ls_lint5 == rs_lint5 ? 1 : 0);
else if (lVal is int li5_lint && rVal is int ri5_lint) stack.Push(li5_lint == ri5_lint ? 1 : 0);
else if (lVal is long ll_lint5 && rVal is long rl_lint5) stack.Push(ll_lint5 == rl_lint5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_LINT:
if (lVal is byte lb_lint6 && rVal is byte rb_lint6) stack.Push(lb_lint6 != rb_lint6 ? 1 : 0);
else if (lVal is short ls_lint6 && rVal is short rs_lint6) stack.Push(ls_lint6 != rs_lint6 ? 1 : 0);
else if (lVal is int li6_lint && rVal is int ri6_lint) stack.Push(li6_lint != ri6_lint ? 1 : 0);
else if (lVal is long ll_lint6 && rVal is long rl_lint6) stack.Push(ll_lint6 != rl_lint6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
// Unsigned comparisons
case Bytecode.OpCodes.LT_USINT:
if (lVal is byte lb_us && rVal is byte rb_us) stack.Push(lb_us < rb_us ? 1 : 0);
else if (lVal is ushort lus_us && rVal is ushort rus_us) stack.Push(lus_us < rus_us ? 1 : 0);
else if (lVal is uint lu1_us && rVal is uint ru1_us) stack.Push(lu1_us < ru1_us ? 1 : 0);
else if (lVal is ulong llu_us && rVal is ulong rlu_us) stack.Push(llu_us < rlu_us ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) < Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_USINT:
if (lVal is byte lb_us2 && rVal is byte rb_us2) stack.Push(lb_us2 > rb_us2 ? 1 : 0);
else if (lVal is ushort lus_us2 && rVal is ushort rus_us2) stack.Push(lus_us2 > rus_us2 ? 1 : 0);
else if (lVal is uint lu2_us && rVal is uint ru2_us) stack.Push(lu2_us > ru2_us ? 1 : 0);
else if (lVal is ulong llu_us2 && rVal is ulong rlu_us2) stack.Push(llu_us2 > rlu_us2 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) > Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_USINT:
if (lVal is byte lb_us3 && rVal is byte rb_us3) stack.Push(lb_us3 <= rb_us3 ? 1 : 0);
else if (lVal is ushort lus_us3 && rVal is ushort rus_us3) stack.Push(lus_us3 <= rus_us3 ? 1 : 0);
else if (lVal is uint lu3_us && rVal is uint ru3_us) stack.Push(lu3_us <= ru3_us ? 1 : 0);
else if (lVal is ulong llu_us3 && rVal is ulong rlu_us3) stack.Push(llu_us3 <= rlu_us3 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) <= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_USINT:
if (lVal is byte lb_us4 && rVal is byte rb_us4) stack.Push(lb_us4 >= rb_us4 ? 1 : 0);
else if (lVal is ushort lus_us4 && rVal is ushort rus_us4) stack.Push(lus_us4 >= rus_us4 ? 1 : 0);
else if (lVal is uint lu4_us && rVal is uint ru4_us) stack.Push(lu4_us >= ru4_us ? 1 : 0);
else if (lVal is ulong llu_us4 && rVal is ulong rlu_us4) stack.Push(llu_us4 >= rlu_us4 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) >= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_USINT:
if (lVal is byte lb_us5 && rVal is byte rb_us5) stack.Push(lb_us5 == rb_us5 ? 1 : 0);
else if (lVal is ushort lus_us5 && rVal is ushort rus_us5) stack.Push(lus_us5 == rus_us5 ? 1 : 0);
else if (lVal is uint lu5_us && rVal is uint ru5_us) stack.Push(lu5_us == ru5_us ? 1 : 0);
else if (lVal is ulong llu_us5 && rVal is ulong rlu_us5) stack.Push(llu_us5 == rlu_us5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_USINT:
if (lVal is byte lb_us6 && rVal is byte rb_us6) stack.Push(lb_us6 != rb_us6 ? 1 : 0);
else if (lVal is ushort lus_us6 && rVal is ushort rus_us6) stack.Push(lus_us6 != rus_us6 ? 1 : 0);
else if (lVal is uint lu6_us && rVal is uint ru6_us) stack.Push(lu6_us != ru6_us ? 1 : 0);
else if (lVal is ulong llu_us6 && rVal is ulong rlu_us6) stack.Push(llu_us6 != rlu_us6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_UINT:
if (lVal is byte lb_uint && rVal is byte rb_uint) stack.Push(lb_uint < rb_uint ? 1 : 0);
else if (lVal is ushort lus_uint && rVal is ushort rus_uint) stack.Push(lus_uint < rus_uint ? 1 : 0);
else if (lVal is uint lu1_uint && rVal is uint ru1_uint) stack.Push(lu1_uint < ru1_uint ? 1 : 0);
else if (lVal is ulong llu_uint && rVal is ulong rlu_uint) stack.Push(llu_uint < rlu_uint ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) < Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_UINT:
if (lVal is byte lb_uint2 && rVal is byte rb_uint2) stack.Push(lb_uint2 > rb_uint2 ? 1 : 0);
else if (lVal is ushort lus_uint2 && rVal is ushort rus_uint2) stack.Push(lus_uint2 > rus_uint2 ? 1 : 0);
else if (lVal is uint lu2_uint && rVal is uint ru2_uint) stack.Push(lu2_uint > ru2_uint ? 1 : 0);
else if (lVal is ulong llu_uint2 && rVal is ulong rlu_uint2) stack.Push(llu_uint2 > rlu_uint2 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) > Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_UINT:
if (lVal is byte lb_uint3 && rVal is byte rb_uint3) stack.Push(lb_uint3 <= rb_uint3 ? 1 : 0);
else if (lVal is ushort lus_uint3 && rVal is ushort rus_uint3) stack.Push(lus_uint3 <= rus_uint3 ? 1 : 0);
else if (lVal is uint lu3_uint && rVal is uint ru3_uint) stack.Push(lu3_uint <= ru3_uint ? 1 : 0);
else if (lVal is ulong llu_uint3 && rVal is ulong rlu_uint3) stack.Push(llu_uint3 <= rlu_uint3 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) <= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_UINT:
if (lVal is byte lb_uint4 && rVal is byte rb_uint4) stack.Push(lb_uint4 >= rb_uint4 ? 1 : 0);
else if (lVal is ushort lus_uint4 && rVal is ushort rus_uint4) stack.Push(lus_uint4 >= rus_uint4 ? 1 : 0);
else if (lVal is uint lu4_uint && rVal is uint ru4_uint) stack.Push(lu4_uint >= ru4_uint ? 1 : 0);
else if (lVal is ulong llu_uint4 && rVal is ulong rlu_uint4) stack.Push(llu_uint4 >= rlu_uint4 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) >= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_UINT:
if (lVal is byte lb_uint5 && rVal is byte rb_uint5) stack.Push(lb_uint5 == rb_uint5 ? 1 : 0);
else if (lVal is ushort lus_uint5 && rVal is ushort rus_uint5) stack.Push(lus_uint5 == rus_uint5 ? 1 : 0);
else if (lVal is uint lu5_uint && rVal is uint ru5_uint) stack.Push(lu5_uint == ru5_uint ? 1 : 0);
else if (lVal is ulong llu_uint5 && rVal is ulong rlu_uint5) stack.Push(llu_uint5 == rlu_uint5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_UINT:
if (lVal is byte lb_uint6 && rVal is byte rb_uint6) stack.Push(lb_uint6 != rb_uint6 ? 1 : 0);
else if (lVal is ushort lus_uint6 && rVal is ushort rus_uint6) stack.Push(lus_uint6 != rus_uint6 ? 1 : 0);
else if (lVal is uint lu6_uint && rVal is uint ru6_uint) stack.Push(lu6_uint != ru6_uint ? 1 : 0);
else if (lVal is ulong llu_uint6 && rVal is ulong rlu_uint6) stack.Push(llu_uint6 != rlu_uint6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_UDINT:
if (lVal is byte lb_udint && rVal is byte rb_udint) stack.Push(lb_udint < rb_udint ? 1 : 0);
else if (lVal is ushort lus_udint && rVal is ushort rus_udint) stack.Push(lus_udint < rus_udint ? 1 : 0);
else if (lVal is uint lu1_udint && rVal is uint ru1_udint) stack.Push(lu1_udint < ru1_udint ? 1 : 0);
else if (lVal is ulong llu_udint && rVal is ulong rlu_udint) stack.Push(llu_udint < rlu_udint ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) < Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_UDINT:
if (lVal is byte lb_udint2 && rVal is byte rb_udint2) stack.Push(lb_udint2 > rb_udint2 ? 1 : 0);
else if (lVal is ushort lus_udint2 && rVal is ushort rus_udint2) stack.Push(lus_udint2 > rus_udint2 ? 1 : 0);
else if (lVal is uint lu2_udint && rVal is uint ru2_udint) stack.Push(lu2_udint > ru2_udint ? 1 : 0);
else if (lVal is ulong llu_udint2 && rVal is ulong rlu_udint2) stack.Push(llu_udint2 > rlu_udint2 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) > Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_UDINT:
if (lVal is byte lb_udint3 && rVal is byte rb_udint3) stack.Push(lb_udint3 <= rb_udint3 ? 1 : 0);
else if (lVal is ushort lus_udint3 && rVal is ushort rus_udint3) stack.Push(lus_udint3 <= rus_udint3 ? 1 : 0);
else if (lVal is uint lu3_udint && rVal is uint ru3_udint) stack.Push(lu3_udint <= ru3_udint ? 1 : 0);
else if (lVal is ulong llu_udint3 && rVal is ulong rlu_udint3) stack.Push(llu_udint3 <= rlu_udint3 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) <= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_UDINT:
if (lVal is byte lb_udint4 && rVal is byte rb_udint4) stack.Push(lb_udint4 >= rb_udint4 ? 1 : 0);
else if (lVal is ushort lus_udint4 && rVal is ushort rus_udint4) stack.Push(lus_udint4 >= rus_udint4 ? 1 : 0);
else if (lVal is uint lu4_udint && rVal is uint ru4_udint) stack.Push(lu4_udint >= ru4_udint ? 1 : 0);
else if (lVal is ulong llu_udint4 && rVal is ulong rlu_udint4) stack.Push(llu_udint4 >= rlu_udint4 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) >= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_UDINT:
if (lVal is byte lb_udint5 && rVal is byte rb_udint5) stack.Push(lb_udint5 == rb_udint5 ? 1 : 0);
else if (lVal is ushort lus_udint5 && rVal is ushort rus_udint5) stack.Push(lus_udint5 == rus_udint5 ? 1 : 0);
else if (lVal is uint lu5_udint && rVal is uint ru5_udint) stack.Push(lu5_udint == ru5_udint ? 1 : 0);
else if (lVal is ulong llu_udint5 && rVal is ulong rlu_udint5) stack.Push(llu_udint5 == rlu_udint5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_UDINT:
if (lVal is byte lb_udint6 && rVal is byte rb_udint6) stack.Push(lb_udint6 != rb_udint6 ? 1 : 0);
else if (lVal is ushort lus_udint6 && rVal is ushort rus_udint6) stack.Push(lus_udint6 != rus_udint6 ? 1 : 0);
else if (lVal is uint lu6_udint && rVal is uint ru6_udint) stack.Push(lu6_udint != ru6_udint ? 1 : 0);
else if (lVal is ulong llu_udint6 && rVal is ulong rlu_udint6) stack.Push(llu_udint6 != rlu_udint6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_ULINT:
if (lVal is byte lb_ulint && rVal is byte rb_ulint) stack.Push(lb_ulint < rb_ulint ? 1 : 0);
else if (lVal is ushort lus_ulint && rVal is ushort rus_ulint) stack.Push(lus_ulint < rus_ulint ? 1 : 0);
else if (lVal is uint lu1_ulint && rVal is uint ru1_ulint) stack.Push(lu1_ulint < ru1_ulint ? 1 : 0);
else if (lVal is ulong llu_ulint && rVal is ulong rlu_ulint) stack.Push(llu_ulint < rlu_ulint ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) < Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_ULINT:
if (lVal is byte lb_ulint2 && rVal is byte rb_ulint2) stack.Push(lb_ulint2 > rb_ulint2 ? 1 : 0);
else if (lVal is ushort lus_ulint2 && rVal is ushort rus_ulint2) stack.Push(lus_ulint2 > rus_ulint2 ? 1 : 0);
else if (lVal is uint lu2_ulint && rVal is uint ru2_ulint) stack.Push(lu2_ulint > ru2_ulint ? 1 : 0);
else if (lVal is ulong llu_ulint2 && rVal is ulong rlu_ulint2) stack.Push(llu_ulint2 > rlu_ulint2 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) > Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_ULINT:
if (lVal is byte lb_ulint3 && rVal is byte rb_ulint3) stack.Push(lb_ulint3 <= rb_ulint3 ? 1 : 0);
else if (lVal is ushort lus_ulint3 && rVal is ushort rus_ulint3) stack.Push(lus_ulint3 <= rus_ulint3 ? 1 : 0);
else if (lVal is uint lu3_ulint && rVal is uint ru3_ulint) stack.Push(lu3_ulint <= ru3_ulint ? 1 : 0);
else if (lVal is ulong llu_ulint3 && rVal is ulong rlu_ulint3) stack.Push(llu_ulint3 <= rlu_ulint3 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) <= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_ULINT:
if (lVal is byte lb_ulint4 && rVal is byte rb_ulint4) stack.Push(lb_ulint4 >= rb_ulint4 ? 1 : 0);
else if (lVal is ushort lus_ulint4 && rVal is ushort rus_ulint4) stack.Push(lus_ulint4 >= rus_ulint4 ? 1 : 0);
else if (lVal is uint lu4_ulint && rVal is uint ru4_ulint) stack.Push(lu4_ulint >= ru4_ulint ? 1 : 0);
else if (lVal is ulong llu_ulint4 && rVal is ulong rlu_ulint4) stack.Push(llu_ulint4 >= rlu_ulint4 ? 1 : 0);
else stack.Push(Convert.ToUInt64(lVal) >= Convert.ToUInt64(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_ULINT:
if (lVal is byte lb_ulint5 && rVal is byte rb_ulint5) stack.Push(lb_ulint5 == rb_ulint5 ? 1 : 0);
else if (lVal is ushort lus_ulint5 && rVal is ushort rus_ulint5) stack.Push(lus_ulint5 == rus_ulint5 ? 1 : 0);
else if (lVal is uint lu5_ulint && rVal is uint ru5_ulint) stack.Push(lu5_ulint == ru5_ulint ? 1 : 0);
else if (lVal is ulong llu_ulint5 && rVal is ulong rlu_ulint5) stack.Push(llu_ulint5 == rlu_ulint5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_ULINT:
if (lVal is byte lb_ulint6 && rVal is byte rb_ulint6) stack.Push(lb_ulint6 != rb_ulint6 ? 1 : 0);
else if (lVal is ushort lus_ulint6 && rVal is ushort rus_ulint6) stack.Push(lus_ulint6 != rus_ulint6 ? 1 : 0);
else if (lVal is uint lu6_ulint && rVal is uint ru6_ulint) stack.Push(lu6_ulint != ru6_ulint ? 1 : 0);
else if (lVal is ulong llu_ulint6 && rVal is ulong rlu_ulint6) stack.Push(llu_ulint6 != rlu_ulint6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
// Floating point comparisons
case Bytecode.OpCodes.LT_REAL:
if (lVal is float lf1_real && rVal is float rf1_real) stack.Push(lf1_real < rf1_real ? 1 : 0);
else if (lVal is double ld_real && rVal is double rd_real) stack.Push(ld_real < rd_real ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) < Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_REAL:
if (lVal is float lf2_real && rVal is float rf2_real) stack.Push(lf2_real > rf2_real ? 1 : 0);
else if (lVal is double ld_real2 && rVal is double rd_real2) stack.Push(ld_real2 > rd_real2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) > Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_REAL:
if (lVal is float lf3_real && rVal is float rf3_real) stack.Push(lf3_real <= rf3_real ? 1 : 0);
else if (lVal is double ld_real3 && rVal is double rd_real3) stack.Push(ld_real3 <= rd_real3 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) <= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_REAL:
if (lVal is float lf4_real && rVal is float rf4_real) stack.Push(lf4_real >= rf4_real ? 1 : 0);
else if (lVal is double ld_real4 && rVal is double rd_real4) stack.Push(ld_real4 >= rd_real4 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) >= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_REAL:
if (lVal is float lf5_real && rVal is float rf5_real) stack.Push(lf5_real == rf5_real ? 1 : 0);
else if (lVal is double ld_real5 && rVal is double rd_real5) stack.Push(ld_real5 == rd_real5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_REAL:
if (lVal is float lf6_real && rVal is float rf6_real) stack.Push(lf6_real != rf6_real ? 1 : 0);
else if (lVal is double ld_real6 && rVal is double rd_real6) stack.Push(ld_real6 != rd_real6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LT_LREAL:
if (lVal is float lf1_lreal && rVal is float rf1_lreal) stack.Push(lf1_lreal < rf1_lreal ? 1 : 0);
else if (lVal is double ld_lreal && rVal is double rd_lreal) stack.Push(ld_lreal < rd_lreal ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) < Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GT_LREAL:
if (lVal is float lf2_lreal && rVal is float rf2_lreal) stack.Push(lf2_lreal > rf2_lreal ? 1 : 0);
else if (lVal is double ld_lreal2 && rVal is double rd_lreal2) stack.Push(ld_lreal2 > rd_lreal2 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) > Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.LE_LREAL:
if (lVal is float lf3_lreal && rVal is float rf3_lreal) stack.Push(lf3_lreal <= rf3_lreal ? 1 : 0);
else if (lVal is double ld_lreal3 && rVal is double rd_lreal3) stack.Push(ld_lreal3 <= rd_lreal3 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) <= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.GE_LREAL:
if (lVal is float lf4_lreal && rVal is float rf4_lreal) stack.Push(lf4_lreal >= rf4_lreal ? 1 : 0);
else if (lVal is double ld_lreal4 && rVal is double rd_lreal4) stack.Push(ld_lreal4 >= rd_lreal4 ? 1 : 0);
else stack.Push(Convert.ToDouble(lVal) >= Convert.ToDouble(rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.EQ_LREAL:
if (lVal is float lf5_lreal && rVal is float rf5_lreal) stack.Push(lf5_lreal == rf5_lreal ? 1 : 0);
else if (lVal is double ld_lreal5 && rVal is double rd_lreal5) stack.Push(ld_lreal5 == rd_lreal5 ? 1 : 0);
else stack.Push(Equals(lVal, rVal) ? 1 : 0);
break;
case Bytecode.OpCodes.NEQ_LREAL:
if (lVal is float lf6_lreal && rVal is float rf6_lreal) stack.Push(lf6_lreal != rf6_lreal ? 1 : 0);
else if (lVal is double ld_lreal6 && rVal is double rd_lreal6) stack.Push(ld_lreal6 != rd_lreal6 ? 1 : 0);
else stack.Push(!Equals(lVal, rVal) ? 1 : 0);
break;
}
break;
}
throw new Exception($"Unknown opcode 0x{op:X2}");
}
// Stop the stopwatch and record execution time
stopwatch.Stop();
// Update execution time and count for this opcode
if (opcodeExecutionTimes.ContainsKey(op)) {
opcodeExecutionTimes[op] += stopwatch.ElapsedTicks;
opcodeCounts[op]++;
} else {
opcodeExecutionTimes[op] = stopwatch.ElapsedTicks;
opcodeCounts[op] = 1;
}
}
if (verbose) Console.WriteLine("Execution finished\n");
// Calculate total execution time in milliseconds
long totalTicks = 0;
foreach (var ticks in opcodeExecutionTimes.Values) {
totalTicks += ticks;
}
double totalMs = totalTicks * 1000.0 / Stopwatch.Frequency;
// Detailed variable summary - always show this
Console.WriteLine("=== Variable summary ===");
Console.WriteLine("Index\tType\t\tValue");
for (int i = 0; i < vars.Length; i++) {
string typeName = (i < varTypes.Length) ? ((VarType)varTypes[i]).ToString() : "Unknown";
var value = vars[i] ?? "null";
Console.WriteLine($"{i}\t{typeName.PadRight(8)}\t{value}");
}
// Performance summary
Console.WriteLine("\n=== Performance summary ===");
Console.WriteLine($"Total execution time: {totalMs:F4} ms");
Console.WriteLine($"Target cycle time: {cycletime} ms");
if (totalMs > cycletime) {
Console.WriteLine($"⚠️ Execution time exceeds target cycle time by {totalMs - cycletime:F4} ms");
} else {
Console.WriteLine($"✅ Execution time is within target cycle time by {cycletime - totalMs:F4} ms");
}
// Show opcode execution statistics
Console.WriteLine("\nOpcode execution statistics:");
Console.WriteLine("Opcode\t\tCount\tAvg Time (ms)\tTotal Time (ms)");
foreach (var opcode in opcodeExecutionTimes.Keys) {
double avgTicks = (double)opcodeExecutionTimes[opcode] / opcodeCounts[opcode];
double avgMs = avgTicks * 1000.0 / Stopwatch.Frequency;
double totalMsForOpcode = opcodeExecutionTimes[opcode] * 1000.0 / Stopwatch.Frequency;
Console.WriteLine($"{Bytecode.OpName(opcode).PadRight(16)}\t{opcodeCounts[opcode]}\t{avgMs:F6}\t\t{totalMsForOpcode:F6}");
}
}
}

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\STCompiler.Common\STCompiler.Common.csproj" />
</ItemGroup>
</Project>

Binary file not shown.

View File

@ -0,0 +1,36 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"STCompiler.Simulator/1.0.0": {
"dependencies": {
"STCompiler.Common": "1.0.0"
},
"runtime": {
"STCompiler.Simulator.dll": {}
}
},
"STCompiler.Common/1.0.0": {
"runtime": {
"STCompiler.Common.dll": {}
}
}
}
},
"libraries": {
"STCompiler.Simulator/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"STCompiler.Common/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Some files were not shown because too many files have changed in this diff Show More