Phase 2 abgeschlossen: Bytecode, TBVM, Runtime-Scheibe, tbc run
- Sema zum Lowering-Pass umgebaut: typisiertes HIR (Slots, explizite Konvertierungsknoten) als Codegen-Eingabe; BYREF verlangt exakten Typ - Bytecode-Feindesign umgesetzt: monomorpher Opcode-Satz, .tbc-Container (Formatversion 1) mit eigenem Writer/Reader - Codegenerator HIR -> Bytecode (Fixup-Listen, keine globalen Passes) - TBVM-Interpreter: Kontrollfluss, GOSUB-Stack je Frame, BYREF/BYVAL, STATIC, DEF FN, DATA/READ/RESTORE, ON [LOCAL] ERROR/RESUME/ERR/ERL, Breakpoints/Einzelschritt/Inspektion, STOP fortsetzbar - Runtime-Scheibe: Host-Trait (Konsole/Capture), Builtin-Tabelle, Konvertierungsmatrix, PRINT-Formatierung/Druckzonen, Stringfunktionen - tbc run/build/check mit Exit-Codes nach Entscheidung D6 - Korpus-Harness (byte-genauer Vergleich) + 3 neue Korpusdateien (konvertierung, fehlerbehandlung, byref); 137 Tests gruen - Benchmarks: Einzelmodul 1,2 ms / Projekt 49.760 Zeilen 124 ms (Budgets eingehalten), VM ~5 Mio Schleifeniterationen/s - Doku fortgeschrieben (tbvm-design, sprachreferenz, PLAN); verlagerte Punkte als explizite Aufgaben in Phase 3 - OpenSpec-Change phase-2-bytecode-vm (27/27 Tasks) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,9 @@ pub enum Expr {
|
||||
},
|
||||
Unary { op: UnOp, operand: Box<Expr>, pos: SourcePos },
|
||||
Binary { op: BinOp, lhs: Box<Expr>, rhs: Box<Expr>, pos: SourcePos },
|
||||
/// Geklammerter Ausdruck. Semantisch transparent, aber als Argument
|
||||
/// erzwingt die Klammer Wertübergabe (BYVAL) statt BYREF.
|
||||
Paren(Box<Expr>),
|
||||
/// Ausgelassenes Argument (`LOCATE , 5`).
|
||||
Missing,
|
||||
}
|
||||
@@ -58,6 +61,7 @@ impl Expr {
|
||||
Expr::Name { pos, .. }
|
||||
| Expr::Unary { pos, .. }
|
||||
| Expr::Binary { pos, .. } => *pos,
|
||||
Expr::Paren(e) => e.pos(),
|
||||
_ => SourcePos::default(),
|
||||
}
|
||||
}
|
||||
@@ -242,9 +246,9 @@ pub enum Stmt {
|
||||
Gosub { target: LabelRef, pos: SourcePos },
|
||||
OnGoto { expr: Expr, targets: Vec<LabelRef>, gosub: bool, pos: SourcePos },
|
||||
Return { target: Option<LabelRef>, pos: SourcePos },
|
||||
End,
|
||||
StopStmt,
|
||||
System,
|
||||
End(SourcePos),
|
||||
StopStmt(SourcePos),
|
||||
System(SourcePos),
|
||||
Exit { kind: ExitKind, pos: SourcePos },
|
||||
Dim { shared: bool, redim: bool, decls: Vec<VarDecl>, pos: SourcePos },
|
||||
/// `SHARED`-Anweisung in einer Prozedur (Zugriff auf Modulvariablen).
|
||||
|
||||
459
crates/tb-frontend/src/hir.rs
Normal file
459
crates/tb-frontend/src/hir.rs
Normal file
@@ -0,0 +1,459 @@
|
||||
//! Typisiertes, abgesenktes HIR — die Ausgabe der semantischen Analyse
|
||||
//! und Eingabe des Codegenerators (`tb-vm`).
|
||||
//!
|
||||
//! Eigenschaften (siehe Design der Phase-2-Änderung):
|
||||
//! - Namen sind aufgelöst: Variablen sind Slot-Indizes (global/lokal),
|
||||
//! Prozeduren und UDTs Tabellenindizes, Sprungziele `LabelId`s je Rumpf.
|
||||
//! - Jeder Ausdrucksknoten trägt seinen Ergebnistyp; implizite
|
||||
//! Konvertierungen sind als explizite `Conv`-Knoten materialisiert
|
||||
//! (Semantik: Konvertierungsmatrix in docs/tbvm-design.md).
|
||||
//! - Kontrollzucker ist abgesenkt: `SELECT CASE` zu Vergleichsketten,
|
||||
//! `ELSEIF` zu verschachteltem `If`, `EXIT FOR/DO` zu `Goto` auf
|
||||
//! synthetisierte Labels, `SWAP` zu Zuweisungen über einen Temp-Slot.
|
||||
//! - `STATIC`-Locals und versteckte Temps liegen im globalen Slot-Bereich.
|
||||
//!
|
||||
//! Ein vollständiges, korrektes HIR ist nur bei diagnose-freier Analyse
|
||||
//! garantiert.
|
||||
|
||||
/// Numerischer Skalartyp (Kürzel wie im Opcode-Satz: I2/I4/CY/R4/R8).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NumTy {
|
||||
Int,
|
||||
Lng,
|
||||
Cur,
|
||||
Sng,
|
||||
Dbl,
|
||||
}
|
||||
|
||||
/// Ganzzahlbreite der Logik-Operatoren.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IntKind {
|
||||
I2,
|
||||
I4,
|
||||
}
|
||||
|
||||
/// Aufgelöster HIR-Typ.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum HTy {
|
||||
Num(NumTy),
|
||||
Str,
|
||||
/// Fester String mit Zeichenlänge (Zuweisung padded/kürzt).
|
||||
FixedStr(u32),
|
||||
/// Benutzerdefinierter Typ (Index in `HirModule::udts`).
|
||||
Udt(u16),
|
||||
}
|
||||
|
||||
impl HTy {
|
||||
pub fn num(&self) -> Option<NumTy> {
|
||||
match self {
|
||||
HTy::Num(n) => Some(*n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn is_str(&self) -> bool {
|
||||
matches!(self, HTy::Str | HTy::FixedStr(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Slot-Referenz: globaler Bereich (Modulvariablen, STATICs, versteckte
|
||||
/// Temps des Hauptprogramms) oder Frame-lokal (Parameter zuerst).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum VarSlot {
|
||||
Global(u16),
|
||||
Local(u16),
|
||||
}
|
||||
|
||||
/// Sprungziel innerhalb eines Prozedurrumpfs.
|
||||
pub type LabelId = u16;
|
||||
|
||||
/// Variablen-/Slotbeschreibung (auch für die Debugger-Inspektion).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HVar {
|
||||
pub name: String,
|
||||
pub ty: HTy,
|
||||
pub array: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HUdt {
|
||||
pub name: String,
|
||||
pub fields: Vec<(String, HTy)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HProcKind {
|
||||
/// Hauptprogramm (Modulrumpf) — immer Prozedur 0.
|
||||
Main,
|
||||
Sub,
|
||||
Function,
|
||||
/// `DEF FN` — Parameter BYVAL, freie Namen binden an Modulvariablen.
|
||||
DefFn,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HParam {
|
||||
pub name: String,
|
||||
pub ty: HTy,
|
||||
pub array: bool,
|
||||
/// Skalar-Parameter, der als Referenz übergeben wird (Arrays und
|
||||
/// UDTs sind implizit immer Referenzen auf ihr Handle).
|
||||
pub by_ref: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HProc {
|
||||
pub name: String,
|
||||
pub kind: HProcKind,
|
||||
pub params: Vec<HParam>,
|
||||
/// Alle Frame-Slots; `params.len()` erste Einträge sind die Parameter.
|
||||
pub locals: Vec<HVar>,
|
||||
/// Slot der Rückgabevariablen (Function/DefFn).
|
||||
pub ret_slot: Option<VarSlot>,
|
||||
pub ret_ty: Option<HTy>,
|
||||
pub body: Vec<HStmt>,
|
||||
/// Anzahl vergebener LabelIds in diesem Rumpf.
|
||||
pub label_count: u16,
|
||||
}
|
||||
|
||||
/// Eine DATA-Konstante (unkonvertiert; `READ` konvertiert zur Laufzeit).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataItem {
|
||||
pub text: String,
|
||||
pub line: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HirModule {
|
||||
pub name: String,
|
||||
pub globals: Vec<HVar>,
|
||||
pub udts: Vec<HUdt>,
|
||||
/// Prozeduren; Index 0 ist das Hauptprogramm.
|
||||
pub procs: Vec<HProc>,
|
||||
pub data: Vec<DataItem>,
|
||||
pub option_base: u8,
|
||||
}
|
||||
|
||||
// ---- Ausdrücke -------------------------------------------------------------
|
||||
|
||||
/// L-Wert: Basis-Slot, optional Array-Indizes, optional UDT-Feldpfad.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HPlace {
|
||||
pub base: VarSlot,
|
||||
/// Skalar-BYREF-Parameter: der Slot enthält eine Referenz.
|
||||
pub base_is_ref: bool,
|
||||
/// Array-Elementzugriff (leer = Skalar bzw. ganzes Array).
|
||||
pub indices: Vec<HExpr>,
|
||||
/// UDT-Feldpfad (Feldindizes je Ebene).
|
||||
pub fields: Vec<u16>,
|
||||
/// Typ des adressierten Werts.
|
||||
pub ty: HTy,
|
||||
/// Element-/Basistyp und Dimension für Auto-DIM impliziter Arrays.
|
||||
pub array_elem: Option<(HTy, u8)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HArith {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
IDiv,
|
||||
Mod,
|
||||
Pow,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HCmp {
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HLogic {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Eqv,
|
||||
Imp,
|
||||
}
|
||||
|
||||
/// Vergleichs-Operandentyp.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CmpKind {
|
||||
Num(NumTy),
|
||||
Str,
|
||||
}
|
||||
|
||||
/// Bibliotheksfunktionen/-anweisungen der Phase-2-Scheibe. Der
|
||||
/// Diskriminant ist zugleich der stabile Index der Dispatch-Tabelle
|
||||
/// (`CALL_BUILTIN`); Phase 3 erweitert ausschließlich am Ende.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u16)]
|
||||
pub enum Builtin {
|
||||
// Strings
|
||||
Len,
|
||||
LeftS,
|
||||
RightS,
|
||||
MidS,
|
||||
InstrF,
|
||||
UcaseS,
|
||||
LcaseS,
|
||||
LtrimS,
|
||||
RtrimS,
|
||||
SpaceS,
|
||||
StringS,
|
||||
ChrS,
|
||||
Asc,
|
||||
StrS,
|
||||
Val,
|
||||
HexS,
|
||||
OctS,
|
||||
/// MID$-Anweisung als reine Funktion: (ziel, start, länge, ersatz) → neuer String.
|
||||
MidAssign,
|
||||
// Mathematik
|
||||
Abs,
|
||||
Sgn,
|
||||
IntF,
|
||||
Fix,
|
||||
Sqr,
|
||||
Exp,
|
||||
Log,
|
||||
Sin,
|
||||
Cos,
|
||||
Tan,
|
||||
Atn,
|
||||
Rnd,
|
||||
Randomize,
|
||||
// Konsole (PRINT-Familie; Wirkung über Host + Druckspalten-Zustand)
|
||||
PrintVal,
|
||||
PrintStrLit,
|
||||
PrintComma,
|
||||
PrintTab,
|
||||
PrintSpc,
|
||||
PrintNewline,
|
||||
// Sonstiges
|
||||
Timer,
|
||||
DateS,
|
||||
TimeS,
|
||||
CommandS,
|
||||
Doevents,
|
||||
Sleep,
|
||||
Beep,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HExpr {
|
||||
Int(i16),
|
||||
Lng(i32),
|
||||
Sng(f32),
|
||||
Dbl(f64),
|
||||
Cur(i64),
|
||||
Str(String),
|
||||
Load(Box<HPlace>),
|
||||
/// Numerische Konvertierung nach Matrix (Rundung/Überlauf).
|
||||
Conv {
|
||||
from: NumTy,
|
||||
to: NumTy,
|
||||
arg: Box<HExpr>,
|
||||
},
|
||||
/// Kürzen/Padden auf feste Stringlänge.
|
||||
FixStr {
|
||||
len: u32,
|
||||
arg: Box<HExpr>,
|
||||
},
|
||||
Neg {
|
||||
ty: NumTy,
|
||||
arg: Box<HExpr>,
|
||||
},
|
||||
/// Monomorphe Arithmetik: beide Operanden und das Ergebnis haben `ty`
|
||||
/// (bei `Div`/`Pow` nur R4/R8, bei `IDiv`/`Mod` nur I2/I4).
|
||||
Bin {
|
||||
op: HArith,
|
||||
ty: NumTy,
|
||||
l: Box<HExpr>,
|
||||
r: Box<HExpr>,
|
||||
},
|
||||
Not {
|
||||
ty: IntKind,
|
||||
arg: Box<HExpr>,
|
||||
},
|
||||
Logic {
|
||||
op: HLogic,
|
||||
ty: IntKind,
|
||||
l: Box<HExpr>,
|
||||
r: Box<HExpr>,
|
||||
},
|
||||
/// Vergleich; Ergebnis ist INTEGER (−1/0).
|
||||
Cmp {
|
||||
op: HCmp,
|
||||
ty: CmpKind,
|
||||
l: Box<HExpr>,
|
||||
r: Box<HExpr>,
|
||||
},
|
||||
Concat(Box<HExpr>, Box<HExpr>),
|
||||
/// FUNCTION-/DEF FN-Aufruf.
|
||||
FnCall {
|
||||
proc: u16,
|
||||
args: Vec<HArg>,
|
||||
ret: HTy,
|
||||
},
|
||||
Builtin {
|
||||
b: Builtin,
|
||||
args: Vec<HExpr>,
|
||||
ret: HTy,
|
||||
},
|
||||
/// LBOUND/UBOUND eines Arrays.
|
||||
ArrayBound {
|
||||
lower: bool,
|
||||
place: Box<HPlace>,
|
||||
dim: Box<HExpr>,
|
||||
},
|
||||
Err,
|
||||
Erl,
|
||||
/// Dokumentiertes, aber erst in einer späteren Phase implementiertes
|
||||
/// Feature: löst zur Laufzeit Fehler 73 „Advanced feature" aus.
|
||||
Unsupported(&'static str),
|
||||
}
|
||||
|
||||
/// Prozedurargument.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HArg {
|
||||
ByRef(HPlace),
|
||||
/// Ganzes Array als Referenz (`prozedur a()`).
|
||||
ArrayRef(HPlace),
|
||||
ByVal(HExpr),
|
||||
}
|
||||
|
||||
// ---- Anweisungen -----------------------------------------------------------
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HPrintItem {
|
||||
Val(HExpr),
|
||||
Tab(HExpr),
|
||||
Spc(HExpr),
|
||||
Comma,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HResume {
|
||||
Retry,
|
||||
Next,
|
||||
Label(LabelId),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HStmt {
|
||||
pub line: u32,
|
||||
pub kind: HStmtKind,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HStmtKind {
|
||||
/// Numerische Zeilennummer durchlaufen (setzt `ERL`).
|
||||
SetErl(u32),
|
||||
Label(LabelId),
|
||||
Assign {
|
||||
place: HPlace,
|
||||
value: HExpr,
|
||||
},
|
||||
Print {
|
||||
items: Vec<HPrintItem>,
|
||||
/// Endet die Anweisung mit `;`/`,` (kein Zeilenumbruch)?
|
||||
trailing: bool,
|
||||
},
|
||||
Input {
|
||||
line_mode: bool,
|
||||
prompt: Option<String>,
|
||||
/// Fragezeichen nach dem Prompt (`;`-Form).
|
||||
question: bool,
|
||||
targets: Vec<HPlace>,
|
||||
},
|
||||
If {
|
||||
cond: HExpr,
|
||||
then: Vec<HStmt>,
|
||||
els: Vec<HStmt>,
|
||||
},
|
||||
/// DO/LOOP, WHILE/WEND (nur `pre`) — Bedingungen: (ist_until, Ausdruck).
|
||||
Loop {
|
||||
pre: Option<(bool, HExpr)>,
|
||||
post: Option<(bool, HExpr)>,
|
||||
body: Vec<HStmt>,
|
||||
exit_label: LabelId,
|
||||
},
|
||||
For {
|
||||
var: HPlace,
|
||||
ty: NumTy,
|
||||
from: HExpr,
|
||||
to: HExpr,
|
||||
step: Option<HExpr>,
|
||||
/// Versteckte Slots für Grenze/Schritt (Schritt nur wenn dynamisch).
|
||||
limit_slot: VarSlot,
|
||||
step_slot: Option<VarSlot>,
|
||||
body: Vec<HStmt>,
|
||||
exit_label: LabelId,
|
||||
},
|
||||
Goto(LabelId),
|
||||
Gosub(LabelId),
|
||||
OnGoto {
|
||||
sel: HExpr,
|
||||
gosub: bool,
|
||||
targets: Vec<LabelId>,
|
||||
},
|
||||
ReturnGosub(Option<LabelId>),
|
||||
/// EXIT SUB/FUNCTION/DEF bzw. Rumpfende.
|
||||
ExitProc,
|
||||
CallSub {
|
||||
proc: u16,
|
||||
args: Vec<HArg>,
|
||||
},
|
||||
BuiltinStmt {
|
||||
b: Builtin,
|
||||
args: Vec<HExpr>,
|
||||
},
|
||||
OnError {
|
||||
local: bool,
|
||||
/// `None` = `GOTO 0` (deaktivieren).
|
||||
target: Option<LabelId>,
|
||||
},
|
||||
OnErrorResumeNext {
|
||||
local: bool,
|
||||
},
|
||||
Resume(HResume),
|
||||
/// `ERROR n`.
|
||||
RaiseError(HExpr),
|
||||
Read(Vec<HPlace>),
|
||||
/// Ziel als Index in `HirModule::data` (0 = Anfang).
|
||||
Restore(u32),
|
||||
/// DIM/REDIM eines Arrays: Grenzen (lo, hi) je Dimension.
|
||||
Dim {
|
||||
slot: VarSlot,
|
||||
elem: HTy,
|
||||
dims: Vec<(HExpr, HExpr)>,
|
||||
redim: bool,
|
||||
},
|
||||
Erase(Vec<VarSlot>),
|
||||
End,
|
||||
Stop,
|
||||
System,
|
||||
/// Dokumentiertes Feature einer späteren Phase → Laufzeitfehler 73.
|
||||
Unsupported(&'static str),
|
||||
}
|
||||
|
||||
/// Konstanter Literalwert eines Ausdrucks (z. B. FOR-STEP-Erkennung im
|
||||
/// Codegen: konstanter Schritt braucht keinen versteckten Slot).
|
||||
pub fn literal_value(e: &HExpr) -> Option<f64> {
|
||||
match e {
|
||||
HExpr::Int(v) => Some(*v as f64),
|
||||
HExpr::Lng(v) => Some(*v as f64),
|
||||
HExpr::Sng(v) => Some(*v as f64),
|
||||
HExpr::Dbl(v) => Some(*v),
|
||||
HExpr::Cur(v) => Some(*v as f64 / 10_000.0),
|
||||
HExpr::Conv { arg, .. } => literal_value(arg),
|
||||
HExpr::Neg { arg, .. } => literal_value(arg).map(|v| -v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
//! übersetzt.
|
||||
|
||||
pub mod ast;
|
||||
pub mod hir;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod sema;
|
||||
@@ -35,15 +36,18 @@ impl std::fmt::Display for Diagnostic {
|
||||
pub struct Analysis {
|
||||
pub module: ast::Module,
|
||||
pub diagnostics: Vec<Diagnostic>,
|
||||
/// Typisiertes HIR (vollständig nur bei leeren Diagnosen).
|
||||
pub hir: Option<hir::HirModule>,
|
||||
}
|
||||
|
||||
/// Komplette Pipeline: Lexen → Parsen → semantische Prüfung.
|
||||
/// Komplette Pipeline: Lexen → Parsen → semantische Prüfung + Lowering.
|
||||
pub fn analyze_source(module_name: &str, source: &str) -> Analysis {
|
||||
let lexed = lexer::lex(source);
|
||||
let mut diagnostics = lexed.diagnostics;
|
||||
let parsed = parser::parse(module_name, &lexed.tokens);
|
||||
diagnostics.extend(parsed.diagnostics);
|
||||
diagnostics.extend(sema::check(&parsed.module));
|
||||
let (hir, sema_diags) = sema::lower(&parsed.module);
|
||||
diagnostics.extend(sema_diags);
|
||||
diagnostics.sort_by_key(|d| (d.pos.line, d.pos.column));
|
||||
Analysis { module: parsed.module, diagnostics }
|
||||
Analysis { module: parsed.module, diagnostics, hir }
|
||||
}
|
||||
|
||||
@@ -285,15 +285,15 @@ impl<'a> P<'a> {
|
||||
return None;
|
||||
}
|
||||
self.advance();
|
||||
Some(Stmt::End)
|
||||
Some(Stmt::End(pos))
|
||||
}
|
||||
TokenKind::Kw(Kw::Stop) => {
|
||||
self.advance();
|
||||
Some(Stmt::StopStmt)
|
||||
Some(Stmt::StopStmt(pos))
|
||||
}
|
||||
TokenKind::Kw(Kw::System) => {
|
||||
self.advance();
|
||||
Some(Stmt::System)
|
||||
Some(Stmt::System(pos))
|
||||
}
|
||||
TokenKind::Kw(Kw::Exit) => {
|
||||
self.advance();
|
||||
@@ -1498,7 +1498,16 @@ impl<'a> P<'a> {
|
||||
}
|
||||
// Impliziter Aufruf: `name [arg [, arg …]]`
|
||||
if let Expr::Name { name, suffix, args, .. } = target {
|
||||
let mut call_args = args.unwrap_or_default();
|
||||
// Ohne CALL-Keyword sind Klammern Wert-Klammern (BYVAL), keine
|
||||
// Argumentlisten-Klammern: `Foo (n%)` übergibt `(n%)`.
|
||||
let mut call_args: Vec<Expr> = args
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|a| match a {
|
||||
p @ Expr::Paren(_) => p,
|
||||
other => Expr::Paren(Box::new(other)),
|
||||
})
|
||||
.collect();
|
||||
if !self.at_stmt_end() && call_args.is_empty() {
|
||||
call_args = self.parse_arg_list_to_stmt_end();
|
||||
}
|
||||
@@ -1722,7 +1731,7 @@ impl<'a> P<'a> {
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
self.err("Expected: )");
|
||||
}
|
||||
Some(e)
|
||||
Some(Expr::Paren(Box::new(e)))
|
||||
}
|
||||
TokenKind::Ident { .. } => self.parse_name_ref(),
|
||||
_ => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user