Disammbler und Simulator angepasst. Arrays begonnen (defunct)

This commit is contained in:
2025-10-13 12:09:15 +02:00
parent 3cb6373915
commit 74f256efb2
50 changed files with 534 additions and 75 deletions

View File

@ -40,6 +40,9 @@ public static class Bytecode {
public const byte JZ = 0x70;
public const byte JMP = 0x71;
// Array operations
public const byte ARRAY_BOUNDS_CHECK = 0xE0;
public const byte HALT = 0xF0;
}
@ -63,6 +66,7 @@ public static class Bytecode {
{ OpCodes.LT_U, "LT_U" }, { OpCodes.GT_U, "GT_U" }, { OpCodes.LE_U, "LE_U" }, { OpCodes.GE_U, "GE_U" }, { OpCodes.EQ_U, "EQ_U" }, { OpCodes.NEQ_U, "NEQ_U" },
{ OpCodes.LT_F, "LT_F" }, { OpCodes.GT_F, "GT_F" }, { OpCodes.LE_F, "LE_F" }, { OpCodes.GE_F, "GE_F" }, { OpCodes.EQ_F, "EQ_F" }, { OpCodes.NEQ_F, "NEQ_F" },
{ OpCodes.JZ, "JZ" }, { OpCodes.JMP, "JMP" },
{ OpCodes.ARRAY_BOUNDS_CHECK, "ARRAY_BOUNDS_CHECK" },
{ OpCodes.HALT, "HALT" }
};

View File

@ -18,5 +18,24 @@ public enum VarType {
TIME_OF_DAY = 18, // Tageszeit (TOD) -> TimeSpan / DateTime
TOD = TIME_OF_DAY,
DATE_AND_TIME = 19,// Datum + Uhrzeit (DT) -> DateTime
DT = DATE_AND_TIME
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

@ -13,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("STCompiler.Common")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ec4d7dc26ee2fd96db003ab3a6c1af70e02894b9")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3cb63739152cd26ec43993953c7b93d4ab9bcce7")]
[assembly: System.Reflection.AssemblyProductAttribute("STCompiler.Common")]
[assembly: System.Reflection.AssemblyTitleAttribute("STCompiler.Common")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Von der MSBuild WriteCodeFragment-Klasse generiert.
// Generated by the MSBuild WriteCodeFragment class.

View File

@ -1 +1 @@
8cfdc237030ec5222d5f9b3cae794a2e89120fd57c83b878660304653c51962d
72a5aa37f3f499d7234a1ea9f8597714d83c5061f8d255b5e45126805bf484a2

View File

@ -13,8 +13,9 @@ public class VarDecl:StNode{
public abstract class Stmt:StNode{}
public class AssignStmt:Stmt{
required public string Target;
required public Expr Expr;
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;
@ -81,3 +82,13 @@ public class BinaryExpr:Expr {
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

@ -13,6 +13,7 @@ public enum TokType {
WHILE, DO, END_WHILE,
FOR, TO, BY, END_FOR,
PROGRAM, VAR, END_VAR, END_PROGRAM,
ARRAY, OF, LBRACKET, RBRACKET, DOTS,
EOF
}
@ -83,6 +84,8 @@ public class StLexer {
"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)
};
}
@ -92,16 +95,23 @@ public class StLexer {
int startLine = currentLine;
bool isFloat = false;
// Ganze Zahl vor dem Dezimalpunkt
// Ganze Zahl vor dem Dezimalpunkt oder Bereichsoperator
while(char.IsDigit(Peek()))
sb.Append(Next());
// Optional: Dezimalpunkt und Nachkommastellen
// Prüfe auf Bereichsoperator (..) oder Dezimalpunkt (.)
if (Peek() == '.') {
isFloat = true;
sb.Append(Next());
while(char.IsDigit(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
@ -150,10 +160,21 @@ public class StLexer {
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

@ -14,6 +14,7 @@ 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;
@ -61,42 +62,96 @@ public class StParser {
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
string name = cur.Text.ToUpperInvariant(); // Variablennamen in Großbuchstaben
Next();
if (cur.Type != TokType.IDENT) {
AddError("Expected type name");
return null;
}
string tname=cur.Text.ToLowerInvariant();
Next();
VarType? vt = tname 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
};
var vt = ParseType(name);
if (vt == null) {
AddError($"Unknown type '{tname}'");
AddError($"Invalid type for variable '{name}'");
return null;
}
@ -121,6 +176,26 @@ public class StParser {
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;
@ -129,7 +204,7 @@ public class StParser {
var e = ParseExpr();
if (e == null) return null;
if (!Expect(TokType.SEMI)) return null;
return new AssignStmt{Target=target, Expr=e};
return new AssignStmt{Target=target, Index=index, Expr=e};
}
Stmt? ParseStmt(){
@ -318,6 +393,29 @@ public class StParser {
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:
@ -332,4 +430,9 @@ public class StParser {
return null;
}
}
public ArrayType? GetArrayType(string name) {
ArrayType? type;
return arrayTypes.TryGetValue(name, out type) ? type : null;
}
}

View File

@ -33,6 +33,10 @@ class Program {
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}");
@ -52,11 +56,17 @@ 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;
@ -65,8 +75,19 @@ public class BytecodeEmitter {
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) {
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);}
@ -149,8 +170,43 @@ public class BytecodeEmitter {
if (!syms.TryGetValue(a.Target, out symbol)) {
throw new Exception($"Undeclared variable '{a.Target}'");
}
EmitExpr(a.Expr);
EmitByte(Bytecode.OpCodes.STORE_VAR); EmitU16((ushort)symbol.Index);
// 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:
@ -226,14 +282,12 @@ public class BytecodeEmitter {
int ci = AddConst(ie.Value);
EmitByte(Bytecode.OpCodes.PUSH_CONST); // PUSH_CONST
EmitU16((ushort)ci);
// removed emitting the type byte into code stream; type is stored with constant table
break;
case RealExpr re:
int cri = AddConst(re.Value);
EmitByte(Bytecode.OpCodes.PUSH_REAL_CONST); // PUSH_REAL_CONST
EmitU16((ushort)cri);
// removed emitting the type byte into code stream; type is stored with constant table
break;
case VarExpr ve:
@ -244,6 +298,42 @@ public class BytecodeEmitter {
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);
@ -253,14 +343,6 @@ public class BytecodeEmitter {
if (!opCodes.ContainsKey(key)) {
throw new Exception($"Unknown operator '{be.Op}' for type {be.Type}");
}
// Wenn nötig, Typenkonvertierung vor der Operation
// Conversion opcodes removed - runtime will handle type differences using constant type markers and operation opcodes
// if (be.L.Type != be.Type)
// EmitByte((byte)(0x50 + (int)be.Type)); // CONVERT_TO_*
// if (be.R.Type != be.Type)
// EmitByte((byte)(0x50 + (int)be.Type)); // CONVERT_TO_*
EmitByte(opCodes[key]);
break;
}
@ -308,11 +390,29 @@ public class BytecodeEmitter {
}
}
// 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)syms.Count);
var types = new byte[syms.Count];
foreach(var kv in syms)
types[kv.Value.Index] = (byte)kv.Value.Type;
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);

View File

@ -8,8 +8,16 @@ VAR
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;

View File

@ -13,7 +13,7 @@ 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+ec4d7dc26ee2fd96db003ab3a6c1af70e02894b9")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3cb63739152cd26ec43993953c7b93d4ab9bcce7")]
[assembly: System.Reflection.AssemblyProductAttribute("STCompiler.Compiler")]
[assembly: System.Reflection.AssemblyTitleAttribute("STCompiler.Compiler")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@ -1 +1 @@
bcd7f5e5efddd7ac50fcf7487abbb9cf17cacc826a4dbbf1dcb95d96e7974c93
cc701f58f98db810dc498092a84a16f3679a63cf316500de1b157491a545da80

View File

@ -46,10 +46,29 @@ class Program {
byte tb = r.ReadByte();
var vt = (VarType)tb;
varTypes[i] = vt;
if (Enum.IsDefined(typeof(VarType), 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
}
else {
Console.WriteLine($" Var[{i}] type = {tb} (unknown)");
}
}
ushort codeLen = r.ReadUInt16();
@ -71,6 +90,12 @@ class Program {
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;
}
default: Console.WriteLine($"{Bytecode.OpName(op)}"); break;
}
}

View File

@ -13,10 +13,10 @@ 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+ec4d7dc26ee2fd96db003ab3a6c1af70e02894b9")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3cb63739152cd26ec43993953c7b93d4ab9bcce7")]
[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.
// Generated by the MSBuild WriteCodeFragment class.

View File

@ -1 +1 @@
124c0ad3412d8c18080f8da6f8923566217268c81c32c97108af077bc918b46f
2175f71304910ca37c6a64a54785d953c58ecd76944c17fb6fe56c6a95a4d3e4

View File

@ -54,6 +54,42 @@ class Program {
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)>();
Console.WriteLine("\nAnalyzing variable types:");
for (int i = 0; i < nVars; i++) {
Console.WriteLine($" [{i}] = {(VarType)varTypes[i]}");
}
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);
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;
@ -61,7 +97,7 @@ class Program {
while (ip < code.Length) {
int addr = ip;
byte op = code[ip++];
Console.Write($"{addr:0000}: 0x{op:X2} {Bytecode.OpName(op)} ");
Console.Write($"{addr:0000}: 0x{op:X2} ");
switch (op) {
case Bytecode.OpCodes.NOP: Console.WriteLine("NOP"); break;
case Bytecode.OpCodes.PUSH_CONST: {
@ -76,12 +112,103 @@ class Program {
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)); Console.WriteLine($"LOAD_VAR {vi}"); stack.Push(vars[vi]); break; }
case Bytecode.OpCodes.STORE_VAR: { ushort vi = (ushort)(code[ip++] | (code[ip++] << 8)); Console.WriteLine($"STORE_VAR {vi}"); vars[vi] = stack.Pop(); break; }
case Bytecode.OpCodes.JZ: { ushort target = (ushort)(code[ip++] | (code[ip++] << 8)); Console.WriteLine($"JZ addr={target}"); 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)); Console.WriteLine($"JMP addr={target}"); ip = target; break; }
case Bytecode.OpCodes.HALT: Console.WriteLine("HALT"); ip = code.Length; break;
default: Console.WriteLine();
case Bytecode.OpCodes.LOAD_VAR: {
ushort vi = (ushort)(code[ip++] | (code[ip++] << 8));
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));
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 {
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)); 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));
Console.WriteLine($"JMP addr={target:0000}");
ip = target;
break;
}
case Bytecode.OpCodes.HALT:
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)) {
Console.WriteLine($"Warning: ARRAY_BOUNDS_CHECK instruction for non-array variable {arrayIndex}");
continue; // Skip this instruction
}
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);
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);
@ -98,6 +225,47 @@ class Program {
stack.Push(res ? 1 : 0);
break;
}
// Array operations
if (op == Bytecode.OpCodes.ARRAY_BOUNDS_CHECK) {
// Extract the array index from the instruction
byte arrayIndexInner = 0;
if (ip < code.Length) {
arrayIndexInner = code[ip];
ip++;
}
// Check if this variable is actually an array
if (!arrayRanges.ContainsKey(arrayIndexInner)) {
Console.WriteLine($"Warning: ARRAY_BOUNDS_CHECK instruction for non-array variable {arrayIndexInner}");
continue; // Skip this instruction
}
Console.WriteLine($"ARRAY_BOUNDS_CHECK for array at index {arrayIndexInner}");
if (stack.Count < 1) {
throw new Exception("Stack underflow during array bounds check");
}
// Convert index value safely
var indexObjInner = stack.Pop();
int indexInner = Convert.ToInt32(indexObjInner);
// The arrayStart from the instruction should be the array marker directly
if (!arrayRanges.TryGetValue(arrayIndexInner, out var arrayInfoInner)) {
// This should not happen since we checked above
throw new Exception($"Array metadata missing for index {arrayIndexInner}");
}
var (elementsStartInner, lengthInner, elementTypeInner) = arrayInfoInner;
if (indexInner < 0 || indexInner >= lengthInner) {
throw new Exception($"Array index {indexInner} out of bounds [0..{lengthInner-1}]");
}
// Calculate the actual element address
stack.Push(elementsStartInner + indexInner);
Console.WriteLine($"Accessing element {indexInner} of array, mapped to variable index {elementsStartInner + indexInner}");
break;
}
throw new Exception($"Unknown opcode 0x{op:X2}");
}
}

View File

@ -13,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("STCompiler.Simulator")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ec4d7dc26ee2fd96db003ab3a6c1af70e02894b9")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3cb63739152cd26ec43993953c7b93d4ab9bcce7")]
[assembly: System.Reflection.AssemblyProductAttribute("STCompiler.Simulator")]
[assembly: System.Reflection.AssemblyTitleAttribute("STCompiler.Simulator")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Von der MSBuild WriteCodeFragment-Klasse generiert.
// Generated by the MSBuild WriteCodeFragment class.

View File

@ -1 +1 @@
9731a6e2cf5ff58c4c9e71ca1d547507058070586723183af16235f22b593078
b05e088189133b131132a72fa879b53ae0f21051a8f25a2e49266f0f500e3142