IF ELSE ENDIF
This commit is contained in:
224
Program.cs
224
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 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{ 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 abstract class StNode {}
|
||||||
public enum TokType { IDENT, INT, ASSIGN, SEMI, LPAREN, RPAREN, PLUS, MINUS, MUL, DIV, PROGRAM, VAR, END_VAR, END_PROGRAM, EOF }
|
public class ProgramNode : StNode { public List<VarDecl> Vars=new(); public List<Stmt> 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<Stmt> ThenStmts=new(); public List<Stmt> 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 Token { public TokType Type; public string Text; public Token(TokType t,string s){Type=t;Text=s;} }
|
||||||
|
|
||||||
public class StLexer {
|
public class StLexer {
|
||||||
private readonly string src; private int i;
|
private readonly string src; private int i;
|
||||||
public StLexer(string s){src=s;}
|
public StLexer(string s){src=s;}
|
||||||
char Peek()=> i<src.Length?src[i]:'\0';
|
char Peek()=> i<src.Length?src[i]:'\0';
|
||||||
char Next()=> i<src.Length?src[i++]:'\0';
|
char Next()=> i<src.Length?src[i++]:'\0';
|
||||||
public Token NextToken(){
|
|
||||||
while(char.IsWhiteSpace(Peek())) Next();
|
public Token NextToken() {
|
||||||
|
while (char.IsWhiteSpace(Peek())) Next();
|
||||||
if (Peek()=='\0') return new Token(TokType.EOF,"");
|
if (Peek()=='\0') return new Token(TokType.EOF,"");
|
||||||
if (char.IsLetter(Peek())||Peek()=='_'){
|
|
||||||
|
if (char.IsLetter(Peek())||Peek()=='_') {
|
||||||
var sb=new StringBuilder();
|
var sb=new StringBuilder();
|
||||||
while(char.IsLetterOrDigit(Peek())||Peek()=='_') sb.Append(Next());
|
while (char.IsLetterOrDigit(Peek())||Peek()=='_') sb.Append(Next());
|
||||||
var s=sb.ToString();
|
var s=sb.ToString();
|
||||||
switch(s.ToUpperInvariant()){
|
switch(s.ToUpperInvariant()) {
|
||||||
case "PROGRAM":return new Token(TokType.PROGRAM,s);
|
case "PROGRAM": return new Token(TokType.PROGRAM,s);
|
||||||
case "VAR":return new Token(TokType.VAR,s);
|
case "VAR": return new Token(TokType.VAR,s);
|
||||||
case "END_VAR":return new Token(TokType.END_VAR,s);
|
case "END_VAR": return new Token(TokType.END_VAR,s);
|
||||||
case "END_PROGRAM":return new Token(TokType.END_PROGRAM,s);
|
case "END_PROGRAM": return new Token(TokType.END_PROGRAM,s);
|
||||||
default:return new Token(TokType.IDENT,s);
|
case "IF": return new Token(TokType.IF,s);
|
||||||
|
case "THEN": return new Token(TokType.THEN,s);
|
||||||
|
case "ELSE": return new Token(TokType.ELSE,s);
|
||||||
|
case "END_IF": return new Token(TokType.END_IF,s);
|
||||||
|
default: return new Token(TokType.IDENT,s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (char.IsDigit(Peek())){
|
|
||||||
|
if (char.IsDigit(Peek())) {
|
||||||
var sb=new StringBuilder();
|
var sb=new StringBuilder();
|
||||||
while(char.IsDigit(Peek())) sb.Append(Next());
|
while (char.IsDigit(Peek())) sb.Append(Next());
|
||||||
return new Token(TokType.INT,sb.ToString());
|
return new Token(TokType.INT,sb.ToString());
|
||||||
}
|
}
|
||||||
if (Peek()==':'){ Next(); if(Peek()=='='){Next(); return new Token(TokType.ASSIGN,":=");} }
|
|
||||||
|
// Operators and symbols
|
||||||
|
if (Peek()==':') { Next(); if(Peek()=='='){Next(); return new Token(TokType.ASSIGN,":=");} }
|
||||||
|
if (Peek()=='<') {
|
||||||
|
Next();
|
||||||
|
if (Peek()=='='){Next(); return new Token(TokType.LE,"<=");}
|
||||||
|
if (Peek()=='>'){Next(); return new Token(TokType.NEQ,"<>");}
|
||||||
|
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();
|
char c=Next();
|
||||||
return c switch {
|
return c switch {
|
||||||
';'=>new Token(TokType.SEMI,";"),
|
';'=>new Token(TokType.SEMI,";"),
|
||||||
@ -81,53 +111,105 @@ public class StParser {
|
|||||||
StLexer lex; Token cur;
|
StLexer lex; Token cur;
|
||||||
public StParser(string s){lex=new StLexer(s);cur=lex.NextToken();}
|
public StParser(string s){lex=new StLexer(s);cur=lex.NextToken();}
|
||||||
void Next()=>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();}
|
void Expect(TokType t){ if(cur.Type!=t) throw new Exception($"Expected {t}, got {cur.Type}"); Next(); }
|
||||||
public ProgramNode ParseProgram(){
|
|
||||||
|
public ProgramNode ParseProgram() {
|
||||||
var p=new ProgramNode();
|
var p=new ProgramNode();
|
||||||
Expect(TokType.PROGRAM);
|
Expect(TokType.PROGRAM);
|
||||||
if(cur.Type==TokType.IDENT) Next();
|
if (cur.Type==TokType.IDENT) Next();
|
||||||
if(cur.Type==TokType.VAR){
|
if (cur.Type==TokType.VAR) {
|
||||||
Next();
|
Next();
|
||||||
while(cur.Type==TokType.IDENT) p.Vars.Add(ParseVarDecl());
|
while (cur.Type==TokType.IDENT) p.Vars.Add(ParseVarDecl());
|
||||||
Expect(TokType.END_VAR);
|
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());
|
p.Stmts.Add(ParseStmt());
|
||||||
Expect(TokType.END_PROGRAM);
|
Expect(TokType.END_PROGRAM);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
VarDecl ParseVarDecl(){
|
|
||||||
|
VarDecl ParseVarDecl() {
|
||||||
string name=cur.Text; Expect(TokType.IDENT);
|
string name=cur.Text; Expect(TokType.IDENT);
|
||||||
string tname=cur.Text.ToLowerInvariant(); Expect(TokType.IDENT);
|
string tname=cur.Text.ToLowerInvariant(); Expect(TokType.IDENT);
|
||||||
VarType vt=tname switch {
|
VarType vt = tname switch {
|
||||||
"int8"=>VarType.Int8,"int16"=>VarType.Int16,"int32"=>VarType.Int32,
|
"int8"=>VarType.Int8, "int16"=>VarType.Int16, "int32"=>VarType.Int32,
|
||||||
"byte"=>VarType.Byte,"bool"=>VarType.Bool,_=>throw new Exception($"Unknown type {tname}")
|
"byte"=>VarType.Byte, "bool"=>VarType.Bool,
|
||||||
|
_=>throw new Exception($"Unknown type {tname}")
|
||||||
};
|
};
|
||||||
Expr? init=null;
|
Expr? init=null;
|
||||||
if(cur.Type==TokType.ASSIGN){Next();init=ParseExpr();}
|
if (cur.Type==TokType.ASSIGN){ Next(); init=ParseExpr(); }
|
||||||
Expect(TokType.SEMI);
|
Expect(TokType.SEMI);
|
||||||
return new VarDecl{Name=name,Type=vt,Init=init};
|
return new VarDecl{Name=name,Type=vt,Init=init};
|
||||||
}
|
}
|
||||||
Stmt ParseStmt(){
|
|
||||||
if(cur.Type==TokType.IDENT){
|
Stmt ParseStmt() {
|
||||||
var n=cur.Text;Next();Expect(TokType.ASSIGN);
|
if (cur.Type==TokType.IF) return ParseIf();
|
||||||
var e=ParseExpr();Expect(TokType.SEMI);
|
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};
|
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;}
|
IfStmt ParseIf() {
|
||||||
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;}
|
Next(); // consume IF
|
||||||
Expr ParsePrimary(){
|
var cond = ParseExpr();
|
||||||
if(cur.Type==TokType.INT){int v=int.Parse(cur.Text);Next();return new IntExpr(v);}
|
Expect(TokType.THEN);
|
||||||
if(cur.Type==TokType.IDENT){string n=cur.Text;Next();return new VarExpr(n);}
|
var ifs = new IfStmt{Cond=cond};
|
||||||
if(cur.Type==TokType.LPAREN){Next();var e=ParseExpr();Expect(TokType.RPAREN);return e;}
|
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}");
|
throw new Exception($"Unexpected token {cur.Type}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === BYTECODE EMITTER ===
|
// === BYTECODE ===
|
||||||
public class BytecodeEmitter {
|
public class BytecodeEmitter {
|
||||||
List<int> consts=new(); Dictionary<string,Symbol> syms=new(); List<byte> code=new();
|
List<int> consts=new(); Dictionary<string,Symbol> syms=new(); List<byte> code=new();
|
||||||
class Symbol{public string Name;public VarType Type;public int Index;}
|
class Symbol{public string Name;public VarType Type;public int Index;}
|
||||||
@ -135,19 +217,12 @@ public class BytecodeEmitter {
|
|||||||
|
|
||||||
public void Compile(ProgramNode p){
|
public void Compile(ProgramNode p){
|
||||||
int idx=0;
|
int idx=0;
|
||||||
// Pass 1: Definiere Variablen (ohne Init)
|
|
||||||
foreach(var v in p.Vars)
|
foreach(var v in p.Vars)
|
||||||
syms[v.Name]=new Symbol{Name=v.Name,Type=v.Type,Index=idx++};
|
syms[v.Name]=new Symbol{Name=v.Name,Type=v.Type,Index=idx++};
|
||||||
|
|
||||||
// Pass 2: Generiere Initialisierungscode
|
foreach(var v in p.Vars)
|
||||||
foreach(var v in p.Vars){
|
if(v.Init!=null){ EmitExpr(v.Init); EmitByte(0x03); EmitU16((ushort)syms[v.Name].Index); }
|
||||||
if(v.Init!=null){
|
|
||||||
EmitExpr(v.Init);
|
|
||||||
EmitByte(0x03); EmitU16((ushort)syms[v.Name].Index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main-Code
|
|
||||||
foreach(var s in p.Stmts)
|
foreach(var s in p.Stmts)
|
||||||
EmitStmt(s);
|
EmitStmt(s);
|
||||||
|
|
||||||
@ -155,25 +230,46 @@ public class BytecodeEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitStmt(Stmt s){
|
void EmitStmt(Stmt s){
|
||||||
if(s is AssignStmt a){
|
switch(s){
|
||||||
EmitExpr(a.Expr);
|
case AssignStmt a:
|
||||||
EmitByte(0x03); EmitU16((ushort)syms[a.Target].Index);
|
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){
|
void EmitExpr(Expr e){
|
||||||
switch(e){
|
switch(e){
|
||||||
case IntExpr ie:
|
case IntExpr ie:
|
||||||
int ci=AddConst(ie.Value); EmitByte(0x01); EmitU16((ushort)ci); break;
|
int ci=AddConst(ie.Value); EmitByte(0x01); EmitU16((ushort)ci); break;
|
||||||
case VarExpr ve:
|
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;
|
int vi=syms[ve.Name].Index; EmitByte(0x02); EmitU16((ushort)vi); break;
|
||||||
case BinaryExpr be:
|
case BinaryExpr be:
|
||||||
EmitExpr(be.L); EmitExpr(be.R);
|
EmitExpr(be.L); EmitExpr(be.R);
|
||||||
EmitByte(be.Op switch {
|
EmitByte(be.Op switch {
|
||||||
TokType.PLUS=>0x10, TokType.MINUS=>0x11, TokType.MUL=>0x12, TokType.DIV=>0x13,
|
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;
|
break;
|
||||||
default: throw new Exception("bad expr");
|
default: throw new Exception("bad expr");
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
13
input.st
13
input.st
@ -1,9 +1,14 @@
|
|||||||
PROGRAM Demo
|
PROGRAM Demo
|
||||||
VAR
|
VAR
|
||||||
a int16 := 5;
|
a int16 := 3;
|
||||||
b byte := a + 3;
|
b int16 := 5;
|
||||||
c int32 := b * 2;
|
c int16;
|
||||||
END_VAR
|
END_VAR
|
||||||
|
|
||||||
a := a + c;
|
IF a < b THEN
|
||||||
|
c := b - a;
|
||||||
|
ELSE
|
||||||
|
c := a - b;
|
||||||
|
END_IF;
|
||||||
|
|
||||||
END_PROGRAM
|
END_PROGRAM
|
||||||
@ -13,10 +13,10 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Compiler")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Compiler")]
|
||||||
[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")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0e549b6e0a943de51ec80a7d726000f16f5498dd")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Compiler")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Compiler")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Compiler")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Compiler")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
// Generated by the MSBuild WriteCodeFragment class.
|
// Von der MSBuild WriteCodeFragment-Klasse generiert.
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
595ce70373ddd00c09969aa302b9639c0cb84849045f0786c137462a6cbd7825
|
f8051df38f7774448e56868f6a91a5557659f77e4a8558098bdfd3e3ba758ea4
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user