Projektmodule und vollständiges TBC-Kompilat umsetzen und Change archivieren
This commit is contained in:
@@ -70,6 +70,8 @@ struct Frame {
|
||||
/// Instruktionsindex der zuletzt begonnenen Anweisung (`Stmt`).
|
||||
last_stmt_pc: usize,
|
||||
line: u32,
|
||||
source: u32,
|
||||
column: u32,
|
||||
/// Gesetzt, wenn dieser Frame der Handler eines Ereignis-Traps ist.
|
||||
/// Er läuft im Modulrumpf und **teilt dessen Locals** — ein eigener
|
||||
/// Satz würde dem Handler leere Modulvariablen zeigen. Sein `RETURN`
|
||||
@@ -117,14 +119,14 @@ pub struct Vm {
|
||||
erl: u32,
|
||||
/// Zuletzt durchlaufene numerische Zeilennummer (0 = keine).
|
||||
zeile_nr: u32,
|
||||
module_handler: Handler,
|
||||
module_handlers: Vec<Handler>,
|
||||
in_handler: bool,
|
||||
resume_pc: usize,
|
||||
// Steuerung
|
||||
flags: u32,
|
||||
/// Zählt Anweisungsgrenzen für die regelmäßige Ereignisabholung.
|
||||
tick_zaehler: u32,
|
||||
breakpoints: HashSet<u32>,
|
||||
breakpoints: HashSet<(u16, u32)>,
|
||||
data_ptr: usize,
|
||||
start_pc: Option<usize>,
|
||||
pub forms: FormsModel,
|
||||
@@ -170,7 +172,15 @@ impl Vm {
|
||||
.iter()
|
||||
.map(|t| default_value(t, &module.udts))
|
||||
.collect();
|
||||
let forms = FormsModel::new(module.objects.clone(), 80, 25);
|
||||
let mut forms = FormsModel::new(module.objects.clone(), 80, 25);
|
||||
for initial in &module.form_initial {
|
||||
initial
|
||||
.apply(&mut forms)
|
||||
.expect("validierte Forms-Anfangsdaten");
|
||||
}
|
||||
if let Some(form) = module.startup_form {
|
||||
forms.show(form, false).expect("validiertes Startformular");
|
||||
}
|
||||
let mut vm = Vm {
|
||||
globals,
|
||||
locals: Vec::new(),
|
||||
@@ -180,7 +190,7 @@ impl Vm {
|
||||
err: 0,
|
||||
erl: 0,
|
||||
zeile_nr: 0,
|
||||
module_handler: Handler::None,
|
||||
module_handlers: vec![Handler::None; module.modules.len()],
|
||||
in_handler: false,
|
||||
resume_pc: 0,
|
||||
flags: 0,
|
||||
@@ -202,7 +212,12 @@ impl Vm {
|
||||
let target = self.module.procs[0]
|
||||
.code
|
||||
.iter()
|
||||
.position(|instruction| matches!(instruction, Instr::Stmt(found) if *found == line));
|
||||
.position(|instruction| matches!(instruction, Instr::SetErl(found) if *found == line))
|
||||
.or_else(|| {
|
||||
self.module.procs[0].code.iter().position(
|
||||
|instruction| matches!(instruction, Instr::Stmt(found) if *found == line),
|
||||
)
|
||||
});
|
||||
let target = target.ok_or(RuntimeError(8))?;
|
||||
if self.module.procs[0]
|
||||
.code
|
||||
@@ -229,6 +244,8 @@ impl Vm {
|
||||
}
|
||||
let stack_base = self.stack.len();
|
||||
self.frames.push(Frame {
|
||||
source: 0,
|
||||
column: 0,
|
||||
proc,
|
||||
pc: 0,
|
||||
locals_base,
|
||||
@@ -361,7 +378,12 @@ impl Vm {
|
||||
if !matches!(self.module.procs[proc].code.as_slice(), [Instr::RetProc]) {
|
||||
return Ok(false);
|
||||
}
|
||||
let name = self.module.procs[proc].name.to_ascii_uppercase();
|
||||
let name = self.module.procs[proc]
|
||||
.name
|
||||
.rsplit('!')
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_ascii_uppercase();
|
||||
if !matches!(
|
||||
name.as_str(),
|
||||
"CMNDLGREGISTER"
|
||||
@@ -767,6 +789,8 @@ impl Vm {
|
||||
let locals_base = self.frames[0].locals_base;
|
||||
let stack_base = self.stack.len();
|
||||
self.frames.push(Frame {
|
||||
source: 0,
|
||||
column: 0,
|
||||
proc: 0,
|
||||
pc: ziel as usize,
|
||||
locals_base,
|
||||
@@ -821,17 +845,54 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub fn add_breakpoint(&mut self, line: u32) {
|
||||
self.breakpoints.insert(line);
|
||||
self.breakpoints.insert((0, line));
|
||||
self.flags |= F_BREAK;
|
||||
}
|
||||
|
||||
pub fn remove_breakpoint(&mut self, line: u32) {
|
||||
self.breakpoints.remove(&line);
|
||||
self.breakpoints.remove(&(0, line));
|
||||
if self.breakpoints.is_empty() {
|
||||
self.flags &= !F_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_module_breakpoint(&mut self, module: u16, line: u32) {
|
||||
self.breakpoints.insert((module, line));
|
||||
self.flags |= F_BREAK;
|
||||
}
|
||||
|
||||
pub fn remove_module_breakpoint(&mut self, module: u16, line: u32) {
|
||||
self.breakpoints.remove(&(module, line));
|
||||
if self.breakpoints.is_empty() {
|
||||
self.flags &= !F_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_source_pos(&self) -> tb_frontend::SourcePos {
|
||||
self.frames
|
||||
.last()
|
||||
.map(|f| tb_frontend::SourcePos {
|
||||
source: f.source,
|
||||
line: f.line,
|
||||
column: f.column,
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn current_module(&self) -> u16 {
|
||||
self.module
|
||||
.sources
|
||||
.get(self.current_source_pos().source as usize)
|
||||
.map_or(0, |s| s.module)
|
||||
}
|
||||
|
||||
pub fn current_file(&self) -> &str {
|
||||
self.module
|
||||
.sources
|
||||
.get(self.current_source_pos().source as usize)
|
||||
.map_or(&self.module.name, |s| &s.path)
|
||||
}
|
||||
|
||||
pub fn current_line(&self) -> u32 {
|
||||
self.frames.last().map(|f| f.line).unwrap_or(0)
|
||||
}
|
||||
@@ -843,26 +904,58 @@ impl Vm {
|
||||
.unwrap_or("")
|
||||
}
|
||||
|
||||
/// Variableninspektion (Debugger): erst Locals des obersten Frames
|
||||
/// (Referenzen werden aufgelöst), dann Modulvariablen. Ein
|
||||
/// Typ-Suffix (`n%`, `s$`) wird toleriert — Slots tragen Basisnamen.
|
||||
/// Variableninspektion: lokale Namen vor Modulvariablen, explizite
|
||||
/// Typ-Suffixe vor einer eindeutigen Suche nach dem Basisnamen.
|
||||
pub fn inspect(&self, name: &str) -> Option<Value> {
|
||||
let name = name.trim_end_matches(['%', '&', '!', '#', '$', '@']);
|
||||
fn matches(stored: &str, requested: &str) -> bool {
|
||||
let suffixes = ['%', '&', '!', '#', '$', '@'];
|
||||
stored.eq_ignore_ascii_case(requested)
|
||||
|| (!(stored.ends_with(suffixes) && requested.ends_with(suffixes))
|
||||
&& stored
|
||||
.trim_end_matches(suffixes)
|
||||
.eq_ignore_ascii_case(requested.trim_end_matches(suffixes)))
|
||||
}
|
||||
fn unique(mut ids: impl Iterator<Item = usize>) -> Option<usize> {
|
||||
let first = ids.next()?;
|
||||
ids.next().is_none().then_some(first)
|
||||
}
|
||||
if let Some(f) = self.frames.last() {
|
||||
let p = &self.module.procs[f.proc];
|
||||
for (i, n) in p.local_names.iter().enumerate() {
|
||||
if n.eq_ignore_ascii_case(name) {
|
||||
let v = self.locals[f.locals_base + i].clone();
|
||||
return Some(self.deref_for_inspect(v));
|
||||
}
|
||||
if let Some(i) = unique(
|
||||
p.local_names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, n)| matches(n, name))
|
||||
.map(|(i, _)| i),
|
||||
) {
|
||||
return Some(self.deref_for_inspect(self.locals[f.locals_base + i].clone()));
|
||||
}
|
||||
}
|
||||
for (i, n) in self.module.global_names.iter().enumerate() {
|
||||
if n.eq_ignore_ascii_case(name) {
|
||||
return Some(self.deref_for_inspect(self.globals[i].clone()));
|
||||
}
|
||||
let qualified = format!(
|
||||
"{}!{name}",
|
||||
self.module.modules[self.current_module() as usize].0
|
||||
);
|
||||
if let Some(id) = unique(
|
||||
self.module
|
||||
.global_names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, n)| matches(n, name) || matches(n, &qualified))
|
||||
.map(|(i, _)| i),
|
||||
) {
|
||||
return Some(self.globals[id].clone());
|
||||
}
|
||||
None
|
||||
let id = unique(
|
||||
self.module
|
||||
.global_names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, n)| {
|
||||
matches(n.split_once('!').map_or(n.as_str(), |(_, name)| name), name)
|
||||
})
|
||||
.map(|(i, _)| i),
|
||||
)?;
|
||||
Some(self.globals[id].clone())
|
||||
}
|
||||
|
||||
/// Arrayelement inspizieren.
|
||||
@@ -993,8 +1086,15 @@ impl Vm {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if target.is_none() && self.module_handler != Handler::None {
|
||||
target = Some((0, self.module_handler));
|
||||
if target.is_none() {
|
||||
for frame in self.frames.iter().rev() {
|
||||
let module = self.module.sources[frame.source as usize].module as usize;
|
||||
let handler = self.module_handlers[module];
|
||||
if handler != Handler::None {
|
||||
target = Some((0, handler));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some((depth, handler)) = target else {
|
||||
return Some(self.error_event(code, line));
|
||||
@@ -1039,7 +1139,7 @@ impl Vm {
|
||||
let code = &self.module.procs[proc].code;
|
||||
let mut i = from + 1;
|
||||
while i < code.len() {
|
||||
if matches!(code[i], Instr::Stmt(_)) {
|
||||
if matches!(code[i], Instr::Stmt(_) | Instr::InitStmt(_)) {
|
||||
return i;
|
||||
}
|
||||
i += 1;
|
||||
@@ -1189,7 +1289,7 @@ impl Vm {
|
||||
match cell {
|
||||
Value::Arr(a) => Ok(a.clone()),
|
||||
Value::Empty => {
|
||||
let lo = self.module.option_base as i32;
|
||||
let lo = self.module.modules[self.current_module() as usize].1 as i32;
|
||||
let bounds = vec![(lo, 10); dims as usize];
|
||||
let arr = ArrayObj::new(elem.clone(), bounds, &self.module.udts)?;
|
||||
let handle = Rc::new(std::cell::RefCell::new(arr));
|
||||
@@ -1223,7 +1323,9 @@ impl Vm {
|
||||
fn exec(&mut self, instr: Instr, pc: usize, host: &mut dyn Host) -> Result<Flow, RuntimeError> {
|
||||
use Instr as I;
|
||||
match instr {
|
||||
I::Stmt(line) => {
|
||||
I::Source(_, _) => Ok(Flow::Normal),
|
||||
I::Stmt(line) | I::InitStmt(line) => {
|
||||
let initializing = matches!(instr, I::InitStmt(_));
|
||||
if line == 0 {
|
||||
if let Some(target) = self.start_pc.take() {
|
||||
self.frames[0].pc = target;
|
||||
@@ -1231,6 +1333,13 @@ impl Vm {
|
||||
}
|
||||
let f = self.frames.last_mut().unwrap();
|
||||
f.line = line;
|
||||
if let Some(I::Source(source, column)) = pc
|
||||
.checked_sub(1)
|
||||
.and_then(|pc| self.module.procs[f.proc].code.get(pc))
|
||||
{
|
||||
f.source = *source;
|
||||
f.column = *column;
|
||||
}
|
||||
f.last_stmt_pc = pc;
|
||||
// Zustellpunkt: anzeigen, wenn sich der Bildschirm geändert
|
||||
// hat, und regelmäßig Ereignisse abholen. Das ist keine
|
||||
@@ -1238,20 +1347,27 @@ impl Vm {
|
||||
// Größenänderungen kämen nie an.
|
||||
self.tick_zaehler = self.tick_zaehler.wrapping_add(1);
|
||||
self.forms.render(&mut self.rt.screen);
|
||||
if self.rt.screen.ist_veraendert() || self.tick_zaehler.is_multiple_of(1024) {
|
||||
if !initializing
|
||||
&& (self.rt.screen.ist_veraendert() || self.tick_zaehler.is_multiple_of(1024))
|
||||
{
|
||||
self.tick(host);
|
||||
self.rt.screen.veraenderung_quittieren();
|
||||
}
|
||||
let erster_eintritt =
|
||||
std::mem::take(&mut self.frames.last_mut().unwrap().handler_start);
|
||||
if !erster_eintritt && self.zustellen(host, Zustellpunkt::Anweisung) {
|
||||
if !initializing
|
||||
&& !erster_eintritt
|
||||
&& self.zustellen(host, Zustellpunkt::Anweisung)
|
||||
{
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
if self.flags != 0 {
|
||||
if self.flags & F_STEP != 0 {
|
||||
return Ok(Flow::Event(RunEvent::Stepped { line }));
|
||||
}
|
||||
if self.flags & F_BREAK != 0 && self.breakpoints.contains(&line) {
|
||||
if self.flags & F_BREAK != 0
|
||||
&& self.breakpoints.contains(&(self.current_module(), line))
|
||||
{
|
||||
return Ok(Flow::Event(RunEvent::Breakpoint { line }));
|
||||
}
|
||||
if self.flags & F_POLL != 0 && self.rt.abbruch {
|
||||
@@ -1328,6 +1444,10 @@ impl Vm {
|
||||
self.push(Value::Lng(v));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::PushUdtId(id) => {
|
||||
self.push(Value::Lng(id as i32));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::PushSng(v) => {
|
||||
self.push(Value::Sng(v));
|
||||
Ok(Flow::Normal)
|
||||
@@ -1716,13 +1836,25 @@ impl Vm {
|
||||
a.data[flat as usize] = v;
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::DimArr(global, slot, dims, elem) => {
|
||||
I::DimArr(global, slot, dims, ref elem)
|
||||
| I::CommonArr(global, slot, dims, ref elem) => {
|
||||
let common = matches!(instr, I::CommonArr(..));
|
||||
let bounds = self.pop_bounds(dims)?;
|
||||
let cell = self.slot_value(global, slot);
|
||||
if common {
|
||||
if let Value::Arr(array) = cell {
|
||||
let array = array.borrow();
|
||||
return if &array.elem == elem && array.dims == bounds {
|
||||
Ok(Flow::Normal)
|
||||
} else {
|
||||
Err(RuntimeError::TYPE_MISMATCH)
|
||||
};
|
||||
}
|
||||
}
|
||||
if !matches!(cell, Value::Empty) {
|
||||
return Err(RuntimeError::DUPLICATE_DEFINITION);
|
||||
}
|
||||
let arr = ArrayObj::new(elem, bounds, &self.module.udts)?;
|
||||
let arr = ArrayObj::new(elem.clone(), bounds, &self.module.udts)?;
|
||||
*self.slot_value(global, slot) = Value::Arr(Rc::new(std::cell::RefCell::new(arr)));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
@@ -2293,7 +2425,8 @@ impl Vm {
|
||||
|
||||
// ---- Fehlerbehandlung ----
|
||||
I::OnErrorGoto(t) => {
|
||||
self.module_handler = Handler::Goto(t);
|
||||
let module = self.current_module() as usize;
|
||||
self.module_handlers[module] = Handler::Goto(t);
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::OnErrorLocal(t) => {
|
||||
@@ -2301,7 +2434,8 @@ impl Vm {
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::OnErrorDisable => {
|
||||
self.module_handler = Handler::None;
|
||||
let module = self.current_module() as usize;
|
||||
self.module_handlers[module] = Handler::None;
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::OnErrorLocalDisable => {
|
||||
@@ -2312,7 +2446,8 @@ impl Vm {
|
||||
if local {
|
||||
self.frames.last_mut().unwrap().local_handler = Handler::ResumeNext;
|
||||
} else {
|
||||
self.module_handler = Handler::ResumeNext;
|
||||
let module = self.current_module() as usize;
|
||||
self.module_handlers[module] = Handler::ResumeNext;
|
||||
}
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user