Compiler aufgeräumt

This commit is contained in:
2025-10-12 22:53:30 +02:00
parent ec4d7dc26e
commit 3cb6373915
46 changed files with 591 additions and 594 deletions

View File

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

View File

@ -1 +1 @@
3ee5798b43eb2bdc5cb3333bf67081acc7fb5bfbfd1c25a96138387cd1048786
8cfdc237030ec5222d5f9b3cae794a2e89120fd57c83b878660304653c51962d

View File

@ -0,0 +1,83 @@
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;
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 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;
}
}

View File

@ -0,0 +1,161 @@
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,
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),
_=>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
while(char.IsDigit(Peek()))
sb.Append(Next());
// Optional: Dezimalpunkt und Nachkommastellen
if (Peek() == '.') {
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.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
}
}

View File

@ -0,0 +1,335 @@
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 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.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
};
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;
// 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
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.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;
}
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;
}
}
}

View File

@ -44,590 +44,8 @@ class Program {
}
}
// === AST ===
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
}
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 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;
}
}
// === TOKENIZER ===
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,
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),
_=>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
while(char.IsDigit(Peek()))
sb.Append(Next());
// Optional: Dezimalpunkt und Nachkommastellen
if (Peek() == '.') {
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.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 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 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.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 {
// Boolean
"bool" => VarType.BOOL,
// Unsigned integers
"byte" => VarType.BYTE,
"word" => VarType.WORD,
"dword" => VarType.DWORD,
"lword" => VarType.LWORD,
// Signed integers
"sint" => VarType.SINT,
"int" => VarType.INT,
"dint" => VarType.DINT,
"lint" => VarType.LINT,
// Unsigned integers (alternative names)
"usint" => VarType.USINT,
"uint" => VarType.UINT,
"udint" => VarType.UDINT,
"ulint" => VarType.ULINT,
// Floating point
"real" => VarType.REAL,
"lreal" => VarType.LREAL,
_ => 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;
// 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() {
// Der Aufrufer hat bereits geprüft, dass wir bei einem IDENT sind
string target = cur.Text.ToUpperInvariant(); // Variablennamen in Großbuchstaben
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.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;
}
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;
}
}
}
// 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 {

View File

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

View File

@ -1 +1 @@
f6dd1650f122ef522a0e29b4e2791479bb04684921f3ea39b7363b57cd76e656
bcd7f5e5efddd7ac50fcf7487abbb9cf17cacc826a4dbbf1dcb95d96e7974c93

View File

@ -1 +1 @@
721350078d8e27a83d96cc6d0dfa91b6ac559a5f81a22d9c78bae64825ed0c98
ef34cbbbf6d7558fb23c1b07b1bdfb963a1168dbf40a10e84ba2675b09fc9880

View File

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

View File

@ -1 +1 @@
1d7c20312d485399a6349304512afd19a40ff18597fb7dff3871190dd81cd69c
124c0ad3412d8c18080f8da6f8923566217268c81c32c97108af077bc918b46f

View File

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

View File

@ -1 +1 @@
7a1166fec5b2204048691a4c5ed6c88cb952b3b293ec9ef79ad85cd86bbe7e09
9731a6e2cf5ff58c4c9e71ca1d547507058070586723183af16235f22b593078