WHILE
This commit is contained in:
127
Program.cs
127
Program.cs
@ -23,7 +23,6 @@ class Program {
|
||||
|
||||
// === 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{ public string Name; public VarType Type; public Expr? Init; }
|
||||
@ -31,6 +30,7 @@ public class VarDecl : StNode { public string Name; public VarType Type; public
|
||||
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 class WhileStmt:Stmt{ public Expr Cond; public List<Stmt> Body=new(); }
|
||||
|
||||
public abstract class Expr:StNode{}
|
||||
public class IntExpr:Expr{ public int Value; public IntExpr(int v){Value=v;} }
|
||||
@ -43,6 +43,7 @@ public enum TokType {
|
||||
PLUS, MINUS, MUL, DIV,
|
||||
LT, GT, LE, GE, EQ, NEQ,
|
||||
IF, THEN, ELSE, END_IF,
|
||||
WHILE, DO, END_WHILE,
|
||||
PROGRAM, VAR, END_VAR, END_PROGRAM,
|
||||
EOF
|
||||
}
|
||||
@ -61,18 +62,21 @@ public class StLexer {
|
||||
if (char.IsLetter(Peek())||Peek()=='_'){
|
||||
var sb=new StringBuilder();
|
||||
while (char.IsLetterOrDigit(Peek())||Peek()=='_') sb.Append(Next());
|
||||
var s=sb.ToString();
|
||||
switch(s.ToUpperInvariant()) {
|
||||
case "PROGRAM": return new Token(TokType.PROGRAM,s);
|
||||
case "VAR": return new Token(TokType.VAR,s);
|
||||
case "END_VAR": return new Token(TokType.END_VAR,s);
|
||||
case "END_PROGRAM": return new Token(TokType.END_PROGRAM,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);
|
||||
}
|
||||
var s=sb.ToString().ToUpperInvariant();
|
||||
return s switch {
|
||||
"PROGRAM"=>new Token(TokType.PROGRAM,s),
|
||||
"VAR"=>new Token(TokType.VAR,s),
|
||||
"END_VAR"=>new Token(TokType.END_VAR,s),
|
||||
"END_PROGRAM"=>new Token(TokType.END_PROGRAM,s),
|
||||
"IF"=>new Token(TokType.IF,s),
|
||||
"THEN"=>new Token(TokType.THEN,s),
|
||||
"ELSE"=>new Token(TokType.ELSE,s),
|
||||
"END_IF"=>new Token(TokType.END_IF,s),
|
||||
"WHILE"=>new Token(TokType.WHILE,s),
|
||||
"DO"=>new Token(TokType.DO,s),
|
||||
"END_WHILE"=>new Token(TokType.END_WHILE,s),
|
||||
_=>new Token(TokType.IDENT,s)
|
||||
};
|
||||
}
|
||||
|
||||
if (char.IsDigit(Peek())) {
|
||||
@ -81,7 +85,6 @@ public class StLexer {
|
||||
return new Token(TokType.INT,sb.ToString());
|
||||
}
|
||||
|
||||
// Operators and symbols
|
||||
if (Peek()==':'){ Next(); if(Peek()=='='){Next(); return new Token(TokType.ASSIGN,":=");} }
|
||||
if (Peek()=='<'){
|
||||
Next();
|
||||
@ -89,7 +92,11 @@ public class StLexer {
|
||||
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();
|
||||
if (Peek()=='='){Next(); return new Token(TokType.GE,">=");}
|
||||
return new Token(TokType.GT,">");
|
||||
}
|
||||
if (Peek()=='='){Next();return new Token(TokType.EQ,"=");}
|
||||
|
||||
char c=Next();
|
||||
@ -101,7 +108,7 @@ public class StLexer {
|
||||
'-'=>new Token(TokType.MINUS,"-"),
|
||||
'*'=>new Token(TokType.MUL,"*"),
|
||||
'/'=>new Token(TokType.DIV,"/"),
|
||||
_=>throw new Exception($"Unexpected char '{c}'")
|
||||
_=>throw new Exception($"Unexpected '{c}'")
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -132,8 +139,7 @@ public class StParser {
|
||||
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,
|
||||
"int8"=>VarType.Int8,"int16"=>VarType.Int16,"int32"=>VarType.Int32,"byte"=>VarType.Byte,"bool"=>VarType.Bool,
|
||||
_=>throw new Exception($"Unknown type {tname}")
|
||||
};
|
||||
Expr? init=null;
|
||||
@ -144,33 +150,41 @@ public class StParser {
|
||||
|
||||
Stmt ParseStmt(){
|
||||
if(cur.Type==TokType.IF) return ParseIf();
|
||||
if(cur.Type==TokType.WHILE) return ParseWhile();
|
||||
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($"Unexpected token in statement: {cur.Type}");
|
||||
throw new Exception($"Unexpected token {cur.Type} in stmt");
|
||||
}
|
||||
|
||||
IfStmt ParseIf(){
|
||||
Next(); // consume IF
|
||||
var cond = ParseExpr();
|
||||
Expect(TokType.THEN);
|
||||
var ifs = new IfStmt{Cond=cond};
|
||||
Next();
|
||||
var cond=ParseExpr(); Expect(TokType.THEN);
|
||||
var node=new IfStmt{Cond=cond};
|
||||
while(cur.Type!=TokType.ELSE&&cur.Type!=TokType.END_IF)
|
||||
ifs.ThenStmts.Add(ParseStmt());
|
||||
node.ThenStmts.Add(ParseStmt());
|
||||
if(cur.Type==TokType.ELSE){
|
||||
Next();
|
||||
while(cur.Type!=TokType.END_IF)
|
||||
ifs.ElseStmts.Add(ParseStmt());
|
||||
node.ElseStmts.Add(ParseStmt());
|
||||
}
|
||||
Expect(TokType.END_IF);
|
||||
Expect(TokType.SEMI);
|
||||
return ifs;
|
||||
Expect(TokType.END_IF); Expect(TokType.SEMI);
|
||||
return node;
|
||||
}
|
||||
|
||||
WhileStmt ParseWhile(){
|
||||
Next(); // WHILE
|
||||
var cond=ParseExpr(); Expect(TokType.DO);
|
||||
var ws=new WhileStmt{Cond=cond};
|
||||
while(cur.Type!=TokType.END_WHILE)
|
||||
ws.Body.Add(ParseStmt());
|
||||
Expect(TokType.END_WHILE); Expect(TokType.SEMI);
|
||||
return ws;
|
||||
}
|
||||
|
||||
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){
|
||||
@ -180,27 +194,20 @@ public class StParser {
|
||||
}
|
||||
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);
|
||||
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);
|
||||
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);}
|
||||
@ -235,28 +242,33 @@ public class BytecodeEmitter {
|
||||
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);
|
||||
EmitByte(0x20); int jz=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);
|
||||
EmitByte(0x21); int jmp=code.Count; EmitU16(0);
|
||||
PatchJump(jz,code.Count);
|
||||
foreach(var st in iff.ElseStmts) EmitStmt(st);
|
||||
PatchJump(jmpPos, code.Count);
|
||||
} else {
|
||||
PatchJump(jzPos, code.Count);
|
||||
}
|
||||
PatchJump(jmp,code.Count);
|
||||
} else PatchJump(jz,code.Count);
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unhandled stmt {s.GetType().Name}");
|
||||
|
||||
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;
|
||||
|
||||
default: throw new Exception($"Unknown 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:
|
||||
@ -269,9 +281,8 @@ public class BytecodeEmitter {
|
||||
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,
|
||||
_=>throw new Exception($"Bad op {be.Op}")
|
||||
});
|
||||
break;
|
||||
_=>throw new Exception("bad op")
|
||||
}); break;
|
||||
default: throw new Exception("bad expr");
|
||||
}
|
||||
}
|
||||
@ -279,17 +290,13 @@ public class BytecodeEmitter {
|
||||
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);
|
||||
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)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;
|
||||
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();
|
||||
|
||||
Binary file not shown.
Binary file not shown.
12
input.st
12
input.st
@ -1,14 +1,10 @@
|
||||
PROGRAM Demo
|
||||
VAR
|
||||
a int16 := 3;
|
||||
b int16 := 5;
|
||||
c int16;
|
||||
a int16 := 0;
|
||||
END_VAR
|
||||
|
||||
IF a < b THEN
|
||||
c := b - a;
|
||||
ELSE
|
||||
c := a - b;
|
||||
END_IF;
|
||||
WHILE a < 5 DO
|
||||
a := a + 1;
|
||||
END_WHILE;
|
||||
|
||||
END_PROGRAM
|
||||
|
||||
@ -13,7 +13,7 @@ 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+0e549b6e0a943de51ec80a7d726000f16f5498dd")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+384f9c719795a70259bf409ac57d221baf7a89b1")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Compiler")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Compiler")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@ -1 +1 @@
|
||||
f8051df38f7774448e56868f6a91a5557659f77e4a8558098bdfd3e3ba758ea4
|
||||
42e892e3dd3396a02f45555f279acee4d0083d66ab01ce9b1baf7c02d466a01c
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user