diff --git a/Program.cs b/Program.cs index ff2ad51..7f9e79c 100644 --- a/Program.cs +++ b/Program.cs @@ -21,47 +21,77 @@ class Program { } } -// === AST & TYPES === +// === AST === public enum VarType { Int8=1, Int16=2, Int32=3, Byte=4, Bool=5 } -public abstract class StNode{} -public class ProgramNode:StNode{ public List Vars=new(); public List Stmts=new(); } -public class VarDecl:StNode{ public string Name; public VarType Type; public Expr? Init; } -public abstract class Stmt:StNode{} -public class AssignStmt:Stmt{ public string Target; public Expr Expr; } -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;} } -// === LEXER === -public enum TokType { IDENT, INT, ASSIGN, SEMI, LPAREN, RPAREN, PLUS, MINUS, MUL, DIV, PROGRAM, VAR, END_VAR, END_PROGRAM, EOF } +public abstract class StNode {} +public class ProgramNode : StNode { public List Vars=new(); public List Stmts=new(); } +public class VarDecl : StNode { public string Name; public VarType Type; public Expr? Init; } + +public abstract class Stmt : StNode {} +public class AssignStmt : Stmt { public string Target; public Expr Expr; } +public class IfStmt : Stmt { public Expr Cond; public List ThenStmts=new(); public List ElseStmts=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, + PROGRAM, VAR, END_VAR, END_PROGRAM, + EOF +} public class Token { public TokType Type; public string Text; public Token(TokType t,string s){Type=t;Text=s;} } + public class StLexer { private readonly string src; private int i; public StLexer(string s){src=s;} char Peek()=> i i");} + return new Token(TokType.LT,"<"); + } + if (Peek()=='>') { Next(); if (Peek()=='='){Next(); return new Token(TokType.GE,">=");} return new Token(TokType.GT,">"); } + if (Peek()=='=') { Next(); return new Token(TokType.EQ,"="); } + char c=Next(); return c switch { ';'=>new Token(TokType.SEMI,";"), @@ -81,53 +111,105 @@ public class StParser { StLexer lex; Token cur; public StParser(string s){lex=new StLexer(s);cur=lex.NextToken();} void Next()=>cur=lex.NextToken(); - void Expect(TokType t){if(cur.Type!=t)throw new Exception($"Expected {t}, got {cur.Type}");Next();} - public ProgramNode ParseProgram(){ + void Expect(TokType t){ if(cur.Type!=t) throw new Exception($"Expected {t}, got {cur.Type}"); Next(); } + + public ProgramNode ParseProgram() { var p=new ProgramNode(); Expect(TokType.PROGRAM); - if(cur.Type==TokType.IDENT) Next(); - if(cur.Type==TokType.VAR){ + if (cur.Type==TokType.IDENT) Next(); + if (cur.Type==TokType.VAR) { Next(); - while(cur.Type==TokType.IDENT) p.Vars.Add(ParseVarDecl()); + while (cur.Type==TokType.IDENT) p.Vars.Add(ParseVarDecl()); Expect(TokType.END_VAR); } - while(cur.Type!=TokType.END_PROGRAM&&cur.Type!=TokType.EOF) + while (cur.Type!=TokType.END_PROGRAM && cur.Type!=TokType.EOF) p.Stmts.Add(ParseStmt()); Expect(TokType.END_PROGRAM); return p; } - VarDecl ParseVarDecl(){ + + VarDecl ParseVarDecl() { string name=cur.Text; Expect(TokType.IDENT); string tname=cur.Text.ToLowerInvariant(); Expect(TokType.IDENT); - VarType vt=tname switch { - "int8"=>VarType.Int8,"int16"=>VarType.Int16,"int32"=>VarType.Int32, - "byte"=>VarType.Byte,"bool"=>VarType.Bool,_=>throw new Exception($"Unknown type {tname}") + VarType vt = tname switch { + "int8"=>VarType.Int8, "int16"=>VarType.Int16, "int32"=>VarType.Int32, + "byte"=>VarType.Byte, "bool"=>VarType.Bool, + _=>throw new Exception($"Unknown type {tname}") }; Expr? init=null; - if(cur.Type==TokType.ASSIGN){Next();init=ParseExpr();} + if (cur.Type==TokType.ASSIGN){ Next(); init=ParseExpr(); } Expect(TokType.SEMI); return new VarDecl{Name=name,Type=vt,Init=init}; } - Stmt ParseStmt(){ - if(cur.Type==TokType.IDENT){ - var n=cur.Text;Next();Expect(TokType.ASSIGN); - var e=ParseExpr();Expect(TokType.SEMI); + + Stmt ParseStmt() { + if (cur.Type==TokType.IF) return ParseIf(); + if (cur.Type==TokType.IDENT) { + var n=cur.Text; Next(); Expect(TokType.ASSIGN); + var e=ParseExpr(); Expect(TokType.SEMI); return new AssignStmt{Target=n,Expr=e}; } - throw new Exception($"Invalid stmt start {cur.Type}"); + throw new Exception($"Unexpected token in statement: {cur.Type}"); } - Expr ParseExpr(){return ParseAddSub();} - Expr ParseAddSub(){var l=ParseMulDiv();while(cur.Type==TokType.PLUS||cur.Type==TokType.MINUS){var op=cur.Type;Next();var r=ParseMulDiv();l=new BinaryExpr(l,op,r);}return l;} - Expr ParseMulDiv(){var l=ParsePrimary();while(cur.Type==TokType.MUL||cur.Type==TokType.DIV){var op=cur.Type;Next();var r=ParsePrimary();l=new BinaryExpr(l,op,r);}return l;} - Expr ParsePrimary(){ - if(cur.Type==TokType.INT){int v=int.Parse(cur.Text);Next();return new IntExpr(v);} - if(cur.Type==TokType.IDENT){string n=cur.Text;Next();return new VarExpr(n);} - if(cur.Type==TokType.LPAREN){Next();var e=ParseExpr();Expect(TokType.RPAREN);return e;} + + IfStmt ParseIf() { + Next(); // consume IF + var cond = ParseExpr(); + Expect(TokType.THEN); + var ifs = new IfStmt{Cond=cond}; + while(cur.Type!=TokType.ELSE && cur.Type!=TokType.END_IF) + ifs.ThenStmts.Add(ParseStmt()); + if (cur.Type==TokType.ELSE) { + Next(); + while(cur.Type!=TokType.END_IF) + ifs.ElseStmts.Add(ParseStmt()); + } + Expect(TokType.END_IF); + Expect(TokType.SEMI); + return ifs; + } + + Expr ParseExpr() => ParseCompare(); + + Expr ParseCompare() { + var l=ParseAddSub(); + 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(); + l=new BinaryExpr(l,op,r); + } + return l; + } + + Expr ParseAddSub() { + var l=ParseMulDiv(); + while(cur.Type==TokType.PLUS||cur.Type==TokType.MINUS){ + var op=cur.Type;Next(); + var r=ParseMulDiv(); + l=new BinaryExpr(l,op,r); + } + return l; + } + + Expr ParseMulDiv() { + var l=ParsePrimary(); + while(cur.Type==TokType.MUL||cur.Type==TokType.DIV){ + var op=cur.Type;Next(); + var r=ParsePrimary(); + l=new BinaryExpr(l,op,r); + } + return l; + } + + Expr ParsePrimary() { + if (cur.Type==TokType.INT){int v=int.Parse(cur.Text);Next();return new IntExpr(v);} + if (cur.Type==TokType.IDENT){string n=cur.Text;Next();return new VarExpr(n);} + if (cur.Type==TokType.LPAREN){Next();var e=ParseExpr();Expect(TokType.RPAREN);return e;} throw new Exception($"Unexpected token {cur.Type}"); } } -// === BYTECODE EMITTER === +// === BYTECODE === public class BytecodeEmitter { List consts=new(); Dictionary syms=new(); List code=new(); class Symbol{public string Name;public VarType Type;public int Index;} @@ -135,19 +217,12 @@ public class BytecodeEmitter { public void Compile(ProgramNode p){ int idx=0; - // Pass 1: Definiere Variablen (ohne Init) foreach(var v in p.Vars) syms[v.Name]=new Symbol{Name=v.Name,Type=v.Type,Index=idx++}; - // Pass 2: Generiere Initialisierungscode - foreach(var v in p.Vars){ - if(v.Init!=null){ - EmitExpr(v.Init); - EmitByte(0x03); EmitU16((ushort)syms[v.Name].Index); - } - } + foreach(var v in p.Vars) + if(v.Init!=null){ EmitExpr(v.Init); EmitByte(0x03); EmitU16((ushort)syms[v.Name].Index); } - // Main-Code foreach(var s in p.Stmts) EmitStmt(s); @@ -155,25 +230,46 @@ public class BytecodeEmitter { } void EmitStmt(Stmt s){ - if(s is AssignStmt a){ - EmitExpr(a.Expr); - EmitByte(0x03); EmitU16((ushort)syms[a.Target].Index); + switch(s){ + case AssignStmt a: + EmitExpr(a.Expr); + EmitByte(0x03); EmitU16((ushort)syms[a.Target].Index); + break; + case IfStmt iff: + EmitExpr(iff.Cond); + EmitByte(0x20); // JZ (jump if zero) + int jzPos = code.Count; EmitU16(0); + foreach(var st in iff.ThenStmts) EmitStmt(st); + if (iff.ElseStmts.Count>0){ + EmitByte(0x21); // JMP unconditional + int jmpPos = code.Count; EmitU16(0); + PatchJump(jzPos, code.Count); + foreach(var st in iff.ElseStmts) EmitStmt(st); + PatchJump(jmpPos, code.Count); + } else { + PatchJump(jzPos, code.Count); + } + break; + default: + throw new Exception($"Unhandled stmt {s.GetType().Name}"); } } + 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: - if(!syms.ContainsKey(ve.Name)) - throw new Exception($"Unknown variable {ve.Name}"); int vi=syms[ve.Name].Index; EmitByte(0x02); EmitU16((ushort)vi); break; case BinaryExpr be: EmitExpr(be.L); EmitExpr(be.R); EmitByte(be.Op switch { TokType.PLUS=>0x10, TokType.MINUS=>0x11, TokType.MUL=>0x12, TokType.DIV=>0x13, - _=>throw new Exception("bad op") + TokType.LT=>0x14, TokType.GT=>0x15, TokType.LE=>0x16, TokType.GE=>0x17, + TokType.EQ=>0x18, TokType.NEQ=>0x19, + _=>throw new Exception($"Bad op {be.Op}") }); break; default: throw new Exception("bad expr"); diff --git a/bin/Debug/net8.0/Compiler.dll b/bin/Debug/net8.0/Compiler.dll index 2a71ec9..0e2dcac 100644 Binary files a/bin/Debug/net8.0/Compiler.dll and b/bin/Debug/net8.0/Compiler.dll differ diff --git a/bin/Debug/net8.0/Compiler.pdb b/bin/Debug/net8.0/Compiler.pdb index da6d3da..46d6a88 100644 Binary files a/bin/Debug/net8.0/Compiler.pdb and b/bin/Debug/net8.0/Compiler.pdb differ diff --git a/input.st b/input.st index 2bbe478..d5c320a 100644 --- a/input.st +++ b/input.st @@ -1,9 +1,14 @@ PROGRAM Demo VAR - a int16 := 5; - b byte := a + 3; - c int32 := b * 2; + a int16 := 3; + b int16 := 5; + c int16; END_VAR -a := a + c; -END_PROGRAM \ No newline at end of file +IF a < b THEN + c := b - a; +ELSE + c := a - b; +END_IF; + +END_PROGRAM diff --git a/obj/Debug/net8.0/Compiler.AssemblyInfo.cs b/obj/Debug/net8.0/Compiler.AssemblyInfo.cs index dd07c32..a354224 100644 --- a/obj/Debug/net8.0/Compiler.AssemblyInfo.cs +++ b/obj/Debug/net8.0/Compiler.AssemblyInfo.cs @@ -13,10 +13,10 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Compiler")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0e549b6e0a943de51ec80a7d726000f16f5498dd")] [assembly: System.Reflection.AssemblyProductAttribute("Compiler")] [assembly: System.Reflection.AssemblyTitleAttribute("Compiler")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -// Generated by the MSBuild WriteCodeFragment class. +// Von der MSBuild WriteCodeFragment-Klasse generiert. diff --git a/obj/Debug/net8.0/Compiler.AssemblyInfoInputs.cache b/obj/Debug/net8.0/Compiler.AssemblyInfoInputs.cache index e7d5643..b9fa331 100644 --- a/obj/Debug/net8.0/Compiler.AssemblyInfoInputs.cache +++ b/obj/Debug/net8.0/Compiler.AssemblyInfoInputs.cache @@ -1 +1 @@ -595ce70373ddd00c09969aa302b9639c0cb84849045f0786c137462a6cbd7825 +f8051df38f7774448e56868f6a91a5557659f77e4a8558098bdfd3e3ba758ea4 diff --git a/obj/Debug/net8.0/Compiler.dll b/obj/Debug/net8.0/Compiler.dll index 2a71ec9..0e2dcac 100644 Binary files a/obj/Debug/net8.0/Compiler.dll and b/obj/Debug/net8.0/Compiler.dll differ diff --git a/obj/Debug/net8.0/Compiler.pdb b/obj/Debug/net8.0/Compiler.pdb index da6d3da..46d6a88 100644 Binary files a/obj/Debug/net8.0/Compiler.pdb and b/obj/Debug/net8.0/Compiler.pdb differ diff --git a/obj/Debug/net8.0/ref/Compiler.dll b/obj/Debug/net8.0/ref/Compiler.dll index 4bf2c32..a29029a 100644 Binary files a/obj/Debug/net8.0/ref/Compiler.dll and b/obj/Debug/net8.0/ref/Compiler.dll differ diff --git a/obj/Debug/net8.0/refint/Compiler.dll b/obj/Debug/net8.0/refint/Compiler.dll index 4bf2c32..a29029a 100644 Binary files a/obj/Debug/net8.0/refint/Compiler.dll and b/obj/Debug/net8.0/refint/Compiler.dll differ diff --git a/out.bin b/out.bin index 03c49f4..0caddde 100644 Binary files a/out.bin and b/out.bin differ