Projektmodule und vollständiges TBC-Kompilat umsetzen und Change archivieren
This commit is contained in:
@@ -150,6 +150,7 @@ struct Scope {
|
||||
/// Stack der Schleifen-Exit-Labels für `EXIT FOR`/`EXIT DO`.
|
||||
loop_exits: Vec<(LoopKind, LabelId)>,
|
||||
current_line: u32,
|
||||
current_pos: SourcePos,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
@@ -412,7 +413,13 @@ pub fn lower_with_forms(
|
||||
{
|
||||
catalog.add(&module.name, ObjectClass::Form, None, false);
|
||||
}
|
||||
let mut s = Sema {
|
||||
let mut s = new_sema(module, &catalog);
|
||||
let hir = s.run(module);
|
||||
(hir, s.diags)
|
||||
}
|
||||
|
||||
fn new_sema(module: &Module, catalog: &FormCatalog) -> Sema {
|
||||
Sema {
|
||||
diags: Vec::new(),
|
||||
deftypes: [const { None }; 26],
|
||||
explicit: false,
|
||||
@@ -427,16 +434,79 @@ pub fn lower_with_forms(
|
||||
module_labels: HashMap::new(),
|
||||
module_line_labels: HashMap::new(),
|
||||
globals: Vec::new(),
|
||||
commons: Vec::new(),
|
||||
data: Vec::new(),
|
||||
data_marks_name: HashMap::new(),
|
||||
data_marks_line: HashMap::new(),
|
||||
hir_procs: Vec::new(),
|
||||
isam_typ: HashMap::new(),
|
||||
forms: catalog,
|
||||
forms: catalog.clone(),
|
||||
module_name: module.name.clone(),
|
||||
event_procs: Vec::new(),
|
||||
};
|
||||
let hir = s.run(module);
|
||||
(hir, s.diags)
|
||||
}
|
||||
}
|
||||
|
||||
/// Exportdeklarationen mit den Typen und Konstanten ihres Ursprungsmoduls.
|
||||
/// Rümpfe werden hier nicht übersetzt; dafür bleibt der reguläre Sema-Durchlauf zuständig.
|
||||
pub fn export_declarations(module: &Module, constants: &[Stmt]) -> Vec<Stmt> {
|
||||
let mut s = new_sema(module, &FormCatalog::default());
|
||||
let mut scope = Scope::default();
|
||||
let mut declarations = Vec::new();
|
||||
let local: HashSet<_> = module
|
||||
.body
|
||||
.iter()
|
||||
.flat_map(|stmt| match stmt {
|
||||
Stmt::ConstDecl { items, .. } => items.iter().map(|i| i.0.as_str()).collect(),
|
||||
_ => Vec::new(),
|
||||
})
|
||||
.collect();
|
||||
for stmt in constants {
|
||||
if let Stmt::ConstDecl { items, .. } = stmt {
|
||||
if !local.contains(items[0].0.as_str()) {
|
||||
s.lower_stmt(stmt, &mut scope, &mut Vec::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
for stmt in &module.body {
|
||||
match stmt {
|
||||
Stmt::DefType { .. } => s.lower_stmt(stmt, &mut scope, &mut Vec::new()),
|
||||
Stmt::ConstDecl { items, pos } => {
|
||||
for item in items {
|
||||
let declaration = Stmt::ConstDecl {
|
||||
items: vec![item.clone()],
|
||||
pos: *pos,
|
||||
};
|
||||
s.lower_stmt(&declaration, &mut scope, &mut Vec::new());
|
||||
let value = match s.consts.get(&item.0).and_then(|(_, value)| value.as_ref()) {
|
||||
Some(ConstVal::Num(n)) => Expr::DoubleLit(*n, item.2.pos()),
|
||||
Some(ConstVal::Str(value)) => Expr::StrLit(value.clone(), item.2.pos()),
|
||||
None => item.2.clone(),
|
||||
};
|
||||
declarations.push(Stmt::ConstDecl {
|
||||
items: vec![(item.0.clone(), item.1, value)],
|
||||
pos: *pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
Stmt::TypeDecl { .. } => declarations.push(stmt.clone()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for proc in &module.procs {
|
||||
let mut sig = proc.sig.clone();
|
||||
if sig.suffix.is_none() && sig.kind == ProcKind::Function {
|
||||
sig.suffix =
|
||||
Suffix::from_char(s.var_key(&sig.name, &None, false).chars().last().unwrap());
|
||||
}
|
||||
for param in &mut sig.params {
|
||||
if param.suffix.is_none() && param.as_type.is_none() {
|
||||
param.suffix =
|
||||
Suffix::from_char(s.var_key(¶m.name, &None, false).chars().last().unwrap());
|
||||
}
|
||||
}
|
||||
declarations.push(Stmt::Declare { sig, pos: proc.pos });
|
||||
}
|
||||
declarations
|
||||
}
|
||||
|
||||
struct Sema {
|
||||
@@ -467,6 +537,7 @@ struct Sema {
|
||||
module_line_labels: HashMap<u32, LabelId>,
|
||||
/// Globale Slots (Modulvariablen, STATICs, versteckte Temps).
|
||||
globals: Vec<hir::HVar>,
|
||||
commons: Vec<hir::HCommon>,
|
||||
/// DATA-Konstanten des Moduls (aus dem Prescan, statisch).
|
||||
data: Vec<hir::DataItem>,
|
||||
data_marks_name: HashMap<String, u32>,
|
||||
@@ -474,12 +545,14 @@ struct Sema {
|
||||
/// Fertige HIR-Prozeduren nach Id (0 = Hauptprogramm).
|
||||
hir_procs: Vec<Option<hir::HProc>>,
|
||||
forms: FormCatalog,
|
||||
module_name: String,
|
||||
event_procs: Vec<hir::HEventProc>,
|
||||
}
|
||||
|
||||
impl Sema {
|
||||
fn err(&mut self, pos: SourcePos, msg: impl Into<String>) {
|
||||
self.diags.push(Diagnostic {
|
||||
file: None,
|
||||
pos,
|
||||
message: msg.into(),
|
||||
});
|
||||
@@ -489,7 +562,10 @@ impl Sema {
|
||||
// Pass 1: Prozeduren, DECLAREs und TYPEs registrieren.
|
||||
for stmt in &module.body {
|
||||
match stmt {
|
||||
Stmt::Declare { sig, .. } => self.register_proc(sig),
|
||||
Stmt::Declare { sig, pos } => self.register_proc(sig, *pos),
|
||||
Stmt::DefType { .. } => {
|
||||
self.lower_stmt(stmt, &mut Scope::default(), &mut Vec::new())
|
||||
}
|
||||
Stmt::TypeDecl { name, fields, pos } => {
|
||||
let mut hfields = Vec::new();
|
||||
for (fname, ftype) in fields {
|
||||
@@ -514,8 +590,12 @@ impl Sema {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut defined = HashSet::new();
|
||||
for proc in &module.procs {
|
||||
self.register_proc(&proc.sig);
|
||||
if !defined.insert(proc.sig.name.clone()) {
|
||||
self.err(proc.pos, "Duplicate definition");
|
||||
}
|
||||
self.register_proc(&proc.sig, proc.pos);
|
||||
}
|
||||
for proc in &module.procs {
|
||||
self.register_event_proc(proc);
|
||||
@@ -524,6 +604,8 @@ impl Sema {
|
||||
self.hir_procs
|
||||
.resize_with(self.next_proc_id as usize, || None);
|
||||
|
||||
self.deftypes = [const { None }; 26];
|
||||
|
||||
// Pass 2: Modulrumpf. Labels, Zeilennummern und DATA-Positionen
|
||||
// werden vorab eingesammelt (RESTORE nach vorn, statisches DATA).
|
||||
let mut scope = Scope {
|
||||
@@ -550,7 +632,9 @@ impl Sema {
|
||||
|
||||
// Pass 3: Prozedurrümpfe.
|
||||
for proc in &module.procs {
|
||||
let deftypes = self.deftypes.clone();
|
||||
self.lower_proc(proc);
|
||||
self.deftypes = deftypes;
|
||||
}
|
||||
|
||||
// Zusammensetzen: fehlende Rümpfe (nur DECLARE) behalten ihre
|
||||
@@ -574,7 +658,7 @@ impl Sema {
|
||||
name: format!("ARG{index}"),
|
||||
ty: self.h_ty(ty),
|
||||
array: *array,
|
||||
by_ref: !array && !matches!(ty, Ty::Udt(_)),
|
||||
by_ref: !array,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut locals = params
|
||||
@@ -614,6 +698,7 @@ impl Sema {
|
||||
Some(hir::HirModule {
|
||||
name: module.name.clone(),
|
||||
globals: std::mem::take(&mut self.globals),
|
||||
commons: std::mem::take(&mut self.commons),
|
||||
udts: std::mem::take(&mut self.udt_defs),
|
||||
procs,
|
||||
data: std::mem::take(&mut self.data),
|
||||
@@ -638,11 +723,11 @@ impl Sema {
|
||||
let hty = self.h_ty(&ty);
|
||||
let slot = scope.locals.len() as u16;
|
||||
scope.locals.push(hir::HVar {
|
||||
name: p.name.clone(),
|
||||
name: key.trim_end_matches("\u{1}AS").to_string(),
|
||||
ty: hty.clone(),
|
||||
array: p.array,
|
||||
});
|
||||
let by_ref = !p.array && !matches!(ty, Ty::Udt(_));
|
||||
let by_ref = !p.array;
|
||||
scope.vars.insert(
|
||||
key,
|
||||
VarInfo {
|
||||
@@ -669,7 +754,7 @@ impl Sema {
|
||||
let key = self.var_key(&proc.sig.name, &proc.sig.suffix, false);
|
||||
let slot = scope.locals.len() as u16;
|
||||
scope.locals.push(hir::HVar {
|
||||
name: proc.sig.name.clone(),
|
||||
name: key.clone(),
|
||||
ty: self.h_ty(&ret),
|
||||
array: false,
|
||||
});
|
||||
@@ -721,7 +806,7 @@ impl Sema {
|
||||
.insert(n.clone(), self.data.len() as u32);
|
||||
}
|
||||
}
|
||||
Stmt::LineNumber(n) => {
|
||||
Stmt::LineNumber(n, _) => {
|
||||
let id = scope.new_label();
|
||||
scope.line_labels.insert(*n, id);
|
||||
if module_data {
|
||||
@@ -745,7 +830,7 @@ impl Sema {
|
||||
..
|
||||
} => {
|
||||
self.prescan(then_body, scope, module_data);
|
||||
for (_, b) in elseifs {
|
||||
for (_, b, _) in elseifs {
|
||||
self.prescan(b, scope, module_data);
|
||||
}
|
||||
if let Some(b) = else_body {
|
||||
@@ -773,18 +858,24 @@ impl Sema {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_proc(&mut self, sig: &ProcSig) {
|
||||
fn register_proc(&mut self, sig: &ProcSig, pos: SourcePos) {
|
||||
let ret = match sig.kind {
|
||||
ProcKind::Function => self.name_ty(&sig.name, &sig.suffix),
|
||||
ProcKind::Sub => Ty::Unknown,
|
||||
};
|
||||
let params = sig
|
||||
let params: Vec<_> = sig
|
||||
.params
|
||||
.iter()
|
||||
.map(|p| (self.param_ty(p), p.array))
|
||||
.collect();
|
||||
let id = match self.procs.get(&sig.name) {
|
||||
Some(p) => p.id,
|
||||
Some(p) => {
|
||||
let id = p.id;
|
||||
if p.kind != sig.kind || p.ret != ret || p.params != params {
|
||||
self.err(pos, "Parameter type mismatch");
|
||||
}
|
||||
id
|
||||
}
|
||||
None => {
|
||||
let id = self.next_proc_id;
|
||||
self.next_proc_id += 1;
|
||||
@@ -895,14 +986,9 @@ impl Sema {
|
||||
fn event_binding(&self, name: &str) -> Option<(u16, ObjectClass, String)> {
|
||||
let (base, event) = name.rsplit_once('_')?;
|
||||
let found = if base == "FORM" {
|
||||
self.forms
|
||||
.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, o)| o.class == ObjectClass::Form)
|
||||
.map(|(i, o)| (i as u16, o))
|
||||
self.current_form()
|
||||
} else {
|
||||
self.forms.find(base)
|
||||
self.find_object(base)
|
||||
}?;
|
||||
Some((found.0, found.1.class, event.to_string()))
|
||||
}
|
||||
@@ -916,12 +1002,68 @@ impl Sema {
|
||||
}
|
||||
}
|
||||
|
||||
fn current_form(&self) -> Option<(u16, &forms::FormObject)> {
|
||||
self.forms
|
||||
.find(&self.module_name)
|
||||
.filter(|(_, o)| o.class == ObjectClass::Form)
|
||||
.or_else(|| {
|
||||
let mut forms = self
|
||||
.forms
|
||||
.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, o)| o.class == ObjectClass::Form);
|
||||
let (id, object) = forms.next()?;
|
||||
forms.next().is_none().then_some((id as u16, object))
|
||||
})
|
||||
}
|
||||
|
||||
fn find_object(&self, name: &str) -> Option<(u16, &forms::FormObject)> {
|
||||
self.forms
|
||||
.find_in(name, &self.module_name)
|
||||
.or_else(|| {
|
||||
self.forms
|
||||
.find(name)
|
||||
.filter(|(_, o)| o.class == ObjectClass::Form || o.class == ObjectClass::Screen)
|
||||
})
|
||||
.or_else(|| {
|
||||
let mut matches = self
|
||||
.forms
|
||||
.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, o)| o.name.eq_ignore_ascii_case(name));
|
||||
let (id, object) = matches.next()?;
|
||||
matches.next().is_none().then_some((id as u16, object))
|
||||
})
|
||||
}
|
||||
|
||||
fn scoped_object(
|
||||
&mut self,
|
||||
name: &str,
|
||||
parent: Option<&str>,
|
||||
pos: SourcePos,
|
||||
) -> Option<(u16, forms::FormObject)> {
|
||||
if let Some(parent) = parent {
|
||||
if let Some((id, object)) = self.forms.find_in(name, parent) {
|
||||
return Some((id, object.clone()));
|
||||
}
|
||||
self.err(
|
||||
pos,
|
||||
format!("Control '{name}' not found in form '{parent}'"),
|
||||
);
|
||||
None
|
||||
} else {
|
||||
self.object_by_name(name, pos)
|
||||
}
|
||||
}
|
||||
|
||||
fn object_by_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
pos: SourcePos,
|
||||
) -> Option<(u16, crate::forms::FormObject)> {
|
||||
if let Some((id, object)) = self.forms.find(name) {
|
||||
if let Some((id, object)) = self.find_object(name) {
|
||||
return Some((id, object.clone()));
|
||||
}
|
||||
self.err(pos, format!("Unknown object '{name}'"));
|
||||
@@ -936,14 +1078,9 @@ impl Sema {
|
||||
if self.declared_var(scope, name).is_some() || self.consts.contains_key(name) {
|
||||
return None;
|
||||
}
|
||||
let (object, form) = self
|
||||
.forms
|
||||
.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, object)| object.class == ObjectClass::Form)?;
|
||||
let (object, form) = self.current_form()?;
|
||||
let (property, spec) = forms::property(form.class, name)?;
|
||||
Some((object as u16, property, spec))
|
||||
Some((object, property, spec))
|
||||
}
|
||||
|
||||
fn object_property(
|
||||
@@ -958,17 +1095,7 @@ impl Sema {
|
||||
let (object, member) = path.split_once('.')?;
|
||||
(object, member, None)
|
||||
};
|
||||
let (id, object) = self.object_by_name(object_name, pos)?;
|
||||
if let Some(parent) = parent {
|
||||
let parent_ok = self.forms.belongs_to(&object, parent);
|
||||
if !parent_ok {
|
||||
self.err(
|
||||
pos,
|
||||
format!("Control '{object_name}' not found in form '{parent}'"),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let (id, object) = self.scoped_object(object_name, parent, pos)?;
|
||||
let Some((property, spec)) = forms::property(object.class, member) else {
|
||||
self.err(
|
||||
pos,
|
||||
@@ -991,16 +1118,7 @@ impl Sema {
|
||||
let (object, member) = path.split_once('.')?;
|
||||
(object, member, None)
|
||||
};
|
||||
let (id, object) = self.object_by_name(object_name, pos)?;
|
||||
if let Some(parent) = parent {
|
||||
if !self.forms.belongs_to(&object, parent) {
|
||||
self.err(
|
||||
pos,
|
||||
format!("Control '{object_name}' not found in form '{parent}'"),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let (id, object) = self.scoped_object(object_name, parent, pos)?;
|
||||
Some((id, object, member.to_string()))
|
||||
}
|
||||
|
||||
@@ -1118,7 +1236,7 @@ impl Sema {
|
||||
fn alloc_global(&mut self, name: &str, ty: &Ty, array: bool) -> u16 {
|
||||
let slot = self.globals.len() as u16;
|
||||
self.globals.push(hir::HVar {
|
||||
name: name.to_string(),
|
||||
name: name.trim_end_matches("\u{1}AS").to_string(),
|
||||
ty: self.h_ty(ty),
|
||||
array,
|
||||
});
|
||||
@@ -1138,7 +1256,7 @@ impl Sema {
|
||||
} else {
|
||||
let slot = scope.locals.len() as u16;
|
||||
scope.locals.push(hir::HVar {
|
||||
name: name.to_string(),
|
||||
name: name.trim_end_matches("\u{1}AS").to_string(),
|
||||
ty: self.h_ty(ty),
|
||||
array,
|
||||
});
|
||||
@@ -1255,7 +1373,7 @@ impl Sema {
|
||||
let ty = self.name_ty(name, suffix);
|
||||
// DEF FN: freie Namen binden an Modulvariablen (Vorbild-Semantik).
|
||||
let info = if scope.is_def_fn {
|
||||
let slot = self.alloc_global(name, &ty, array);
|
||||
let slot = self.alloc_global(&key, &ty, array);
|
||||
let info = VarInfo {
|
||||
ty,
|
||||
array,
|
||||
@@ -1267,7 +1385,7 @@ impl Sema {
|
||||
self.module_vars.insert(key, info.clone());
|
||||
info
|
||||
} else {
|
||||
let (slot, global) = self.alloc_var(scope, name, &ty, array);
|
||||
let (slot, global) = self.alloc_var(scope, &key, &ty, array);
|
||||
let info = VarInfo {
|
||||
ty,
|
||||
array,
|
||||
@@ -1285,12 +1403,12 @@ impl Sema {
|
||||
/// Konstantenfaltung für `CONST`-Ausdrücke.
|
||||
fn fold_const(&self, e: &Expr) -> Option<ConstVal> {
|
||||
match e {
|
||||
Expr::IntLit(v) => Some(ConstVal::Num(*v as f64)),
|
||||
Expr::LongLit(v) => Some(ConstVal::Num(*v as f64)),
|
||||
Expr::SingleLit(v) => Some(ConstVal::Num(*v as f64)),
|
||||
Expr::DoubleLit(v) => Some(ConstVal::Num(*v)),
|
||||
Expr::CurrencyLit(v) => Some(ConstVal::Num(*v as f64 / 10_000.0)),
|
||||
Expr::StrLit(s) => Some(ConstVal::Str(s.clone())),
|
||||
Expr::IntLit(v, _) => Some(ConstVal::Num(*v as f64)),
|
||||
Expr::LongLit(v, _) => Some(ConstVal::Num(*v as f64)),
|
||||
Expr::SingleLit(v, _) => Some(ConstVal::Num(*v as f64)),
|
||||
Expr::DoubleLit(v, _) => Some(ConstVal::Num(*v)),
|
||||
Expr::CurrencyLit(v, _) => Some(ConstVal::Num(*v as f64 / 10_000.0)),
|
||||
Expr::StrLit(s, _) => Some(ConstVal::Str(s.clone())),
|
||||
Expr::Paren(e) => self.fold_const(e),
|
||||
Expr::Name {
|
||||
name, args: None, ..
|
||||
@@ -1400,15 +1518,18 @@ impl Sema {
|
||||
// ---- Anweisungen -------------------------------------------------------
|
||||
|
||||
fn lower_body(&mut self, stmts: &[Stmt], scope: &mut Scope) -> Vec<HStmt> {
|
||||
let enclosing = (scope.current_line, scope.current_pos);
|
||||
let mut out = Vec::new();
|
||||
for stmt in stmts {
|
||||
self.lower_stmt(stmt, scope, &mut out);
|
||||
}
|
||||
(scope.current_line, scope.current_pos) = enclosing;
|
||||
out
|
||||
}
|
||||
|
||||
fn push(&mut self, out: &mut Vec<HStmt>, scope: &Scope, kind: HStmtKind) {
|
||||
out.push(HStmt {
|
||||
pos: scope.current_pos,
|
||||
line: scope.current_line,
|
||||
kind,
|
||||
});
|
||||
@@ -1419,6 +1540,7 @@ impl Sema {
|
||||
let pos = stmt_pos(stmt);
|
||||
if pos.line > 0 {
|
||||
scope.current_line = pos.line;
|
||||
scope.current_pos = pos;
|
||||
}
|
||||
match stmt {
|
||||
Stmt::Data { .. }
|
||||
@@ -1430,7 +1552,7 @@ impl Sema {
|
||||
self.push(out, scope, HStmtKind::Label(id));
|
||||
}
|
||||
}
|
||||
Stmt::LineNumber(n) => {
|
||||
Stmt::LineNumber(n, _) => {
|
||||
if let Some(id) = scope.line_labels.get(n).copied() {
|
||||
self.push(out, scope, HStmtKind::Label(id));
|
||||
}
|
||||
@@ -1637,7 +1759,7 @@ impl Sema {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if self.forms.find(name).is_some() {
|
||||
if self.find_object(name).is_some() {
|
||||
self.err(*pos, format!("Object '{name}' has no default property"));
|
||||
self.lower_expr(value, scope);
|
||||
return;
|
||||
@@ -1895,11 +2017,12 @@ impl Sema {
|
||||
Some(b) => self.lower_body(b, scope),
|
||||
None => Vec::new(),
|
||||
};
|
||||
for (ec, eb) in elseifs.iter().rev() {
|
||||
for (ec, eb, elseif_pos) in elseifs.iter().rev() {
|
||||
let c2 = self.lower_cond(ec, scope);
|
||||
let body2 = self.lower_body(eb, scope);
|
||||
let stmt = HStmt {
|
||||
line: scope.current_line,
|
||||
pos: *elseif_pos,
|
||||
line: elseif_pos.line,
|
||||
kind: HStmtKind::If {
|
||||
cond: c2,
|
||||
then: body2,
|
||||
@@ -1920,6 +2043,7 @@ impl Sema {
|
||||
step,
|
||||
body,
|
||||
pos,
|
||||
end_pos,
|
||||
} => {
|
||||
let (place, vt) = self.lower_place(var, scope);
|
||||
if !is_num(&vt) {
|
||||
@@ -1956,6 +2080,7 @@ impl Sema {
|
||||
ty,
|
||||
from: from_e,
|
||||
to: to_e,
|
||||
end_pos: *end_pos,
|
||||
step: step_e,
|
||||
limit_slot,
|
||||
step_slot,
|
||||
@@ -1966,7 +2091,11 @@ impl Sema {
|
||||
}
|
||||
}
|
||||
Stmt::DoLoop {
|
||||
pre, post, body, ..
|
||||
pre,
|
||||
post,
|
||||
body,
|
||||
end_pos,
|
||||
..
|
||||
} => {
|
||||
let pre_c = pre.as_ref().map(|(u, c)| (*u, self.lower_cond(c, scope)));
|
||||
let exit_label = scope.new_label();
|
||||
@@ -1978,6 +2107,7 @@ impl Sema {
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::Loop {
|
||||
end_pos: *end_pos,
|
||||
pre: pre_c,
|
||||
post: post_c,
|
||||
body: hbody,
|
||||
@@ -1985,7 +2115,12 @@ impl Sema {
|
||||
},
|
||||
);
|
||||
}
|
||||
Stmt::While { cond, body, .. } => {
|
||||
Stmt::While {
|
||||
cond,
|
||||
body,
|
||||
end_pos,
|
||||
..
|
||||
} => {
|
||||
let c = self.lower_cond(cond, scope);
|
||||
let exit_label = scope.new_label();
|
||||
let hbody = self.lower_body(body, scope);
|
||||
@@ -1993,6 +2128,7 @@ impl Sema {
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::Loop {
|
||||
end_pos: *end_pos,
|
||||
pre: Some((false, c)),
|
||||
post: None,
|
||||
body: hbody,
|
||||
@@ -2120,8 +2256,11 @@ impl Sema {
|
||||
let ty = self.decl_ty(d);
|
||||
let key = self.var_key(&d.name, &d.suffix, d.as_type.is_some());
|
||||
if let Entry::Vacant(entry) = scope.vars.entry(key) {
|
||||
let slot =
|
||||
self.alloc_global(&format!("STATIC.{}", d.name), &ty, d.dims.is_some());
|
||||
let slot = self.alloc_global(
|
||||
&format!("STATIC.{}", entry.key()),
|
||||
&ty,
|
||||
d.dims.is_some(),
|
||||
);
|
||||
entry.insert(VarInfo {
|
||||
ty,
|
||||
array: d.dims.is_some(),
|
||||
@@ -2133,7 +2272,12 @@ impl Sema {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::CommonDecl { shared, decls, .. } => {
|
||||
Stmt::CommonDecl {
|
||||
shared,
|
||||
decls,
|
||||
block,
|
||||
..
|
||||
} => {
|
||||
for d in decls {
|
||||
if *shared && scope.is_module {
|
||||
self.shared_vars.insert(self.var_key(
|
||||
@@ -2143,6 +2287,30 @@ impl Sema {
|
||||
));
|
||||
}
|
||||
self.declare(d, false, scope, out);
|
||||
if scope.is_module {
|
||||
let key = self.var_key(&d.name, &d.suffix, d.as_type.is_some());
|
||||
let slot = self.module_vars[&key].slot;
|
||||
let dims = d.dims.as_ref().map(|dims| {
|
||||
dims.iter()
|
||||
.map(|(lo, hi)| {
|
||||
let lower =
|
||||
lo.as_ref().map_or(Some(self.option_base as i32), |e| {
|
||||
self.common_bound(e)
|
||||
});
|
||||
let upper = self.common_bound(hi);
|
||||
(lower, upper)
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
self.commons.push(hir::HCommon {
|
||||
slot,
|
||||
block: block.clone(),
|
||||
key,
|
||||
ty: self.globals[slot as usize].ty.clone(),
|
||||
dims,
|
||||
pos: d.pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::SharedDecl { decls, .. } => {
|
||||
@@ -2153,7 +2321,7 @@ impl Sema {
|
||||
Some(v) => v.clone(),
|
||||
None => {
|
||||
let ty = self.decl_ty(d);
|
||||
let slot = self.alloc_global(&d.name, &ty, d.dims.is_some());
|
||||
let slot = self.alloc_global(&key, &ty, d.dims.is_some());
|
||||
let v = VarInfo {
|
||||
ty,
|
||||
array: d.dims.is_some(),
|
||||
@@ -2255,7 +2423,7 @@ impl Sema {
|
||||
body,
|
||||
pos,
|
||||
} => {
|
||||
self.lower_def_fn(name, suffix, params, None, Some(body), scope, *pos);
|
||||
self.lower_def_fn(name, suffix, params, None, Some(body), *pos);
|
||||
}
|
||||
Stmt::DefFnBlock {
|
||||
name,
|
||||
@@ -2264,7 +2432,7 @@ impl Sema {
|
||||
body,
|
||||
pos,
|
||||
} => {
|
||||
self.lower_def_fn(name, suffix, params, Some(body), None, scope, *pos);
|
||||
self.lower_def_fn(name, suffix, params, Some(body), None, *pos);
|
||||
}
|
||||
// ---- Datei-E/A (Grammatik Phase 1, Laufzeit Phase 3) ----
|
||||
Stmt::Open {
|
||||
@@ -2318,7 +2486,7 @@ impl Sema {
|
||||
ne,
|
||||
HExpr::Str(table.clone()),
|
||||
HExpr::Str(spalten),
|
||||
HExpr::Lng(udt as i32),
|
||||
HExpr::UdtId(udt),
|
||||
],
|
||||
},
|
||||
);
|
||||
@@ -2848,7 +3016,7 @@ impl Sema {
|
||||
);
|
||||
|
||||
// Arme in Bedingungs-/Rumpf-Paare übersetzen; CASE ELSE gesondert.
|
||||
let mut cases: Vec<(HExpr, Vec<HStmt>)> = Vec::new();
|
||||
let mut cases: Vec<(HExpr, Vec<HStmt>, SourcePos)> = Vec::new();
|
||||
let mut else_body: Vec<HStmt> = Vec::new();
|
||||
for arm in arms {
|
||||
let body = self.lower_body(&arm.body, scope);
|
||||
@@ -2869,13 +3037,14 @@ impl Sema {
|
||||
},
|
||||
});
|
||||
}
|
||||
cases.push((cond.unwrap(), body));
|
||||
cases.push((cond.unwrap(), body, arm.pos));
|
||||
}
|
||||
// Von hinten zu verschachteltem If zusammensetzen.
|
||||
let mut els = else_body;
|
||||
for (cond, body) in cases.into_iter().rev() {
|
||||
for (cond, body, pos) in cases.into_iter().rev() {
|
||||
let stmt = HStmt {
|
||||
line: scope.current_line,
|
||||
pos,
|
||||
line: pos.line,
|
||||
kind: HStmtKind::If {
|
||||
cond,
|
||||
then: body,
|
||||
@@ -2947,7 +3116,6 @@ impl Sema {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn lower_def_fn(
|
||||
&mut self,
|
||||
name: &str,
|
||||
@@ -2955,7 +3123,6 @@ impl Sema {
|
||||
params: &[Param],
|
||||
block: Option<&[Stmt]>,
|
||||
single: Option<&Expr>,
|
||||
scope: &mut Scope,
|
||||
pos: SourcePos,
|
||||
) {
|
||||
// Registrierung (mit Prozedur-Id).
|
||||
@@ -2981,12 +3148,12 @@ impl Sema {
|
||||
def_fn: true,
|
||||
},
|
||||
);
|
||||
let _ = pos;
|
||||
|
||||
// Rumpf-Scope: Parameter (BYVAL) und Rückgabevariable lokal,
|
||||
// freie Namen binden an Modulvariablen.
|
||||
let mut fscope = Scope {
|
||||
is_def_fn: true,
|
||||
current_line: pos.line,
|
||||
current_pos: pos,
|
||||
..Scope::default()
|
||||
};
|
||||
let mut hparams = Vec::new();
|
||||
@@ -2995,7 +3162,7 @@ impl Sema {
|
||||
let key = self.var_key(&p.name, &p.suffix, false);
|
||||
let slot = fscope.locals.len() as u16;
|
||||
fscope.locals.push(hir::HVar {
|
||||
name: p.name.clone(),
|
||||
name: key.trim_end_matches("\u{1}AS").to_string(),
|
||||
ty: self.h_ty(&ty),
|
||||
array: false,
|
||||
});
|
||||
@@ -3020,7 +3187,7 @@ impl Sema {
|
||||
let ret_key = self.var_key(name, suffix, false);
|
||||
let ret_slot_idx = fscope.locals.len() as u16;
|
||||
fscope.locals.push(hir::HVar {
|
||||
name: name.to_string(),
|
||||
name: name.trim_end_matches("\u{1}AS").to_string(),
|
||||
ty: self.h_ty(&ret),
|
||||
array: false,
|
||||
});
|
||||
@@ -3045,6 +3212,7 @@ impl Sema {
|
||||
let (e, et) = self.lower_expr(expr, &mut fscope);
|
||||
let value = self.coerce(e, &et, &ret, expr.pos());
|
||||
vec![HStmt {
|
||||
pos: fscope.current_pos,
|
||||
line: fscope.current_line,
|
||||
kind: HStmtKind::Assign {
|
||||
place: HPlace {
|
||||
@@ -3072,14 +3240,24 @@ impl Sema {
|
||||
body,
|
||||
label_count: fscope.next_label,
|
||||
};
|
||||
// Modul-Scope-Zeile weiterführen.
|
||||
scope.current_line = scope.current_line.max(fscope.current_line);
|
||||
while self.hir_procs.len() <= id as usize {
|
||||
self.hir_procs.push(None);
|
||||
}
|
||||
self.hir_procs[id as usize] = Some(hproc);
|
||||
}
|
||||
|
||||
fn common_bound(&self, expr: &Expr) -> Option<i32> {
|
||||
match self.fold_const(expr)? {
|
||||
ConstVal::Num(n)
|
||||
if n.is_finite()
|
||||
&& (i32::MIN as f64..=i32::MAX as f64).contains(&n.round_ties_even()) =>
|
||||
{
|
||||
Some(n.round_ties_even() as i32)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn declare(&mut self, d: &VarDecl, redim: bool, scope: &mut Scope, out: &mut Vec<HStmt>) {
|
||||
let ty = self.decl_ty(d);
|
||||
let mut hdims = Vec::new();
|
||||
@@ -3120,7 +3298,7 @@ impl Sema {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let (slot, global) = self.alloc_var(scope, &d.name, &ty, is_array);
|
||||
let (slot, global) = self.alloc_var(scope, &key, &ty, is_array);
|
||||
self.insert_var(
|
||||
scope,
|
||||
key,
|
||||
@@ -3249,13 +3427,7 @@ impl Sema {
|
||||
return;
|
||||
}
|
||||
if matches!(name, "SHOW" | "HIDE") && args.is_empty() {
|
||||
if let Some((object, _)) = self
|
||||
.forms
|
||||
.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, object)| object.class == ObjectClass::Form)
|
||||
{
|
||||
if let Some((object, _)) = self.current_form() {
|
||||
let method = forms::methods(ObjectClass::Form)
|
||||
.iter()
|
||||
.position(|method| *method == name)
|
||||
@@ -3264,7 +3436,7 @@ impl Sema {
|
||||
out,
|
||||
scope,
|
||||
HStmtKind::ObjectMethod {
|
||||
object: object as u16,
|
||||
object,
|
||||
index: None,
|
||||
method,
|
||||
args: Vec::new(),
|
||||
@@ -3343,20 +3515,15 @@ impl Sema {
|
||||
pos: object_pos,
|
||||
} = &args[0]
|
||||
{
|
||||
if let Some((object, info)) =
|
||||
self.forms.find(object_name).map(|(i, o)| (i, o.clone()))
|
||||
{
|
||||
if index.is_some() && !info.array {
|
||||
self.err(
|
||||
*object_pos,
|
||||
format!("Object '{object_name}' is not an array"),
|
||||
);
|
||||
let (parent, object_name) = object_name
|
||||
.split_once('!')
|
||||
.map_or((None, object_name.as_str()), |(parent, name)| {
|
||||
(Some(parent), name)
|
||||
});
|
||||
if let Some((object, _)) = self.scoped_object(object_name, parent, *object_pos) {
|
||||
let Ok(index) = self.object_index(object, index, scope, *object_pos) else {
|
||||
return;
|
||||
}
|
||||
let index = index
|
||||
.as_ref()
|
||||
.and_then(|a| a.first())
|
||||
.map(|e| self.lower_num_as(e, scope, NumTy::Lng));
|
||||
};
|
||||
self.push(
|
||||
out,
|
||||
scope,
|
||||
@@ -3368,7 +3535,6 @@ impl Sema {
|
||||
);
|
||||
return;
|
||||
}
|
||||
self.err(*object_pos, format!("Unknown object '{object_name}'"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -3514,7 +3680,7 @@ impl Sema {
|
||||
if self.consts.contains_key(name) {
|
||||
return false;
|
||||
}
|
||||
if self.forms.find(name).is_some()
|
||||
if (suffix.is_none() && self.find_object(name).is_some())
|
||||
|| (name.contains('!') && !self.is_udt_path(scope, name))
|
||||
{
|
||||
return false;
|
||||
@@ -3978,7 +4144,7 @@ impl Sema {
|
||||
/// Greift nur bei Stringliteralen; ein zur Laufzeit gebildeter Gerätename
|
||||
/// bleibt der Datei-E/A überlassen.
|
||||
fn reject_com_device(&mut self, file: &Expr, pos: SourcePos) {
|
||||
if let Expr::StrLit(s) = file {
|
||||
if let Expr::StrLit(s, _) = file {
|
||||
if ist_com_geraet(s) {
|
||||
self.err(pos, "Feature unavailable");
|
||||
}
|
||||
@@ -4108,7 +4274,7 @@ impl Sema {
|
||||
return (
|
||||
Some(HPlace {
|
||||
base: base_slot,
|
||||
base_is_ref: false,
|
||||
base_is_ref: info.by_ref,
|
||||
indices: indices.unwrap_or_default(),
|
||||
fields: fpath,
|
||||
ty: hty,
|
||||
@@ -4157,12 +4323,12 @@ impl Sema {
|
||||
|
||||
fn lower_expr(&mut self, e: &Expr, scope: &mut Scope) -> (HExpr, Ty) {
|
||||
match e {
|
||||
Expr::IntLit(v) => (HExpr::Int(*v), Ty::Int),
|
||||
Expr::LongLit(v) => (HExpr::Lng(*v), Ty::Lng),
|
||||
Expr::SingleLit(v) => (HExpr::Sng(*v), Ty::Sng),
|
||||
Expr::DoubleLit(v) => (HExpr::Dbl(*v), Ty::Dbl),
|
||||
Expr::CurrencyLit(v) => (HExpr::Cur(*v), Ty::Cur),
|
||||
Expr::StrLit(s) => (HExpr::Str(s.clone()), Ty::Str),
|
||||
Expr::IntLit(v, _) => (HExpr::Int(*v), Ty::Int),
|
||||
Expr::LongLit(v, _) => (HExpr::Lng(*v), Ty::Lng),
|
||||
Expr::SingleLit(v, _) => (HExpr::Sng(*v), Ty::Sng),
|
||||
Expr::DoubleLit(v, _) => (HExpr::Dbl(*v), Ty::Dbl),
|
||||
Expr::CurrencyLit(v, _) => (HExpr::Cur(*v), Ty::Cur),
|
||||
Expr::StrLit(s, _) => (HExpr::Str(s.clone()), Ty::Str),
|
||||
Expr::Paren(inner) => self.lower_expr(inner, scope),
|
||||
Expr::Missing => (HExpr::Int(0), Ty::Unknown),
|
||||
Expr::TypeOf { value, class, pos } => {
|
||||
@@ -4659,7 +4825,11 @@ impl Sema {
|
||||
);
|
||||
return (HExpr::Int(0), Ty::Unknown);
|
||||
}
|
||||
if let Some((object, info)) = self.forms.find(name).map(|(id, info)| (id, info.clone())) {
|
||||
if let Some((object, info)) = self
|
||||
.find_object(name)
|
||||
.filter(|_| suffix.is_none())
|
||||
.map(|(id, info)| (id, info.clone()))
|
||||
{
|
||||
if args.is_none() || info.array {
|
||||
let Ok(index) = self.object_index(object, args, scope, pos) else {
|
||||
return (HExpr::Int(0), Ty::Unknown);
|
||||
@@ -5296,7 +5466,7 @@ impl Sema {
|
||||
}
|
||||
|
||||
/// Position einer Anweisung (für Zeileninfo der Anweisungsgrenzen).
|
||||
fn stmt_pos(stmt: &Stmt) -> SourcePos {
|
||||
pub fn stmt_pos(stmt: &Stmt) -> SourcePos {
|
||||
match stmt {
|
||||
Stmt::Assign { pos, .. }
|
||||
| Stmt::Print { pos, .. }
|
||||
@@ -5352,7 +5522,8 @@ fn stmt_pos(stmt: &Stmt) -> SourcePos {
|
||||
| Stmt::End(pos)
|
||||
| Stmt::StopStmt(pos)
|
||||
| Stmt::System(pos) => *pos,
|
||||
Stmt::Label(_) | Stmt::LineNumber(_) => SourcePos::default(),
|
||||
Stmt::LineNumber(_, pos) => *pos,
|
||||
Stmt::Label(_) => SourcePos::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5377,10 +5548,10 @@ fn trap_art(device: &str) -> Option<u8> {
|
||||
/// Konstanter Zahlenwert eines Ausdrucks, soweit direkt ablesbar.
|
||||
fn const_zahl(e: &Expr) -> Option<i64> {
|
||||
match e {
|
||||
Expr::IntLit(n) => Some(*n as i64),
|
||||
Expr::LongLit(n) => Some(*n as i64),
|
||||
Expr::SingleLit(n) => Some(*n as i64),
|
||||
Expr::DoubleLit(n) => Some(*n as i64),
|
||||
Expr::IntLit(n, _) => Some(*n as i64),
|
||||
Expr::LongLit(n, _) => Some(*n as i64),
|
||||
Expr::SingleLit(n, _) => Some(*n as i64),
|
||||
Expr::DoubleLit(n, _) => Some(*n as i64),
|
||||
Expr::Paren(inner) => const_zahl(inner),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user