Projektmodule und vollständiges TBC-Kompilat umsetzen und Change archivieren

This commit is contained in:
2026-09-05 23:06:56 +02:00
parent 58b1f620ea
commit 815825dde7
40 changed files with 4177 additions and 555 deletions

View File

@@ -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(&param.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,
}