Phase 1: Sprach-Frontend (Lexer, AST, Parser, Semantik) + Entscheidungen
Frontend: - Lexer komplett: Typ-Suffixe, Literal-Typisierung (Entscheidung: > 7 signifikante Stellen -> DOUBLE), Hex/Oktal, Zeilenfortsetzung mit _, Strings mit ""-Escape, case-insensitive Keywords (Bibliotheksnamen bleiben Bezeichner) - AST fuer Module/Prozeduren/Anweisungen/Ausdruecke - Parser: fehlertolerant, zeilenorientiert; Kern-Anweisungssatz inkl. Bloecke, ON [LOCAL] ERROR, DEF FN (einzeilig); Datei-E/A als Phase-3-Platzhalter - Semantik: Symboltabellen, implizite Deklaration, DEFtype, OPTION EXPLICIT, Arrays, Builtin-Signaturen, Labelpruefung; Hardware-Features (PEEK/POKE/...) werden zur Compile-Zeit abgewiesen - Meilenstein: Testkorpus parst und wird typgeprueft (corpus.rs); 27 Tests Entscheidungen eingearbeitet: - Binaries heissen tb (IDE) und tbc (Compiler) - Dynamische Terminalgroesse statt 80x25 (Minimum 80x25, btop-artiger Hinweis darunter); tb-ui::screen mit resize(), Spike angepasst - Vollstaendigkeits-Leitplanke: 100% Sprache/Stdlib minus deklarierte Non-Features; Original-Doku als Guiding Principle; Inventar-Aufgabe - CURRENCY als i64-Festkomma; ISAM wird implementiert; breite Zeichen belegen 2 Zellen; GET/PUT-Strings als UTF-32; Blink als hell simuliert - LICENSE: MIT Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,247 @@
|
||||
//! AST-Definitionen: Module, Prozeduren (`SUB`/`FUNCTION`), Anweisungen, Ausdrücke,
|
||||
//! benutzerdefinierte Typen (`TYPE … END TYPE`), Deklarationen.
|
||||
//! AST-Definitionen: Module, Prozeduren, Anweisungen, Ausdrücke,
|
||||
//! Deklarationen und benutzerdefinierte Typen.
|
||||
|
||||
// Platzhalter — wird in Phase 1 ausgearbeitet (siehe PLAN.md)
|
||||
use crate::lexer::Suffix;
|
||||
use crate::SourcePos;
|
||||
|
||||
/// Typangabe in `AS`-Klauseln.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeName {
|
||||
Integer,
|
||||
Long,
|
||||
Single,
|
||||
Double,
|
||||
Currency,
|
||||
Str,
|
||||
FixedStr(i64),
|
||||
Udt(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UnOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BinOp {
|
||||
Pow, Mul, Div, IntDiv, Mod, Add, Sub,
|
||||
Eq, Ne, Lt, Le, Gt, Ge,
|
||||
And, Or, Xor, Eqv, Imp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expr {
|
||||
IntLit(i16),
|
||||
LongLit(i32),
|
||||
SingleLit(f32),
|
||||
DoubleLit(f64),
|
||||
CurrencyLit(i64),
|
||||
StrLit(String),
|
||||
/// Benannter Zugriff: Variable, Arrayelement, Funktionsaufruf oder
|
||||
/// Konstante — Auflösung erfolgt in der Semantik.
|
||||
Name {
|
||||
name: String,
|
||||
suffix: Option<Suffix>,
|
||||
args: Option<Vec<Expr>>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
Unary { op: UnOp, operand: Box<Expr>, pos: SourcePos },
|
||||
Binary { op: BinOp, lhs: Box<Expr>, rhs: Box<Expr>, pos: SourcePos },
|
||||
/// Ausgelassenes Argument (`LOCATE , 5`).
|
||||
Missing,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn pos(&self) -> SourcePos {
|
||||
match self {
|
||||
Expr::Name { pos, .. }
|
||||
| Expr::Unary { pos, .. }
|
||||
| Expr::Binary { pos, .. } => *pos,
|
||||
_ => SourcePos::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sprungziel: alphanumerisches Label oder Zeilennummer.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum LabelRef {
|
||||
Name(String),
|
||||
Line(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PrintItem {
|
||||
Expr(Expr),
|
||||
Comma,
|
||||
Semicolon,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum CaseSpec {
|
||||
Expr(Expr),
|
||||
Range(Expr, Expr),
|
||||
Is(BinOp, Expr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct CaseArm {
|
||||
/// Leer = `CASE ELSE`.
|
||||
pub specs: Vec<CaseSpec>,
|
||||
pub body: Vec<Stmt>,
|
||||
pub pos: SourcePos,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct VarDecl {
|
||||
pub name: String,
|
||||
pub suffix: Option<Suffix>,
|
||||
/// `None` = Skalar; sonst Dimensionen `(untergrenze TO obergrenze)`.
|
||||
pub dims: Option<Vec<(Option<Expr>, Expr)>>,
|
||||
pub as_type: Option<TypeName>,
|
||||
pub pos: SourcePos,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExitKind {
|
||||
For,
|
||||
Do,
|
||||
Sub,
|
||||
Function,
|
||||
Def,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OptionKind {
|
||||
Explicit,
|
||||
Base(u8),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum OnErrorAction {
|
||||
Goto(LabelRef),
|
||||
ResumeNext,
|
||||
Disable, // GOTO 0
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ResumeKind {
|
||||
Retry, // RESUME [0]
|
||||
Next,
|
||||
Label(LabelRef),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Param {
|
||||
pub name: String,
|
||||
pub suffix: Option<Suffix>,
|
||||
pub array: bool,
|
||||
pub as_type: Option<TypeName>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProcKind {
|
||||
Sub,
|
||||
Function,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ProcSig {
|
||||
pub kind: ProcKind,
|
||||
pub name: String,
|
||||
pub suffix: Option<Suffix>,
|
||||
pub params: Vec<Param>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Proc {
|
||||
pub sig: ProcSig,
|
||||
pub is_static: bool,
|
||||
pub body: Vec<Stmt>,
|
||||
pub pos: SourcePos,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Stmt {
|
||||
Label(String),
|
||||
LineNumber(u32),
|
||||
Assign { target: Expr, value: Expr, pos: SourcePos },
|
||||
Print {
|
||||
file: Option<Expr>,
|
||||
using: Option<Expr>,
|
||||
items: Vec<PrintItem>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
Input {
|
||||
line: bool,
|
||||
file: Option<Expr>,
|
||||
keep_cursor: bool,
|
||||
prompt: Option<(String, bool)>, // (Text, mit Fragezeichen)
|
||||
vars: Vec<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
If {
|
||||
cond: Expr,
|
||||
then_body: Vec<Stmt>,
|
||||
elseifs: Vec<(Expr, Vec<Stmt>)>,
|
||||
else_body: Option<Vec<Stmt>>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
Select { expr: Expr, arms: Vec<CaseArm>, pos: SourcePos },
|
||||
For {
|
||||
var: Expr,
|
||||
from: Expr,
|
||||
to: Expr,
|
||||
step: Option<Expr>,
|
||||
body: Vec<Stmt>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
DoLoop {
|
||||
pre: Option<(bool, Expr)>, // (ist UNTIL, Bedingung)
|
||||
post: Option<(bool, Expr)>,
|
||||
body: Vec<Stmt>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
While { cond: Expr, body: Vec<Stmt>, pos: SourcePos },
|
||||
Goto { target: LabelRef, pos: SourcePos },
|
||||
Gosub { target: LabelRef, pos: SourcePos },
|
||||
OnGoto { expr: Expr, targets: Vec<LabelRef>, gosub: bool, pos: SourcePos },
|
||||
Return { target: Option<LabelRef>, pos: SourcePos },
|
||||
End,
|
||||
StopStmt,
|
||||
System,
|
||||
Exit { kind: ExitKind, pos: SourcePos },
|
||||
Dim { shared: bool, redim: bool, decls: Vec<VarDecl>, pos: SourcePos },
|
||||
Erase { names: Vec<Expr>, pos: SourcePos },
|
||||
ConstDecl { items: Vec<(String, Option<Suffix>, Expr)>, pos: SourcePos },
|
||||
DefType { ty: TypeName, ranges: Vec<(char, char)>, pos: SourcePos },
|
||||
OptionStmt { kind: OptionKind, pos: SourcePos },
|
||||
TypeDecl { name: String, fields: Vec<(String, TypeName)>, pos: SourcePos },
|
||||
Declare { sig: ProcSig, pos: SourcePos },
|
||||
/// Expliziter oder impliziter Prozedur-/Builtin-Aufruf als Anweisung.
|
||||
Call { name: String, suffix: Option<Suffix>, args: Vec<Expr>, pos: SourcePos },
|
||||
OnError { local: bool, action: OnErrorAction, pos: SourcePos },
|
||||
Resume { kind: ResumeKind, pos: SourcePos },
|
||||
ErrorStmt { code: Expr, pos: SourcePos },
|
||||
Data { items: Vec<String>, pos: SourcePos },
|
||||
ReadStmt { vars: Vec<Expr>, pos: SourcePos },
|
||||
Restore { target: Option<LabelRef>, pos: SourcePos },
|
||||
/// Einzeilige `DEF FNname(...) = ausdruck`-Definition.
|
||||
DefFn {
|
||||
name: String,
|
||||
suffix: Option<Suffix>,
|
||||
params: Vec<Param>,
|
||||
body: Expr,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// Geparst, aber erst in Phase 3 implementiert (Datei-E/A u. ä.);
|
||||
/// die Tokens der Anweisung wurden übersprungen.
|
||||
NotYetImplemented { keyword: String, pos: SourcePos },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Module {
|
||||
pub name: String,
|
||||
pub body: Vec<Stmt>,
|
||||
pub procs: Vec<Proc>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user