Phase 4: Formularmodell und Objektsprache
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -637,6 +637,7 @@ dependencies = [
|
||||
"log",
|
||||
"ratatui",
|
||||
"signal-hook",
|
||||
"tb-frontend",
|
||||
"tb-runtime",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -648,6 +649,7 @@ dependencies = [
|
||||
"log",
|
||||
"tb-frontend",
|
||||
"tb-runtime",
|
||||
"tb-ui",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
||||
4
PLAN.md
4
PLAN.md
@@ -399,7 +399,7 @@ Maßgeblich ist seit 2026-09-03 das Inventar, nicht diese Liste.
|
||||
Ereignisprozedur)
|
||||
Befund: Die VM stellt klassische Traps über einen eigenen Frame zu;
|
||||
Forms-Ereignisprozeduren verwenden später denselben Rückrufweg.
|
||||
- [ ] Formular-Modell: Eigenschaften, Lade-/Entladezyklus, `SHOW`/`HIDE`
|
||||
- [x] Formular-Modell: Eigenschaften, Lade-/Entladezyklus, `SHOW`/`HIDE`
|
||||
(modal/nicht-modal)
|
||||
- [ ] Steuerelemente: CommandButton, TextBox, ListBox, ComboBox, CheckBox,
|
||||
OptionButton, Frame, Label, HScrollBar/VScrollBar, PictureBox (Text),
|
||||
@@ -416,7 +416,7 @@ Maßgeblich ist seit 2026-09-03 das Inventar, nicht diese Liste.
|
||||
- [ ] Meilenstein (erster Kompatibilitätstest): die Programme aus
|
||||
https://github.com/cout/vbdos ohne Non-Features kompilieren und
|
||||
sind nutzbar (Konsolenprogramme bereits ab Phase 3)
|
||||
- [ ] Ereignisdispatch: Event-Queue ↔ VM (Ereignisprozeduren `Name_Ereignis`)
|
||||
- [x] Ereignisdispatch: Event-Queue ↔ VM (Ereignisprozeduren `Name_Ereignis`)
|
||||
- [x] **Klassische Ereignis-Traps** (ohne Forms, Sprachreferenz §8) —
|
||||
`ON TIMER(n)`, `ON KEY(n)`, `ON UEVENT` und `ON SIGNAL(n)` je mit
|
||||
`GOSUB label`, dazu die Steueranweisungen `TIMER`, `KEY(n)`,
|
||||
|
||||
@@ -14,6 +14,8 @@ pub enum TypeName {
|
||||
Currency,
|
||||
Str,
|
||||
FixedStr(i64),
|
||||
Form,
|
||||
Control,
|
||||
Udt(String),
|
||||
}
|
||||
|
||||
@@ -25,9 +27,24 @@ pub enum UnOp {
|
||||
|
||||
#[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,
|
||||
Pow,
|
||||
Mul,
|
||||
Div,
|
||||
IntDiv,
|
||||
Mod,
|
||||
Add,
|
||||
Sub,
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Eqv,
|
||||
Imp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -46,11 +63,26 @@ pub enum Expr {
|
||||
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 },
|
||||
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>),
|
||||
/// `TYPEOF ausdruck IS klasse`.
|
||||
TypeOf {
|
||||
value: Box<Expr>,
|
||||
class: String,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// Ausgelassenes Argument (`LOCATE , 5`).
|
||||
Missing,
|
||||
}
|
||||
@@ -60,7 +92,8 @@ impl Expr {
|
||||
match self {
|
||||
Expr::Name { pos, .. }
|
||||
| Expr::Unary { pos, .. }
|
||||
| Expr::Binary { pos, .. } => *pos,
|
||||
| Expr::Binary { pos, .. }
|
||||
| Expr::TypeOf { pos, .. } => *pos,
|
||||
Expr::Paren(e) => e.pos(),
|
||||
_ => SourcePos::default(),
|
||||
}
|
||||
@@ -202,7 +235,11 @@ pub enum EventAction {
|
||||
pub enum Stmt {
|
||||
Label(String),
|
||||
LineNumber(u32),
|
||||
Assign { target: Expr, value: Expr, pos: SourcePos },
|
||||
Assign {
|
||||
target: Expr,
|
||||
value: Expr,
|
||||
pos: SourcePos,
|
||||
},
|
||||
Print {
|
||||
/// LPRINT (Druckerausgabe) statt PRINT.
|
||||
printer: bool,
|
||||
@@ -226,7 +263,11 @@ pub enum Stmt {
|
||||
else_body: Option<Vec<Stmt>>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
Select { expr: Expr, arms: Vec<CaseArm>, pos: SourcePos },
|
||||
Select {
|
||||
expr: Expr,
|
||||
arms: Vec<CaseArm>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
For {
|
||||
var: Expr,
|
||||
from: Expr,
|
||||
@@ -236,25 +277,57 @@ pub enum Stmt {
|
||||
pos: SourcePos,
|
||||
},
|
||||
DoLoop {
|
||||
pre: Option<(bool, Expr)>, // (ist UNTIL, Bedingung)
|
||||
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 },
|
||||
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(SourcePos),
|
||||
StopStmt(SourcePos),
|
||||
System(SourcePos),
|
||||
Exit { kind: ExitKind, pos: SourcePos },
|
||||
Dim { shared: bool, redim: bool, decls: Vec<VarDecl>, pos: SourcePos },
|
||||
Exit {
|
||||
kind: ExitKind,
|
||||
pos: SourcePos,
|
||||
},
|
||||
Dim {
|
||||
shared: bool,
|
||||
redim: bool,
|
||||
decls: Vec<VarDecl>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `SHARED`-Anweisung in einer Prozedur (Zugriff auf Modulvariablen).
|
||||
SharedDecl { decls: Vec<VarDecl>, pos: SourcePos },
|
||||
SharedDecl {
|
||||
decls: Vec<VarDecl>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `STATIC`-Anweisung in einer Prozedur.
|
||||
StaticDecl { decls: Vec<VarDecl>, pos: SourcePos },
|
||||
StaticDecl {
|
||||
decls: Vec<VarDecl>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `COMMON [SHARED] [/block/] liste`.
|
||||
CommonDecl {
|
||||
shared: bool,
|
||||
@@ -262,20 +335,64 @@ pub enum Stmt {
|
||||
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 },
|
||||
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 },
|
||||
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,
|
||||
@@ -312,8 +429,15 @@ pub enum Stmt {
|
||||
len: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
CloseStmt { files: Vec<Expr>, pos: SourcePos },
|
||||
FieldStmt { file: Expr, fields: Vec<(Expr, Expr)>, pos: SourcePos },
|
||||
CloseStmt {
|
||||
files: Vec<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
FieldStmt {
|
||||
file: Expr,
|
||||
fields: Vec<(Expr, Expr)>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
GetPut {
|
||||
put: bool,
|
||||
file: Expr,
|
||||
@@ -321,9 +445,22 @@ pub enum Stmt {
|
||||
var: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
LsetRset { rset: bool, target: Expr, value: Expr, pos: SourcePos },
|
||||
WriteStmt { file: Option<Expr>, items: Vec<Expr>, pos: SourcePos },
|
||||
SeekStmt { file: Expr, position: Expr, pos: SourcePos },
|
||||
LsetRset {
|
||||
rset: bool,
|
||||
target: Expr,
|
||||
value: Expr,
|
||||
pos: SourcePos,
|
||||
},
|
||||
WriteStmt {
|
||||
file: Option<Expr>,
|
||||
items: Vec<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
SeekStmt {
|
||||
file: Expr,
|
||||
position: Expr,
|
||||
pos: SourcePos,
|
||||
},
|
||||
LockStmt {
|
||||
unlock: bool,
|
||||
file: Expr,
|
||||
@@ -362,9 +499,18 @@ pub enum Stmt {
|
||||
},
|
||||
// ---- Metabefehle ----
|
||||
/// `'$INCLUDE: 'datei''` — Auflösung übernimmt der Compile-Treiber.
|
||||
Include { path: String, pos: SourcePos },
|
||||
Include {
|
||||
path: String,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `'$STATIC` / `'$DYNAMIC`.
|
||||
MetaArrays { static_arrays: bool, pos: SourcePos },
|
||||
MetaArrays {
|
||||
static_arrays: bool,
|
||||
pos: SourcePos,
|
||||
},
|
||||
MetaForm {
|
||||
pos: SourcePos,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
||||
631
crates/tb-frontend/src/forms.rs
Normal file
631
crates/tb-frontend/src/forms.rs
Normal file
@@ -0,0 +1,631 @@
|
||||
//! Statische Forms-Typinformation. Namen werden beim Übersetzen in diese
|
||||
//! Tabellenindizes aufgelöst; die Laufzeit muss keine Strings durchsuchen.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ObjectClass {
|
||||
Form,
|
||||
CheckBox,
|
||||
ComboBox,
|
||||
CommandButton,
|
||||
DirListBox,
|
||||
DriveListBox,
|
||||
FileListBox,
|
||||
Frame,
|
||||
HScrollBar,
|
||||
Label,
|
||||
ListBox,
|
||||
Menu,
|
||||
OptionButton,
|
||||
PictureBox,
|
||||
TextBox,
|
||||
Timer,
|
||||
VScrollBar,
|
||||
Screen,
|
||||
}
|
||||
|
||||
impl ObjectClass {
|
||||
pub const ALL: [Self; 18] = [
|
||||
Self::Form,
|
||||
Self::CheckBox,
|
||||
Self::ComboBox,
|
||||
Self::CommandButton,
|
||||
Self::DirListBox,
|
||||
Self::DriveListBox,
|
||||
Self::FileListBox,
|
||||
Self::Frame,
|
||||
Self::HScrollBar,
|
||||
Self::Label,
|
||||
Self::ListBox,
|
||||
Self::Menu,
|
||||
Self::OptionButton,
|
||||
Self::PictureBox,
|
||||
Self::TextBox,
|
||||
Self::Timer,
|
||||
Self::VScrollBar,
|
||||
Self::Screen,
|
||||
];
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Form => "FORM",
|
||||
Self::CheckBox => "CHECKBOX",
|
||||
Self::ComboBox => "COMBOBOX",
|
||||
Self::CommandButton => "COMMANDBUTTON",
|
||||
Self::DirListBox => "DIRLISTBOX",
|
||||
Self::DriveListBox => "DRIVELISTBOX",
|
||||
Self::FileListBox => "FILELISTBOX",
|
||||
Self::Frame => "FRAME",
|
||||
Self::HScrollBar => "HSCROLLBAR",
|
||||
Self::Label => "LABEL",
|
||||
Self::ListBox => "LISTBOX",
|
||||
Self::Menu => "MENU",
|
||||
Self::OptionButton => "OPTIONBUTTON",
|
||||
Self::PictureBox => "PICTUREBOX",
|
||||
Self::TextBox => "TEXTBOX",
|
||||
Self::Timer => "TIMER",
|
||||
Self::VScrollBar => "VSCROLLBAR",
|
||||
Self::Screen => "SCREEN",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(name: &str) -> Option<Self> {
|
||||
Self::ALL
|
||||
.into_iter()
|
||||
.find(|c| c.name().eq_ignore_ascii_case(name))
|
||||
}
|
||||
|
||||
pub fn id(self) -> u8 {
|
||||
Self::ALL.iter().position(|c| *c == self).unwrap() as u8
|
||||
}
|
||||
|
||||
pub fn from_id(id: u8) -> Option<Self> {
|
||||
Self::ALL.get(id as usize).copied()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PropertyType {
|
||||
Integer,
|
||||
Single,
|
||||
String,
|
||||
Boolean,
|
||||
Object,
|
||||
IntegerArray,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PropertyDefault {
|
||||
Integer(i32),
|
||||
Single(f32),
|
||||
String(&'static str),
|
||||
Boolean(bool),
|
||||
Empty,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct PropertySpec {
|
||||
pub name: &'static str,
|
||||
pub ty: PropertyType,
|
||||
pub default: PropertyDefault,
|
||||
pub min: Option<i32>,
|
||||
pub max: Option<i32>,
|
||||
pub writable: bool,
|
||||
}
|
||||
|
||||
const fn int(name: &'static str, default: i32) -> PropertySpec {
|
||||
PropertySpec {
|
||||
name,
|
||||
ty: PropertyType::Integer,
|
||||
default: PropertyDefault::Integer(default),
|
||||
min: None,
|
||||
max: None,
|
||||
writable: true,
|
||||
}
|
||||
}
|
||||
const fn range(name: &'static str, default: i32, min: i32, max: i32) -> PropertySpec {
|
||||
PropertySpec {
|
||||
name,
|
||||
ty: PropertyType::Integer,
|
||||
default: PropertyDefault::Integer(default),
|
||||
min: Some(min),
|
||||
max: Some(max),
|
||||
writable: true,
|
||||
}
|
||||
}
|
||||
const fn boolp(name: &'static str, default: bool) -> PropertySpec {
|
||||
PropertySpec {
|
||||
name,
|
||||
ty: PropertyType::Boolean,
|
||||
default: PropertyDefault::Boolean(default),
|
||||
min: None,
|
||||
max: None,
|
||||
writable: true,
|
||||
}
|
||||
}
|
||||
const fn string(name: &'static str, default: &'static str) -> PropertySpec {
|
||||
PropertySpec {
|
||||
name,
|
||||
ty: PropertyType::String,
|
||||
default: PropertyDefault::String(default),
|
||||
min: None,
|
||||
max: None,
|
||||
writable: true,
|
||||
}
|
||||
}
|
||||
const fn ro(name: &'static str, ty: PropertyType) -> PropertySpec {
|
||||
PropertySpec {
|
||||
name,
|
||||
ty,
|
||||
default: PropertyDefault::Empty,
|
||||
min: None,
|
||||
max: None,
|
||||
writable: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Eigenschaftstabelle in stabiler Reihenfolge. Der Vektor ist klein und wird
|
||||
/// nur beim Übersetzen bzw. Erzeugen eines Objekts aufgebaut.
|
||||
pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
|
||||
use ObjectClass::*;
|
||||
let mut p = Vec::new();
|
||||
if !matches!(class, Timer | Menu | Screen) {
|
||||
p.extend([
|
||||
range("BACKCOLOR", 7, 0, 15),
|
||||
boolp("ENABLED", true),
|
||||
range("HEIGHT", 1, 1, 254),
|
||||
range("LEFT", 0, 0, 254),
|
||||
range("MOUSEPOINTER", 0, 0, 12),
|
||||
ro("PARENT", PropertyType::Object),
|
||||
string("TAG", ""),
|
||||
range("TOP", 0, 0, 254),
|
||||
boolp("VISIBLE", true),
|
||||
range("WIDTH", 1, 1, 254),
|
||||
]);
|
||||
}
|
||||
if !matches!(
|
||||
class,
|
||||
Form | Frame | Label | PictureBox | Timer | Menu | Screen
|
||||
) {
|
||||
p.extend([int("INDEX", 0), int("TABINDEX", 0), boolp("TABSTOP", true)]);
|
||||
}
|
||||
if !matches!(class, Form | Timer | Menu | Screen) {
|
||||
p.push(string("CTLNAME", ""));
|
||||
}
|
||||
if matches!(
|
||||
class,
|
||||
Form | TextBox
|
||||
| ListBox
|
||||
| ComboBox
|
||||
| CheckBox
|
||||
| OptionButton
|
||||
| Label
|
||||
| HScrollBar
|
||||
| VScrollBar
|
||||
| PictureBox
|
||||
) {
|
||||
p.push(range("FORECOLOR", 0, 0, 15));
|
||||
}
|
||||
if matches!(
|
||||
class,
|
||||
Form | CommandButton
|
||||
| TextBox
|
||||
| ListBox
|
||||
| ComboBox
|
||||
| CheckBox
|
||||
| OptionButton
|
||||
| Frame
|
||||
| Label
|
||||
| HScrollBar
|
||||
| VScrollBar
|
||||
| PictureBox
|
||||
) {
|
||||
p.push(range("DRAGMODE", 0, 0, 1));
|
||||
}
|
||||
match class {
|
||||
Form => p.extend([
|
||||
boolp("AUTOREDRAW", false),
|
||||
range("BORDERSTYLE", 2, 0, 6),
|
||||
string("CAPTION", ""),
|
||||
boolp("CONTROLBOX", true),
|
||||
int("CURRENTX", 0),
|
||||
int("CURRENTY", 0),
|
||||
string("FORMNAME", ""),
|
||||
range("FORMTYPE", 0, 0, 1),
|
||||
boolp("MAXBUTTON", true),
|
||||
boolp("MINBUTTON", true),
|
||||
ro("SCALEHEIGHT", PropertyType::Integer),
|
||||
ro("SCALEWIDTH", PropertyType::Integer),
|
||||
range("WINDOWSTATE", 0, 0, 2),
|
||||
]),
|
||||
CommandButton => p.extend([
|
||||
boolp("CANCEL", false),
|
||||
string("CAPTION", ""),
|
||||
boolp("DEFAULT", false),
|
||||
boolp("VALUE", false),
|
||||
]),
|
||||
TextBox => p.extend([
|
||||
range("BORDERSTYLE", 1, 0, 1),
|
||||
boolp("MULTILINE", false),
|
||||
range("SCROLLBARS", 0, 0, 3),
|
||||
int("SELLENGTH", 0),
|
||||
int("SELSTART", 0),
|
||||
string("SELTEXT", ""),
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
ListBox => p.extend([
|
||||
ro("LIST", PropertyType::String),
|
||||
ro("LISTCOUNT", PropertyType::Integer),
|
||||
int("LISTINDEX", -1),
|
||||
boolp("SORTED", false),
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
ComboBox => p.extend([
|
||||
ro("LIST", PropertyType::String),
|
||||
ro("LISTCOUNT", PropertyType::Integer),
|
||||
int("LISTINDEX", -1),
|
||||
boolp("SORTED", false),
|
||||
int("SELLENGTH", 0),
|
||||
int("SELSTART", 0),
|
||||
string("SELTEXT", ""),
|
||||
range("STYLE", 0, 0, 2),
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
CheckBox => p.extend([string("CAPTION", ""), range("VALUE", 0, 0, 2)]),
|
||||
OptionButton => p.extend([string("CAPTION", ""), range("VALUE", 0, -1, 0)]),
|
||||
Frame => p.push(string("CAPTION", "")),
|
||||
Label => p.extend([
|
||||
range("ALIGNMENT", 0, 0, 2),
|
||||
boolp("AUTOSIZE", false),
|
||||
range("BORDERSTYLE", 0, 0, 2),
|
||||
string("CAPTION", ""),
|
||||
]),
|
||||
HScrollBar | VScrollBar => p.extend([
|
||||
ro("ATTACHED", PropertyType::Boolean),
|
||||
range("LARGECHANGE", 1, 1, 32767),
|
||||
range("MIN", 0, -32768, 32767),
|
||||
range("MAX", 32767, -32768, 32767),
|
||||
range("SMALLCHANGE", 1, 1, 32767),
|
||||
range("VALUE", 0, -32768, 32767),
|
||||
]),
|
||||
PictureBox => p.extend([
|
||||
boolp("AUTOREDRAW", false),
|
||||
range("BORDERSTYLE", 1, 0, 2),
|
||||
int("CURRENTX", 0),
|
||||
int("CURRENTY", 0),
|
||||
ro("SCALEHEIGHT", PropertyType::Integer),
|
||||
ro("SCALEWIDTH", PropertyType::Integer),
|
||||
]),
|
||||
Timer => p.extend([
|
||||
string("CTLNAME", ""),
|
||||
boolp("ENABLED", true),
|
||||
int("INDEX", 0),
|
||||
range("INTERVAL", 0, 0, 65535),
|
||||
ro("PARENT", PropertyType::Object),
|
||||
string("TAG", ""),
|
||||
]),
|
||||
Menu => p.extend([
|
||||
string("CAPTION", ""),
|
||||
boolp("CHECKED", false),
|
||||
string("CTLNAME", ""),
|
||||
boolp("ENABLED", true),
|
||||
int("INDEX", 0),
|
||||
ro("PARENT", PropertyType::Object),
|
||||
boolp("SEPARATOR", false),
|
||||
string("TAG", ""),
|
||||
boolp("VISIBLE", true),
|
||||
]),
|
||||
DirListBox => p.extend([string("PATH", ""), string("TEXT", "")]),
|
||||
DriveListBox => p.extend([string("DRIVE", ""), string("TEXT", "")]),
|
||||
FileListBox => p.extend([
|
||||
string("FILENAME", ""),
|
||||
string("PATH", ""),
|
||||
string("PATTERN", "*.*"),
|
||||
boolp("ARCHIVE", true),
|
||||
boolp("HIDDEN", false),
|
||||
boolp("NORMAL", true),
|
||||
boolp("READONLY", true),
|
||||
boolp("SYSTEM", false),
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
Screen => p.extend([
|
||||
ro("ACTIVECONTROL", PropertyType::Object),
|
||||
ro("ACTIVEFORM", PropertyType::Object),
|
||||
boolp("AUTOREDRAW", false),
|
||||
PropertySpec {
|
||||
name: "CONTROLPANEL",
|
||||
ty: PropertyType::IntegerArray,
|
||||
default: PropertyDefault::Integer(0),
|
||||
min: Some(0),
|
||||
max: Some(15),
|
||||
writable: true,
|
||||
},
|
||||
ro("HEIGHT", PropertyType::Integer),
|
||||
ro("WIDTH", PropertyType::Integer),
|
||||
range("MOUSEPOINTER", 0, 0, 12),
|
||||
]),
|
||||
}
|
||||
if class == Form {
|
||||
if let Some(visible) = p.iter_mut().find(|p| p.name == "VISIBLE") {
|
||||
visible.default = PropertyDefault::Boolean(false);
|
||||
}
|
||||
}
|
||||
p
|
||||
}
|
||||
|
||||
pub fn property(class: ObjectClass, name: &str) -> Option<(u16, PropertySpec)> {
|
||||
properties(class)
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.find(|(_, p)| p.name.eq_ignore_ascii_case(name))
|
||||
.map(|(i, p)| (i as u16, p))
|
||||
}
|
||||
|
||||
pub fn methods(class: ObjectClass) -> &'static [&'static str] {
|
||||
use ObjectClass::*;
|
||||
match class {
|
||||
Form => &[
|
||||
"CLS",
|
||||
"DRAG",
|
||||
"HIDE",
|
||||
"LOAD",
|
||||
"MOVE",
|
||||
"PRINT",
|
||||
"PRINTFORM",
|
||||
"REFRESH",
|
||||
"SHOW",
|
||||
"TEXTHEIGHT",
|
||||
"TEXTWIDTH",
|
||||
"UNLOAD",
|
||||
],
|
||||
ListBox | ComboBox => &[
|
||||
"ADDITEM",
|
||||
"DRAG",
|
||||
"MOVE",
|
||||
"REFRESH",
|
||||
"REMOVEITEM",
|
||||
"SETFOCUS",
|
||||
],
|
||||
CommandButton | TextBox | CheckBox | OptionButton => {
|
||||
&["DRAG", "MOVE", "REFRESH", "SETFOCUS"]
|
||||
}
|
||||
Frame | Label | HScrollBar | VScrollBar => &["DRAG", "MOVE", "REFRESH"],
|
||||
PictureBox => &[
|
||||
"CLS",
|
||||
"DRAG",
|
||||
"MOVE",
|
||||
"PRINT",
|
||||
"REFRESH",
|
||||
"SETFOCUS",
|
||||
"TEXTHEIGHT",
|
||||
"TEXTWIDTH",
|
||||
],
|
||||
Screen => &["HIDE", "SHOW"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn method_is_implemented(class: ObjectClass, name: &str) -> bool {
|
||||
matches!(
|
||||
(class, name),
|
||||
(ObjectClass::Form, "HIDE" | "LOAD" | "SHOW" | "UNLOAD")
|
||||
| (ObjectClass::Screen, "HIDE" | "SHOW")
|
||||
)
|
||||
}
|
||||
|
||||
pub fn method_arity(class: ObjectClass, name: &str) -> Option<(usize, usize)> {
|
||||
match (class, name) {
|
||||
(ObjectClass::Form, "SHOW") => Some((0, 1)),
|
||||
(ObjectClass::Form, "HIDE" | "LOAD" | "UNLOAD")
|
||||
| (ObjectClass::Screen, "HIDE" | "SHOW") => Some((0, 0)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn events(class: ObjectClass) -> &'static [&'static str] {
|
||||
use ObjectClass::*;
|
||||
match class {
|
||||
Form => &[
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOAD",
|
||||
"LOSTFOCUS",
|
||||
"MOUSEDOWN",
|
||||
"MOUSEMOVE",
|
||||
"MOUSEUP",
|
||||
"PAINT",
|
||||
"RESIZE",
|
||||
"UNLOAD",
|
||||
],
|
||||
CommandButton => &[
|
||||
"CLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
TextBox => &[
|
||||
"CHANGE",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
ListBox => &[
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
"MOUSEDOWN",
|
||||
"MOUSEMOVE",
|
||||
"MOUSEUP",
|
||||
],
|
||||
ComboBox => &[
|
||||
"CHANGE",
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"DROPDOWN",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
"MOUSEDOWN",
|
||||
"MOUSEMOVE",
|
||||
"MOUSEUP",
|
||||
],
|
||||
CheckBox => &[
|
||||
"CLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
OptionButton => &[
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
Frame => &["DRAGDROP", "DRAGOVER"],
|
||||
Label => &[
|
||||
"CHANGE",
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"MOUSEDOWN",
|
||||
"MOUSEMOVE",
|
||||
"MOUSEUP",
|
||||
],
|
||||
HScrollBar | VScrollBar => &[
|
||||
"CHANGE",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
PictureBox => &[
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
"MOUSEDOWN",
|
||||
"MOUSEMOVE",
|
||||
"MOUSEUP",
|
||||
"PAINT",
|
||||
],
|
||||
Timer => &["TIMER"],
|
||||
Menu => &["CLICK"],
|
||||
DirListBox => &["CHANGE", "CLICK", "DBLCLICK", "PATHCHANGE"],
|
||||
DriveListBox => &["CHANGE"],
|
||||
FileListBox => &["CLICK", "DBLCLICK", "PATTERNCHANGE", "PATHCHANGE"],
|
||||
Screen => &[],
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EventParamType {
|
||||
Integer,
|
||||
Single,
|
||||
Control,
|
||||
}
|
||||
|
||||
pub fn event_params(event: &str) -> Option<&'static [(&'static str, EventParamType)]> {
|
||||
use EventParamType::*;
|
||||
Some(match event.to_ascii_uppercase().as_str() {
|
||||
"KEYDOWN" | "KEYUP" => &[("KEYCODE", Integer), ("SHIFT", Integer)],
|
||||
"KEYPRESS" => &[("KEYASCII", Integer)],
|
||||
"MOUSEDOWN" | "MOUSEMOVE" | "MOUSEUP" => &[
|
||||
("BUTTON", Integer),
|
||||
("SHIFT", Integer),
|
||||
("X", Single),
|
||||
("Y", Single),
|
||||
],
|
||||
"DRAGDROP" => &[("SOURCE", Control), ("X", Single), ("Y", Single)],
|
||||
"DRAGOVER" => &[
|
||||
("SOURCE", Control),
|
||||
("X", Single),
|
||||
("Y", Single),
|
||||
("STATE", Integer),
|
||||
],
|
||||
"UNLOAD" => &[("CANCEL", Integer)],
|
||||
"CLICK" | "DBLCLICK" | "CHANGE" | "DROPDOWN" | "GOTFOCUS" | "LOSTFOCUS" | "LOAD"
|
||||
| "PAINT" | "RESIZE" | "TIMER" | "PATHCHANGE" | "PATTERNCHANGE" => &[],
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FormObject {
|
||||
pub name: String,
|
||||
pub class: ObjectClass,
|
||||
pub parent_form: Option<String>,
|
||||
pub array: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct FormCatalog {
|
||||
pub objects: Vec<FormObject>,
|
||||
}
|
||||
|
||||
impl FormCatalog {
|
||||
pub fn add(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
class: ObjectClass,
|
||||
parent_form: Option<&str>,
|
||||
array: bool,
|
||||
) -> u16 {
|
||||
let id = self.objects.len() as u16;
|
||||
self.objects.push(FormObject {
|
||||
name: name.into().to_uppercase(),
|
||||
class,
|
||||
parent_form: parent_form.map(str::to_uppercase),
|
||||
array,
|
||||
});
|
||||
id
|
||||
}
|
||||
|
||||
pub fn find(&self, name: &str) -> Option<(u16, &FormObject)> {
|
||||
self.objects
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, o)| o.name.eq_ignore_ascii_case(name))
|
||||
.map(|(i, o)| (i as u16, o))
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ pub enum HTy {
|
||||
FixedStr(u32),
|
||||
/// Benutzerdefinierter Typ (Index in `HirModule::udts`).
|
||||
Udt(u16),
|
||||
Form,
|
||||
Control,
|
||||
}
|
||||
|
||||
impl HTy {
|
||||
@@ -131,6 +133,17 @@ pub struct HirModule {
|
||||
pub procs: Vec<HProc>,
|
||||
pub data: Vec<DataItem>,
|
||||
pub option_base: u8,
|
||||
/// Zur Übersetzungszeit bekannte Forms-Objekte in Indexreihenfolge.
|
||||
pub objects: Vec<crate::forms::FormObject>,
|
||||
/// Ereignisprozeduren: Objektindex, Ereignisname, Prozedurindex.
|
||||
pub event_procs: Vec<HEventProc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HEventProc {
|
||||
pub object: u16,
|
||||
pub event: String,
|
||||
pub proc: u16,
|
||||
}
|
||||
|
||||
// ---- Ausdrücke -------------------------------------------------------------
|
||||
@@ -367,6 +380,25 @@ pub enum HExpr {
|
||||
Cur(i64),
|
||||
Str(String),
|
||||
Load(Box<HPlace>),
|
||||
ObjectProperty {
|
||||
object: u16,
|
||||
index: Option<Box<HExpr>>,
|
||||
property: u16,
|
||||
ty: HTy,
|
||||
},
|
||||
DynamicObjectProperty {
|
||||
object: Box<HExpr>,
|
||||
property: String,
|
||||
},
|
||||
ObjectRef {
|
||||
object: u16,
|
||||
index: Option<Box<HExpr>>,
|
||||
class: crate::forms::ObjectClass,
|
||||
},
|
||||
TypeOf {
|
||||
value: Box<HExpr>,
|
||||
class: crate::forms::ObjectClass,
|
||||
},
|
||||
/// Numerische Konvertierung nach Matrix (Rundung/Überlauf).
|
||||
Conv {
|
||||
from: NumTy,
|
||||
@@ -473,6 +505,27 @@ pub enum HStmtKind {
|
||||
place: HPlace,
|
||||
value: HExpr,
|
||||
},
|
||||
SetObjectProperty {
|
||||
object: u16,
|
||||
index: Option<HExpr>,
|
||||
property: u16,
|
||||
value: HExpr,
|
||||
},
|
||||
SetDynamicObjectProperty {
|
||||
object: HExpr,
|
||||
property: String,
|
||||
value: HExpr,
|
||||
},
|
||||
ObjectMethod {
|
||||
object: u16,
|
||||
method: u16,
|
||||
args: Vec<HExpr>,
|
||||
},
|
||||
ObjectLoad {
|
||||
object: u16,
|
||||
index: Option<HExpr>,
|
||||
unload: bool,
|
||||
},
|
||||
Print {
|
||||
items: Vec<HPrintItem>,
|
||||
/// Endet die Anweisung mit `;`/`,` (kein Zeilenumbruch)?
|
||||
|
||||
@@ -62,43 +62,169 @@ pub enum NumValue {
|
||||
/// und -anweisungen sind KEINE Keywords, sondern Builtins der Semantik).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Kw {
|
||||
And, As, Call, Case, Common, Const, Data, Declare, Def, DefCur, DefDbl, DefInt,
|
||||
DefLng, DefSng, DefStr, Dim, Do, Double, Else, ElseIf, End, Eqv, Erase,
|
||||
Error, Exit, For, Function, Gosub, Goto, If, Imp, Input, Integer, Is,
|
||||
Let, Line, Local, Long, Loop, Mod, Next, Not, On, Option, Or, Print,
|
||||
Read, ReDim, Rem, Restore, Resume, Return, Select, Shared, Single,
|
||||
Static, Step, Stop, String, Sub, System, Then, To, Type, Until, Using,
|
||||
Wend, While, Xor, Currency,
|
||||
And,
|
||||
As,
|
||||
Call,
|
||||
Case,
|
||||
Common,
|
||||
Const,
|
||||
Data,
|
||||
Declare,
|
||||
Def,
|
||||
DefCur,
|
||||
DefDbl,
|
||||
DefInt,
|
||||
DefLng,
|
||||
DefSng,
|
||||
DefStr,
|
||||
Dim,
|
||||
Do,
|
||||
Double,
|
||||
Else,
|
||||
ElseIf,
|
||||
End,
|
||||
Eqv,
|
||||
Erase,
|
||||
Error,
|
||||
Exit,
|
||||
For,
|
||||
Function,
|
||||
Gosub,
|
||||
Goto,
|
||||
If,
|
||||
Imp,
|
||||
Input,
|
||||
Integer,
|
||||
Is,
|
||||
Let,
|
||||
Line,
|
||||
Local,
|
||||
Long,
|
||||
Loop,
|
||||
Mod,
|
||||
Next,
|
||||
Not,
|
||||
On,
|
||||
Option,
|
||||
Or,
|
||||
Print,
|
||||
Read,
|
||||
ReDim,
|
||||
Rem,
|
||||
Restore,
|
||||
Resume,
|
||||
Return,
|
||||
Select,
|
||||
Shared,
|
||||
Single,
|
||||
Static,
|
||||
Step,
|
||||
Stop,
|
||||
String,
|
||||
Sub,
|
||||
System,
|
||||
Then,
|
||||
To,
|
||||
Type,
|
||||
Until,
|
||||
Using,
|
||||
Wend,
|
||||
While,
|
||||
Xor,
|
||||
Currency,
|
||||
// Datei-E/A-Keywords: werden geparst, aber erst in Phase 3 implementiert
|
||||
Open, Close, Write, Field, Get, Put, Seek, Lset, Rset,
|
||||
Open,
|
||||
Close,
|
||||
Write,
|
||||
Field,
|
||||
Get,
|
||||
Put,
|
||||
Seek,
|
||||
Lset,
|
||||
Rset,
|
||||
}
|
||||
|
||||
fn keyword(upper: &str) -> Option<Kw> {
|
||||
use Kw::*;
|
||||
Some(match upper {
|
||||
"AND" => And, "AS" => As, "CALL" => Call, "CASE" => Case,
|
||||
"COMMON" => Common, "CONST" => Const, "CURRENCY" => Currency,
|
||||
"AND" => And,
|
||||
"AS" => As,
|
||||
"CALL" => Call,
|
||||
"CASE" => Case,
|
||||
"COMMON" => Common,
|
||||
"CONST" => Const,
|
||||
"CURRENCY" => Currency,
|
||||
"DATA" => Data,
|
||||
"DECLARE" => Declare, "DEF" => Def, "DEFCUR" => DefCur,
|
||||
"DEFDBL" => DefDbl, "DEFINT" => DefInt, "DEFLNG" => DefLng,
|
||||
"DEFSNG" => DefSng, "DEFSTR" => DefStr, "DIM" => Dim, "DO" => Do,
|
||||
"DOUBLE" => Double, "ELSE" => Else, "ELSEIF" => ElseIf, "END" => End,
|
||||
"EQV" => Eqv, "ERASE" => Erase, "ERROR" => Error, "EXIT" => Exit,
|
||||
"FIELD" => Field, "FOR" => For, "FUNCTION" => Function,
|
||||
"GET" => Get, "GOSUB" => Gosub, "GOTO" => Goto, "IF" => If,
|
||||
"IMP" => Imp, "INPUT" => Input, "INTEGER" => Integer, "IS" => Is,
|
||||
"LET" => Let, "LINE" => Line, "LOCAL" => Local, "LONG" => Long,
|
||||
"LOOP" => Loop, "LSET" => Lset, "MOD" => Mod, "NEXT" => Next,
|
||||
"NOT" => Not, "ON" => On, "OPEN" => Open, "OPTION" => Option,
|
||||
"OR" => Or, "PRINT" => Print, "PUT" => Put, "READ" => Read,
|
||||
"REDIM" => ReDim, "REM" => Rem, "RESTORE" => Restore,
|
||||
"RESUME" => Resume, "RETURN" => Return, "RSET" => Rset,
|
||||
"SEEK" => Seek, "SELECT" => Select, "SHARED" => Shared,
|
||||
"SINGLE" => Single, "STATIC" => Static, "STEP" => Step,
|
||||
"STOP" => Stop, "STRING" => String, "SUB" => Sub,
|
||||
"SYSTEM" => System, "THEN" => Then,
|
||||
"TO" => To, "TYPE" => Type, "UNTIL" => Until, "USING" => Using,
|
||||
"WEND" => Wend, "WHILE" => While, "WRITE" => Write, "XOR" => Xor,
|
||||
"DECLARE" => Declare,
|
||||
"DEF" => Def,
|
||||
"DEFCUR" => DefCur,
|
||||
"DEFDBL" => DefDbl,
|
||||
"DEFINT" => DefInt,
|
||||
"DEFLNG" => DefLng,
|
||||
"DEFSNG" => DefSng,
|
||||
"DEFSTR" => DefStr,
|
||||
"DIM" => Dim,
|
||||
"DO" => Do,
|
||||
"DOUBLE" => Double,
|
||||
"ELSE" => Else,
|
||||
"ELSEIF" => ElseIf,
|
||||
"END" => End,
|
||||
"EQV" => Eqv,
|
||||
"ERASE" => Erase,
|
||||
"ERROR" => Error,
|
||||
"EXIT" => Exit,
|
||||
"FIELD" => Field,
|
||||
"FOR" => For,
|
||||
"FUNCTION" => Function,
|
||||
"GET" => Get,
|
||||
"GOSUB" => Gosub,
|
||||
"GOTO" => Goto,
|
||||
"IF" => If,
|
||||
"IMP" => Imp,
|
||||
"INPUT" => Input,
|
||||
"INTEGER" => Integer,
|
||||
"IS" => Is,
|
||||
"LET" => Let,
|
||||
"LINE" => Line,
|
||||
"LOCAL" => Local,
|
||||
"LONG" => Long,
|
||||
"LOOP" => Loop,
|
||||
"LSET" => Lset,
|
||||
"MOD" => Mod,
|
||||
"NEXT" => Next,
|
||||
"NOT" => Not,
|
||||
"ON" => On,
|
||||
"OPEN" => Open,
|
||||
"OPTION" => Option,
|
||||
"OR" => Or,
|
||||
"PRINT" => Print,
|
||||
"PUT" => Put,
|
||||
"READ" => Read,
|
||||
"REDIM" => ReDim,
|
||||
"REM" => Rem,
|
||||
"RESTORE" => Restore,
|
||||
"RESUME" => Resume,
|
||||
"RETURN" => Return,
|
||||
"RSET" => Rset,
|
||||
"SEEK" => Seek,
|
||||
"SELECT" => Select,
|
||||
"SHARED" => Shared,
|
||||
"SINGLE" => Single,
|
||||
"STATIC" => Static,
|
||||
"STEP" => Step,
|
||||
"STOP" => Stop,
|
||||
"STRING" => String,
|
||||
"SUB" => Sub,
|
||||
"SYSTEM" => System,
|
||||
"THEN" => Then,
|
||||
"TO" => To,
|
||||
"TYPE" => Type,
|
||||
"UNTIL" => Until,
|
||||
"USING" => Using,
|
||||
"WEND" => Wend,
|
||||
"WHILE" => While,
|
||||
"WRITE" => Write,
|
||||
"XOR" => Xor,
|
||||
"CLOSE" => Close,
|
||||
_ => return None,
|
||||
})
|
||||
@@ -107,18 +233,39 @@ fn keyword(upper: &str) -> Option<Kw> {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TokenKind {
|
||||
/// Bezeichner; `name` ist bereits in Großschreibung normalisiert.
|
||||
Ident { name: String, suffix: Option<Suffix> },
|
||||
Ident {
|
||||
name: String,
|
||||
suffix: Option<Suffix>,
|
||||
},
|
||||
Kw(Kw),
|
||||
Num(NumValue),
|
||||
Str(String),
|
||||
Plus, Minus, Star, Slash, Backslash, Caret,
|
||||
Eq, Ne, Lt, Le, Gt, Ge,
|
||||
LParen, RParen, Comma, Semicolon, Colon, Hash,
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Backslash,
|
||||
Caret,
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
LParen,
|
||||
RParen,
|
||||
Comma,
|
||||
Semicolon,
|
||||
Colon,
|
||||
Hash,
|
||||
Dot,
|
||||
/// Metabefehl `'$INCLUDE: 'datei''` (Pfad; leer = fehlerhafte Syntax).
|
||||
MetaInclude(String),
|
||||
/// Metabefehle `'$STATIC` / `'$DYNAMIC`.
|
||||
MetaStatic,
|
||||
MetaDynamic,
|
||||
/// Metabefehl `'$FORM`: das Modul ist ein Formularmodul.
|
||||
MetaForm,
|
||||
/// Rohtext einer `DATA`-Anweisung bis zum Anweisungsende. Er wird
|
||||
/// bewusst **nicht** zerlegt oder normalisiert: unquotierte Elemente
|
||||
/// behalten ihre Groß- und Kleinschreibung und ihren inneren Leerraum.
|
||||
@@ -139,6 +286,9 @@ fn meta_token(rest: &[char]) -> Option<TokenKind> {
|
||||
if up.starts_with("$DYNAMIC") {
|
||||
return Some(TokenKind::MetaDynamic);
|
||||
}
|
||||
if up.starts_with("$FORM") {
|
||||
return Some(TokenKind::MetaForm);
|
||||
}
|
||||
if up.starts_with("$INCLUDE") {
|
||||
if let Some(colon) = t.find(':') {
|
||||
let after = t[colon + 1..].trim();
|
||||
@@ -191,14 +341,15 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
break;
|
||||
}
|
||||
let start = i;
|
||||
let pos = SourcePos { line: line_no, column: (start + 1) as u32 };
|
||||
let pos = SourcePos {
|
||||
line: line_no,
|
||||
column: (start + 1) as u32,
|
||||
};
|
||||
let c = chars[i];
|
||||
|
||||
// Zeilenfortsetzung: `_` nach Leerraum, danach nur noch Leerraum
|
||||
if c == '_'
|
||||
&& (start == 0
|
||||
|| chars[start - 1] == ' '
|
||||
|| chars[start - 1] == '\t')
|
||||
&& (start == 0 || chars[start - 1] == ' ' || chars[start - 1] == '\t')
|
||||
&& chars[start + 1..].iter().all(|&ch| ch == ' ' || ch == '\t')
|
||||
{
|
||||
line_continued = true;
|
||||
@@ -236,17 +387,16 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
// Das Vorbild toleriert fehlende schließende
|
||||
// Anführungszeichen am Zeilenende.
|
||||
}
|
||||
tokens.push(Token { kind: TokenKind::Str(s), pos });
|
||||
tokens.push(Token {
|
||||
kind: TokenKind::Str(s),
|
||||
pos,
|
||||
});
|
||||
}
|
||||
'&' if i + 1 < chars.len()
|
||||
&& matches!(chars[i + 1], 'h' | 'H' | 'o' | 'O') =>
|
||||
{
|
||||
'&' if i + 1 < chars.len() && matches!(chars[i + 1], 'h' | 'H' | 'o' | 'O') => {
|
||||
let hex = matches!(chars[i + 1], 'h' | 'H');
|
||||
i += 2;
|
||||
let digit_start = i;
|
||||
while i < chars.len()
|
||||
&& chars[i].is_ascii_alphanumeric()
|
||||
{
|
||||
while i < chars.len() && chars[i].is_ascii_alphanumeric() {
|
||||
i += 1;
|
||||
}
|
||||
let digits: String = chars[digit_start..i].iter().collect();
|
||||
@@ -276,8 +426,8 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
}),
|
||||
}
|
||||
}
|
||||
'0'..='9' | '.' if c != '.'
|
||||
|| (i + 1 < chars.len() && chars[i + 1].is_ascii_digit()) =>
|
||||
'0'..='9' | '.'
|
||||
if c != '.' || (i + 1 < chars.len() && chars[i + 1].is_ascii_digit()) =>
|
||||
{
|
||||
let int_start = i;
|
||||
while i < chars.len() && chars[i].is_ascii_digit() {
|
||||
@@ -298,9 +448,7 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
// Exponent E/D
|
||||
let mut exp_kind: Option<char> = None;
|
||||
let mut exp_str = String::new();
|
||||
if i < chars.len()
|
||||
&& matches!(chars[i], 'e' | 'E' | 'd' | 'D')
|
||||
{
|
||||
if i < chars.len() && matches!(chars[i], 'e' | 'E' | 'd' | 'D') {
|
||||
let save = i;
|
||||
let k = chars[i].to_ascii_uppercase();
|
||||
let mut j = i + 1;
|
||||
@@ -403,22 +551,25 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
}
|
||||
}
|
||||
};
|
||||
tokens.push(Token { kind: TokenKind::Num(value), pos });
|
||||
tokens.push(Token {
|
||||
kind: TokenKind::Num(value),
|
||||
pos,
|
||||
});
|
||||
}
|
||||
c if c.is_alphabetic() => {
|
||||
i += 1;
|
||||
while i < chars.len()
|
||||
&& (chars[i].is_alphanumeric()
|
||||
|| chars[i] == '.'
|
||||
|| chars[i] == '_')
|
||||
&& (chars[i].is_alphanumeric() || chars[i] == '.' || chars[i] == '_')
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
let mut name: String = chars[start..i]
|
||||
.iter()
|
||||
.collect::<String>()
|
||||
.to_uppercase();
|
||||
let suffix = if i < chars.len() {
|
||||
let mut name: String =
|
||||
chars[start..i].iter().collect::<String>().to_uppercase();
|
||||
let container_bang = i < chars.len()
|
||||
&& chars[i] == '!'
|
||||
&& i + 1 < chars.len()
|
||||
&& chars[i + 1].is_alphabetic();
|
||||
let suffix = if i < chars.len() && !container_bang {
|
||||
Suffix::from_char(chars[i])
|
||||
} else {
|
||||
None
|
||||
@@ -426,6 +577,16 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
if suffix.is_some() {
|
||||
i += 1;
|
||||
}
|
||||
if container_bang {
|
||||
name.push('!');
|
||||
i += 1;
|
||||
while i < chars.len()
|
||||
&& (chars[i].is_alphanumeric() || chars[i] == '.' || chars[i] == '_')
|
||||
{
|
||||
name.push(chars[i].to_ascii_uppercase());
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if suffix.is_none() {
|
||||
if let Some(kw) = keyword(&name) {
|
||||
if kw == Kw::Rem {
|
||||
@@ -435,7 +596,10 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
}
|
||||
break 'line;
|
||||
}
|
||||
tokens.push(Token { kind: TokenKind::Kw(kw), pos });
|
||||
tokens.push(Token {
|
||||
kind: TokenKind::Kw(kw),
|
||||
pos,
|
||||
});
|
||||
if kw == Kw::Data {
|
||||
// Der Rest der Anweisung ist Rohtext: bis zum
|
||||
// `:` außerhalb von Anführungszeichen oder bis
|
||||
@@ -486,8 +650,9 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
';' => TokenKind::Semicolon,
|
||||
':' => TokenKind::Colon,
|
||||
'#' => TokenKind::Hash,
|
||||
'.' => TokenKind::Dot,
|
||||
'?' => TokenKind::Kw(Kw::Print), // Editor-Kurzform
|
||||
'&' => TokenKind::Kw(Kw::Long), // isoliertes & (selten)
|
||||
'&' => TokenKind::Kw(Kw::Long), // isoliertes & (selten)
|
||||
'<' => {
|
||||
if i < chars.len() && chars[i] == '=' {
|
||||
i += 1;
|
||||
@@ -538,9 +703,15 @@ pub fn lex(source: &str) -> LexOutput {
|
||||
|
||||
tokens.push(Token {
|
||||
kind: TokenKind::Eof,
|
||||
pos: SourcePos { line: (source.lines().count() + 1) as u32, column: 1 },
|
||||
pos: SourcePos {
|
||||
line: (source.lines().count() + 1) as u32,
|
||||
column: 1,
|
||||
},
|
||||
});
|
||||
LexOutput { tokens, diagnostics }
|
||||
LexOutput {
|
||||
tokens,
|
||||
diagnostics,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -565,7 +736,10 @@ mod tests {
|
||||
let k = kinds("STRING$ STRING");
|
||||
assert_eq!(
|
||||
k[0],
|
||||
TokenKind::Ident { name: "STRING".into(), suffix: Some(Suffix::Str) }
|
||||
TokenKind::Ident {
|
||||
name: "STRING".into(),
|
||||
suffix: Some(Suffix::Str)
|
||||
}
|
||||
);
|
||||
assert_eq!(k[1], TokenKind::Kw(Kw::String));
|
||||
}
|
||||
@@ -620,4 +794,23 @@ mod tests {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bang_ist_suffix_oder_containeroperator_nach_folgetoken() {
|
||||
let k = kinds("Wert! = 1.5\nForm1!Text1.Text = \"a\"");
|
||||
assert_eq!(
|
||||
k[0],
|
||||
TokenKind::Ident {
|
||||
name: "WERT".into(),
|
||||
suffix: Some(Suffix::Single)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
k[4],
|
||||
TokenKind::Ident {
|
||||
name: "FORM1!TEXT1.TEXT".into(),
|
||||
suffix: None
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//! übersetzt.
|
||||
|
||||
pub mod ast;
|
||||
pub mod forms;
|
||||
pub mod hir;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
@@ -42,12 +43,24 @@ pub struct Analysis {
|
||||
|
||||
/// Komplette Pipeline: Lexen → Parsen → semantische Prüfung + Lowering.
|
||||
pub fn analyze_source(module_name: &str, source: &str) -> Analysis {
|
||||
analyze_source_with_forms(module_name, source, &forms::FormCatalog::default())
|
||||
}
|
||||
|
||||
pub fn analyze_source_with_forms(
|
||||
module_name: &str,
|
||||
source: &str,
|
||||
forms: &forms::FormCatalog,
|
||||
) -> Analysis {
|
||||
let lexed = lexer::lex(source);
|
||||
let mut diagnostics = lexed.diagnostics;
|
||||
let parsed = parser::parse(module_name, &lexed.tokens);
|
||||
diagnostics.extend(parsed.diagnostics);
|
||||
let (hir, sema_diags) = sema::lower(&parsed.module);
|
||||
let (hir, sema_diags) = sema::lower_with_forms(&parsed.module, forms);
|
||||
diagnostics.extend(sema_diags);
|
||||
diagnostics.sort_by_key(|d| (d.pos.line, d.pos.column));
|
||||
Analysis { module: parsed.module, diagnostics, hir }
|
||||
Analysis {
|
||||
module: parsed.module,
|
||||
diagnostics,
|
||||
hir,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,12 @@ pub struct ParseOutput {
|
||||
}
|
||||
|
||||
pub fn parse(module_name: &str, tokens: &[Token]) -> ParseOutput {
|
||||
let mut p = P { toks: tokens, i: 0, diags: Vec::new(), at_line_start: true };
|
||||
let mut p = P {
|
||||
toks: tokens,
|
||||
i: 0,
|
||||
diags: Vec::new(),
|
||||
at_line_start: true,
|
||||
};
|
||||
let mut body = Vec::new();
|
||||
let mut procs = Vec::new();
|
||||
|
||||
@@ -40,7 +45,11 @@ pub fn parse(module_name: &str, tokens: &[Token]) -> ParseOutput {
|
||||
}
|
||||
|
||||
ParseOutput {
|
||||
module: Module { name: module_name.to_string(), body, procs },
|
||||
module: Module {
|
||||
name: module_name.to_string(),
|
||||
body,
|
||||
procs,
|
||||
},
|
||||
diagnostics: p.diags,
|
||||
}
|
||||
}
|
||||
@@ -85,7 +94,10 @@ impl<'a> P<'a> {
|
||||
}
|
||||
fn err(&mut self, msg: impl Into<String>) {
|
||||
let pos = self.pos();
|
||||
self.diags.push(Diagnostic { pos, message: msg.into() });
|
||||
self.diags.push(Diagnostic {
|
||||
pos,
|
||||
message: msg.into(),
|
||||
});
|
||||
}
|
||||
fn expect_kw(&mut self, kw: Kw, what: &str) -> bool {
|
||||
if self.eat_kw(kw) {
|
||||
@@ -205,11 +217,21 @@ impl<'a> P<'a> {
|
||||
}
|
||||
TokenKind::MetaStatic => {
|
||||
self.advance();
|
||||
Some(Stmt::MetaArrays { static_arrays: true, pos })
|
||||
Some(Stmt::MetaArrays {
|
||||
static_arrays: true,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::MetaDynamic => {
|
||||
self.advance();
|
||||
Some(Stmt::MetaArrays { static_arrays: false, pos })
|
||||
Some(Stmt::MetaArrays {
|
||||
static_arrays: false,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::MetaForm => {
|
||||
self.advance();
|
||||
Some(Stmt::MetaForm { pos })
|
||||
}
|
||||
TokenKind::Kw(Kw::Print) => self.parse_print(pos, false),
|
||||
TokenKind::Kw(Kw::Input) => {
|
||||
@@ -368,8 +390,9 @@ impl<'a> P<'a> {
|
||||
}
|
||||
Some(Stmt::ConstDecl { items, pos })
|
||||
}
|
||||
TokenKind::Kw(k @ (Kw::DefInt | Kw::DefLng | Kw::DefSng
|
||||
| Kw::DefDbl | Kw::DefStr | Kw::DefCur)) => {
|
||||
TokenKind::Kw(
|
||||
k @ (Kw::DefInt | Kw::DefLng | Kw::DefSng | Kw::DefDbl | Kw::DefStr | Kw::DefCur),
|
||||
) => {
|
||||
self.advance();
|
||||
let ty = match k {
|
||||
Kw::DefInt => TypeName::Integer,
|
||||
@@ -382,9 +405,7 @@ impl<'a> P<'a> {
|
||||
let mut ranges = Vec::new();
|
||||
loop {
|
||||
let a = match self.k() {
|
||||
TokenKind::Ident { name, suffix: None }
|
||||
if name.len() == 1 =>
|
||||
{
|
||||
TokenKind::Ident { name, suffix: None } if name.len() == 1 => {
|
||||
self.advance();
|
||||
name.chars().next().unwrap()
|
||||
}
|
||||
@@ -396,9 +417,7 @@ impl<'a> P<'a> {
|
||||
};
|
||||
let b = if self.eat(&TokenKind::Minus) {
|
||||
match self.k() {
|
||||
TokenKind::Ident { name, suffix: None }
|
||||
if name.len() == 1 =>
|
||||
{
|
||||
TokenKind::Ident { name, suffix: None } if name.len() == 1 => {
|
||||
self.advance();
|
||||
name.chars().next().unwrap()
|
||||
}
|
||||
@@ -420,11 +439,12 @@ impl<'a> P<'a> {
|
||||
TokenKind::Kw(Kw::Option) => {
|
||||
self.advance();
|
||||
match self.k() {
|
||||
TokenKind::Ident { name, suffix: None }
|
||||
if name == "EXPLICIT" =>
|
||||
{
|
||||
TokenKind::Ident { name, suffix: None } if name == "EXPLICIT" => {
|
||||
self.advance();
|
||||
Some(Stmt::OptionStmt { kind: OptionKind::Explicit, pos })
|
||||
Some(Stmt::OptionStmt {
|
||||
kind: OptionKind::Explicit,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::Ident { name, suffix: None } if name == "BASE" => {
|
||||
self.advance();
|
||||
@@ -439,7 +459,10 @@ impl<'a> P<'a> {
|
||||
0
|
||||
}
|
||||
};
|
||||
Some(Stmt::OptionStmt { kind: OptionKind::Base(base), pos })
|
||||
Some(Stmt::OptionStmt {
|
||||
kind: OptionKind::Base(base),
|
||||
pos,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: BASE or EXPLICIT");
|
||||
@@ -466,7 +489,12 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Some(Stmt::Call { name, suffix, args, pos })
|
||||
Some(Stmt::Call {
|
||||
name,
|
||||
suffix,
|
||||
args,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: identifier");
|
||||
@@ -510,7 +538,11 @@ impl<'a> P<'a> {
|
||||
_ => cur.push(c),
|
||||
}
|
||||
}
|
||||
items.push(if quotiert { cur } else { cur.trim().to_string() });
|
||||
items.push(if quotiert {
|
||||
cur
|
||||
} else {
|
||||
cur.trim().to_string()
|
||||
});
|
||||
Some(Stmt::Data { items, pos })
|
||||
}
|
||||
TokenKind::Kw(Kw::Read) => {
|
||||
@@ -594,7 +626,13 @@ impl<'a> P<'a> {
|
||||
var = Some(self.parse_name_ref()?);
|
||||
}
|
||||
}
|
||||
Some(Stmt::GetPut { put: k == Kw::Put, file, recnum, var, pos })
|
||||
Some(Stmt::GetPut {
|
||||
put: k == Kw::Put,
|
||||
file,
|
||||
recnum,
|
||||
var,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::Kw(k @ (Kw::Lset | Kw::Rset)) => {
|
||||
self.advance();
|
||||
@@ -605,7 +643,12 @@ impl<'a> P<'a> {
|
||||
return None;
|
||||
}
|
||||
let value = self.parse_expr()?;
|
||||
Some(Stmt::LsetRset { rset: k == Kw::Rset, target, value, pos })
|
||||
Some(Stmt::LsetRset {
|
||||
rset: k == Kw::Rset,
|
||||
target,
|
||||
value,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::Kw(Kw::Write) => {
|
||||
self.advance();
|
||||
@@ -636,7 +679,11 @@ impl<'a> P<'a> {
|
||||
self.err("Expected: ,");
|
||||
}
|
||||
let position = self.parse_expr()?;
|
||||
Some(Stmt::SeekStmt { file, position, pos })
|
||||
Some(Stmt::SeekStmt {
|
||||
file,
|
||||
position,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::Kw(Kw::Common) => {
|
||||
self.advance();
|
||||
@@ -654,7 +701,12 @@ impl<'a> P<'a> {
|
||||
}
|
||||
}
|
||||
let decls = self.parse_var_decls();
|
||||
Some(Stmt::CommonDecl { shared, block, decls, pos })
|
||||
Some(Stmt::CommonDecl {
|
||||
shared,
|
||||
block,
|
||||
decls,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::Kw(Kw::Shared) => {
|
||||
self.advance();
|
||||
@@ -681,19 +733,20 @@ impl<'a> P<'a> {
|
||||
}
|
||||
Some(Stmt::ViewPrint { top, bottom, pos })
|
||||
}
|
||||
TokenKind::Ident { ref name, suffix: None }
|
||||
if name == "NAME" && self.k_at(1) != TokenKind::Eq =>
|
||||
{
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if name == "NAME" && self.k_at(1) != TokenKind::Eq => {
|
||||
self.advance();
|
||||
let old = self.parse_expr()?;
|
||||
self.expect_kw(Kw::As, "AS");
|
||||
let new = self.parse_expr()?;
|
||||
Some(Stmt::NameStmt { old, new, pos })
|
||||
}
|
||||
TokenKind::Ident { ref name, suffix: None }
|
||||
if (name == "LOCK" || name == "UNLOCK")
|
||||
&& self.k_at(1) != TokenKind::Eq =>
|
||||
{
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if (name == "LOCK" || name == "UNLOCK") && self.k_at(1) != TokenKind::Eq => {
|
||||
let unlock = name == "UNLOCK";
|
||||
self.advance();
|
||||
self.eat(&TokenKind::Hash);
|
||||
@@ -707,7 +760,13 @@ impl<'a> P<'a> {
|
||||
to = Some(self.parse_expr()?);
|
||||
}
|
||||
}
|
||||
Some(Stmt::LockStmt { unlock, file, from, to, pos })
|
||||
Some(Stmt::LockStmt {
|
||||
unlock,
|
||||
file,
|
||||
from,
|
||||
to,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
// ISAM-Anweisungen: `NAME [#]n [, arg …]`. Sie unterscheiden sich
|
||||
// von einem gewöhnlichen impliziten Aufruf nur durch das
|
||||
@@ -716,10 +775,12 @@ impl<'a> P<'a> {
|
||||
// `=` und `(` schließen die Anweisungsdeutung aus: dann ist der
|
||||
// Name eine Variable bzw. ein Array (`DELETE = 1`, `DELETE(2) = 1`).
|
||||
// Keine ISAM-Syntaxform beginnt mit einer Klammer.
|
||||
TokenKind::Ident { ref name, suffix: None }
|
||||
if ist_isam_anweisung(name)
|
||||
&& self.k_at(1) != TokenKind::Eq
|
||||
&& self.k_at(1) != TokenKind::LParen =>
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if ist_isam_anweisung(name)
|
||||
&& self.k_at(1) != TokenKind::Eq
|
||||
&& self.k_at(1) != TokenKind::LParen =>
|
||||
{
|
||||
self.parse_isam(name.clone(), pos)
|
||||
}
|
||||
@@ -740,11 +801,20 @@ impl<'a> P<'a> {
|
||||
// Es senkt auf die Sentinel-Kennung ab, damit `ROLLBACK` eine
|
||||
// einzige Signatur behält.
|
||||
if name == "ROLLBACK" {
|
||||
if let TokenKind::Ident { name: ref w, suffix: None } = self.k() {
|
||||
if let TokenKind::Ident {
|
||||
name: ref w,
|
||||
suffix: None,
|
||||
} = self.k()
|
||||
{
|
||||
if w == "ALL" {
|
||||
self.advance();
|
||||
let args = vec![Expr::LongLit(ROLLBACK_ALL)];
|
||||
return Some(Stmt::Call { name, suffix: None, args, pos });
|
||||
return Some(Stmt::Call {
|
||||
name,
|
||||
suffix: None,
|
||||
args,
|
||||
pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,7 +824,12 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
self.parse_arg_list_to_stmt_end()
|
||||
};
|
||||
Some(Stmt::Call { name, suffix: None, args, pos })
|
||||
Some(Stmt::Call {
|
||||
name,
|
||||
suffix: None,
|
||||
args,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_print(&mut self, pos: SourcePos, printer: bool) -> Option<Stmt> {
|
||||
@@ -793,7 +868,13 @@ impl<'a> P<'a> {
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(Stmt::Print { printer, file, using, items, pos })
|
||||
Some(Stmt::Print {
|
||||
printer,
|
||||
file,
|
||||
using,
|
||||
items,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
/// `OPEN` in beiden Syntaxformen (FOR-Klausel und Kurzform).
|
||||
@@ -813,7 +894,13 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return Some(Stmt::OpenLegacy { mode: first, number, file, len, pos });
|
||||
return Some(Stmt::OpenLegacy {
|
||||
mode: first,
|
||||
number,
|
||||
file,
|
||||
len,
|
||||
pos,
|
||||
});
|
||||
}
|
||||
let mut mode = None;
|
||||
let mut isam = None;
|
||||
@@ -823,7 +910,10 @@ impl<'a> P<'a> {
|
||||
self.advance();
|
||||
OpenMode::Input
|
||||
}
|
||||
TokenKind::Ident { ref name, suffix: None } => {
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} => {
|
||||
let m = match name.as_str() {
|
||||
"OUTPUT" => OpenMode::Output,
|
||||
"APPEND" => OpenMode::Append,
|
||||
@@ -873,7 +963,11 @@ impl<'a> P<'a> {
|
||||
}
|
||||
}
|
||||
let mut access = None;
|
||||
if let TokenKind::Ident { ref name, suffix: None } = self.k() {
|
||||
if let TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} = self.k()
|
||||
{
|
||||
if name == "ACCESS" {
|
||||
self.advance();
|
||||
let read = self.eat_kw(Kw::Read);
|
||||
@@ -892,7 +986,11 @@ impl<'a> P<'a> {
|
||||
let mut lock = None;
|
||||
if self.eat_kw(Kw::Shared) {
|
||||
lock = Some(LockClause::Shared);
|
||||
} else if let TokenKind::Ident { ref name, suffix: None } = self.k() {
|
||||
} else if let TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} = self.k()
|
||||
{
|
||||
if name == "LOCK" {
|
||||
self.advance();
|
||||
let read = self.eat_kw(Kw::Read);
|
||||
@@ -912,7 +1010,11 @@ impl<'a> P<'a> {
|
||||
self.eat(&TokenKind::Hash);
|
||||
let number = self.parse_expr()?;
|
||||
let mut len = None;
|
||||
if let TokenKind::Ident { ref name, suffix: None } = self.k() {
|
||||
if let TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} = self.k()
|
||||
{
|
||||
if name == "LEN" {
|
||||
self.advance();
|
||||
if !self.eat(&TokenKind::Eq) {
|
||||
@@ -921,7 +1023,16 @@ impl<'a> P<'a> {
|
||||
len = Some(self.parse_expr()?);
|
||||
}
|
||||
}
|
||||
Some(Stmt::Open { file: first, mode, isam, access, lock, number, len, pos })
|
||||
Some(Stmt::Open {
|
||||
file: first,
|
||||
mode,
|
||||
isam,
|
||||
access,
|
||||
lock,
|
||||
number,
|
||||
len,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_input(&mut self, line: bool, pos: SourcePos) -> Option<Stmt> {
|
||||
@@ -936,8 +1047,7 @@ impl<'a> P<'a> {
|
||||
let mut prompt = None;
|
||||
if file.is_none() {
|
||||
if let TokenKind::Str(s) = self.k() {
|
||||
if matches!(self.k_at(1), TokenKind::Semicolon | TokenKind::Comma)
|
||||
{
|
||||
if matches!(self.k_at(1), TokenKind::Semicolon | TokenKind::Comma) {
|
||||
self.advance();
|
||||
let with_question = self.k() == TokenKind::Semicolon;
|
||||
self.advance();
|
||||
@@ -959,7 +1069,14 @@ impl<'a> P<'a> {
|
||||
if vars.is_empty() {
|
||||
self.err("Expected: variable");
|
||||
}
|
||||
Some(Stmt::Input { line, file, keep_cursor, prompt, vars, pos })
|
||||
Some(Stmt::Input {
|
||||
line,
|
||||
file,
|
||||
keep_cursor,
|
||||
prompt,
|
||||
vars,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
@@ -991,7 +1108,13 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
self.err("Expected: END IF");
|
||||
}
|
||||
Some(Stmt::If { cond, then_body, elseifs, else_body, pos })
|
||||
Some(Stmt::If {
|
||||
cond,
|
||||
then_body,
|
||||
elseifs,
|
||||
else_body,
|
||||
pos,
|
||||
})
|
||||
} else {
|
||||
// Einzeilig
|
||||
let then_body = if let Some(n) = self.line_number_target() {
|
||||
@@ -1008,7 +1131,13 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Stmt::If { cond, then_body, elseifs: Vec::new(), else_body, pos })
|
||||
Some(Stmt::If {
|
||||
cond,
|
||||
then_body,
|
||||
elseifs: Vec::new(),
|
||||
else_body,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1112,7 +1241,14 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
self.err("FOR without NEXT");
|
||||
}
|
||||
Some(Stmt::For { var, from, to, step, body, pos })
|
||||
Some(Stmt::For {
|
||||
var,
|
||||
from,
|
||||
to,
|
||||
step,
|
||||
body,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_do(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
@@ -1135,7 +1271,12 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
self.err("DO without LOOP");
|
||||
}
|
||||
Some(Stmt::DoLoop { pre, post, body, pos })
|
||||
Some(Stmt::DoLoop {
|
||||
pre,
|
||||
post,
|
||||
body,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_while(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
@@ -1252,13 +1393,23 @@ impl<'a> P<'a> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(Stmt::OnGoto { expr, targets, gosub, pos })
|
||||
Some(Stmt::OnGoto {
|
||||
expr,
|
||||
targets,
|
||||
gosub,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_dim(&mut self, redim: bool, pos: SourcePos) -> Option<Stmt> {
|
||||
let shared = self.eat_kw(Kw::Shared);
|
||||
let decls = self.parse_var_decls();
|
||||
Some(Stmt::Dim { shared, redim, decls, pos })
|
||||
Some(Stmt::Dim {
|
||||
shared,
|
||||
redim,
|
||||
decls,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
/// Deklarationsliste `name[suffix][(dims)] [AS typ], …` — gemeinsame
|
||||
@@ -1309,7 +1460,13 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
decls.push(VarDecl { name, suffix, dims, as_type, pos: dpos });
|
||||
decls.push(VarDecl {
|
||||
name,
|
||||
suffix,
|
||||
dims,
|
||||
as_type,
|
||||
pos: dpos,
|
||||
});
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
@@ -1362,7 +1519,11 @@ impl<'a> P<'a> {
|
||||
}
|
||||
TokenKind::Ident { name, suffix: None } => {
|
||||
self.advance();
|
||||
Some(TypeName::Udt(name))
|
||||
Some(match name.as_str() {
|
||||
"FORM" => TypeName::Form,
|
||||
"CONTROL" => TypeName::Control,
|
||||
_ => TypeName::Udt(name),
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: type");
|
||||
@@ -1397,7 +1558,10 @@ impl<'a> P<'a> {
|
||||
break;
|
||||
}
|
||||
match self.k() {
|
||||
TokenKind::Ident { name: fname, suffix: None } => {
|
||||
TokenKind::Ident {
|
||||
name: fname,
|
||||
suffix: None,
|
||||
} => {
|
||||
self.advance();
|
||||
if self.expect_kw(Kw::As, "AS") {
|
||||
if let Some(t) = self.parse_type_name() {
|
||||
@@ -1442,7 +1606,10 @@ impl<'a> P<'a> {
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
match self.k() {
|
||||
TokenKind::Ident { name: pname, suffix: psfx } => {
|
||||
TokenKind::Ident {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
} => {
|
||||
self.advance();
|
||||
let array = if self.eat(&TokenKind::LParen) {
|
||||
self.eat(&TokenKind::RParen);
|
||||
@@ -1474,7 +1641,12 @@ impl<'a> P<'a> {
|
||||
self.eat(&TokenKind::RParen);
|
||||
}
|
||||
}
|
||||
Some(ProcSig { kind, name, suffix, params })
|
||||
Some(ProcSig {
|
||||
kind,
|
||||
name,
|
||||
suffix,
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_proc(&mut self) -> Option<Proc> {
|
||||
@@ -1495,14 +1667,19 @@ impl<'a> P<'a> {
|
||||
ProcKind::Function => "Expected: END FUNCTION",
|
||||
});
|
||||
}
|
||||
Some(Proc { sig, is_static, body, pos })
|
||||
Some(Proc {
|
||||
sig,
|
||||
is_static,
|
||||
body,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_def_fn(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
self.advance(); // DEF
|
||||
// `DEF SEG` ist Segmentadressierung und damit deklariertes
|
||||
// Non-Feature — an der Syntax erkennbar, weil `DEF` sonst nur
|
||||
// `DEF FNname` einleitet.
|
||||
// `DEF SEG` ist Segmentadressierung und damit deklariertes
|
||||
// Non-Feature — an der Syntax erkennbar, weil `DEF` sonst nur
|
||||
// `DEF FNname` einleitet.
|
||||
if matches!(self.k(), TokenKind::Ident { ref name, suffix: None } if name == "SEG") {
|
||||
self.err("Feature unavailable");
|
||||
self.sync();
|
||||
@@ -1524,7 +1701,10 @@ impl<'a> P<'a> {
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
match self.k() {
|
||||
TokenKind::Ident { name: pname, suffix: psfx } => {
|
||||
TokenKind::Ident {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
} => {
|
||||
self.advance();
|
||||
params.push(Param {
|
||||
name: pname,
|
||||
@@ -1547,7 +1727,13 @@ impl<'a> P<'a> {
|
||||
}
|
||||
if self.eat(&TokenKind::Eq) {
|
||||
let body = self.parse_expr()?;
|
||||
Some(Stmt::DefFn { name, suffix, params, body, pos })
|
||||
Some(Stmt::DefFn {
|
||||
name,
|
||||
suffix,
|
||||
params,
|
||||
body,
|
||||
pos,
|
||||
})
|
||||
} else {
|
||||
// Blockform: DEF FNname(...) … [EXIT DEF] … END DEF
|
||||
let body = self.parse_stmt_list(|p: &P| p.at_end_pair(Kw::Def));
|
||||
@@ -1557,7 +1743,13 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
self.err("Expected: END DEF");
|
||||
}
|
||||
Some(Stmt::DefFnBlock { name, suffix, params, body, pos })
|
||||
Some(Stmt::DefFnBlock {
|
||||
name,
|
||||
suffix,
|
||||
params,
|
||||
body,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1612,7 +1804,10 @@ impl<'a> P<'a> {
|
||||
}
|
||||
}
|
||||
// Impliziter Aufruf: `name [arg [, arg …]]`
|
||||
if let Expr::Name { name, suffix, args, .. } = target {
|
||||
if let Expr::Name {
|
||||
name, suffix, args, ..
|
||||
} = target
|
||||
{
|
||||
// Ohne CALL-Keyword sind Klammern Wert-Klammern (BYVAL), keine
|
||||
// Argumentlisten-Klammern: `Foo (n%)` übergibt `(n%)`.
|
||||
let mut call_args: Vec<Expr> = args
|
||||
@@ -1626,7 +1821,12 @@ impl<'a> P<'a> {
|
||||
if !self.at_stmt_end() && call_args.is_empty() {
|
||||
call_args = self.parse_arg_list_to_stmt_end();
|
||||
}
|
||||
Some(Stmt::Call { name, suffix, args: call_args, pos })
|
||||
Some(Stmt::Call {
|
||||
name,
|
||||
suffix,
|
||||
args: call_args,
|
||||
pos,
|
||||
})
|
||||
} else {
|
||||
self.err("Syntax error");
|
||||
self.sync();
|
||||
@@ -1640,7 +1840,7 @@ impl<'a> P<'a> {
|
||||
fn parse_name_ref(&mut self) -> Option<Expr> {
|
||||
let pos = self.pos();
|
||||
match self.k() {
|
||||
TokenKind::Ident { name, suffix } => {
|
||||
TokenKind::Ident { mut name, suffix } => {
|
||||
self.advance();
|
||||
let args = if self.eat(&TokenKind::LParen) {
|
||||
let a = self.parse_arg_list(&TokenKind::RParen);
|
||||
@@ -1649,7 +1849,25 @@ impl<'a> P<'a> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Expr::Name { name, suffix, args, pos })
|
||||
if self.eat(&TokenKind::Dot) {
|
||||
match self.k() {
|
||||
TokenKind::Ident {
|
||||
name: member,
|
||||
suffix: None,
|
||||
} => {
|
||||
name.push('.');
|
||||
name.push_str(&member);
|
||||
self.advance();
|
||||
}
|
||||
_ => self.err("Expected: property"),
|
||||
}
|
||||
}
|
||||
Some(Expr::Name {
|
||||
name,
|
||||
suffix,
|
||||
args,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: variable");
|
||||
@@ -1665,7 +1883,12 @@ impl<'a> P<'a> {
|
||||
match self.k() {
|
||||
TokenKind::Ident { name, suffix } => {
|
||||
self.advance();
|
||||
Some(Expr::Name { name, suffix, args: None, pos })
|
||||
Some(Expr::Name {
|
||||
name,
|
||||
suffix,
|
||||
args: None,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: variable");
|
||||
@@ -1773,7 +1996,12 @@ impl<'a> P<'a> {
|
||||
let pos = self.pos();
|
||||
self.advance();
|
||||
let rhs = self.parse_bin(prec + 1)?;
|
||||
lhs = Expr::Binary { op, lhs: Box::new(lhs), rhs: Box::new(rhs), pos };
|
||||
lhs = Expr::Binary {
|
||||
op,
|
||||
lhs: Box::new(lhs),
|
||||
rhs: Box::new(rhs),
|
||||
pos,
|
||||
};
|
||||
}
|
||||
Some(lhs)
|
||||
}
|
||||
@@ -1809,7 +2037,11 @@ impl<'a> P<'a> {
|
||||
TokenKind::Minus => {
|
||||
self.advance();
|
||||
let operand = self.parse_bin(12)?;
|
||||
Some(Expr::Unary { op: UnOp::Neg, operand: Box::new(operand), pos })
|
||||
Some(Expr::Unary {
|
||||
op: UnOp::Neg,
|
||||
operand: Box::new(operand),
|
||||
pos,
|
||||
})
|
||||
}
|
||||
TokenKind::Plus => {
|
||||
self.advance();
|
||||
@@ -1818,7 +2050,11 @@ impl<'a> P<'a> {
|
||||
TokenKind::Kw(Kw::Not) => {
|
||||
self.advance();
|
||||
let operand = self.parse_bin(6)?;
|
||||
Some(Expr::Unary { op: UnOp::Not, operand: Box::new(operand), pos })
|
||||
Some(Expr::Unary {
|
||||
op: UnOp::Not,
|
||||
operand: Box::new(operand),
|
||||
pos,
|
||||
})
|
||||
}
|
||||
_ => self.parse_primary(),
|
||||
}
|
||||
@@ -1842,6 +2078,30 @@ impl<'a> P<'a> {
|
||||
pos,
|
||||
});
|
||||
}
|
||||
if matches!(self.k(), TokenKind::Ident { ref name, suffix: None } if name == "TYPEOF") {
|
||||
let pos = self.pos();
|
||||
self.advance();
|
||||
let value = self.parse_primary()?;
|
||||
if !self.eat_kw(Kw::Is) {
|
||||
self.err("Expected: IS");
|
||||
return None;
|
||||
}
|
||||
let class = match self.k() {
|
||||
TokenKind::Ident { name, suffix: None } => {
|
||||
self.advance();
|
||||
name
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: control class");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
return Some(Expr::TypeOf {
|
||||
value: Box::new(value),
|
||||
class,
|
||||
pos,
|
||||
});
|
||||
}
|
||||
match self.k() {
|
||||
TokenKind::Num(n) => {
|
||||
self.advance();
|
||||
@@ -1913,13 +2173,21 @@ mod tests {
|
||||
"IF a > 1 THEN\nPRINT 1\nELSEIF a > 0 THEN\nPRINT 2\nELSE\nPRINT 3\nEND IF\nIF b THEN PRINT 4 ELSE PRINT 5",
|
||||
);
|
||||
match &m.body[0] {
|
||||
Stmt::If { elseifs, else_body, .. } => {
|
||||
Stmt::If {
|
||||
elseifs, else_body, ..
|
||||
} => {
|
||||
assert_eq!(elseifs.len(), 1);
|
||||
assert!(else_body.is_some());
|
||||
}
|
||||
other => panic!("erwartet IF, war {other:?}"),
|
||||
}
|
||||
assert!(matches!(&m.body[1], Stmt::If { else_body: Some(_), .. }));
|
||||
assert!(matches!(
|
||||
&m.body[1],
|
||||
Stmt::If {
|
||||
else_body: Some(_),
|
||||
..
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1943,8 +2211,22 @@ mod tests {
|
||||
"FOR i = 1 TO 10 STEP 2\nNEXT i\nDO WHILE x\nLOOP\nDO\nLOOP UNTIL x\nWHILE x\nWEND",
|
||||
);
|
||||
assert!(matches!(&m.body[0], Stmt::For { step: Some(_), .. }));
|
||||
assert!(matches!(&m.body[1], Stmt::DoLoop { pre: Some((false, _)), post: None, .. }));
|
||||
assert!(matches!(&m.body[2], Stmt::DoLoop { pre: None, post: Some((true, _)), .. }));
|
||||
assert!(matches!(
|
||||
&m.body[1],
|
||||
Stmt::DoLoop {
|
||||
pre: Some((false, _)),
|
||||
post: None,
|
||||
..
|
||||
}
|
||||
));
|
||||
assert!(matches!(
|
||||
&m.body[2],
|
||||
Stmt::DoLoop {
|
||||
pre: None,
|
||||
post: Some((true, _)),
|
||||
..
|
||||
}
|
||||
));
|
||||
assert!(matches!(&m.body[3], Stmt::While { .. }));
|
||||
}
|
||||
|
||||
@@ -1963,9 +2245,29 @@ mod tests {
|
||||
let m = parse_ok(
|
||||
"ON ERROR GOTO Handler\nON LOCAL ERROR RESUME NEXT\nON ERROR GOTO 0\nHandler:\nRESUME NEXT",
|
||||
);
|
||||
assert!(matches!(&m.body[0], Stmt::OnError { local: false, action: OnErrorAction::Goto(_), .. }));
|
||||
assert!(matches!(&m.body[1], Stmt::OnError { local: true, action: OnErrorAction::ResumeNext, .. }));
|
||||
assert!(matches!(&m.body[2], Stmt::OnError { action: OnErrorAction::Disable, .. }));
|
||||
assert!(matches!(
|
||||
&m.body[0],
|
||||
Stmt::OnError {
|
||||
local: false,
|
||||
action: OnErrorAction::Goto(_),
|
||||
..
|
||||
}
|
||||
));
|
||||
assert!(matches!(
|
||||
&m.body[1],
|
||||
Stmt::OnError {
|
||||
local: true,
|
||||
action: OnErrorAction::ResumeNext,
|
||||
..
|
||||
}
|
||||
));
|
||||
assert!(matches!(
|
||||
&m.body[2],
|
||||
Stmt::OnError {
|
||||
action: OnErrorAction::Disable,
|
||||
..
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1973,7 +2275,15 @@ mod tests {
|
||||
let m = parse_ok("x = 1 + 2 * 3 ^ 2");
|
||||
// 1 + (2 * (3 ^ 2))
|
||||
match &m.body[0] {
|
||||
Stmt::Assign { value: Expr::Binary { op: BinOp::Add, rhs, .. }, .. } => {
|
||||
Stmt::Assign {
|
||||
value:
|
||||
Expr::Binary {
|
||||
op: BinOp::Add,
|
||||
rhs,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
assert!(matches!(**rhs, Expr::Binary { op: BinOp::Mul, .. }));
|
||||
}
|
||||
other => panic!("unerwartet: {other:?}"),
|
||||
@@ -1989,4 +2299,18 @@ mod tests {
|
||||
assert!(matches!(&m.body[1], Stmt::Dim { shared: true, decls, .. } if decls.len() == 2));
|
||||
assert!(matches!(&m.body[3], Stmt::DefType { ranges, .. } if ranges.len() == 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forms_syntax() {
|
||||
let m = parse_ok(
|
||||
"'$FORM\nDIM f AS FORM, c AS CONTROL\nIF TYPEOF c IS CommandButton THEN PRINT 1",
|
||||
);
|
||||
assert!(matches!(m.body[0], Stmt::MetaForm { .. }));
|
||||
assert!(matches!(&m.body[1], Stmt::Dim { decls, .. }
|
||||
if decls[0].as_type == Some(TypeName::Form) && decls[1].as_type == Some(TypeName::Control)));
|
||||
assert!(
|
||||
matches!(&m.body[2], Stmt::If { cond: Expr::TypeOf { class, .. }, .. }
|
||||
if class == "COMMANDBUTTON")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ struct Eintrag {
|
||||
art: String,
|
||||
status: String,
|
||||
fundstelle: String,
|
||||
quelle: String,
|
||||
zeile: usize,
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ fn inventar() -> Vec<Eintrag> {
|
||||
art: spalten[1].to_string(),
|
||||
status: spalten[3].to_string(),
|
||||
fundstelle: spalten[4].to_string(),
|
||||
quelle: spalten[5].to_string(),
|
||||
zeile: i + 1,
|
||||
});
|
||||
}
|
||||
@@ -155,7 +157,10 @@ fn stand() -> Stand {
|
||||
signaturen,
|
||||
verboten: literale(ban),
|
||||
nicht_verfuegbar,
|
||||
syntaktisch: literale(syn).into_iter().map(|s| s.to_uppercase()).collect(),
|
||||
syntaktisch: literale(syn)
|
||||
.into_iter()
|
||||
.map(|s| s.to_uppercase())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +172,15 @@ fn statusvokabular_ist_beschraenkt() {
|
||||
assert!(
|
||||
matches!(
|
||||
e.art.as_str(),
|
||||
"Anweisung" | "Funktion" | "Metabefehl" | "Routine"
|
||||
"Anweisung"
|
||||
| "Funktion"
|
||||
| "Metabefehl"
|
||||
| "Routine"
|
||||
| "Eigenschaft"
|
||||
| "Methode"
|
||||
| "Ereignis"
|
||||
| "Datentyp"
|
||||
| "Operator"
|
||||
),
|
||||
"Zeile {}: `{}` trägt unzulässige Art `{}`",
|
||||
e.zeile,
|
||||
@@ -185,6 +198,84 @@ fn statusvokabular_ist_beschraenkt() {
|
||||
}
|
||||
}
|
||||
|
||||
fn pruefe_forms(inv: &[Eintrag]) -> Vec<String> {
|
||||
use tb_frontend::forms::{events, method_is_implemented, methods, properties, ObjectClass};
|
||||
let mut erwartet = BTreeMap::new();
|
||||
for class in ObjectClass::ALL {
|
||||
for p in properties(class) {
|
||||
erwartet.insert(
|
||||
format!("{}.{}", class.name(), p.name),
|
||||
("Eigenschaft", IMPLEMENTIERT, "tb-ui::forms"),
|
||||
);
|
||||
}
|
||||
for m in methods(class) {
|
||||
erwartet.insert(
|
||||
format!("{}.{m}", class.name()),
|
||||
if method_is_implemented(class, m) {
|
||||
("Methode", IMPLEMENTIERT, "tb-ui::forms")
|
||||
} else {
|
||||
("Methode", OFFEN, "-")
|
||||
},
|
||||
);
|
||||
}
|
||||
for e in events(class) {
|
||||
erwartet.insert(
|
||||
format!("{}_{e}", class.name()),
|
||||
("Ereignis", IMPLEMENTIERT, "tb-vm::interp"),
|
||||
);
|
||||
}
|
||||
}
|
||||
let gefunden: BTreeMap<_, _> = inv
|
||||
.iter()
|
||||
.filter(|e| matches!(e.art.as_str(), "Eigenschaft" | "Methode" | "Ereignis"))
|
||||
.map(|e| (e.name.to_uppercase(), e))
|
||||
.collect();
|
||||
let mut fehler = Vec::new();
|
||||
for (name, (art, status, fundstelle)) in &erwartet {
|
||||
match gefunden.get(name) {
|
||||
None => fehler.push(format!("`{name}` fehlt")),
|
||||
Some(e)
|
||||
if e.art != *art
|
||||
|| e.status != *status
|
||||
|| e.fundstelle != *fundstelle
|
||||
|| e.quelle != "forms-referenz" =>
|
||||
{
|
||||
fehler.push(format!("`{name}`: Art/Status/Fundstelle/Quelle falsch"))
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
for name in gefunden.keys() {
|
||||
if !erwartet.contains_key(name) {
|
||||
fehler.push(format!("`{name}` ist nicht in der Klassentabelle"));
|
||||
}
|
||||
}
|
||||
fehler
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forms_inventar_stimmt_mit_der_klassentabelle_ueberein() {
|
||||
let fehler = pruefe_forms(&inventar());
|
||||
assert!(
|
||||
fehler.is_empty(),
|
||||
"Forms-Inventar weicht ab:\n {}",
|
||||
fehler.join("\n ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn falsch_gesetzte_eigenschaft_wird_erkannt() {
|
||||
let mut inv = inventar();
|
||||
let e = inv
|
||||
.iter_mut()
|
||||
.find(|e| e.name == "TEXTBOX.SELSTART")
|
||||
.unwrap();
|
||||
e.status = OFFEN.into();
|
||||
assert!(pruefe_forms(&inv)
|
||||
.iter()
|
||||
.any(|f| f.contains("TEXTBOX.SELSTART")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_features_nennen_eine_fundstelle_in_der_sprachreferenz() {
|
||||
for e in inventar() {
|
||||
@@ -343,11 +434,7 @@ fn inventar_stimmt_mit_code_ueberein() {
|
||||
fn abdeckungsstand_wird_ausgewiesen() {
|
||||
let inv = inventar();
|
||||
let zaehle = |s: &str| inv.iter().filter(|e| e.status == s).count();
|
||||
let (i, o, n) = (
|
||||
zaehle(IMPLEMENTIERT),
|
||||
zaehle(OFFEN),
|
||||
zaehle(NON_FEATURE),
|
||||
);
|
||||
let (i, o, n) = (zaehle(IMPLEMENTIERT), zaehle(OFFEN), zaehle(NON_FEATURE));
|
||||
println!(
|
||||
"Abdeckung: implementiert {i} · offen {o} · Non-Feature {n} · gesamt {}",
|
||||
inv.len()
|
||||
@@ -384,10 +471,22 @@ fn dokumentierte_elemente_werden_namentlich_abgewiesen() {
|
||||
("CALLS Foo", "CALLS", "Feature unavailable"),
|
||||
// Non-Feature über die Syntax (teilt das Schlüsselwort)
|
||||
("LINE (1, 1)-(2, 2)", "LINE", "Feature unavailable"),
|
||||
("GET (1, 1)-(2, 2), A", "GET (Grafik)", "Feature unavailable"),
|
||||
(r#"OPEN "COM1:9600,N,8,1" AS #1"#, "OPEN COM", "Feature unavailable"),
|
||||
(
|
||||
"GET (1, 1)-(2, 2), A",
|
||||
"GET (Grafik)",
|
||||
"Feature unavailable",
|
||||
),
|
||||
(
|
||||
r#"OPEN "COM1:9600,N,8,1" AS #1"#,
|
||||
"OPEN COM",
|
||||
"Feature unavailable",
|
||||
),
|
||||
// Ereignisgerät als Funktionsform
|
||||
("ON PEN GOSUB Ziel\nZiel:\nRETURN", "ON PEN", "Feature unavailable"),
|
||||
(
|
||||
"ON PEN GOSUB Ziel\nZiel:\nRETURN",
|
||||
"ON PEN",
|
||||
"Feature unavailable",
|
||||
),
|
||||
];
|
||||
|
||||
for (quelle, element, erwartet) in faelle {
|
||||
@@ -411,7 +510,12 @@ fn dokumentierte_elemente_werden_namentlich_abgewiesen() {
|
||||
/// generischen Syntaxfehler.
|
||||
#[test]
|
||||
fn offene_elemente_erzeugen_keinen_syntaxfehler() {
|
||||
for quelle in ["CLS", "LOCATE 5, 10", "COLOR 14, 1", r#"OPEN "d.txt" FOR OUTPUT AS #1"#] {
|
||||
for quelle in [
|
||||
"CLS",
|
||||
"LOCATE 5, 10",
|
||||
"COLOR 14, 1",
|
||||
r#"OPEN "d.txt" FOR OUTPUT AS #1"#,
|
||||
] {
|
||||
let a = tb_frontend::analyze_source("probe.bas", quelle);
|
||||
assert!(
|
||||
a.diagnostics.is_empty(),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,11 @@ impl RuntimeError {
|
||||
pub const INVALID_SCREEN_MODE: Self = Self(271);
|
||||
pub const INVALID_WHEN_FORMS_SHOWING: Self = Self(272);
|
||||
pub const INVALID_PROPERTY_VALUE: Self = Self(380);
|
||||
pub const DESIGN_TIME_CONTROL: Self = Self(362);
|
||||
pub const FORM_ALREADY_DISPLAYED: Self = Self(400);
|
||||
pub const NON_MODAL_WHILE_MODAL: Self = Self(401);
|
||||
pub const MODAL_NOT_TOPMOST: Self = Self(402);
|
||||
pub const MDI_FORM_MODAL: Self = Self(403);
|
||||
pub const NO_ACTIVE_CONTROL: Self = Self(430);
|
||||
pub const NO_ACTIVE_FORM: Self = Self(431);
|
||||
|
||||
@@ -219,7 +224,10 @@ mod tests {
|
||||
"Invalid when forms are showing"
|
||||
);
|
||||
assert_eq!(RuntimeError::NO_ACTIVE_FORM.code(), 431);
|
||||
assert_eq!(RuntimeError(403).message(), "MDI form cannot be shown modally");
|
||||
assert_eq!(
|
||||
RuntimeError(403).message(),
|
||||
"MDI form cannot be shown modally"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,6 +18,8 @@ pub enum Value {
|
||||
Dbl(f64),
|
||||
Cur(i64),
|
||||
Str(Rc<str>),
|
||||
/// Formular-/Control-Objekt mit optionalem Index eines Control-Arrays.
|
||||
Obj(u16, Option<i32>),
|
||||
Arr(Rc<RefCell<ArrayObj>>),
|
||||
Rec(Rc<RefCell<RecordObj>>),
|
||||
/// Referenz (BYREF-Parameter-Slot).
|
||||
@@ -91,7 +93,11 @@ pub struct ArrayObj {
|
||||
}
|
||||
|
||||
impl ArrayObj {
|
||||
pub fn new(elem: TypeInit, dims: Vec<(i32, i32)>, udts: &[UdtLayout]) -> Result<Self, RuntimeError> {
|
||||
pub fn new(
|
||||
elem: TypeInit,
|
||||
dims: Vec<(i32, i32)>,
|
||||
udts: &[UdtLayout],
|
||||
) -> Result<Self, RuntimeError> {
|
||||
let mut len: usize = 1;
|
||||
for (lo, hi) in &dims {
|
||||
if hi < lo {
|
||||
|
||||
@@ -8,6 +8,7 @@ authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tb-runtime.workspace = true
|
||||
tb-frontend.workspace = true
|
||||
ratatui.workspace = true
|
||||
crossterm.workspace = true
|
||||
signal-hook.workspace = true
|
||||
|
||||
@@ -3,4 +3,592 @@
|
||||
//! Ziel ist volle Kompatibilität zum Forms-Modell des Vorbilds inklusive
|
||||
//! des textbasierten Formular-Dateiformats (`.FRM`).
|
||||
|
||||
// Platzhalter — wird in Phase 4 ausgearbeitet (siehe PLAN.md)
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
|
||||
use tb_frontend::forms::{self, FormObject, ObjectClass, PropertyDefault, PropertyType};
|
||||
use tb_runtime::errors::RuntimeError;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PropertyValue {
|
||||
Integer(i32),
|
||||
Single(f32),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Object(Option<(u16, Option<i32>)>),
|
||||
IntegerArray(Vec<i32>),
|
||||
}
|
||||
|
||||
impl PropertyValue {
|
||||
fn from_default(default: PropertyDefault, ty: PropertyType) -> Self {
|
||||
match default {
|
||||
PropertyDefault::Integer(v) if ty == PropertyType::IntegerArray => {
|
||||
Self::IntegerArray(vec![v; 18])
|
||||
}
|
||||
PropertyDefault::Integer(v) => Self::Integer(v),
|
||||
PropertyDefault::Single(v) => Self::Single(v),
|
||||
PropertyDefault::String(v) => Self::String(v.into()),
|
||||
PropertyDefault::Boolean(v) => Self::Boolean(v),
|
||||
PropertyDefault::Empty if ty == PropertyType::Object => Self::Object(None),
|
||||
PropertyDefault::Empty if ty == PropertyType::String => Self::String(String::new()),
|
||||
PropertyDefault::Empty => Self::Integer(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjectInstance {
|
||||
pub description: FormObject,
|
||||
properties: Vec<PropertyValue>,
|
||||
pub loaded: bool,
|
||||
pub visible: bool,
|
||||
pub design_time: bool,
|
||||
pub array_index: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FormEvent {
|
||||
pub object: u16,
|
||||
pub array_index: Option<i32>,
|
||||
pub name: String,
|
||||
pub args: Vec<PropertyValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ShowResult {
|
||||
Modeless,
|
||||
ModalWait,
|
||||
}
|
||||
|
||||
/// Reines Zustandsmodell. Zeichnen und Bedienung kommen im Folge-Change;
|
||||
/// diese Schicht hält Objekte, Lebenszyklus, Modalität und Ereignisqueue.
|
||||
pub struct FormsModel {
|
||||
pub objects: Vec<ObjectInstance>,
|
||||
dynamic: BTreeMap<(u16, i32), ObjectInstance>,
|
||||
pub events: VecDeque<FormEvent>,
|
||||
modal: Vec<u16>,
|
||||
visible_forms: Vec<u16>,
|
||||
active_form: Option<u16>,
|
||||
active_control: Option<(u16, Option<i32>)>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
screen_visible: bool,
|
||||
}
|
||||
|
||||
impl FormsModel {
|
||||
fn set_visible_property(obj: &mut ObjectInstance, visible: bool) {
|
||||
if let Some((property, _)) = forms::property(obj.description.class, "VISIBLE") {
|
||||
obj.properties[property as usize] = PropertyValue::Boolean(visible);
|
||||
}
|
||||
}
|
||||
fn clear_active_control_for_form(&mut self, form: u16) {
|
||||
let Some((control, _)) = self.active_control else {
|
||||
return;
|
||||
};
|
||||
let Some(form_name) = self
|
||||
.objects
|
||||
.get(form as usize)
|
||||
.map(|o| o.description.name.as_str())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if self
|
||||
.objects
|
||||
.get(control as usize)
|
||||
.and_then(|o| o.description.parent_form.as_deref())
|
||||
.is_some_and(|parent| parent.eq_ignore_ascii_case(form_name))
|
||||
{
|
||||
self.active_control = None;
|
||||
}
|
||||
}
|
||||
pub fn new(objects: Vec<FormObject>, width: usize, height: usize) -> Self {
|
||||
let objects = objects
|
||||
.into_iter()
|
||||
.map(|description| {
|
||||
let properties = forms::properties(description.class)
|
||||
.into_iter()
|
||||
.map(|p| PropertyValue::from_default(p.default, p.ty))
|
||||
.collect();
|
||||
let loaded = description.class == ObjectClass::Screen;
|
||||
ObjectInstance {
|
||||
description,
|
||||
properties,
|
||||
loaded,
|
||||
visible: false,
|
||||
design_time: true,
|
||||
array_index: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Self {
|
||||
objects,
|
||||
dynamic: BTreeMap::new(),
|
||||
events: VecDeque::new(),
|
||||
modal: Vec::new(),
|
||||
visible_forms: Vec::new(),
|
||||
active_form: None,
|
||||
active_control: None,
|
||||
width,
|
||||
height,
|
||||
screen_visible: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: usize, height: usize) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
}
|
||||
|
||||
pub fn get(&mut self, object: u16, property: u16) -> Result<PropertyValue, RuntimeError> {
|
||||
self.get_at(object, None, property)
|
||||
}
|
||||
|
||||
pub fn get_at(
|
||||
&mut self,
|
||||
object: u16,
|
||||
index: Option<i32>,
|
||||
property: u16,
|
||||
) -> Result<PropertyValue, RuntimeError> {
|
||||
self.ensure_loaded_at(object, index)?;
|
||||
let obj = if index.is_some_and(|i| i != 0) {
|
||||
self.dynamic
|
||||
.get(&(object, index.unwrap()))
|
||||
.ok_or(RuntimeError(340))?
|
||||
} else {
|
||||
self.objects.get(object as usize).ok_or(RuntimeError(420))?
|
||||
};
|
||||
let spec = forms::properties(obj.description.class)
|
||||
.get(property as usize)
|
||||
.copied()
|
||||
.ok_or(RuntimeError(422))?;
|
||||
if obj.description.class == ObjectClass::Screen {
|
||||
return Ok(match spec.name {
|
||||
"WIDTH" => PropertyValue::Integer(self.width as i32),
|
||||
"HEIGHT" => PropertyValue::Integer(self.height as i32),
|
||||
"ACTIVEFORM" => PropertyValue::Object(self.active_form.map(|id| (id, None))),
|
||||
"ACTIVECONTROL" => PropertyValue::Object(self.active_control),
|
||||
_ => obj.properties[property as usize].clone(),
|
||||
});
|
||||
}
|
||||
Ok(obj.properties[property as usize].clone())
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&mut self,
|
||||
object: u16,
|
||||
property: u16,
|
||||
value: PropertyValue,
|
||||
) -> Result<(), RuntimeError> {
|
||||
self.set_at(object, None, property, value)
|
||||
}
|
||||
|
||||
pub fn set_at(
|
||||
&mut self,
|
||||
object: u16,
|
||||
index: Option<i32>,
|
||||
property: u16,
|
||||
value: PropertyValue,
|
||||
) -> Result<(), RuntimeError> {
|
||||
self.ensure_loaded_at(object, index)?;
|
||||
let obj = if index.is_some_and(|i| i != 0) {
|
||||
self.dynamic
|
||||
.get_mut(&(object, index.unwrap()))
|
||||
.ok_or(RuntimeError(340))?
|
||||
} else {
|
||||
self.objects
|
||||
.get_mut(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
};
|
||||
let spec = forms::properties(obj.description.class)
|
||||
.get(property as usize)
|
||||
.copied()
|
||||
.ok_or(RuntimeError(422))?;
|
||||
if !spec.writable {
|
||||
return Err(RuntimeError(383));
|
||||
}
|
||||
let type_ok = matches!(
|
||||
(&value, spec.ty),
|
||||
(PropertyValue::Integer(_), PropertyType::Integer)
|
||||
| (PropertyValue::Single(_), PropertyType::Single)
|
||||
| (PropertyValue::String(_), PropertyType::String)
|
||||
| (PropertyValue::Boolean(_), PropertyType::Boolean)
|
||||
| (PropertyValue::Object(_), PropertyType::Object)
|
||||
| (PropertyValue::IntegerArray(_), PropertyType::IntegerArray)
|
||||
);
|
||||
if !type_ok {
|
||||
return Err(RuntimeError::TYPE_MISMATCH);
|
||||
}
|
||||
if let PropertyValue::Integer(v) = value {
|
||||
if spec.min.is_some_and(|min| v < min) || spec.max.is_some_and(|max| v > max) {
|
||||
return Err(RuntimeError::ILLEGAL_FUNCTION_CALL);
|
||||
}
|
||||
obj.properties[property as usize] = PropertyValue::Integer(v);
|
||||
} else {
|
||||
obj.properties[property as usize] = value;
|
||||
}
|
||||
if spec.name == "VISIBLE" {
|
||||
obj.visible = matches!(
|
||||
obj.properties[property as usize],
|
||||
PropertyValue::Boolean(true)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_loaded(&mut self, object: u16) -> Result<(), RuntimeError> {
|
||||
let obj = self
|
||||
.objects
|
||||
.get_mut(object as usize)
|
||||
.ok_or(RuntimeError(420))?;
|
||||
if obj.loaded {
|
||||
return Ok(());
|
||||
}
|
||||
obj.loaded = true;
|
||||
if obj.description.class == ObjectClass::Form {
|
||||
self.events.push_back(FormEvent {
|
||||
object,
|
||||
array_index: None,
|
||||
name: "LOAD".into(),
|
||||
args: Vec::new(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_loaded_at(
|
||||
&mut self,
|
||||
object: u16,
|
||||
index: Option<i32>,
|
||||
) -> Result<(), RuntimeError> {
|
||||
if index.is_some_and(|i| i != 0) {
|
||||
self.dynamic
|
||||
.get(&(object, index.unwrap()))
|
||||
.map(|_| ())
|
||||
.ok_or(RuntimeError(340))
|
||||
} else {
|
||||
self.ensure_loaded(object)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(&mut self, object: u16, modal: bool) -> Result<ShowResult, RuntimeError> {
|
||||
self.ensure_loaded(object)?;
|
||||
let obj = self
|
||||
.objects
|
||||
.get_mut(object as usize)
|
||||
.ok_or(RuntimeError(420))?;
|
||||
if obj.description.class != ObjectClass::Form {
|
||||
return Err(RuntimeError(421));
|
||||
}
|
||||
if modal {
|
||||
if obj.visible {
|
||||
return Err(RuntimeError(400));
|
||||
}
|
||||
let form_type = forms::property(ObjectClass::Form, "FORMTYPE").unwrap().0 as usize;
|
||||
if obj.properties.get(form_type) == Some(&PropertyValue::Integer(1)) {
|
||||
return Err(RuntimeError(403));
|
||||
}
|
||||
self.modal.push(object);
|
||||
} else if !self.modal.is_empty() {
|
||||
return Err(RuntimeError(401));
|
||||
}
|
||||
obj.visible = true;
|
||||
Self::set_visible_property(obj, true);
|
||||
self.visible_forms.retain(|id| *id != object);
|
||||
self.visible_forms.push(object);
|
||||
self.active_form = Some(object);
|
||||
Ok(if modal {
|
||||
ShowResult::ModalWait
|
||||
} else {
|
||||
ShowResult::Modeless
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hide(&mut self, object: u16) -> Result<(), RuntimeError> {
|
||||
if self.modal.contains(&object) && self.modal.last().copied() != Some(object) {
|
||||
return Err(RuntimeError(402));
|
||||
}
|
||||
let obj = self
|
||||
.objects
|
||||
.get_mut(object as usize)
|
||||
.ok_or(RuntimeError(420))?;
|
||||
obj.visible = false;
|
||||
Self::set_visible_property(obj, false);
|
||||
self.visible_forms.retain(|id| *id != object);
|
||||
if self.modal.last().copied() == Some(object) {
|
||||
self.modal.pop();
|
||||
}
|
||||
if self.active_form == Some(object) {
|
||||
self.active_form = self.visible_forms.last().copied();
|
||||
}
|
||||
self.clear_active_control_for_form(object);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unload_with(
|
||||
&mut self,
|
||||
object: u16,
|
||||
mut handler: impl FnMut(&mut i16),
|
||||
) -> Result<bool, RuntimeError> {
|
||||
if self.modal.contains(&object) && self.modal.last().copied() != Some(object) {
|
||||
return Err(RuntimeError(402));
|
||||
}
|
||||
let class = self
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
.description
|
||||
.class;
|
||||
if class != ObjectClass::Form {
|
||||
return Err(RuntimeError(361));
|
||||
}
|
||||
let mut cancel = 0i16;
|
||||
handler(&mut cancel);
|
||||
if cancel != 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
let obj = &mut self.objects[object as usize];
|
||||
obj.visible = false;
|
||||
Self::set_visible_property(obj, false);
|
||||
obj.loaded = false;
|
||||
self.visible_forms.retain(|id| *id != object);
|
||||
if self.modal.last().copied() == Some(object) {
|
||||
self.modal.pop();
|
||||
}
|
||||
if self.active_form == Some(object) {
|
||||
self.active_form = self.visible_forms.last().copied();
|
||||
}
|
||||
self.clear_active_control_for_form(object);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn load_array(&mut self, base: u16, index: i32) -> Result<(), RuntimeError> {
|
||||
let template = self.objects.get(base as usize).ok_or(RuntimeError(420))?;
|
||||
if !template.description.array {
|
||||
return Err(RuntimeError(343));
|
||||
}
|
||||
if index == 0 {
|
||||
return Err(RuntimeError(360));
|
||||
}
|
||||
let key = (base, index);
|
||||
if self.dynamic.contains_key(&key) {
|
||||
return Err(RuntimeError(360));
|
||||
}
|
||||
let mut obj = template.clone();
|
||||
obj.loaded = true;
|
||||
obj.visible = false;
|
||||
obj.design_time = false;
|
||||
obj.array_index = Some(index);
|
||||
self.dynamic.insert(key, obj);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unload_array(&mut self, base: u16, index: i32) -> Result<(), RuntimeError> {
|
||||
let template = self.objects.get(base as usize).ok_or(RuntimeError(420))?;
|
||||
if !template.description.array {
|
||||
return Err(RuntimeError(343));
|
||||
}
|
||||
if template.array_index == Some(index) || (template.array_index.is_none() && index == 0) {
|
||||
return Err(RuntimeError(362));
|
||||
}
|
||||
self.dynamic
|
||||
.remove(&(base, index))
|
||||
.map(|_| ())
|
||||
.ok_or(RuntimeError(340))
|
||||
}
|
||||
|
||||
pub fn queue(&mut self, event: FormEvent) {
|
||||
self.events.push_back(event);
|
||||
}
|
||||
pub fn next_event(&mut self) -> Option<FormEvent> {
|
||||
self.events.pop_front()
|
||||
}
|
||||
pub fn is_visible(&self, object: u16) -> bool {
|
||||
self.objects.get(object as usize).is_some_and(|o| o.visible)
|
||||
}
|
||||
pub fn is_loaded(&self, object: u16) -> bool {
|
||||
self.objects.get(object as usize).is_some_and(|o| o.loaded)
|
||||
}
|
||||
pub fn is_loaded_at(&self, object: u16, index: Option<i32>) -> bool {
|
||||
if index.is_some_and(|i| i != 0) {
|
||||
self.dynamic.contains_key(&(object, index.unwrap()))
|
||||
} else {
|
||||
self.is_loaded(object)
|
||||
}
|
||||
}
|
||||
pub fn active_form(&self) -> Option<u16> {
|
||||
self.active_form
|
||||
}
|
||||
pub fn set_active_control(
|
||||
&mut self,
|
||||
object: u16,
|
||||
index: Option<i32>,
|
||||
) -> Result<(), RuntimeError> {
|
||||
self.ensure_loaded_at(object, index)?;
|
||||
let description = if index.is_some_and(|i| i != 0) {
|
||||
&self
|
||||
.dynamic
|
||||
.get(&(object, index.unwrap()))
|
||||
.unwrap()
|
||||
.description
|
||||
} else {
|
||||
&self
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
.description
|
||||
};
|
||||
if matches!(description.class, ObjectClass::Form | ObjectClass::Screen) {
|
||||
return Err(RuntimeError(421));
|
||||
}
|
||||
let parent = description
|
||||
.parent_form
|
||||
.as_deref()
|
||||
.ok_or(RuntimeError(421))?;
|
||||
self.active_form = self
|
||||
.objects
|
||||
.iter()
|
||||
.position(|o| {
|
||||
o.description.class == ObjectClass::Form
|
||||
&& o.description.name.eq_ignore_ascii_case(parent)
|
||||
})
|
||||
.map(|id| id as u16);
|
||||
self.active_control = Some((object, index));
|
||||
Ok(())
|
||||
}
|
||||
pub fn modal_top(&self) -> Option<u16> {
|
||||
self.modal.last().copied()
|
||||
}
|
||||
pub fn screen_show(&mut self, visible: bool) {
|
||||
self.screen_visible = visible;
|
||||
}
|
||||
pub fn screen_visible(&self) -> bool {
|
||||
self.screen_visible
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn model() -> FormsModel {
|
||||
let mut c = forms::FormCatalog::default();
|
||||
c.add("Form1", ObjectClass::Form, None, false);
|
||||
c.add("Check1", ObjectClass::CheckBox, Some("Form1"), false);
|
||||
c.add("Command1", ObjectClass::CommandButton, Some("Form1"), true);
|
||||
c.add("SCREEN", ObjectClass::Screen, None, false);
|
||||
FormsModel::new(c.objects, 80, 25)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defaults_bereich_und_implizites_laden() {
|
||||
let mut m = model();
|
||||
let value = forms::property(ObjectClass::CheckBox, "VALUE").unwrap().0;
|
||||
assert_eq!(m.get(1, value).unwrap(), PropertyValue::Integer(0));
|
||||
assert!(m.objects[1].loaded);
|
||||
assert_eq!(
|
||||
m.set(1, value, PropertyValue::Integer(3)),
|
||||
Err(RuntimeError(5))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hide_entlaedt_nicht_und_unload_ist_abbrechbar() {
|
||||
let mut m = model();
|
||||
m.show(0, false).unwrap();
|
||||
m.hide(0).unwrap();
|
||||
assert!(m.objects[0].loaded);
|
||||
assert!(!m.unload_with(0, |cancel| *cancel = 1).unwrap());
|
||||
assert!(m.objects[0].loaded);
|
||||
assert!(m.unload_with(0, |_| {}).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrays_und_designzeitfehler() {
|
||||
let mut m = model();
|
||||
let caption = forms::property(ObjectClass::CommandButton, "CAPTION")
|
||||
.unwrap()
|
||||
.0;
|
||||
m.set(2, caption, PropertyValue::String("Vorlage".into()))
|
||||
.unwrap();
|
||||
assert_eq!(m.load_array(2, 0), Err(RuntimeError(360)));
|
||||
m.load_array(2, 3).unwrap();
|
||||
assert_eq!(
|
||||
m.get_at(2, Some(3), caption).unwrap(),
|
||||
PropertyValue::String("Vorlage".into())
|
||||
);
|
||||
m.set_at(2, Some(3), caption, PropertyValue::String("Klon".into()))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
m.get(2, caption).unwrap(),
|
||||
PropertyValue::String("Vorlage".into())
|
||||
);
|
||||
assert_eq!(m.unload_array(2, 0), Err(RuntimeError(362)));
|
||||
m.unload_array(2, 3).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn screen_meldet_oberstes_formular_und_aktives_control() {
|
||||
let mut c = forms::FormCatalog::default();
|
||||
c.add("A", ObjectClass::Form, None, false);
|
||||
c.add("B", ObjectClass::Form, None, false);
|
||||
c.add("Text1", ObjectClass::TextBox, Some("B"), false);
|
||||
c.add("SCREEN", ObjectClass::Screen, None, false);
|
||||
let mut m = FormsModel::new(c.objects, 80, 25);
|
||||
m.show(0, false).unwrap();
|
||||
m.show(1, false).unwrap();
|
||||
m.set_active_control(2, None).unwrap();
|
||||
let active_form = forms::property(ObjectClass::Screen, "ACTIVEFORM")
|
||||
.unwrap()
|
||||
.0;
|
||||
let active_control = forms::property(ObjectClass::Screen, "ACTIVECONTROL")
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(
|
||||
m.get(3, active_form).unwrap(),
|
||||
PropertyValue::Object(Some((1, None)))
|
||||
);
|
||||
assert_eq!(
|
||||
m.get(3, active_control).unwrap(),
|
||||
PropertyValue::Object(Some((2, None)))
|
||||
);
|
||||
m.hide(1).unwrap();
|
||||
assert_eq!(
|
||||
m.get(3, active_form).unwrap(),
|
||||
PropertyValue::Object(Some((0, None)))
|
||||
);
|
||||
assert_eq!(
|
||||
m.get(3, active_control).unwrap(),
|
||||
PropertyValue::Object(None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn screen_folgt_resize() {
|
||||
let mut m = model();
|
||||
m.resize(120, 40);
|
||||
let width = forms::property(ObjectClass::Screen, "WIDTH").unwrap().0;
|
||||
let height = forms::property(ObjectClass::Screen, "HEIGHT").unwrap().0;
|
||||
assert_eq!(m.get(3, width).unwrap(), PropertyValue::Integer(120));
|
||||
assert_eq!(m.get(3, height).unwrap(), PropertyValue::Integer(40));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modalfehler_400_bis_403() {
|
||||
let mut m = model();
|
||||
m.show(0, false).unwrap();
|
||||
assert_eq!(m.show(0, true), Err(RuntimeError(400)));
|
||||
m.hide(0).unwrap();
|
||||
m.show(0, true).unwrap();
|
||||
assert_eq!(m.show(0, false), Err(RuntimeError(401)));
|
||||
let mut c = forms::FormCatalog::default();
|
||||
c.add("A", ObjectClass::Form, None, false);
|
||||
c.add("B", ObjectClass::Form, None, false);
|
||||
let mut stacked = FormsModel::new(c.objects, 80, 25);
|
||||
stacked.show(0, true).unwrap();
|
||||
stacked.show(1, true).unwrap();
|
||||
assert_eq!(stacked.hide(0), Err(RuntimeError(402)));
|
||||
let ft = forms::property(ObjectClass::Form, "FORMTYPE").unwrap().0;
|
||||
let mut mdi = model();
|
||||
mdi.set(0, ft, PropertyValue::Integer(1)).unwrap();
|
||||
assert_eq!(mdi.show(0, true), Err(RuntimeError(403)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ tb-frontend.workspace = true
|
||||
thiserror.workspace = true
|
||||
log.workspace = true
|
||||
tb-runtime.workspace = true
|
||||
tb-ui.workspace = true
|
||||
|
||||
[[bench]]
|
||||
name = "compile"
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use tb_frontend::forms::{FormObject, ObjectClass};
|
||||
use tb_frontend::hir::HEventProc;
|
||||
use tb_runtime::value::{TypeInit, UdtLayout};
|
||||
|
||||
pub const TBC_MAGIC: &[u8; 4] = b"TBC\0";
|
||||
pub const TBC_VERSION: u16 = 1;
|
||||
pub const TBC_VERSION: u16 = 3;
|
||||
|
||||
/// Vergleichsoperator (Operand der `Cmp*`-Instruktionen).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -52,7 +54,10 @@ impl fmt::Display for LoadError {
|
||||
match self {
|
||||
LoadError::BadMagic => write!(f, "Keine .tbc-Datei (Magic fehlt)"),
|
||||
LoadError::Version(v) => {
|
||||
write!(f, "Unbekannte .tbc-Formatversion {v} (unterstützt: {TBC_VERSION})")
|
||||
write!(
|
||||
f,
|
||||
"Unbekannte .tbc-Formatversion {v} (unterstützt: {TBC_VERSION})"
|
||||
)
|
||||
}
|
||||
LoadError::Corrupt(what) => write!(f, "Beschädigte .tbc-Datei ({what})"),
|
||||
}
|
||||
@@ -303,6 +308,14 @@ instrs! {
|
||||
0xB1 RetProc;
|
||||
0xB2 RetFn;
|
||||
0xB3 CallBuiltin(a: u16, b: u8);
|
||||
0xB4 LoadObjectProperty(a: u16, b: u16, c: bool);
|
||||
0xB5 StoreObjectProperty(a: u16, b: u16, c: bool);
|
||||
0xB6 PushObject(a: u16, b: bool);
|
||||
0xB7 TypeOf(a: u8);
|
||||
0xB8 ObjectMethod(a: u16, b: u16, c: u8);
|
||||
0xB9 ObjectLoad(a: u16, b: bool, c: bool); // unload?, Index liegt auf Stack?
|
||||
0xBA LoadDynamicObjectProperty(a: u16);
|
||||
0xBB StoreDynamicObjectProperty(a: u16);
|
||||
|
||||
// 0xE0 — Ereignis-Traps (Sprachreferenz §8); Kennung vom Stack
|
||||
0xE0 TrapDefine(a: u8, b: u32); // Quellenart, Sprungziel
|
||||
@@ -375,6 +388,8 @@ pub struct CompiledModule {
|
||||
pub data: Vec<DataItem>,
|
||||
/// Sprungtabellen für `ON n GOTO/GOSUB`.
|
||||
pub jump_tables: Vec<Vec<u32>>,
|
||||
pub objects: Vec<FormObject>,
|
||||
pub event_procs: Vec<HEventProc>,
|
||||
}
|
||||
|
||||
fn w_string(out: &mut Vec<u8>, s: &str) {
|
||||
@@ -458,6 +473,22 @@ impl CompiledModule {
|
||||
}
|
||||
sections.push((*b"JMPT", jmpt));
|
||||
|
||||
let mut objs = Vec::new();
|
||||
objs.extend_from_slice(&(self.objects.len() as u32).to_le_bytes());
|
||||
for o in &self.objects {
|
||||
w_string(&mut objs, &o.name);
|
||||
objs.push(o.class.id());
|
||||
w_string(&mut objs, o.parent_form.as_deref().unwrap_or(""));
|
||||
objs.push(o.array as u8);
|
||||
}
|
||||
objs.extend_from_slice(&(self.event_procs.len() as u32).to_le_bytes());
|
||||
for e in &self.event_procs {
|
||||
objs.extend_from_slice(&e.object.to_le_bytes());
|
||||
w_string(&mut objs, &e.event);
|
||||
objs.extend_from_slice(&e.proc.to_le_bytes());
|
||||
}
|
||||
sections.push((*b"OBJS", objs));
|
||||
|
||||
// Header + Abschnittstabelle
|
||||
let mut out = Vec::new();
|
||||
out.extend_from_slice(TBC_MAGIC);
|
||||
@@ -567,7 +598,13 @@ impl CompiledModule {
|
||||
for _ in 0..n_instr {
|
||||
code.push(Instr::decode(&mut cr)?);
|
||||
}
|
||||
procs.push(ProcCode { name, n_params, locals_init, local_names, code });
|
||||
procs.push(ProcCode {
|
||||
name,
|
||||
n_params,
|
||||
locals_init,
|
||||
local_names,
|
||||
code,
|
||||
});
|
||||
}
|
||||
|
||||
let mut r = section(b"DATA")?;
|
||||
@@ -591,6 +628,31 @@ impl CompiledModule {
|
||||
jump_tables.push(t);
|
||||
}
|
||||
|
||||
let mut r = section(b"OBJS")?;
|
||||
let n = r.u32()? as usize;
|
||||
let mut objects = Vec::with_capacity(n);
|
||||
for _ in 0..n {
|
||||
let name = r.string()?;
|
||||
let class = ObjectClass::from_id(r.u8()?).ok_or(LoadError::Corrupt("Objektklasse"))?;
|
||||
let parent = r.string()?;
|
||||
let array = r.u8()? != 0;
|
||||
objects.push(FormObject {
|
||||
name,
|
||||
class,
|
||||
parent_form: (!parent.is_empty()).then_some(parent),
|
||||
array,
|
||||
});
|
||||
}
|
||||
let n = r.u32()? as usize;
|
||||
let mut event_procs = Vec::with_capacity(n);
|
||||
for _ in 0..n {
|
||||
event_procs.push(HEventProc {
|
||||
object: r.u16()?,
|
||||
event: r.string()?,
|
||||
proc: r.u16()?,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(CompiledModule {
|
||||
name,
|
||||
option_base,
|
||||
@@ -601,6 +663,8 @@ impl CompiledModule {
|
||||
procs,
|
||||
data,
|
||||
jump_tables,
|
||||
objects,
|
||||
event_procs,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -660,8 +724,13 @@ mod tests {
|
||||
local_names: vec![],
|
||||
code: vec![Instr::Stmt(1), Instr::PushStr(0), Instr::End],
|
||||
}],
|
||||
data: vec![DataItem { text: "1.5".into(), line: 3 }],
|
||||
data: vec![DataItem {
|
||||
text: "1.5".into(),
|
||||
line: 3,
|
||||
}],
|
||||
jump_tables: vec![vec![4, 9]],
|
||||
objects: vec![],
|
||||
event_procs: vec![],
|
||||
};
|
||||
let bytes = m.to_tbc();
|
||||
let back = CompiledModule::from_tbc(&bytes).unwrap();
|
||||
@@ -687,6 +756,8 @@ mod tests {
|
||||
procs: vec![],
|
||||
data: vec![],
|
||||
jump_tables: vec![],
|
||||
objects: vec![],
|
||||
event_procs: vec![],
|
||||
};
|
||||
let mut bytes = m.to_tbc();
|
||||
bytes[4] = 0xFF; // Version hochsetzen
|
||||
|
||||
@@ -43,9 +43,14 @@ pub fn compile(hir: &HirModule) -> CompiledModule {
|
||||
data: hir
|
||||
.data
|
||||
.iter()
|
||||
.map(|d| DataItem { text: d.text.clone(), line: d.line })
|
||||
.map(|d| DataItem {
|
||||
text: d.text.clone(),
|
||||
line: d.line,
|
||||
})
|
||||
.collect(),
|
||||
jump_tables: cg.jump_tables,
|
||||
objects: hir.objects.clone(),
|
||||
event_procs: hir.event_procs.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +73,7 @@ fn type_init(t: &HTy) -> TypeInit {
|
||||
HTy::Str => TypeInit::Str,
|
||||
HTy::FixedStr(n) => TypeInit::FixedStr(*n),
|
||||
HTy::Udt(id) => TypeInit::Udt(*id),
|
||||
HTy::Form | HTy::Control => TypeInit::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +175,8 @@ impl Codegen {
|
||||
for (idx, label) in std::mem::take(&mut ctx.fixups) {
|
||||
// Modulweites `ON ERROR GOTO` in einer Prozedur: das Label lebt
|
||||
// im Modulrumpf, nicht im eigenen.
|
||||
let modulweit = proc.kind != hir::HProcKind::Main
|
||||
&& matches!(ctx.code[idx], Instr::OnErrorGoto(_));
|
||||
let modulweit =
|
||||
proc.kind != hir::HProcKind::Main && matches!(ctx.code[idx], Instr::OnErrorGoto(_));
|
||||
let pc = if modulweit {
|
||||
self.modul_label_pc
|
||||
.get(label as usize)
|
||||
@@ -241,6 +247,52 @@ impl Codegen {
|
||||
HStmtKind::Assign { place, value } => {
|
||||
self.store_place(ctx, place, |cg, ctx| cg.expr(ctx, value));
|
||||
}
|
||||
HStmtKind::SetObjectProperty {
|
||||
object,
|
||||
index,
|
||||
property,
|
||||
value,
|
||||
} => {
|
||||
if let Some(index) = index {
|
||||
self.expr(ctx, index);
|
||||
}
|
||||
self.expr(ctx, value);
|
||||
ctx.emit(Instr::StoreObjectProperty(
|
||||
*object,
|
||||
*property,
|
||||
index.is_some(),
|
||||
));
|
||||
}
|
||||
HStmtKind::SetDynamicObjectProperty {
|
||||
object,
|
||||
property,
|
||||
value,
|
||||
} => {
|
||||
self.expr(ctx, object);
|
||||
self.expr(ctx, value);
|
||||
let property = self.pool(property);
|
||||
ctx.emit(Instr::StoreDynamicObjectProperty(property));
|
||||
}
|
||||
HStmtKind::ObjectMethod {
|
||||
object,
|
||||
method,
|
||||
args,
|
||||
} => {
|
||||
for arg in args {
|
||||
self.expr(ctx, arg);
|
||||
}
|
||||
ctx.emit(Instr::ObjectMethod(*object, *method, args.len() as u8));
|
||||
}
|
||||
HStmtKind::ObjectLoad {
|
||||
object,
|
||||
index,
|
||||
unload,
|
||||
} => {
|
||||
if let Some(index) = index {
|
||||
self.expr(ctx, index);
|
||||
}
|
||||
ctx.emit(Instr::ObjectLoad(*object, *unload, index.is_some()));
|
||||
}
|
||||
HStmtKind::Print { items, trailing } => {
|
||||
for item in items {
|
||||
match item {
|
||||
@@ -277,12 +329,21 @@ impl Codegen {
|
||||
}
|
||||
ctx.emit(Instr::Field(fields.len() as u8));
|
||||
}
|
||||
HStmtKind::LsetRset { rset, target, value } => {
|
||||
HStmtKind::LsetRset {
|
||||
rset,
|
||||
target,
|
||||
value,
|
||||
} => {
|
||||
self.make_ref(ctx, target);
|
||||
self.expr(ctx, value);
|
||||
ctx.emit(Instr::LsetRset(*rset));
|
||||
}
|
||||
HStmtKind::GetPut { put, file, recnum, var } => {
|
||||
HStmtKind::GetPut {
|
||||
put,
|
||||
file,
|
||||
recnum,
|
||||
var,
|
||||
} => {
|
||||
self.expr(ctx, file);
|
||||
if let Some(r) = recnum {
|
||||
self.expr(ctx, r);
|
||||
@@ -301,12 +362,19 @@ impl Codegen {
|
||||
HTy::Udt(i) => (7, *i),
|
||||
// Variable Strings: Länge erst zur Laufzeit.
|
||||
HTy::Str => (8, 0),
|
||||
HTy::Form | HTy::Control => (8, 0),
|
||||
}
|
||||
}
|
||||
};
|
||||
ctx.emit(Instr::GetPut(*put, recnum.is_some(), art, zusatz));
|
||||
}
|
||||
HStmtKind::Input { file, line_mode, prompt, question, targets } => {
|
||||
HStmtKind::Input {
|
||||
file,
|
||||
line_mode,
|
||||
prompt,
|
||||
question,
|
||||
targets,
|
||||
} => {
|
||||
if let Some(f) = file {
|
||||
// Dateinummer zuerst, dann die Referenzen darüber.
|
||||
self.expr(ctx, f);
|
||||
@@ -506,7 +574,12 @@ impl Codegen {
|
||||
}
|
||||
}
|
||||
HStmtKind::Restore(idx) => ctx.emit(Instr::Restore(*idx)),
|
||||
HStmtKind::Dim { slot, elem, dims, redim } => {
|
||||
HStmtKind::Dim {
|
||||
slot,
|
||||
elem,
|
||||
dims,
|
||||
redim,
|
||||
} => {
|
||||
for (lo, hi) in dims {
|
||||
self.expr(ctx, lo);
|
||||
self.expr(ctx, hi);
|
||||
@@ -637,6 +710,36 @@ impl Codegen {
|
||||
ctx.emit(Instr::PushStr(idx));
|
||||
}
|
||||
HExpr::Load(p) => self.load_place(ctx, p),
|
||||
HExpr::ObjectProperty {
|
||||
object,
|
||||
index,
|
||||
property,
|
||||
..
|
||||
} => {
|
||||
if let Some(index) = index {
|
||||
self.expr(ctx, index);
|
||||
}
|
||||
ctx.emit(Instr::LoadObjectProperty(
|
||||
*object,
|
||||
*property,
|
||||
index.is_some(),
|
||||
));
|
||||
}
|
||||
HExpr::DynamicObjectProperty { object, property } => {
|
||||
self.expr(ctx, object);
|
||||
let property = self.pool(property);
|
||||
ctx.emit(Instr::LoadDynamicObjectProperty(property));
|
||||
}
|
||||
HExpr::ObjectRef { object, index, .. } => {
|
||||
if let Some(index) = index {
|
||||
self.expr(ctx, index);
|
||||
}
|
||||
ctx.emit(Instr::PushObject(*object, index.is_some()));
|
||||
}
|
||||
HExpr::TypeOf { value, class } => {
|
||||
self.expr(ctx, value);
|
||||
ctx.emit(Instr::TypeOf(class.id()));
|
||||
}
|
||||
HExpr::Conv { from, to, arg } => {
|
||||
self.expr(ctx, arg);
|
||||
emit_conv(ctx, *from, *to);
|
||||
@@ -1276,7 +1379,12 @@ mod tests {
|
||||
let m = compile_src("SUB Inc (x%)\nx% = x% + 1\nEND SUB\nn% = 1\nInc n%\nInc (n%)");
|
||||
let code = main_code(&m);
|
||||
assert!(code.contains(&Instr::MakeRefGlobal(0)));
|
||||
assert!(code.iter().filter(|i| matches!(i, Instr::Call(1, 1))).count() == 2);
|
||||
assert!(
|
||||
code.iter()
|
||||
.filter(|i| matches!(i, Instr::Call(1, 1)))
|
||||
.count()
|
||||
== 2
|
||||
);
|
||||
// Prozedurrumpf liest/schreibt über Referenz
|
||||
let sub = &m.procs[1].code;
|
||||
assert!(sub.contains(&Instr::LoadRef(0)));
|
||||
@@ -1309,7 +1417,12 @@ mod tests {
|
||||
let m = compile_src("DATA 1, 2\nREAD a%, b%\nRESTORE\nREAD c%");
|
||||
let code = main_code(&m);
|
||||
assert_eq!(m.data.len(), 2);
|
||||
assert!(code.iter().filter(|i| matches!(i, Instr::ReadData(1))).count() == 3);
|
||||
assert!(
|
||||
code.iter()
|
||||
.filter(|i| matches!(i, Instr::ReadData(1)))
|
||||
.count()
|
||||
== 3
|
||||
);
|
||||
assert!(code.contains(&Instr::Restore(0)));
|
||||
assert!(code.contains(&Instr::ConvR8I2));
|
||||
}
|
||||
@@ -1318,7 +1431,9 @@ mod tests {
|
||||
fn fehlerbehandlung_emit() {
|
||||
let m = compile_src("ON ERROR GOTO H\nERROR 5\nEND\nH:\nRESUME NEXT");
|
||||
let code = main_code(&m);
|
||||
assert!(code.iter().any(|i| matches!(i, Instr::OnErrorGoto(t) if *t > 0)));
|
||||
assert!(code
|
||||
.iter()
|
||||
.any(|i| matches!(i, Instr::OnErrorGoto(t) if *t > 0)));
|
||||
assert!(code.contains(&Instr::RaiseError));
|
||||
assert!(code.contains(&Instr::ResumeNext));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use tb_runtime::errors::RuntimeError;
|
||||
use tb_runtime::host::Host;
|
||||
use tb_runtime::traps::Quelle;
|
||||
use tb_runtime::value::{self, default_value, ArrayObj, RecordObj, TypeInit, Value, VarRef};
|
||||
use tb_ui::forms::{FormEvent, FormsModel, PropertyValue, ShowResult};
|
||||
|
||||
/// Warum die VM die Kontrolle abgibt.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -24,12 +25,24 @@ pub enum RunEvent {
|
||||
/// `END`, `SYSTEM` oder Programmende.
|
||||
Ended,
|
||||
/// `STOP` — VM-Zustand bleibt fortsetzbar (IDE: CONT).
|
||||
Stopped { line: u32 },
|
||||
Breakpoint { line: u32 },
|
||||
Stepped { line: u32 },
|
||||
Interrupted { line: u32 },
|
||||
Stopped {
|
||||
line: u32,
|
||||
},
|
||||
Breakpoint {
|
||||
line: u32,
|
||||
},
|
||||
Stepped {
|
||||
line: u32,
|
||||
},
|
||||
Interrupted {
|
||||
line: u32,
|
||||
},
|
||||
/// Unbehandelter Laufzeitfehler.
|
||||
Error { code: u16, line: u32, message: String },
|
||||
Error {
|
||||
code: u16,
|
||||
line: u32,
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
const F_STEP: u32 = 1;
|
||||
@@ -58,6 +71,15 @@ struct Frame {
|
||||
/// Satz würde dem Handler leere Modulvariablen zeigen. Sein `RETURN`
|
||||
/// beendet den Handler (design.md, D2).
|
||||
trap: Option<Quelle>,
|
||||
waiting_form: Option<u16>,
|
||||
pending_show: Option<u16>,
|
||||
form_event: Option<FormEventReturn>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum FormEventReturn {
|
||||
Normal,
|
||||
Unload(u16),
|
||||
}
|
||||
|
||||
pub struct Vm {
|
||||
@@ -84,6 +106,7 @@ pub struct Vm {
|
||||
tick_zaehler: u32,
|
||||
breakpoints: HashSet<u32>,
|
||||
data_ptr: usize,
|
||||
pub forms: FormsModel,
|
||||
}
|
||||
|
||||
/// Quellenarten, wie der Codegenerator sie kodiert.
|
||||
@@ -126,6 +149,7 @@ impl Vm {
|
||||
.iter()
|
||||
.map(|t| default_value(t, &module.udts))
|
||||
.collect();
|
||||
let forms = FormsModel::new(module.objects.clone(), 80, 25);
|
||||
let mut vm = Vm {
|
||||
globals,
|
||||
locals: Vec::new(),
|
||||
@@ -142,6 +166,7 @@ impl Vm {
|
||||
tick_zaehler: 0,
|
||||
breakpoints: HashSet::new(),
|
||||
data_ptr: 0,
|
||||
forms,
|
||||
module,
|
||||
};
|
||||
// ISAM leitet das Satzlayout aus dem Typ der `OPEN`-Anweisung ab und
|
||||
@@ -173,9 +198,156 @@ impl Vm {
|
||||
last_stmt_pc: 0,
|
||||
line: 0,
|
||||
trap: None,
|
||||
waiting_form: None,
|
||||
pending_show: None,
|
||||
form_event: None,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn queue_form_event(&mut self, event: FormEvent) {
|
||||
self.forms.queue(event);
|
||||
}
|
||||
|
||||
fn form_value(v: PropertyValue) -> Value {
|
||||
match v {
|
||||
PropertyValue::Integer(v) => Value::Int(v as i16),
|
||||
PropertyValue::Single(v) => Value::Sng(v),
|
||||
PropertyValue::String(v) => Value::Str(Rc::from(v)),
|
||||
PropertyValue::Boolean(v) => Value::Int(if v { -1 } else { 0 }),
|
||||
PropertyValue::Object(v) => v
|
||||
.map(|(object, index)| Value::Obj(object, index))
|
||||
.unwrap_or(Value::Empty),
|
||||
PropertyValue::IntegerArray(_) => Value::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
fn property_value(
|
||||
&self,
|
||||
object: u16,
|
||||
property: u16,
|
||||
v: Value,
|
||||
) -> Result<PropertyValue, RuntimeError> {
|
||||
let class = self
|
||||
.module
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
.class;
|
||||
let spec = tb_frontend::forms::properties(class)
|
||||
.get(property as usize)
|
||||
.copied()
|
||||
.ok_or(RuntimeError(422))?;
|
||||
Ok(match (spec.ty, v) {
|
||||
(tb_frontend::forms::PropertyType::Integer, Value::Int(v)) => {
|
||||
PropertyValue::Integer(v as i32)
|
||||
}
|
||||
(tb_frontend::forms::PropertyType::Integer, Value::Lng(v)) => PropertyValue::Integer(v),
|
||||
(tb_frontend::forms::PropertyType::Boolean, Value::Int(v)) => {
|
||||
PropertyValue::Boolean(v != 0)
|
||||
}
|
||||
(tb_frontend::forms::PropertyType::Boolean, Value::Lng(v)) => {
|
||||
PropertyValue::Boolean(v != 0)
|
||||
}
|
||||
(tb_frontend::forms::PropertyType::Single, Value::Sng(v)) => PropertyValue::Single(v),
|
||||
(tb_frontend::forms::PropertyType::String, Value::Str(v)) => {
|
||||
PropertyValue::String(v.to_string())
|
||||
}
|
||||
(tb_frontend::forms::PropertyType::Object, Value::Obj(object, index)) => {
|
||||
PropertyValue::Object(Some((object, index)))
|
||||
}
|
||||
_ => return Err(RuntimeError::TYPE_MISMATCH),
|
||||
})
|
||||
}
|
||||
|
||||
fn dispatch_form_event(&mut self, event: FormEvent, on_return: FormEventReturn) -> bool {
|
||||
if self
|
||||
.module
|
||||
.objects
|
||||
.get(event.object as usize)
|
||||
.is_some_and(|o| {
|
||||
!matches!(
|
||||
o.class,
|
||||
tb_frontend::forms::ObjectClass::Form | tb_frontend::forms::ObjectClass::Screen
|
||||
)
|
||||
})
|
||||
{
|
||||
let _ = self
|
||||
.forms
|
||||
.set_active_control(event.object, event.array_index);
|
||||
}
|
||||
let Some(binding) = self
|
||||
.module
|
||||
.event_procs
|
||||
.iter()
|
||||
.find(|e| e.object == event.object && e.event.eq_ignore_ascii_case(&event.name))
|
||||
.cloned()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if let Some(index) = event.array_index {
|
||||
self.push(Value::Int(index as i16));
|
||||
}
|
||||
for arg in event.args {
|
||||
self.push(Self::form_value(arg));
|
||||
}
|
||||
self.push_frame(
|
||||
binding.proc as usize,
|
||||
self.module.procs[binding.proc as usize].n_params as usize,
|
||||
);
|
||||
if let Some(frame) = self.frames.last_mut() {
|
||||
frame.form_event = Some(on_return);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn dispatch_next_form_event(&mut self) -> bool {
|
||||
while let Some(event) = self.forms.next_event() {
|
||||
if self.dispatch_form_event(event, FormEventReturn::Normal) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn request_unload(&mut self, object: u16) -> Result<(), RuntimeError> {
|
||||
let event = FormEvent {
|
||||
object,
|
||||
array_index: None,
|
||||
name: "UNLOAD".into(),
|
||||
args: vec![PropertyValue::Integer(0)],
|
||||
};
|
||||
if !self.dispatch_form_event(event, FormEventReturn::Unload(object)) {
|
||||
self.forms.unload_with(object, |_| {})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn forms_zustellen(&mut self) -> bool {
|
||||
self.forms
|
||||
.resize(self.rt.screen.cols(), self.rt.screen.rows());
|
||||
while let Some(m) = self.rt.maus.pop_front() {
|
||||
if let Some(object) = self.forms.active_form() {
|
||||
let name = match m.art {
|
||||
tb_runtime::host::MausArt::Druck => "MOUSEDOWN",
|
||||
tb_runtime::host::MausArt::Loslassen => "MOUSEUP",
|
||||
tb_runtime::host::MausArt::Bewegung => "MOUSEMOVE",
|
||||
};
|
||||
self.forms.queue(FormEvent {
|
||||
object,
|
||||
array_index: None,
|
||||
name: name.into(),
|
||||
args: vec![
|
||||
PropertyValue::Integer(m.taste as i32),
|
||||
PropertyValue::Integer(m.shift as i32),
|
||||
PropertyValue::Single(m.spalte as f32),
|
||||
PropertyValue::Single(m.zeile as f32),
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
self.dispatch_next_form_event()
|
||||
}
|
||||
|
||||
/// Anstehendes Ereignis zustellen, falls eines fällig ist. Liefert
|
||||
/// `true`, wenn ein Handler aufgesetzt wurde.
|
||||
fn zustellen(&mut self, host: &mut dyn Host) -> bool {
|
||||
@@ -243,6 +415,9 @@ impl Vm {
|
||||
last_stmt_pc: 0,
|
||||
line: 0,
|
||||
trap: Some(q),
|
||||
waiting_form: None,
|
||||
pending_show: None,
|
||||
form_event: None,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -358,6 +533,19 @@ impl Vm {
|
||||
|
||||
pub fn run(&mut self, host: &mut dyn Host) -> RunEvent {
|
||||
loop {
|
||||
if let Some(form) = self.frames.last().and_then(|f| f.waiting_form) {
|
||||
if !self.forms.is_visible(form) {
|
||||
self.frames.last_mut().unwrap().waiting_form = None;
|
||||
} else {
|
||||
self.rt.tick(host);
|
||||
self.forms_zustellen();
|
||||
if self.rt.ende {
|
||||
return RunEvent::Ended;
|
||||
}
|
||||
std::thread::yield_now();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let frame = self.frames.last_mut().expect("kein Frame");
|
||||
let proc = frame.proc;
|
||||
let pc = frame.pc;
|
||||
@@ -642,6 +830,9 @@ impl Vm {
|
||||
self.rt.tick(host);
|
||||
self.rt.screen.veraenderung_quittieren();
|
||||
}
|
||||
if self.forms_zustellen() {
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
// Ereigniszustellung (design.md, D1): nur wenn überhaupt
|
||||
// ein Trap definiert ist — sonst kostet die Grenze nichts.
|
||||
if self.rt.traps.aktiv() {
|
||||
@@ -711,6 +902,7 @@ impl Vm {
|
||||
// lässt den Wert unberührt.
|
||||
self.stack.push(Value::Int(0));
|
||||
self.rt.tick(host);
|
||||
self.forms_zustellen();
|
||||
self.zustellen(host);
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
@@ -757,6 +949,202 @@ impl Vm {
|
||||
self.push(Value::Str(self.module.strings[i as usize].clone()));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::LoadObjectProperty(object, property, has_index) => {
|
||||
let index = if has_index {
|
||||
Some(self.pop_i32()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !self.forms.is_loaded_at(object, index) {
|
||||
self.forms.ensure_loaded_at(object, index)?;
|
||||
if index.is_none() && self.dispatch_next_form_event() {
|
||||
let caller = self.frames.len() - 2;
|
||||
self.frames[caller].pc = self.frames[caller].pc.saturating_sub(1);
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
}
|
||||
let value = self.forms.get_at(object, index, property)?;
|
||||
self.push(Self::form_value(value));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::StoreObjectProperty(object, property, has_index) => {
|
||||
let value = self.pop()?;
|
||||
let index = if has_index {
|
||||
Some(self.pop_i32()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !self.forms.is_loaded_at(object, index) {
|
||||
self.forms.ensure_loaded_at(object, index)?;
|
||||
if index.is_none() {
|
||||
self.push(value.clone());
|
||||
}
|
||||
if index.is_none() && self.dispatch_next_form_event() {
|
||||
let caller = self.frames.len() - 2;
|
||||
self.frames[caller].pc = self.frames[caller].pc.saturating_sub(1);
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
if index.is_none() {
|
||||
let value = self.pop()?;
|
||||
let value = self.property_value(object, property, value)?;
|
||||
self.forms.set_at(object, index, property, value)?;
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
}
|
||||
let value = self.property_value(object, property, value)?;
|
||||
self.forms.set_at(object, index, property, value)?;
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::PushObject(object, has_index) => {
|
||||
let index = if has_index {
|
||||
Some(self.pop_i32()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.forms.ensure_loaded_at(object, index)?;
|
||||
self.push(Value::Obj(object, index));
|
||||
self.dispatch_next_form_event();
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::LoadDynamicObjectProperty(property) => {
|
||||
let Value::Obj(object, index) = self.pop()? else {
|
||||
return Err(RuntimeError::TYPE_MISMATCH);
|
||||
};
|
||||
let class = self
|
||||
.module
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
.class;
|
||||
let name = self
|
||||
.module
|
||||
.strings
|
||||
.get(property as usize)
|
||||
.ok_or(RuntimeError(422))?;
|
||||
let property = tb_frontend::forms::property(class, name)
|
||||
.map(|(property, _)| property)
|
||||
.ok_or(RuntimeError(422))?;
|
||||
let value = self.forms.get_at(object, index, property)?;
|
||||
self.push(Self::form_value(value));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::StoreDynamicObjectProperty(property) => {
|
||||
let value = self.pop()?;
|
||||
let Value::Obj(object, index) = self.pop()? else {
|
||||
return Err(RuntimeError::TYPE_MISMATCH);
|
||||
};
|
||||
let class = self
|
||||
.module
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
.class;
|
||||
let name = self
|
||||
.module
|
||||
.strings
|
||||
.get(property as usize)
|
||||
.ok_or(RuntimeError(422))?;
|
||||
let property = tb_frontend::forms::property(class, name)
|
||||
.map(|(property, _)| property)
|
||||
.ok_or(RuntimeError(422))?;
|
||||
let value = self.property_value(object, property, value)?;
|
||||
self.forms.set_at(object, index, property, value)?;
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::TypeOf(class) => {
|
||||
let matches = match self.pop()? {
|
||||
Value::Obj(object, _) => self
|
||||
.module
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.is_some_and(|o| o.class.id() == class),
|
||||
_ => false,
|
||||
};
|
||||
self.push(Value::Int(if matches { -1 } else { 0 }));
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::ObjectMethod(object, method, argc) => {
|
||||
let class = self
|
||||
.module
|
||||
.objects
|
||||
.get(object as usize)
|
||||
.ok_or(RuntimeError(420))?
|
||||
.class;
|
||||
let name = *tb_frontend::forms::methods(class)
|
||||
.get(method as usize)
|
||||
.ok_or(RuntimeError(421))?;
|
||||
let resumed_show = self.frames.last().unwrap().pending_show == Some(object);
|
||||
if resumed_show {
|
||||
self.frames.last_mut().unwrap().pending_show = None;
|
||||
} else if class == tb_frontend::forms::ObjectClass::Form
|
||||
&& name == "SHOW"
|
||||
&& !self.forms.is_loaded(object)
|
||||
{
|
||||
self.forms.ensure_loaded(object)?;
|
||||
if self.dispatch_next_form_event() {
|
||||
let caller = self.frames.len() - 2;
|
||||
self.frames[caller].pending_show = Some(object);
|
||||
self.frames[caller].pc = self.frames[caller].pc.saturating_sub(1);
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
}
|
||||
let mut args = Vec::with_capacity(argc as usize);
|
||||
for _ in 0..argc {
|
||||
args.push(self.pop()?);
|
||||
}
|
||||
args.reverse();
|
||||
if resumed_show && !self.forms.is_loaded(object) {
|
||||
return Ok(Flow::Normal);
|
||||
}
|
||||
match (class, name) {
|
||||
(tb_frontend::forms::ObjectClass::Form, "SHOW") => {
|
||||
let style = match args.first() {
|
||||
None => 0,
|
||||
Some(Value::Int(v)) => *v as i32,
|
||||
Some(Value::Lng(v)) => *v,
|
||||
_ => return Err(RuntimeError::TYPE_MISMATCH),
|
||||
};
|
||||
if !matches!(style, 0 | 1) {
|
||||
return Err(RuntimeError::ILLEGAL_FUNCTION_CALL);
|
||||
}
|
||||
if self.forms.show(object, style == 1)? == ShowResult::ModalWait {
|
||||
self.frames.last_mut().unwrap().waiting_form = Some(object);
|
||||
}
|
||||
}
|
||||
(tb_frontend::forms::ObjectClass::Form, "HIDE") => self.forms.hide(object)?,
|
||||
(tb_frontend::forms::ObjectClass::Form, "LOAD") => {
|
||||
self.forms.ensure_loaded(object)?
|
||||
}
|
||||
(tb_frontend::forms::ObjectClass::Form, "UNLOAD") => {
|
||||
self.request_unload(object)?
|
||||
}
|
||||
(tb_frontend::forms::ObjectClass::Screen, "SHOW") => {
|
||||
self.forms.screen_show(true)
|
||||
}
|
||||
(tb_frontend::forms::ObjectClass::Screen, "HIDE") => {
|
||||
self.forms.screen_show(false)
|
||||
}
|
||||
_ => return Err(RuntimeError::FEATURE_UNAVAILABLE),
|
||||
}
|
||||
self.dispatch_next_form_event();
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::ObjectLoad(object, unload, has_index) => {
|
||||
if has_index {
|
||||
let index = self.pop_i32()?;
|
||||
if unload {
|
||||
self.forms.unload_array(object, index)?;
|
||||
} else {
|
||||
self.forms.load_array(object, index)?;
|
||||
}
|
||||
} else if unload {
|
||||
self.request_unload(object)?;
|
||||
} else {
|
||||
self.forms.ensure_loaded(object)?;
|
||||
self.dispatch_next_form_event();
|
||||
}
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::Dup => {
|
||||
let v = self.stack.last().cloned().ok_or(RuntimeError(51))?;
|
||||
self.push(v);
|
||||
@@ -906,8 +1294,7 @@ impl Vm {
|
||||
let dst = self.pop_rec()?;
|
||||
let src = self.pop_rec()?;
|
||||
if !Rc::ptr_eq(&dst, &src) {
|
||||
let copied: Vec<Value> =
|
||||
src.borrow().fields.iter().map(deep_copy).collect();
|
||||
let copied: Vec<Value> = src.borrow().fields.iter().map(deep_copy).collect();
|
||||
dst.borrow_mut().fields = copied;
|
||||
}
|
||||
Ok(Flow::Normal)
|
||||
@@ -1334,7 +1721,25 @@ impl Vm {
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::RetProc => {
|
||||
let event = self.frames.last().and_then(|f| f.form_event);
|
||||
let cancel = self
|
||||
.frames
|
||||
.last()
|
||||
.and_then(|f| {
|
||||
let base = f.locals_base;
|
||||
match self.locals.get(base) {
|
||||
Some(Value::Int(v)) => Some(*v),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.unwrap_or(0);
|
||||
self.pop_frame();
|
||||
if let Some(FormEventReturn::Unload(object)) = event {
|
||||
self.forms.unload_with(object, |c| *c = cancel)?;
|
||||
}
|
||||
if event.is_some() {
|
||||
self.dispatch_next_form_event();
|
||||
}
|
||||
Ok(Flow::Normal)
|
||||
}
|
||||
I::RetFn => {
|
||||
@@ -1616,13 +2021,7 @@ impl Vm {
|
||||
match treffer {
|
||||
Some((nummer, start, laenge)) => {
|
||||
let datei = self.rt.dateien.get(nummer)?;
|
||||
tb_runtime::fileio::feld_setzen(
|
||||
&mut datei.puffer,
|
||||
start,
|
||||
laenge,
|
||||
text,
|
||||
rset,
|
||||
);
|
||||
tb_runtime::fileio::feld_setzen(&mut datei.puffer, start, laenge, text, rset);
|
||||
let neu = tb_runtime::fileio::feld_lesen(&datei.puffer, start, laenge);
|
||||
self.write_ref(&ziel, Value::Str(Rc::from(neu.as_str())))?;
|
||||
}
|
||||
@@ -1918,7 +2317,10 @@ fn input_value(target: &Value, text: &str) -> Option<Value> {
|
||||
let t = text.trim();
|
||||
match target {
|
||||
Value::Str(_) => {
|
||||
let s = t.strip_prefix('"').and_then(|s| s.strip_suffix('"')).unwrap_or(t);
|
||||
let s = t
|
||||
.strip_prefix('"')
|
||||
.and_then(|s| s.strip_suffix('"'))
|
||||
.unwrap_or(t);
|
||||
Some(Value::Str(Rc::from(s)))
|
||||
}
|
||||
Value::Int(_) => {
|
||||
@@ -1946,4 +2348,3 @@ fn strict_number(t: &str) -> Option<f64> {
|
||||
let cleaned = t.replace(['d', 'D'], "E").replace('e', "E");
|
||||
cleaned.parse::<f64>().ok()
|
||||
}
|
||||
|
||||
|
||||
@@ -27,3 +27,17 @@ pub fn compile_source(
|
||||
let hir = analysis.hir.expect("diagnose-frei, aber kein HIR");
|
||||
Ok(codegen::compile(&hir))
|
||||
}
|
||||
|
||||
pub fn compile_source_with_forms(
|
||||
module_name: &str,
|
||||
source: &str,
|
||||
forms: &tb_frontend::forms::FormCatalog,
|
||||
) -> Result<bytecode::CompiledModule, Vec<Diagnostic>> {
|
||||
let analysis = tb_frontend::analyze_source_with_forms(module_name, source, forms);
|
||||
if !analysis.diagnostics.is_empty() {
|
||||
return Err(analysis.diagnostics);
|
||||
}
|
||||
Ok(codegen::compile(
|
||||
&analysis.hir.expect("diagnose-frei, aber kein HIR"),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -99,7 +99,10 @@ fn gemischte_arithmetik() {
|
||||
|
||||
#[test]
|
||||
fn logik_bitweise() {
|
||||
assert_eq!(out("PRINT 6 AND 3; 6 OR 3; 6 XOR 3; NOT 0"), " 2 7 5 -1 \n");
|
||||
assert_eq!(
|
||||
out("PRINT 6 AND 3; 6 OR 3; 6 XOR 3; NOT 0"),
|
||||
" 2 7 5 -1 \n"
|
||||
);
|
||||
// Operanden werden gerundet: 1.5 AND 1 → 2 AND 1 = 0
|
||||
assert_eq!(out("PRINT 1.5 AND 1"), " 0 \n");
|
||||
}
|
||||
@@ -122,12 +125,18 @@ fn stringvergleich_und_verkettung() {
|
||||
#[test]
|
||||
fn for_ohne_durchlauf() {
|
||||
// Spec-Szenario: FOR i% = 3 TO 1 → Körper wird nicht betreten
|
||||
assert_eq!(out("FOR i% = 3 TO 1\nPRINT i%\nNEXT\nPRINT \"ende\""), "ende\n");
|
||||
assert_eq!(
|
||||
out("FOR i% = 3 TO 1\nPRINT i%\nNEXT\nPRINT \"ende\""),
|
||||
"ende\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_mit_negativem_step() {
|
||||
assert_eq!(out("FOR i% = 3 TO 1 STEP -1\nPRINT i%;\nNEXT\nPRINT"), " 3 2 1 \n");
|
||||
assert_eq!(
|
||||
out("FOR i% = 3 TO 1 STEP -1\nPRINT i%;\nNEXT\nPRINT"),
|
||||
" 3 2 1 \n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -172,11 +181,13 @@ fn exit_for_und_do() {
|
||||
fn kontrollfluss_korpusdatei() {
|
||||
// 5.2-Verifikation: kontrollfluss.bas byte-genau korrekt.
|
||||
let src = std::fs::read_to_string(
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/compat/kontrollfluss.bas"),
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../tests/compat/kontrollfluss.bas"),
|
||||
)
|
||||
.unwrap();
|
||||
let want = std::fs::read_to_string(
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/compat/kontrollfluss.out"),
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../tests/compat/kontrollfluss.out"),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(out(&src), want);
|
||||
@@ -268,7 +279,8 @@ fn data_read_restore() {
|
||||
// Anmerkung: unquotierte DATA-Texte verlieren derzeit die
|
||||
// Groß-/Kleinschreibung (Lexer normalisiert Bezeichner);
|
||||
// Rohtext-Erhalt ist als Aufgabe in PLAN.md Phase 3 eingeplant.
|
||||
let src = "DATA 1, 2.5, \"hallo\"\nREAD a%, b!, c$\nPRINT a%; b!; c$\nRESTORE\nREAD x%\nPRINT x%";
|
||||
let src =
|
||||
"DATA 1, 2.5, \"hallo\"\nREAD a%, b!, c$\nPRINT a%; b!; c$\nRESTORE\nREAD x%\nPRINT x%";
|
||||
assert_eq!(out(src), " 1 2.5 hallo\n 1 \n");
|
||||
}
|
||||
|
||||
@@ -444,10 +456,7 @@ fn end_und_system() {
|
||||
|
||||
#[test]
|
||||
fn input_mit_redo() {
|
||||
let (ev, output) = run_with_input(
|
||||
"INPUT \"Zahl\"; n%\nPRINT n% * 2",
|
||||
&["abc", "21"],
|
||||
);
|
||||
let (ev, output) = run_with_input("INPUT \"Zahl\"; n%\nPRINT n% * 2", &["abc", "21"]);
|
||||
assert_eq!(ev, RunEvent::Ended);
|
||||
assert!(output.contains("Redo from start"));
|
||||
assert!(output.ends_with(" 42 \n"), "{output}");
|
||||
@@ -464,7 +473,10 @@ fn line_input_liest_ganze_zeile() {
|
||||
|
||||
#[test]
|
||||
fn mid_anweisung_mutiert() {
|
||||
assert_eq!(out("s$ = \"hallo\"\nMID$(s$, 2, 2) = \"EY\"\nPRINT s$"), "hEYlo\n");
|
||||
assert_eq!(
|
||||
out("s$ = \"hallo\"\nMID$(s$, 2, 2) = \"EY\"\nPRINT s$"),
|
||||
"hEYlo\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -495,12 +507,10 @@ fn unsupported_feature_fehler_73() {
|
||||
#[test]
|
||||
fn open_for_isam_endet_nicht_mehr_mit_fehler_73() {
|
||||
let _dir = TempVerzeichnis::neu("open_isam");
|
||||
let (ev, ausgabe) = run(
|
||||
"TYPE T\n f AS INTEGER\nEND TYPE\n\
|
||||
let (ev, ausgabe) = run("TYPE T\n f AS INTEGER\nEND TYPE\n\
|
||||
OPEN \"db.isam\" FOR ISAM T \"Tab\" AS #1\n\
|
||||
PRINT \"offen\"\n\
|
||||
CLOSE #1",
|
||||
);
|
||||
CLOSE #1");
|
||||
assert_eq!(ev, RunEvent::Ended, "unerwartetes Ende: {ev:?}\n{ausgabe}");
|
||||
assert!(ausgabe.contains("offen"), "{ausgabe}");
|
||||
}
|
||||
@@ -552,7 +562,10 @@ fn groessenaenderung_wirkt_zur_laufzeit() {
|
||||
let module = tb_vm::compile_source("TEST", "PRINT \"a\"\nFOR i% = 1 TO 50\nNEXT i%\n").unwrap();
|
||||
let mut vm = Vm::new(module);
|
||||
let mut host = CaptureHost::default();
|
||||
host.ereignis(Ereignis::Groesse { cols: 120, rows: 40 });
|
||||
host.ereignis(Ereignis::Groesse {
|
||||
cols: 120,
|
||||
rows: 40,
|
||||
});
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!((vm.rt.screen.cols(), vm.rt.screen.rows()), (120, 40));
|
||||
// Inhalt bleibt oben links erhalten.
|
||||
@@ -639,10 +652,7 @@ fn cls_setzt_cursor_zurueck() {
|
||||
|
||||
#[test]
|
||||
fn width_hebt_die_groesse_an() {
|
||||
assert_eq!(
|
||||
out("WIDTH 120, 40\nPRINT SCREEN(40, 120)"),
|
||||
" 32 \n"
|
||||
);
|
||||
assert_eq!(out("WIDTH 120, 40\nPRINT SCREEN(40, 120)"), " 32 \n");
|
||||
}
|
||||
|
||||
// ---- ON ERROR-Scoping (Spec sprach-frontend) -------------------------------
|
||||
@@ -736,8 +746,14 @@ fn time_und_now_stimmen_ueberein() {
|
||||
#[test]
|
||||
fn fester_versatz_verschiebt_die_uhr() {
|
||||
let src = "PRINT INT(TIMER)";
|
||||
let ohne: f64 = out_mit_zone(src, Zeitzone::Unbekannt).trim().parse().unwrap();
|
||||
let mit: f64 = out_mit_zone(src, Zeitzone::Fest(3600)).trim().parse().unwrap();
|
||||
let ohne: f64 = out_mit_zone(src, Zeitzone::Unbekannt)
|
||||
.trim()
|
||||
.parse()
|
||||
.unwrap();
|
||||
let mit: f64 = out_mit_zone(src, Zeitzone::Fest(3600))
|
||||
.trim()
|
||||
.parse()
|
||||
.unwrap();
|
||||
// Modulo Tageslänge, damit ein Mitternachtsübergang nichts kaputt macht.
|
||||
let diff = (mit - ohne).rem_euclid(86_400.0);
|
||||
assert!((diff - 3600.0).abs() <= 2.0, "Differenz {diff}");
|
||||
@@ -746,7 +762,10 @@ fn fester_versatz_verschiebt_die_uhr() {
|
||||
/// `TIMER` zählt ab der lokalen Mitternacht, nicht ab der UTC-Mitternacht.
|
||||
#[test]
|
||||
fn timer_zaehlt_ab_lokaler_mitternacht() {
|
||||
let t = out_mit_zone("PRINT INT(TIMER / 3600); VAL(LEFT$(TIME$, 2))", Zeitzone::Fest(-18000));
|
||||
let t = out_mit_zone(
|
||||
"PRINT INT(TIMER / 3600); VAL(LEFT$(TIME$, 2))",
|
||||
Zeitzone::Fest(-18000),
|
||||
);
|
||||
let zahlen: Vec<f64> = t
|
||||
.split_whitespace()
|
||||
.filter_map(|w| w.parse().ok())
|
||||
@@ -1093,3 +1112,169 @@ fn erl_nennt_die_fehlerzeile_nicht_die_handlerzeile() {
|
||||
110 RESUME NEXT\n");
|
||||
assert_eq!(out.trim(), "20");
|
||||
}
|
||||
|
||||
fn forms_catalog() -> tb_frontend::forms::FormCatalog {
|
||||
use tb_frontend::forms::ObjectClass;
|
||||
let mut c = tb_frontend::forms::FormCatalog::default();
|
||||
c.add("Form1", ObjectClass::Form, None, false);
|
||||
c.add("Text1", ObjectClass::TextBox, Some("Form1"), false);
|
||||
c.add("Command1", ObjectClass::CommandButton, Some("Form1"), true);
|
||||
c
|
||||
}
|
||||
|
||||
fn form_vm(src: &str) -> Vm {
|
||||
let module = tb_vm::compile_source_with_forms("FORM1", src, &forms_catalog())
|
||||
.unwrap_or_else(|d| panic!("Compile-Fehler: {d:?}"));
|
||||
Vm::new(module)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn objektzugriff_laeuft_ueber_objekt_und_eigenschaftsindex() {
|
||||
let mut vm = form_vm("Form1!Text1.Text = \"hallo\"\nPRINT Form1!Text1.Text");
|
||||
let ev = vm.run(&mut CaptureHost::default());
|
||||
assert_eq!(ev, RunEvent::Ended);
|
||||
assert_eq!(tb_runtime::snapshot::text(&vm.rt.screen).trim(), "hallo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn steuerarray_eigenschaften_und_objektparameter_funktionieren() {
|
||||
let mut vm = form_vm(
|
||||
"LOAD Command1(3)\nCommand1(3).Caption = \"drei\"\nPRINT Command1(3).Caption\n\
|
||||
CALL Aus(Text1)\nPRINT Text1.Visible\nCALL FormAus(Form1)\nPRINT Form1.Caption\nEND\n\
|
||||
SUB Aus(c AS CONTROL)\nPRINT TYPEOF c IS TextBox\nc.Visible = 0\nEND SUB\n\
|
||||
SUB FormAus(f AS FORM)\nPRINT TYPEOF f IS Form\nf.Caption = \"Formular\"\nEND SUB",
|
||||
);
|
||||
assert_eq!(vm.run(&mut CaptureHost::default()), RunEvent::Ended);
|
||||
assert_eq!(
|
||||
tb_runtime::snapshot::text(&vm.rt.screen).trim(),
|
||||
"drei\n-1 \n 0 \n-1 \nFormular"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modales_show_setzt_nach_unload_fort() {
|
||||
let mut vm = form_vm(
|
||||
"DIM SHARED gesehen%\nForm1.Show 1\nPRINT gesehen%\nEND\n\
|
||||
SUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\n\
|
||||
SHARED gesehen%\ngesehen% = 1\nUNLOAD Form1\nEND SUB",
|
||||
);
|
||||
let mut host = CaptureHost::default();
|
||||
host.ereignis_nach(
|
||||
2,
|
||||
tb_runtime::host::Ereignis::Maus(tb_runtime::host::MausEreignis {
|
||||
art: tb_runtime::host::MausArt::Druck,
|
||||
taste: 1,
|
||||
shift: 0,
|
||||
zeile: 1,
|
||||
spalte: 1,
|
||||
}),
|
||||
);
|
||||
let ev = vm.run(&mut host);
|
||||
assert_eq!(ev, RunEvent::Ended);
|
||||
assert_eq!(tb_runtime::snapshot::text(&vm.rt.screen).trim(), "1");
|
||||
assert!(host.presents >= 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fehlende_ereignisprozedur_verfaellt_und_typeof_prueft_klasse() {
|
||||
let mut vm = form_vm("Form1.Show\nIF TYPEOF Text1 IS TextBox THEN PRINT \"ja\"");
|
||||
vm.queue_form_event(tb_ui::forms::FormEvent {
|
||||
object: 0,
|
||||
array_index: None,
|
||||
name: "CLICK".into(),
|
||||
args: vec![],
|
||||
});
|
||||
assert_eq!(vm.run(&mut CaptureHost::default()), RunEvent::Ended);
|
||||
assert_eq!(tb_runtime::snapshot::text(&vm.rt.screen).trim(), "ja");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mausargumente_kommen_in_zellen_an() {
|
||||
use tb_runtime::host::{Ereignis, MausArt, MausEreignis};
|
||||
let mut vm = form_vm(
|
||||
"DIM SHARED b%, s%, gx!, gy!\nForm1.Show\nSTOP\nDOEVENTS\nEND\n\
|
||||
SUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\n\
|
||||
SHARED b%, s%, gx!, gy!\nb% = Button: s% = Shift: gx! = X: gy! = Y\nEND SUB",
|
||||
);
|
||||
let mut host = CaptureHost::default();
|
||||
assert!(matches!(vm.run(&mut host), RunEvent::Stopped { .. }));
|
||||
host.ereignis(Ereignis::Maus(MausEreignis {
|
||||
art: MausArt::Druck,
|
||||
taste: 1,
|
||||
shift: 2,
|
||||
zeile: 5,
|
||||
spalte: 7,
|
||||
}));
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert!(matches!(vm.inspect("b"), Some(Value::Int(1))));
|
||||
assert!(matches!(vm.inspect("s"), Some(Value::Int(2))));
|
||||
assert!(matches!(vm.inspect("gx"), Some(Value::Sng(7.0))));
|
||||
assert!(matches!(vm.inspect("gy"), Some(Value::Sng(5.0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn form_load_laeuft_vor_dem_sichtbarsetzen() {
|
||||
let mut vm = form_vm(
|
||||
"Form1.Show\nEND\n\
|
||||
SUB Form_Load()\nPRINT Form1.Visible\nEND SUB",
|
||||
);
|
||||
assert_eq!(vm.run(&mut CaptureHost::default()), RunEvent::Ended);
|
||||
assert_eq!(tb_runtime::snapshot::text(&vm.rt.screen).trim(), "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn steuerarray_ereignis_stellt_index_voran() {
|
||||
let mut vm = form_vm(
|
||||
"DIM SHARED seen%\nSTOP\nDOEVENTS\nEND\n\
|
||||
SUB Command1_Click(Index AS INTEGER)\nSHARED seen%\nseen% = Index\nEND SUB",
|
||||
);
|
||||
let mut host = CaptureHost::default();
|
||||
assert!(matches!(vm.run(&mut host), RunEvent::Stopped { .. }));
|
||||
vm.forms.load_array(2, 3).unwrap();
|
||||
vm.queue_form_event(tb_ui::forms::FormEvent {
|
||||
object: 2,
|
||||
array_index: Some(3),
|
||||
name: "CLICK".into(),
|
||||
args: vec![],
|
||||
});
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert!(matches!(vm.inspect("seen"), Some(Value::Int(3))));
|
||||
let active =
|
||||
tb_frontend::forms::property(tb_frontend::forms::ObjectClass::Screen, "ACTIVECONTROL")
|
||||
.unwrap()
|
||||
.0;
|
||||
assert_eq!(
|
||||
vm.forms.get(3, active).unwrap(),
|
||||
tb_ui::forms::PropertyValue::Object(Some((2, Some(3))))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn form_load_laeuft_einmal_und_unload_cancel_verhindert_entladen() {
|
||||
let mut vm = form_vm(
|
||||
"DIM SHARED loads%\nx$ = Form1.Caption\nForm1.Hide\nx$ = Form1.Caption\n\
|
||||
Form1.Show\nUNLOAD Form1\nEND\n\
|
||||
SUB Form_Load()\nSHARED loads%\nloads% = loads% + 1\nEND SUB\n\
|
||||
SUB Form_Unload(Cancel AS INTEGER)\nCancel = 1\nEND SUB",
|
||||
);
|
||||
assert_eq!(vm.run(&mut CaptureHost::default()), RunEvent::Ended);
|
||||
assert!(matches!(vm.inspect("loads"), Some(Value::Int(1))));
|
||||
assert!(vm.forms.objects[0].loaded);
|
||||
assert!(vm.forms.objects[0].visible);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn objektcode_erzeugt_keine_zusaetzlichen_zustellopcodes() {
|
||||
let module = tb_vm::compile_source_with_forms(
|
||||
"FORM1",
|
||||
"Form1.Caption = \"x\"\nDOEVENTS\nx$ = Form1.Caption",
|
||||
&forms_catalog(),
|
||||
)
|
||||
.unwrap();
|
||||
let n = module.procs[0]
|
||||
.code
|
||||
.iter()
|
||||
.filter(|i| matches!(i, tb_vm::bytecode::Instr::Doevents))
|
||||
.count();
|
||||
assert_eq!(n, 1, "nur das explizite DOEVENTS ist ein Zustellopcode");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,14 @@ Rekonstruierte Referenz der Forms-Engine des Vorbilds (VBDOS 1.0) —
|
||||
Grundlage für `tb-ui::forms` und den Formular-Designer. Primärquellen:
|
||||
Original-Hilfedatei (dekodiert), `CONSTANT.BI`, Stringtabellen der
|
||||
Original-Programme, Beispielprojekte. Aussagen ohne TODO sind daraus belegt.
|
||||
Die zeilenweise Matrix mit Typ, Bereich, Vorgabe, Laufzeit-Schreibbarkeit
|
||||
und Klassenquelle liegt im Change-Dokument
|
||||
`openspec/changes/phase-4-objektmodell/klassen-und-eigenschaften.md`.
|
||||
Als Beispiel-Gegenprobe dient `cout/vbdos` Commit
|
||||
`1cdd2b32b829fe1721d0b6aecc433abc47a96fb6`; dessen binäre Formulare
|
||||
`microsoft/check.frm`, `notepad.frm`, `qlbview.frm`, `seek.frm`,
|
||||
`spindemo.frm`, `graphics/graphics.frm` und `misc/mentors/mentors.frm`
|
||||
enthalten die dort ausgewiesenen Eigenschaften und Ereignisprozeduren.
|
||||
|
||||
## Koordinatenmodell
|
||||
|
||||
@@ -135,6 +143,9 @@ DragOver-State 0 Enter · 1 Leave · 2 Over. Bei Steuerelement-Arrays steht
|
||||
`Index AS INTEGER` immer vorn. KeyAscii ist bei uns ein Unicode-Codepoint
|
||||
(UTF-8-Abweichung); klassische KeyCode-Konstanten bleiben gültig
|
||||
(Vorbild-Eigenheit: Entf = 127).
|
||||
Ereignisse ohne eine der oben genannten Argumentfamilien haben keine
|
||||
Parameter. Bei Arrays wird ausschließlich `Index AS INTEGER` vorangestellt;
|
||||
die restliche Signatur bleibt unverändert.
|
||||
|
||||
## SCREEN-Objekt
|
||||
|
||||
@@ -154,7 +165,8 @@ Modal-Stacking-Regeln → Fehler 400–403 (u. a. „MDI form cannot be shown
|
||||
modally"). `HIDE` setzt nur `Visible = False`; `LOAD`/`UNLOAD` mit
|
||||
Load-/Unload-Ereignis (Cancel-Parameter). `LOAD/UNLOAD ctl(index%)` für
|
||||
Steuerelement-Arrays (Entwurfszeit-Elemente sind nicht entladbar,
|
||||
Fehler 362).
|
||||
Fehler 362); deren Eigenschaften sind über `ctl(index%).Eigenschaft`
|
||||
adressierbar und werden beim `LOAD` vom Entwurfszeit-Element 0 geklont.
|
||||
|
||||
## Menüsystem
|
||||
|
||||
|
||||
518
docs/inventar.md
518
docs/inventar.md
@@ -9,8 +9,9 @@ nicht falsch werden, ohne dass die Testsuite rot wird.
|
||||
**Quellen.** `bas7advr` = Themenliste von `bas7advr.hlp` (BASIC 7
|
||||
Advisor). `qb45advr` = Gegenprobe über `qb45advr.hlp`, nötig weil die
|
||||
erste Liste nachweislich unvollständig ist. `sprachreferenz` =
|
||||
docs/sprachreferenz.md für Elemente des Forms-Teils, die keine der beiden
|
||||
Themenlisten führt. Erhebung siehe
|
||||
docs/sprachreferenz.md für Sprachelemente des Forms-Teils;
|
||||
`forms-referenz` = docs/forms-referenz.md für das Forms-Objektmodell.
|
||||
Erhebung siehe
|
||||
`openspec/changes/phase-3-runtime-bildschirm/rohliste-original-hilfe.md`.
|
||||
|
||||
**Status.** `implementiert` = Frontend senkt ab und die Laufzeit führt
|
||||
@@ -21,7 +22,7 @@ namentlich abgewiesen. `Non-Feature` = abgelehnt, gelistet unter
|
||||
**Fundstelle.** Bei `implementiert` das Modul, bei `Non-Feature` der
|
||||
Abschnitt der Sprachreferenz, bei `offen` ein Strich.
|
||||
|
||||
**Abdeckung.** implementiert 227 · offen 5 · Non-Feature 53 · gesamt 285
|
||||
**Abdeckung.** implementiert 682 · offen 61 · Non-Feature 53 · gesamt 796
|
||||
|
||||
| Name | Art | Gruppe | Status | Fundstelle | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
@@ -310,3 +311,514 @@ Abschnitt der Sprachreferenz, bei `offen` ein Strich.
|
||||
| `INKEY$` | Funktion | Tastatur | implementiert | tb-runtime::builtins | bas7advr |
|
||||
| `INPUT$` | Funktion | Tastatur | implementiert | tb-runtime::builtins | bas7advr |
|
||||
| `KEY` | Anweisung | Tastatur | implementiert | tb-runtime::builtins | bas7advr |
|
||||
| `$FORM` | Metabefehl | Forms/Objektsprache | implementiert | tb-frontend::lexer | forms-referenz |
|
||||
| `FORM` | Datentyp | Forms/Objektsprache | implementiert | tb-frontend::sema | forms-referenz |
|
||||
| `CONTROL` | Datentyp | Forms/Objektsprache | implementiert | tb-frontend::sema | forms-referenz |
|
||||
| `TYPEOF … IS` | Operator | Forms/Objektsprache | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `!` | Operator | Forms/Objektsprache | implementiert | tb-frontend::lexer | forms-referenz |
|
||||
| `FORM.BACKCOLOR` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.ENABLED` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.HEIGHT` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.LEFT` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.MOUSEPOINTER` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.PARENT` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.TAG` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.TOP` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.VISIBLE` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.WIDTH` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.FORECOLOR` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.DRAGMODE` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.AUTOREDRAW` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.BORDERSTYLE` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.CAPTION` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.CONTROLBOX` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.CURRENTX` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.CURRENTY` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.FORMNAME` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.FORMTYPE` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.MAXBUTTON` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.MINBUTTON` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.SCALEHEIGHT` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.SCALEWIDTH` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.WINDOWSTATE` | Eigenschaft | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.CLS` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.DRAG` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.HIDE` | Methode | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.LOAD` | Methode | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.MOVE` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.PRINT` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.PRINTFORM` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.REFRESH` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.SHOW` | Methode | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM.TEXTHEIGHT` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.TEXTWIDTH` | Methode | Forms/FORM | offen | - | forms-referenz |
|
||||
| `FORM.UNLOAD` | Methode | Forms/FORM | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FORM_CLICK` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_DBLCLICK` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_DRAGDROP` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_DRAGOVER` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_GOTFOCUS` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_KEYDOWN` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_KEYPRESS` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_KEYUP` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_LOAD` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_LOSTFOCUS` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_MOUSEDOWN` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_MOUSEMOVE` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_MOUSEUP` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_PAINT` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_RESIZE` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FORM_UNLOAD` | Ereignis | Forms/FORM | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX.BACKCOLOR` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.ENABLED` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.HEIGHT` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.LEFT` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.MOUSEPOINTER` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.PARENT` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.TAG` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.TOP` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.VISIBLE` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.WIDTH` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.INDEX` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.TABINDEX` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.TABSTOP` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.CTLNAME` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.FORECOLOR` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.DRAGMODE` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.CAPTION` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.VALUE` | Eigenschaft | Forms/CHECKBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `CHECKBOX.DRAG` | Methode | Forms/CHECKBOX | offen | - | forms-referenz |
|
||||
| `CHECKBOX.MOVE` | Methode | Forms/CHECKBOX | offen | - | forms-referenz |
|
||||
| `CHECKBOX.REFRESH` | Methode | Forms/CHECKBOX | offen | - | forms-referenz |
|
||||
| `CHECKBOX.SETFOCUS` | Methode | Forms/CHECKBOX | offen | - | forms-referenz |
|
||||
| `CHECKBOX_CLICK` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_DRAGDROP` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_DRAGOVER` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_GOTFOCUS` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_KEYDOWN` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_KEYPRESS` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_KEYUP` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `CHECKBOX_LOSTFOCUS` | Ereignis | Forms/CHECKBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX.BACKCOLOR` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.ENABLED` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.HEIGHT` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.LEFT` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.MOUSEPOINTER` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.PARENT` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.TAG` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.TOP` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.VISIBLE` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.WIDTH` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.INDEX` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.TABINDEX` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.TABSTOP` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.CTLNAME` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.FORECOLOR` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.DRAGMODE` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.LIST` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.LISTCOUNT` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.LISTINDEX` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.SORTED` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.SELLENGTH` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.SELSTART` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.SELTEXT` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.STYLE` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.TEXT` | Eigenschaft | Forms/COMBOBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMBOBOX.ADDITEM` | Methode | Forms/COMBOBOX | offen | - | forms-referenz |
|
||||
| `COMBOBOX.DRAG` | Methode | Forms/COMBOBOX | offen | - | forms-referenz |
|
||||
| `COMBOBOX.MOVE` | Methode | Forms/COMBOBOX | offen | - | forms-referenz |
|
||||
| `COMBOBOX.REFRESH` | Methode | Forms/COMBOBOX | offen | - | forms-referenz |
|
||||
| `COMBOBOX.REMOVEITEM` | Methode | Forms/COMBOBOX | offen | - | forms-referenz |
|
||||
| `COMBOBOX.SETFOCUS` | Methode | Forms/COMBOBOX | offen | - | forms-referenz |
|
||||
| `COMBOBOX_CHANGE` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_CLICK` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_DBLCLICK` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_DRAGDROP` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_DRAGOVER` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_DROPDOWN` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_GOTFOCUS` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_KEYDOWN` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_KEYPRESS` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_KEYUP` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_LOSTFOCUS` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_MOUSEDOWN` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_MOUSEMOVE` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMBOBOX_MOUSEUP` | Ereignis | Forms/COMBOBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON.BACKCOLOR` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.ENABLED` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.HEIGHT` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.LEFT` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.MOUSEPOINTER` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.PARENT` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.TAG` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.TOP` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.VISIBLE` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.WIDTH` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.INDEX` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.TABINDEX` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.TABSTOP` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.CTLNAME` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.DRAGMODE` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.CANCEL` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.CAPTION` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.DEFAULT` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.VALUE` | Eigenschaft | Forms/COMMANDBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `COMMANDBUTTON.DRAG` | Methode | Forms/COMMANDBUTTON | offen | - | forms-referenz |
|
||||
| `COMMANDBUTTON.MOVE` | Methode | Forms/COMMANDBUTTON | offen | - | forms-referenz |
|
||||
| `COMMANDBUTTON.REFRESH` | Methode | Forms/COMMANDBUTTON | offen | - | forms-referenz |
|
||||
| `COMMANDBUTTON.SETFOCUS` | Methode | Forms/COMMANDBUTTON | offen | - | forms-referenz |
|
||||
| `COMMANDBUTTON_CLICK` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_DRAGDROP` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_DRAGOVER` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_GOTFOCUS` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_KEYDOWN` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_KEYPRESS` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_KEYUP` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `COMMANDBUTTON_LOSTFOCUS` | Ereignis | Forms/COMMANDBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `DIRLISTBOX.BACKCOLOR` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.ENABLED` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.HEIGHT` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.LEFT` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.MOUSEPOINTER` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.PARENT` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.TAG` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.TOP` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.VISIBLE` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.WIDTH` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.INDEX` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.TABINDEX` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.TABSTOP` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.CTLNAME` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.PATH` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX.TEXT` | Eigenschaft | Forms/DIRLISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DIRLISTBOX_CHANGE` | Ereignis | Forms/DIRLISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `DIRLISTBOX_CLICK` | Ereignis | Forms/DIRLISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `DIRLISTBOX_DBLCLICK` | Ereignis | Forms/DIRLISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `DIRLISTBOX_PATHCHANGE` | Ereignis | Forms/DIRLISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `DRIVELISTBOX.BACKCOLOR` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.ENABLED` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.HEIGHT` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.LEFT` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.MOUSEPOINTER` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.PARENT` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.TAG` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.TOP` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.VISIBLE` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.WIDTH` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.INDEX` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.TABINDEX` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.TABSTOP` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.CTLNAME` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.DRIVE` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX.TEXT` | Eigenschaft | Forms/DRIVELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `DRIVELISTBOX_CHANGE` | Ereignis | Forms/DRIVELISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FILELISTBOX.BACKCOLOR` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.ENABLED` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.HEIGHT` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.LEFT` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.MOUSEPOINTER` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.PARENT` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.TAG` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.TOP` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.VISIBLE` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.WIDTH` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.INDEX` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.TABINDEX` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.TABSTOP` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.CTLNAME` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.FILENAME` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.PATH` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.PATTERN` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.ARCHIVE` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.HIDDEN` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.NORMAL` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.READONLY` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.SYSTEM` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX.TEXT` | Eigenschaft | Forms/FILELISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FILELISTBOX_CLICK` | Ereignis | Forms/FILELISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FILELISTBOX_DBLCLICK` | Ereignis | Forms/FILELISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FILELISTBOX_PATTERNCHANGE` | Ereignis | Forms/FILELISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FILELISTBOX_PATHCHANGE` | Ereignis | Forms/FILELISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FRAME.BACKCOLOR` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.ENABLED` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.HEIGHT` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.LEFT` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.MOUSEPOINTER` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.PARENT` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.TAG` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.TOP` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.VISIBLE` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.WIDTH` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.CTLNAME` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.DRAGMODE` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.CAPTION` | Eigenschaft | Forms/FRAME | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `FRAME.DRAG` | Methode | Forms/FRAME | offen | - | forms-referenz |
|
||||
| `FRAME.MOVE` | Methode | Forms/FRAME | offen | - | forms-referenz |
|
||||
| `FRAME.REFRESH` | Methode | Forms/FRAME | offen | - | forms-referenz |
|
||||
| `FRAME_DRAGDROP` | Ereignis | Forms/FRAME | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `FRAME_DRAGOVER` | Ereignis | Forms/FRAME | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR.BACKCOLOR` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.ENABLED` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.HEIGHT` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.LEFT` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.MOUSEPOINTER` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.PARENT` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.TAG` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.TOP` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.VISIBLE` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.WIDTH` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.INDEX` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.TABINDEX` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.TABSTOP` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.CTLNAME` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.FORECOLOR` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.DRAGMODE` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.ATTACHED` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.LARGECHANGE` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.MIN` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.MAX` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.SMALLCHANGE` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.VALUE` | Eigenschaft | Forms/HSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `HSCROLLBAR.DRAG` | Methode | Forms/HSCROLLBAR | offen | - | forms-referenz |
|
||||
| `HSCROLLBAR.MOVE` | Methode | Forms/HSCROLLBAR | offen | - | forms-referenz |
|
||||
| `HSCROLLBAR.REFRESH` | Methode | Forms/HSCROLLBAR | offen | - | forms-referenz |
|
||||
| `HSCROLLBAR_CHANGE` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_DRAGDROP` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_DRAGOVER` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_GOTFOCUS` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_KEYDOWN` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_KEYPRESS` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_KEYUP` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `HSCROLLBAR_LOSTFOCUS` | Ereignis | Forms/HSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL.BACKCOLOR` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.ENABLED` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.HEIGHT` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.LEFT` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.MOUSEPOINTER` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.PARENT` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.TAG` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.TOP` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.VISIBLE` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.WIDTH` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.CTLNAME` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.FORECOLOR` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.DRAGMODE` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.ALIGNMENT` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.AUTOSIZE` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.BORDERSTYLE` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.CAPTION` | Eigenschaft | Forms/LABEL | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LABEL.DRAG` | Methode | Forms/LABEL | offen | - | forms-referenz |
|
||||
| `LABEL.MOVE` | Methode | Forms/LABEL | offen | - | forms-referenz |
|
||||
| `LABEL.REFRESH` | Methode | Forms/LABEL | offen | - | forms-referenz |
|
||||
| `LABEL_CHANGE` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_CLICK` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_DBLCLICK` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_DRAGDROP` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_DRAGOVER` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_MOUSEDOWN` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_MOUSEMOVE` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LABEL_MOUSEUP` | Ereignis | Forms/LABEL | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX.BACKCOLOR` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.ENABLED` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.HEIGHT` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.LEFT` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.MOUSEPOINTER` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.PARENT` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.TAG` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.TOP` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.VISIBLE` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.WIDTH` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.INDEX` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.TABINDEX` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.TABSTOP` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.CTLNAME` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.FORECOLOR` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.DRAGMODE` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.LIST` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.LISTCOUNT` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.LISTINDEX` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.SORTED` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.TEXT` | Eigenschaft | Forms/LISTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `LISTBOX.ADDITEM` | Methode | Forms/LISTBOX | offen | - | forms-referenz |
|
||||
| `LISTBOX.DRAG` | Methode | Forms/LISTBOX | offen | - | forms-referenz |
|
||||
| `LISTBOX.MOVE` | Methode | Forms/LISTBOX | offen | - | forms-referenz |
|
||||
| `LISTBOX.REFRESH` | Methode | Forms/LISTBOX | offen | - | forms-referenz |
|
||||
| `LISTBOX.REMOVEITEM` | Methode | Forms/LISTBOX | offen | - | forms-referenz |
|
||||
| `LISTBOX.SETFOCUS` | Methode | Forms/LISTBOX | offen | - | forms-referenz |
|
||||
| `LISTBOX_CLICK` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_DBLCLICK` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_DRAGDROP` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_DRAGOVER` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_GOTFOCUS` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_KEYDOWN` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_KEYPRESS` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_KEYUP` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_LOSTFOCUS` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_MOUSEDOWN` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_MOUSEMOVE` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `LISTBOX_MOUSEUP` | Ereignis | Forms/LISTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `MENU.CAPTION` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.CHECKED` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.CTLNAME` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.ENABLED` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.INDEX` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.PARENT` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.SEPARATOR` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.TAG` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU.VISIBLE` | Eigenschaft | Forms/MENU | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `MENU_CLICK` | Ereignis | Forms/MENU | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON.BACKCOLOR` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.ENABLED` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.HEIGHT` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.LEFT` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.MOUSEPOINTER` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.PARENT` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.TAG` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.TOP` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.VISIBLE` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.WIDTH` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.INDEX` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.TABINDEX` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.TABSTOP` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.CTLNAME` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.FORECOLOR` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.DRAGMODE` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.CAPTION` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.VALUE` | Eigenschaft | Forms/OPTIONBUTTON | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `OPTIONBUTTON.DRAG` | Methode | Forms/OPTIONBUTTON | offen | - | forms-referenz |
|
||||
| `OPTIONBUTTON.MOVE` | Methode | Forms/OPTIONBUTTON | offen | - | forms-referenz |
|
||||
| `OPTIONBUTTON.REFRESH` | Methode | Forms/OPTIONBUTTON | offen | - | forms-referenz |
|
||||
| `OPTIONBUTTON.SETFOCUS` | Methode | Forms/OPTIONBUTTON | offen | - | forms-referenz |
|
||||
| `OPTIONBUTTON_CLICK` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_DBLCLICK` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_DRAGDROP` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_DRAGOVER` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_GOTFOCUS` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_KEYDOWN` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_KEYPRESS` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_KEYUP` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `OPTIONBUTTON_LOSTFOCUS` | Ereignis | Forms/OPTIONBUTTON | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX.BACKCOLOR` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.ENABLED` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.HEIGHT` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.LEFT` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.MOUSEPOINTER` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.PARENT` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.TAG` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.TOP` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.VISIBLE` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.WIDTH` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.CTLNAME` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.FORECOLOR` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.DRAGMODE` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.AUTOREDRAW` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.BORDERSTYLE` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.CURRENTX` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.CURRENTY` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.SCALEHEIGHT` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.SCALEWIDTH` | Eigenschaft | Forms/PICTUREBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `PICTUREBOX.CLS` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.DRAG` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.MOVE` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.PRINT` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.REFRESH` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.SETFOCUS` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.TEXTHEIGHT` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX.TEXTWIDTH` | Methode | Forms/PICTUREBOX | offen | - | forms-referenz |
|
||||
| `PICTUREBOX_CLICK` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_DBLCLICK` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_DRAGDROP` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_DRAGOVER` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_GOTFOCUS` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_KEYDOWN` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_KEYPRESS` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_KEYUP` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_LOSTFOCUS` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_MOUSEDOWN` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_MOUSEMOVE` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_MOUSEUP` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `PICTUREBOX_PAINT` | Ereignis | Forms/PICTUREBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX.BACKCOLOR` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.ENABLED` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.HEIGHT` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.LEFT` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.MOUSEPOINTER` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.PARENT` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.TAG` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.TOP` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.VISIBLE` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.WIDTH` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.INDEX` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.TABINDEX` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.TABSTOP` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.CTLNAME` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.FORECOLOR` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.DRAGMODE` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.BORDERSTYLE` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.MULTILINE` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.SCROLLBARS` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.SELLENGTH` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.SELSTART` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.SELTEXT` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.TEXT` | Eigenschaft | Forms/TEXTBOX | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TEXTBOX.DRAG` | Methode | Forms/TEXTBOX | offen | - | forms-referenz |
|
||||
| `TEXTBOX.MOVE` | Methode | Forms/TEXTBOX | offen | - | forms-referenz |
|
||||
| `TEXTBOX.REFRESH` | Methode | Forms/TEXTBOX | offen | - | forms-referenz |
|
||||
| `TEXTBOX.SETFOCUS` | Methode | Forms/TEXTBOX | offen | - | forms-referenz |
|
||||
| `TEXTBOX_CHANGE` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_DRAGDROP` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_DRAGOVER` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_GOTFOCUS` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_KEYDOWN` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_KEYPRESS` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_KEYUP` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TEXTBOX_LOSTFOCUS` | Ereignis | Forms/TEXTBOX | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `TIMER.CTLNAME` | Eigenschaft | Forms/TIMER | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TIMER.ENABLED` | Eigenschaft | Forms/TIMER | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TIMER.INDEX` | Eigenschaft | Forms/TIMER | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TIMER.INTERVAL` | Eigenschaft | Forms/TIMER | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TIMER.PARENT` | Eigenschaft | Forms/TIMER | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TIMER.TAG` | Eigenschaft | Forms/TIMER | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `TIMER_TIMER` | Ereignis | Forms/TIMER | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR.BACKCOLOR` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.ENABLED` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.HEIGHT` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.LEFT` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.MOUSEPOINTER` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.PARENT` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.TAG` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.TOP` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.VISIBLE` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.WIDTH` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.INDEX` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.TABINDEX` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.TABSTOP` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.CTLNAME` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.FORECOLOR` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.DRAGMODE` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.ATTACHED` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.LARGECHANGE` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.MIN` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.MAX` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.SMALLCHANGE` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.VALUE` | Eigenschaft | Forms/VSCROLLBAR | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `VSCROLLBAR.DRAG` | Methode | Forms/VSCROLLBAR | offen | - | forms-referenz |
|
||||
| `VSCROLLBAR.MOVE` | Methode | Forms/VSCROLLBAR | offen | - | forms-referenz |
|
||||
| `VSCROLLBAR.REFRESH` | Methode | Forms/VSCROLLBAR | offen | - | forms-referenz |
|
||||
| `VSCROLLBAR_CHANGE` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_DRAGDROP` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_DRAGOVER` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_GOTFOCUS` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_KEYDOWN` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_KEYPRESS` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_KEYUP` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `VSCROLLBAR_LOSTFOCUS` | Ereignis | Forms/VSCROLLBAR | implementiert | tb-vm::interp | forms-referenz |
|
||||
| `SCREEN.ACTIVECONTROL` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.ACTIVEFORM` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.AUTOREDRAW` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.CONTROLPANEL` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.HEIGHT` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.WIDTH` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.MOUSEPOINTER` | Eigenschaft | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.HIDE` | Methode | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
| `SCREEN.SHOW` | Methode | Forms/SCREEN | implementiert | tb-ui::forms | forms-referenz |
|
||||
|
||||
@@ -430,11 +430,40 @@ uns Pseudowerte), `VARPTR`/`VARSEG`/`SADD`/`PEEK`/`POKE` → Nicht-Ziel
|
||||
|
||||
## 12. Forms-Anbindung (Details in forms-referenz.md)
|
||||
|
||||
`form.eigenschaft = wert`, `form!steuerelement.eigenschaft`,
|
||||
Ereignisprozeduren `SUB name_Ereignis (…)`, `LOAD`/`UNLOAD form`,
|
||||
`form.SHOW [style%]` (0 = modeless, Standard; 1 = modal), `form.HIDE`,
|
||||
`DOEVENTS()`, `SCREEN`-Objekt, Datentypen `FORM`/`CONTROL`, `IF TYPEOF`,
|
||||
`$FORM`-Metabefehl, Include-Dateien (Konstanten-`.BI`).
|
||||
`'$FORM` kennzeichnet ein Formularmodul. Formulare und die aus seiner
|
||||
Beschreibung bekannten Controls sind benannte, statisch typisierte Objekte.
|
||||
Der Punkt liest oder schreibt eine explizite Eigenschaft
|
||||
(`Text1.Text = "x"`); es gibt keine Standardeigenschaft. Der
|
||||
Container-Operator `Form1!Text1.Text` wählt dasselbe Control innerhalb
|
||||
seines Formulars. Ein `!` vor einem Bezeichner ist dieser Operator, sonst
|
||||
bleibt es das SINGLE-Suffix. Unbekannte Objekte, Eigenschaften und Methoden
|
||||
werden bei der Übersetzung mit Namen abgewiesen; UDT-Feldpfade behalten ihre
|
||||
bisherige Bedeutung.
|
||||
|
||||
`FORM` und `CONTROL` sind als Parametertypen zulässig; ihre Eigenschaften
|
||||
werden anhand der zur Laufzeit übergebenen Objektklasse gelesen und geschrieben. Mit
|
||||
`IF TYPEOF ausdruck IS Klasse THEN` wird die konkrete, bekannte Klasse
|
||||
geprüft. `SCREEN` ist das globale Objekt mit `ActiveForm`, `ActiveControl`,
|
||||
`Height`, `Width`, `MousePointer`, `ControlPanel` sowie `SHOW`/`HIDE`;
|
||||
`Height` und `Width` folgen der aktuellen Terminalgröße und sind read-only.
|
||||
|
||||
`LOAD form` lädt unsichtbar und ruft zuerst `Form_Load` auf. `UNLOAD form`
|
||||
ruft `Form_Unload(Cancel AS INTEGER)` auf; `Cancel <> 0` verhindert das
|
||||
Entladen. `form.HIDE` ändert nur die Sichtbarkeit. Ein Eigenschaftszugriff
|
||||
lädt ein noch nicht geladenes Formular implizit. `form.SHOW [style%]` zeigt
|
||||
es mit 0 (Standard) modeless oder mit 1 modal; bei modal wird der aufrufende
|
||||
VM-Frame bis `HIDE`/`UNLOAD` markiert, während die Ereignisschleife weiter
|
||||
zustellt. Stapelverstöße liefern Fehler 400–403.
|
||||
|
||||
Steuerelement-Arrays verwenden `LOAD name(index)` und
|
||||
`UNLOAD name(index)`; Eigenschaften werden als `name(index).Eigenschaft`
|
||||
adressiert. Entwurfszeit-Elemente liefern beim Entladen Fehler 362.
|
||||
Ihre Ereignisprozedur erhält `Index AS INTEGER` als erstes Argument.
|
||||
Ansonsten heißt ein Handler `SUB Name_Ereignis (…)` und muss exakt die
|
||||
Signatur der Forms-Referenz tragen. Fehlt er, verfällt das Ereignis. Der
|
||||
Dispatch nutzt ausschließlich die vorhandenen Zustellpunkte der
|
||||
Ereignissteuerung. Mausargumente sind Taste und Shift als INTEGER sowie X/Y
|
||||
als SINGLE in 1-basierten Textzellen.
|
||||
|
||||
Dialoge:
|
||||
- `MSGBOX msg$ [,type% [,title$]]` (Anweisung) bzw. Funktion → INTEGER.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
@@ -0,0 +1,140 @@
|
||||
# Design — Formularmodell und Objektsprache
|
||||
|
||||
## Context
|
||||
|
||||
Siehe proposal.md — Why. Drei Bestandsbefunde schneiden den Entwurf zu:
|
||||
|
||||
1. **Der Punkt gehört heute zum Bezeichner.** Der Lexer nimmt `.` in den
|
||||
Namen auf (`a.b` ist ein Token), die Semantik zerlegt ihn nur, wenn
|
||||
die Basis ein UDT ist. Ein Objektzugriff fällt deshalb durch bis zur
|
||||
impliziten Variablen.
|
||||
2. **`!` ist heute immer Typsuffix.** `Form1!Text1` liest sich als
|
||||
`Form1` (SINGLE) gefolgt von `Text1`.
|
||||
3. **Der Rückrufweg existiert bereits** aus `phase-4-ereignisschleife`:
|
||||
die VM kann ein Ziel anspringen und zurückkehren. Ereignisprozeduren
|
||||
brauchen davon die Variante „Prozedur mit Argumenten" statt
|
||||
„`GOSUB`-Ziel".
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Objektzugriffe, die zur Übersetzungszeit gegen die Klasse geprüft
|
||||
werden — kein Nachschlagen von Eigenschaftsnamen zur Laufzeit im
|
||||
Normalfall.
|
||||
- Ein Formularmodell, das die Steuerelemente des Folge-Changes nur noch
|
||||
füllen, nicht umbauen müssen.
|
||||
- Modales `SHOW` ohne zweiten Ausführungsstrang.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Darstellung und Eingabebehandlung der Steuerelemente.
|
||||
- Ein offenes Objektsystem für Anwenderklassen (Stufe 2).
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1 — Objektnamen werden zur Übersetzungszeit aufgelöst
|
||||
|
||||
Formulare und Steuerelemente sind zur Übersetzungszeit bekannt: sie
|
||||
stammen aus der `.FRM`-Beschreibung bzw. dem Formular-Designer. Der
|
||||
Compiler löst `Text1.Text` deshalb in „Objekt 7, Eigenschaft 12" auf und
|
||||
prüft Typ und Schreibbarkeit sofort.
|
||||
|
||||
| Alternative | Warum nicht |
|
||||
|---|---|
|
||||
| Eigenschaftsnamen zur Laufzeit nachschlagen | Verlagert jeden Tippfehler in eine Laufzeitmeldung — genau die stille Lücke, die der Guiding Principle verbietet; zudem eine Hashsuche pro Zugriff |
|
||||
|
||||
Der Zugriff über `CONTROL`-Parameter (`SUB Setze (c AS CONTROL)`) bleibt
|
||||
dynamisch; dort prüft die Laufzeit und `TYPEOF` fragt die Klasse ab.
|
||||
|
||||
### D2 — Der Punkt wird im Lexer nicht mehr gefaltet, sondern im Parser zerlegt
|
||||
|
||||
Der Lexer liefert weiterhin ein Token, das Punkte enthalten kann (die
|
||||
Rückwärtskompatibilität für Variablennamen mit Punkt bleibt: klassische
|
||||
Programme nutzen `Kunde.Name` als gewöhnlichen Variablennamen). Die
|
||||
Auflösung entscheidet die Semantik in dieser Reihenfolge:
|
||||
|
||||
```
|
||||
Basis ist UDT-Variable -> Feldzugriff wie bisher
|
||||
Basis ist Formular/Control -> Eigenschaftszugriff
|
||||
Basis ist SCREEN -> Bildschirmobjekt
|
||||
sonst -> Diagnose mit Basis- und Gliedname
|
||||
(heute: implizite Variable)
|
||||
```
|
||||
|
||||
Damit ändert sich für bestehende Programme nichts, solange kein Objekt
|
||||
den Namen trägt; Objekte gewinnen gegen implizite Variablen, weil
|
||||
implizite Variablen mit Punkt im Namen ohnehin ein Bug waren.
|
||||
|
||||
### D3 — `!` entscheidet sich am folgenden Token
|
||||
|
||||
Folgt auf `!` unmittelbar ein Bezeichner, ist es der Container-Operator;
|
||||
sonst das Typsuffix. `Wert! = 1.5` und `Form1!Text1.Text = "a"` sind
|
||||
damit unterscheidbar, ohne dass der Lexer wissen muss, was `Form1` ist.
|
||||
Der Fall `Wert!Text` — SINGLE-Variable direkt gefolgt von einem
|
||||
Bezeichner — ist in keiner gültigen Syntax des Dialekts erreichbar.
|
||||
|
||||
### D4 — Modales SHOW ist ein Frame-Zustand, kein zweiter Strang
|
||||
|
||||
`SHOW 1` markiert den aufrufenden Frame als „wartet auf Formular X" und
|
||||
gibt an die Ereignisschleife zurück. Die Schleife stellt weiter zu; wird
|
||||
X entladen oder verborgen, läuft der markierte Frame an der Anweisung
|
||||
nach `SHOW` weiter.
|
||||
|
||||
```
|
||||
Form1.SHOW 1
|
||||
|
|
||||
+-- Frame markieren (wartet auf Form1) --> Ereignisschleife
|
||||
|
|
||||
Ereignisse zustellen <--+
|
||||
|
|
||||
UNLOAD Form1 ------------+
|
||||
|
|
||||
PRINT "danach" <-- Marke faellt, Frame laeuft weiter
|
||||
```
|
||||
|
||||
Eine geschachtelte Schleife innerhalb des `SHOW`-Aufrufs wäre die
|
||||
naheliegende Variante, macht aber die Stapeltiefe von der Zahl
|
||||
verschachtelter modaler Formulare abhängig und verträgt sich nicht mit
|
||||
dem Einzelschritt des Debuggers in Phase 5.
|
||||
|
||||
### D5 — Ereignisprozeduren sind gewöhnliche Prozeduren mit fester Signatur
|
||||
|
||||
Der Dispatch ruft `Command1_Click` wie jeden anderen Prozeduraufruf, nur
|
||||
angestoßen von der Schleife statt von einer Anweisung. Damit gelten
|
||||
Frames, `ON ERROR`, `EXIT SUB` und der Debugger unverändert. Die
|
||||
Zuordnung Name → Objekt/Ereignis entsteht zur Übersetzungszeit; zur
|
||||
Laufzeit ist es ein Index.
|
||||
|
||||
### D6 — Das Inventar bekommt eine zweite Quelle
|
||||
|
||||
Die Themenliste der Original-Hilfe führt das Objektmodell nicht. Statt
|
||||
es unerfasst zu lassen — und damit die Abnahme der Phase 6 blind zu
|
||||
machen —, führt das Inventar Eigenschaften, Methoden und Ereignisse je
|
||||
Klasse mit der Forms-Referenz als Quelle. Der bestehende Abgleichstest
|
||||
prüft sie mit denselben Regeln.
|
||||
|
||||
Größenordnung: 17 Klassen, in Summe einige hundert Einträge. Das ist der
|
||||
Preis dafür, dass „Vollständigkeit ist das Soll" für Forms überhaupt
|
||||
messbar wird.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Namenskollision Objekt gegen Variable** → Objekte gewinnen; ein
|
||||
Programm mit einer Variablen `Text1` und einem Steuerelement `Text1`
|
||||
bekommt eine Diagnose statt einer stillen Umdeutung.
|
||||
- **Der Inventarzuwachs erschlägt die Tabelle** → eigene Gruppe je
|
||||
Klasse, damit die bestehenden Gruppen lesbar bleiben; der Abgleichstest
|
||||
nennt weiterhin einzelne Einträge.
|
||||
- **Modale Marke und Debugger** → der Fortsetzungspunkt ist derselbe
|
||||
Mechanismus wie `STOP`/`CONT`; er wird zusammen mit Phase 5 geprüft.
|
||||
- **`.FRM` fehlt noch** → bis dahin entstehen Formulare im Test aus einer
|
||||
im Code aufgebauten Beschreibung; das Objektmodell hängt nicht am
|
||||
Dateiformat.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Z-Reihenfolge bei überlappenden Steuerelementen (offene Detailfrage der
|
||||
Forms-Referenz) — betrifft die Darstellung, nicht das Objektmodell.
|
||||
- Ob `ControlPanel` alle 18 Systemfarbwerte des Vorbilds abbildet oder
|
||||
eine Teilmenge, entscheidet sich mit der Darstellung im Folge-Change.
|
||||
@@ -0,0 +1,644 @@
|
||||
# Klassen und Eigenschaften — Belegstand
|
||||
|
||||
Diese Matrix ist die maschinennahe Auswertung der bestehenden
|
||||
[Forms-Referenz](../../../docs/forms-referenz.md). Deren Primärquellen sind die
|
||||
dekodierte Originalhilfe, `CONSTANT.BI`, die Stringtabellen der
|
||||
Originalprogramme und zeitgenössische Beispielprojekte. Die Quelle steht in
|
||||
jeder Tabellenzeile; „Forms-Referenz: Klasse-Art“ meint den gleichnamigen,
|
||||
quellengeprüften Klassenabschnitt, nicht eine neue Behauptung dieses Changes.
|
||||
|
||||
Als unabhängige Gegenprobe wurde `cout/vbdos` in Commit
|
||||
`1cdd2b32b829fe1721d0b6aecc433abc47a96fb6` ausgewertet. Die binären
|
||||
Formulare belegen unter anderem `Form_Load`, `Form_Unload`,
|
||||
`Form_Resize`, `*_Click`, `Caption`, `Text`, `Visible`,
|
||||
`Enabled`, `SelText` und `MousePointer` in
|
||||
`microsoft/check.frm`, `notepad.frm`, `qlbview.frm`, `seek.frm`,
|
||||
`spindemo.frm`, `graphics/graphics.frm` und
|
||||
`misc/mentors/mentors.frm`. Die SHA-256-Werte sind der Reihe nach
|
||||
`ed552601…52c9`, `e11b76f6…3c97`, `358e7bab…b97`,
|
||||
`2c7bf1df…b68`, `259e67a3…c738`, `58901348…ecb0f` und
|
||||
`e3ac6a4e…87fb`. Die Beispiele erweitern die Originalhilfe nicht; sie
|
||||
bestätigen nur tatsächlich verwendete Namen und Signaturen.
|
||||
|
||||
„INTEGER (Boolean)“ wird in BASIC als 0/−1 gesehen. Ein fehlender
|
||||
Wertebereich bedeutet, dass der Typ selbst begrenzt. „leer/Objektzustand“
|
||||
ist kein frei schreibbarer Zahlenwert. Die Tabelle enthält bewusst auch
|
||||
Elemente des Folge-Changes: hier werden ihre Namen und Verträge festgelegt,
|
||||
nicht Darstellung oder Eingabeverhalten.
|
||||
|
||||
## Ereignissignaturen und Steuerelement-Arrays
|
||||
|
||||
| Ereignisfamilie | Forms-Referenz | Compiler-Signatur |
|
||||
|---|---|---|
|
||||
| `KeyDown`, `KeyUp` | `(KeyCode AS INTEGER, Shift AS INTEGER)` | identisch |
|
||||
| `KeyPress` | `(KeyAscii AS INTEGER)` | identisch |
|
||||
| `MouseDown`, `MouseMove`, `MouseUp` | `(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)` | identisch |
|
||||
| `DragDrop` | `(Source AS CONTROL, X AS SINGLE, Y AS SINGLE)` | identisch |
|
||||
| `DragOver` | `(Source AS CONTROL, X AS SINGLE, Y AS SINGLE, State AS INTEGER)` | identisch |
|
||||
| `Unload` | `(Cancel AS INTEGER)` | identisch; ungleich 0 verhindert Entladen |
|
||||
| alle übrigen geführten Ereignisse | keine Parameter | identisch |
|
||||
|
||||
Bei einem Steuerelement-Array steht gemäß Forms-Referenz
|
||||
`Index AS INTEGER` immer als erster Parameter vor der jeweiligen
|
||||
Signatur. `LOAD name(index)` klont Element 0 als Vorlage;
|
||||
`UNLOAD name(index)` darf ein Entwurfszeit-Element nicht entfernen
|
||||
(Fehler 362). `KeyAscii` ist die dokumentierte UTF-8-Abweichung als
|
||||
Unicode-Codepoint; Shift-Bits (1/2/4), Maustasten (1/2/4) und
|
||||
DragOver-State (0/1/2) bleiben kompatibel.
|
||||
|
||||
|
||||
## FORM
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `AUTOREDRAW` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `BORDERSTYLE` | Eigenschaft / INTEGER | 0…6 | 2 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `CONTROLBOX` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `CURRENTX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `CURRENTY` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `FORMNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `FORMTYPE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `MAXBUTTON` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `MINBUTTON` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `SCALEHEIGHT` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `SCALEWIDTH` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `WINDOWSTATE` | Eigenschaft / INTEGER | 0…2 | 0 | lesen/schreiben | Forms-Referenz: FORM-Eigenschaften |
|
||||
| `CLS` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `HIDE` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `LOAD` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `PRINT` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `PRINTFORM` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `SHOW` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `TEXTHEIGHT` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `TEXTWIDTH` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `UNLOAD` | Methode | — | — | Aufruf | Forms-Referenz: FORM-Methoden |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `LOAD` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `MOUSEDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `MOUSEMOVE` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `MOUSEUP` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `PAINT` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `RESIZE` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
| `UNLOAD` | Ereignis | — | — | Dispatch | Forms-Referenz: FORM-Ereignisse |
|
||||
|
||||
## CHECKBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `VALUE` | Eigenschaft / INTEGER | 0…2 | 0 | lesen/schreiben | Forms-Referenz: CHECKBOX-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: CHECKBOX-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: CHECKBOX-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: CHECKBOX-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: CHECKBOX-Methoden |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: CHECKBOX-Ereignisse |
|
||||
|
||||
## COMBOBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `LIST` | Eigenschaft / STRING | — | leer/Objektzustand | read-only | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `LISTCOUNT` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `LISTINDEX` | Eigenschaft / INTEGER | — | -1 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `SORTED` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `SELLENGTH` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `SELSTART` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `SELTEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `STYLE` | Eigenschaft / INTEGER | 0…2 | 0 | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `TEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMBOBOX-Eigenschaften |
|
||||
| `ADDITEM` | Methode | — | — | Aufruf | Forms-Referenz: COMBOBOX-Methoden |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: COMBOBOX-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: COMBOBOX-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: COMBOBOX-Methoden |
|
||||
| `REMOVEITEM` | Methode | — | — | Aufruf | Forms-Referenz: COMBOBOX-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: COMBOBOX-Methoden |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `DROPDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `MOUSEDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `MOUSEMOVE` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
| `MOUSEUP` | Ereignis | — | — | Dispatch | Forms-Referenz: COMBOBOX-Ereignisse |
|
||||
|
||||
## COMMANDBUTTON
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `CANCEL` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `DEFAULT` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `VALUE` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: COMMANDBUTTON-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: COMMANDBUTTON-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: COMMANDBUTTON-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: COMMANDBUTTON-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: COMMANDBUTTON-Methoden |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: COMMANDBUTTON-Ereignisse |
|
||||
|
||||
## DIRLISTBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `PATH` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `TEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DIRLISTBOX-Eigenschaften |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: DIRLISTBOX-Ereignisse |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: DIRLISTBOX-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: DIRLISTBOX-Ereignisse |
|
||||
| `PATHCHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: DIRLISTBOX-Ereignisse |
|
||||
|
||||
## DRIVELISTBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `DRIVE` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `TEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: DRIVELISTBOX-Eigenschaften |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: DRIVELISTBOX-Ereignisse |
|
||||
|
||||
## FILELISTBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `FILENAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `PATH` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `PATTERN` | Eigenschaft / STRING | — | `*.*` | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `ARCHIVE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `HIDDEN` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `NORMAL` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `READONLY` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `SYSTEM` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `TEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FILELISTBOX-Eigenschaften |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: FILELISTBOX-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: FILELISTBOX-Ereignisse |
|
||||
| `PATTERNCHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: FILELISTBOX-Ereignisse |
|
||||
| `PATHCHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: FILELISTBOX-Ereignisse |
|
||||
|
||||
## FRAME
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: FRAME-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: FRAME-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: FRAME-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: FRAME-Methoden |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: FRAME-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: FRAME-Ereignisse |
|
||||
|
||||
## HSCROLLBAR
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `ATTACHED` | Eigenschaft / INTEGER (Boolean) | — | leer/Objektzustand | read-only | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `LARGECHANGE` | Eigenschaft / INTEGER | 1…32767 | 1 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `MIN` | Eigenschaft / INTEGER | -32768…32767 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `MAX` | Eigenschaft / INTEGER | -32768…32767 | 32767 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `SMALLCHANGE` | Eigenschaft / INTEGER | 1…32767 | 1 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `VALUE` | Eigenschaft / INTEGER | -32768…32767 | 0 | lesen/schreiben | Forms-Referenz: HSCROLLBAR-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: HSCROLLBAR-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: HSCROLLBAR-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: HSCROLLBAR-Methoden |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: HSCROLLBAR-Ereignisse |
|
||||
|
||||
## LABEL
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `ALIGNMENT` | Eigenschaft / INTEGER | 0…2 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `AUTOSIZE` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `BORDERSTYLE` | Eigenschaft / INTEGER | 0…2 | 0 | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: LABEL-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: LABEL-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: LABEL-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: LABEL-Methoden |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `MOUSEDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `MOUSEMOVE` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
| `MOUSEUP` | Ereignis | — | — | Dispatch | Forms-Referenz: LABEL-Ereignisse |
|
||||
|
||||
## LISTBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `LIST` | Eigenschaft / STRING | — | leer/Objektzustand | read-only | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `LISTCOUNT` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `LISTINDEX` | Eigenschaft / INTEGER | — | -1 | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `SORTED` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `TEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: LISTBOX-Eigenschaften |
|
||||
| `ADDITEM` | Methode | — | — | Aufruf | Forms-Referenz: LISTBOX-Methoden |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: LISTBOX-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: LISTBOX-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: LISTBOX-Methoden |
|
||||
| `REMOVEITEM` | Methode | — | — | Aufruf | Forms-Referenz: LISTBOX-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: LISTBOX-Methoden |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `MOUSEDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `MOUSEMOVE` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
| `MOUSEUP` | Ereignis | — | — | Dispatch | Forms-Referenz: LISTBOX-Ereignisse |
|
||||
|
||||
## MENU
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `CHECKED` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `SEPARATOR` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: MENU-Eigenschaften |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: MENU-Ereignisse |
|
||||
|
||||
## OPTIONBUTTON
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `CAPTION` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `VALUE` | Eigenschaft / INTEGER | -1…0 | 0 | lesen/schreiben | Forms-Referenz: OPTIONBUTTON-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: OPTIONBUTTON-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: OPTIONBUTTON-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: OPTIONBUTTON-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: OPTIONBUTTON-Methoden |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: OPTIONBUTTON-Ereignisse |
|
||||
|
||||
## PICTUREBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `AUTOREDRAW` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `BORDERSTYLE` | Eigenschaft / INTEGER | 0…2 | 1 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `CURRENTX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `CURRENTY` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `SCALEHEIGHT` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `SCALEWIDTH` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: PICTUREBOX-Eigenschaften |
|
||||
| `CLS` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `PRINT` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `TEXTHEIGHT` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `TEXTWIDTH` | Methode | — | — | Aufruf | Forms-Referenz: PICTUREBOX-Methoden |
|
||||
| `CLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `DBLCLICK` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `MOUSEDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `MOUSEMOVE` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `MOUSEUP` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
| `PAINT` | Ereignis | — | — | Dispatch | Forms-Referenz: PICTUREBOX-Ereignisse |
|
||||
|
||||
## TEXTBOX
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `BORDERSTYLE` | Eigenschaft / INTEGER | 0…1 | 1 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `MULTILINE` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `SCROLLBARS` | Eigenschaft / INTEGER | 0…3 | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `SELLENGTH` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `SELSTART` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `SELTEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `TEXT` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: TEXTBOX-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: TEXTBOX-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: TEXTBOX-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: TEXTBOX-Methoden |
|
||||
| `SETFOCUS` | Methode | — | — | Aufruf | Forms-Referenz: TEXTBOX-Methoden |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: TEXTBOX-Ereignisse |
|
||||
|
||||
## TIMER
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: TIMER-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: TIMER-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: TIMER-Eigenschaften |
|
||||
| `INTERVAL` | Eigenschaft / INTEGER | 0…65535 | 0 | lesen/schreiben | Forms-Referenz: TIMER-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: TIMER-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: TIMER-Eigenschaften |
|
||||
| `TIMER` | Ereignis | — | — | Dispatch | Forms-Referenz: TIMER-Ereignisse |
|
||||
|
||||
## VSCROLLBAR
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `BACKCOLOR` | Eigenschaft / INTEGER | 0…15 | 7 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `ENABLED` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `LEFT` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `PARENT` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `TAG` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `TOP` | Eigenschaft / INTEGER | 0…254 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `VISIBLE` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | 1…254 | 1 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `INDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `TABINDEX` | Eigenschaft / INTEGER | — | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `TABSTOP` | Eigenschaft / INTEGER (Boolean) | — | True | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `CTLNAME` | Eigenschaft / STRING | — | `` | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `FORECOLOR` | Eigenschaft / INTEGER | 0…15 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `DRAGMODE` | Eigenschaft / INTEGER | 0…1 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `ATTACHED` | Eigenschaft / INTEGER (Boolean) | — | leer/Objektzustand | read-only | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `LARGECHANGE` | Eigenschaft / INTEGER | 1…32767 | 1 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `MIN` | Eigenschaft / INTEGER | -32768…32767 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `MAX` | Eigenschaft / INTEGER | -32768…32767 | 32767 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `SMALLCHANGE` | Eigenschaft / INTEGER | 1…32767 | 1 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `VALUE` | Eigenschaft / INTEGER | -32768…32767 | 0 | lesen/schreiben | Forms-Referenz: VSCROLLBAR-Eigenschaften |
|
||||
| `DRAG` | Methode | — | — | Aufruf | Forms-Referenz: VSCROLLBAR-Methoden |
|
||||
| `MOVE` | Methode | — | — | Aufruf | Forms-Referenz: VSCROLLBAR-Methoden |
|
||||
| `REFRESH` | Methode | — | — | Aufruf | Forms-Referenz: VSCROLLBAR-Methoden |
|
||||
| `CHANGE` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `DRAGDROP` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `DRAGOVER` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `GOTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `KEYDOWN` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `KEYPRESS` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `KEYUP` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
| `LOSTFOCUS` | Ereignis | — | — | Dispatch | Forms-Referenz: VSCROLLBAR-Ereignisse |
|
||||
|
||||
## SCREEN
|
||||
|
||||
| Element | Art/Typ | Bereich | Vorgabe | Laufzeit | Quelle |
|
||||
|---|---|---|---|---|---|
|
||||
| `ACTIVECONTROL` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `ACTIVEFORM` | Eigenschaft / CONTROL/FORM | — | leer/Objektzustand | read-only | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `AUTOREDRAW` | Eigenschaft / INTEGER (Boolean) | — | False | lesen/schreiben | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `CONTROLPANEL` | Eigenschaft / INTEGER-Array | 0…15 | 0 | lesen/schreiben | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `HEIGHT` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `WIDTH` | Eigenschaft / INTEGER | — | leer/Objektzustand | read-only | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `MOUSEPOINTER` | Eigenschaft / INTEGER | 0…12 | 0 | lesen/schreiben | Forms-Referenz: SCREEN-Eigenschaften |
|
||||
| `HIDE` | Methode | — | — | Aufruf | Forms-Referenz: SCREEN-Methoden |
|
||||
| `SHOW` | Methode | — | — | Aufruf | Forms-Referenz: SCREEN-Methoden |
|
||||
@@ -0,0 +1,106 @@
|
||||
# Phase 4 — Formularmodell und Objektsprache
|
||||
|
||||
## Why
|
||||
|
||||
Nach der Ereignisschleife (`phase-4-ereignisschleife`) fehlt der Sprache
|
||||
alles, worauf ein Formularprogramm zugreift: Formulare als Objekte,
|
||||
Steuerelemente als benannte Eigenschaftsträger, Ereignisprozeduren
|
||||
`Name_Ereignis` und die Sprachmittel aus Sprachreferenz §12
|
||||
(`FORM`/`CONTROL`, `IF TYPEOF`, `$FORM`, `form!steuerelement`,
|
||||
`SCREEN`-Objekt).
|
||||
|
||||
Heute kompiliert ein Formularprogramm nicht etwa mit einer Ablehnung,
|
||||
sondern **falsch**. Belegt an einem Dreizeiler:
|
||||
|
||||
```
|
||||
Text1.Text = "hallo" -> Type mismatch (implizite SINGLE-Variable "Text1.Text")
|
||||
Form1.Show 1 -> Subprogram not defined
|
||||
UNLOAD Form1 -> Subprogram not defined
|
||||
```
|
||||
|
||||
Der Lexer faltet den Punkt in den Bezeichner, die Semantik löst Punktpfade
|
||||
nur auf, wenn die Basis ein benutzerdefinierter Typ ist — sonst entsteht
|
||||
stillschweigend eine Variable namens `Text1.Text`. Das ist genau der Fall,
|
||||
den der Guiding Principle als Bug definiert: ein dokumentiertes Element
|
||||
wird ohne Namensnennung abgewiesen. Zusätzlich kollidiert der
|
||||
Container-Operator `form!steuerelement` mit dem Typsuffix `!` für SINGLE.
|
||||
|
||||
Dazu kommt eine Lücke im Messinstrument: das Inventar führt zum
|
||||
Forms-Objektmodell **null** Einträge — weder `SHOW` noch `TYPEOF`, keine
|
||||
Eigenschaft, keine Steuerelementklasse. Es ist Abnahmekriterium für
|
||||
Phase 6 und läuft für den größten Teil der Phase 4 ins Leere.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Objektsprache im Frontend**: Punktpfade auf Formulare und
|
||||
Steuerelemente (`Form1.Caption`, `Text1.Text`), der Container-Operator
|
||||
`form!steuerelement.eigenschaft` mit einer Auflösung der Kollision zum
|
||||
SINGLE-Suffix, die Datentypen `FORM` und `CONTROL` als Parametertypen,
|
||||
`IF TYPEOF ctl IS klasse`, der Metabefehl `$FORM` und die
|
||||
Ereignisprozeduren `SUB Name_Ereignis (…)` mit den Signaturen der
|
||||
Forms-Referenz.
|
||||
- **Namentliche Abweisung statt Falschübersetzung** (**BREAKING** für
|
||||
bestehende Diagnosen): Ein Punktpfad auf ein unbekanntes Objekt oder
|
||||
eine unbekannte Eigenschaft MUST das Objekt und die Eigenschaft
|
||||
namentlich nennen; eine implizite Variable mit Punkt im Namen MUST NOT
|
||||
mehr entstehen.
|
||||
- **Formularmodell zur Laufzeit**: Formulare mit Eigenschaften,
|
||||
Steuerelementbaum, Steuerelement-Arrays (`ctl(index%)`),
|
||||
Lade-/Entladezyklus (`LOAD`/`UNLOAD` mit `Form_Load`/`Form_Unload` und
|
||||
wirksamem `Cancel`), `SHOW [style%]` modeless und modal, `HIDE`,
|
||||
Sichtbarkeit und Z-Reihenfolge.
|
||||
- **Modalität**: `form.SHOW 1` gibt die Kontrolle erst zurück, wenn das
|
||||
Formular entladen oder verborgen wird; die Ereignisschleife läuft
|
||||
währenddessen weiter. Die Stapelregeln des Vorbilds lösen die Fehler
|
||||
400–403 aus.
|
||||
- **Ereignisdispatch**: Ein Ereignis der Warteschlange findet die
|
||||
Prozedur `Name_Ereignis` des betroffenen Objekts und ruft sie mit den
|
||||
Argumenten ihrer Signatur auf; fehlt die Prozedur, verfällt das
|
||||
Ereignis wirkungslos. Die Zustellpunkte sind die der
|
||||
Ereignissteuerung, nicht neue.
|
||||
- **`SCREEN`-Objekt**: `ActiveControl`, `ActiveForm`, `Height`, `Width`
|
||||
(aktuelle Terminalgröße, Abweichung vom Vorbild), `MousePointer`,
|
||||
`ControlPanel`, `SHOW`/`HIDE`.
|
||||
- **Inventar deckt das Objektmodell** (**BREAKING** für das
|
||||
Inventarformat): Eigenschaften, Methoden und Ereignisse je Klasse
|
||||
werden mit eigener Quellenangabe (Forms-Referenz statt Themenliste der
|
||||
Hilfe) als Einträge geführt, damit die Leitplanke „Vollständigkeit ist
|
||||
das Soll" auch für Forms ein Maß hat.
|
||||
|
||||
**Non-Goals:** Darstellung, Fokus, Tastatur- und Maussteuerung der
|
||||
einzelnen Steuerelemente sowie Menüs und Dialoge (Change
|
||||
`phase-4-steuerelemente`); das `.FRM`-Dateiformat (Change
|
||||
`phase-4-frm`); native Erweiterungssteuerelemente (Stufe 2).
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `forms-objektmodell`: Formulare und Steuerelemente als benannte
|
||||
Objekte mit Eigenschaften, Lade-/Entladezyklus, Sichtbarkeit und
|
||||
Modalität, Steuerelement-Arrays, Ereignisprozeduren und ihr Dispatch,
|
||||
`SCREEN`-Objekt.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `sprach-frontend`: Grammatik und Semantik SHALL Punktpfade auf Objekte,
|
||||
den Container-Operator, `FORM`/`CONTROL`, `TYPEOF`, `$FORM` und
|
||||
Ereignisprozeduren umfassen; ein Punktpfad MUST NOT mehr zu einer
|
||||
impliziten Variablen mit Punkt im Namen werden.
|
||||
- `sprachinventar`: Das Inventar SHALL neben den Elementen der
|
||||
Original-Hilfe auch das Forms-Objektmodell führen.
|
||||
|
||||
## Impact
|
||||
|
||||
- `crates/tb-frontend`: Lexer (Punkt und `!` im Objektkontext), Parser
|
||||
(`TYPEOF`, `$FORM`, Ereignisprozedur-Signaturen), Semantik
|
||||
(Objektauflösung, Eigenschaftstypen, Ereignisprozedur-Erkennung).
|
||||
- `crates/tb-ui`: Formularmodell, Steuerelementbaum, Lade-/Entladezyklus,
|
||||
Dispatch — der Platzhalter `forms.rs` wird ausgefüllt.
|
||||
- `crates/tb-vm`: Aufruf von Ereignisprozeduren über den Rückrufweg aus
|
||||
`phase-4-ereignisschleife`; Fortsetzungspunkt für modales `SHOW`.
|
||||
- `crates/tb-runtime`: Fehler 362 und 400–403 als Auslöser.
|
||||
- `docs/`: `inventar.md` um das Objektmodell erweitert;
|
||||
`forms-referenz.md` um die belegten Detailfragen ergänzt;
|
||||
`sprachreferenz.md` §12 ausformuliert.
|
||||
- PLAN.md: Punkte „Formular-Modell" und „Ereignisdispatch" der Phase 4.
|
||||
@@ -0,0 +1,136 @@
|
||||
## Purpose
|
||||
|
||||
Das Forms-Objektmodell macht Formulare und Steuerelemente zu benannten
|
||||
Objekten des laufenden Programms: Eigenschaften lesen und schreiben,
|
||||
Formulare laden, zeigen, verbergen und entladen — modal wie nicht-modal —,
|
||||
Steuerelement-Arrays führen und Ereignisse an die Prozeduren
|
||||
`Name_Ereignis` zustellen. Es ist die Grundlage der Steuerelemente und der
|
||||
vordefinierten Dialoge.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Formulare und Steuerelemente als benannte Objekte
|
||||
Ein Formular SHALL unter seinem Namen ansprechbar sein, ein
|
||||
Steuerelement unter seinem Namen innerhalb seines Formulars. Eigenschaften
|
||||
SHALL über `objekt.eigenschaft` gelesen und geschrieben werden, mit dem
|
||||
in der Forms-Referenz festgelegten Typ und Wertebereich. Es SHALL keine
|
||||
Standardeigenschaft geben: `Text1 = "x"` MUST abgewiesen werden. Das
|
||||
Schreiben eines Wertes außerhalb des Wertebereichs MUST Laufzeitfehler 5
|
||||
auslösen; eine zur Laufzeit schreibgeschützte Eigenschaft MUST bei
|
||||
Zuweisung einen Fehler auslösen.
|
||||
|
||||
#### Scenario: Eigenschaft lesen und schreiben
|
||||
- **WHEN** `Text1.Text = "hallo"` ausgeführt und danach `Text1.Text` gelesen wird
|
||||
- **THEN** liefert der Zugriff `hallo`
|
||||
|
||||
#### Scenario: Keine Standardeigenschaft
|
||||
- **WHEN** ein Programm `Text1 = "x"` enthält
|
||||
- **THEN** wird es abgewiesen und die Meldung nennt `Text1`
|
||||
|
||||
#### Scenario: Wert außerhalb des Bereichs
|
||||
- **WHEN** `Check1.Value = 3` ausgeführt wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: Container-Zugriff über den Bang-Operator
|
||||
`form!steuerelement.eigenschaft` SHALL ein Steuerelement eines benannten
|
||||
Formulars ansprechen. Die Schreibweise MUST NOT mit dem Typsuffix `!`
|
||||
für SINGLE verwechselt werden; eine Variable `Form1!` und ein Zugriff
|
||||
`Form1!Text1` MUST unterscheidbar bleiben.
|
||||
|
||||
#### Scenario: Zugriff über den Container
|
||||
- **WHEN** `Form1!Text1.Text = "a"` ausgeführt wird
|
||||
- **THEN** trägt die Eigenschaft `Text` des Steuerelements `Text1` in `Form1` den Wert `a`
|
||||
|
||||
#### Scenario: Typsuffix bleibt Typsuffix
|
||||
- **WHEN** ein Programm die Variable `Wert!` verwendet
|
||||
- **THEN** bleibt sie eine SINGLE-Variable und wird nicht als Containerzugriff gelesen
|
||||
|
||||
### Requirement: Lade- und Entladezyklus
|
||||
`LOAD form` SHALL das Formular anlegen und `Form_Load` ausführen, bevor
|
||||
es sichtbar wird. `UNLOAD form` SHALL `Form_Unload (Cancel)` ausführen;
|
||||
ein von der Prozedur gesetzter Wert `Cancel <> 0` MUST das Entladen
|
||||
verhindern. `HIDE` SHALL nur die Sichtbarkeit zurücknehmen und das
|
||||
Formular geladen lassen. Der Zugriff auf ein nicht geladenes Formular
|
||||
SHALL es implizit laden.
|
||||
|
||||
#### Scenario: Load läuft vor der Anzeige
|
||||
- **WHEN** ein Formular gezeigt wird, dessen `Form_Load` eine Eigenschaft setzt
|
||||
- **THEN** ist der gesetzte Wert bereits in der ersten Anzeige sichtbar
|
||||
|
||||
#### Scenario: Entladen abbrechen
|
||||
- **WHEN** `Form_Unload` den Parameter `Cancel` auf 1 setzt und `UNLOAD Form1` ausgeführt wird
|
||||
- **THEN** bleibt das Formular geladen und sichtbar
|
||||
|
||||
#### Scenario: HIDE entlädt nicht
|
||||
- **WHEN** `Form1.HIDE` ausgeführt und danach eine Eigenschaft von `Form1` gelesen wird
|
||||
- **THEN** ist der zuvor gesetzte Wert erhalten und `Form_Load` läuft nicht erneut
|
||||
|
||||
### Requirement: Modales und nicht-modales Anzeigen
|
||||
`form.SHOW [style%]` SHALL das Formular anzeigen; `style%` 0 (Vorgabe)
|
||||
zeigt es nicht-modal, 1 modal. Bei modaler Anzeige MUST die Anweisung
|
||||
nach `SHOW` erst ausgeführt werden, wenn das Formular entladen oder
|
||||
verborgen wurde; die Ereignisschleife MUST währenddessen weiterlaufen.
|
||||
Verstöße gegen die Stapelregeln des Vorbilds MUST die Fehler 400–403
|
||||
auslösen.
|
||||
|
||||
#### Scenario: Modal hält den Ablauf an
|
||||
- **WHEN** ein Programm `Form1.SHOW 1 : PRINT "danach"` ausführt und ein Ereignis `Form1` entlädt
|
||||
- **THEN** erscheint `danach` erst nach dem Entladen
|
||||
|
||||
#### Scenario: Ereignisse laufen während der Modalität
|
||||
- **WHEN** ein modales Formular angezeigt wird und ein Ereignis eintrifft
|
||||
- **THEN** läuft die zugehörige Ereignisprozedur
|
||||
|
||||
### Requirement: Steuerelement-Arrays
|
||||
Steuerelemente mit gleichem Namen und `Index` SHALL als Array
|
||||
ansprechbar sein. `LOAD ctl(index%)` SHALL ein weiteres Element zur
|
||||
Laufzeit anlegen, `UNLOAD ctl(index%)` es entfernen. Der Versuch, ein zur
|
||||
Entwurfszeit angelegtes Element zu entladen, MUST Fehler 362 auslösen.
|
||||
Bei einem Array MUST `Index AS INTEGER` als erster Parameter der
|
||||
Ereignisprozedur übergeben werden.
|
||||
|
||||
#### Scenario: Element zur Laufzeit anlegen
|
||||
- **WHEN** `LOAD Command1(3)` ausgeführt wird
|
||||
- **THEN** existiert `Command1(3)` mit den Eigenschaften des Elements 0 als Vorlage
|
||||
|
||||
#### Scenario: Entwurfszeit-Element ist nicht entladbar
|
||||
- **WHEN** `UNLOAD Command1(0)` für ein zur Entwurfszeit angelegtes Element ausgeführt wird
|
||||
- **THEN** tritt Fehler 362 auf
|
||||
|
||||
#### Scenario: Index als erster Parameter
|
||||
- **WHEN** ein Klick auf `Command1(3)` zugestellt wird
|
||||
- **THEN** wird `Command1_Click` mit `Index = 3` aufgerufen
|
||||
|
||||
### Requirement: Ereignisdispatch an Name_Ereignis
|
||||
Ein Ereignis SHALL die Prozedur `Name_Ereignis` des betroffenen Objekts
|
||||
aufrufen und ihr die Argumente der in der Forms-Referenz festgelegten
|
||||
Signatur übergeben. Fehlt die Prozedur, MUST das Ereignis wirkungslos
|
||||
verfallen, ohne Fehler. Die Zustellung SHALL an den Zustellpunkten der
|
||||
Ereignissteuerung erfolgen; es MUST NOT neue Zustellpunkte geben.
|
||||
|
||||
#### Scenario: Ereignisprozedur wird gerufen
|
||||
- **WHEN** ein Klick auf `Command1` zugestellt wird und `SUB Command1_Click` existiert
|
||||
- **THEN** läuft die Prozedur genau einmal
|
||||
|
||||
#### Scenario: Fehlende Ereignisprozedur
|
||||
- **WHEN** ein Klick auf ein Steuerelement ohne passende Prozedur zugestellt wird
|
||||
- **THEN** läuft das Programm unverändert weiter und es entsteht kein Fehler
|
||||
|
||||
#### Scenario: Signatur mit Mausargumenten
|
||||
- **WHEN** `Form_MouseDown` definiert ist und ein Mausereignis in Zeile 5, Spalte 7 zugestellt wird
|
||||
- **THEN** erhält die Prozedur Taste, Umschaltzustand und die Position in Zellen
|
||||
|
||||
### Requirement: SCREEN-Objekt
|
||||
`SCREEN` SHALL als Objekt ansprechbar sein mit `ActiveControl`,
|
||||
`ActiveForm`, `Height`, `Width`, `MousePointer` und `ControlPanel` sowie
|
||||
den Methoden `SHOW` und `HIDE`. `Height` und `Width` SHALL die
|
||||
**aktuelle** Terminalgröße in Zeichen liefern und schreibgeschützt sein;
|
||||
die feste Größe des Vorbilds MUST als Abweichung dokumentiert sein.
|
||||
|
||||
#### Scenario: Größe folgt dem Terminal
|
||||
- **WHEN** die Darstellungsfläche 120×40 misst und `SCREEN.Width` gelesen wird
|
||||
- **THEN** liefert es 120
|
||||
|
||||
#### Scenario: Aktives Formular
|
||||
- **WHEN** zwei Formulare geladen sind und eines den Fokus hat
|
||||
- **THEN** liefert `SCREEN.ActiveForm` dieses Formular
|
||||
@@ -0,0 +1,53 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Objektzugriffe in Grammatik und Semantik
|
||||
Der Parser SHALL Punktpfade auf Formulare und Steuerelemente
|
||||
(`objekt.eigenschaft`), den Container-Operator
|
||||
`form!steuerelement[.eigenschaft]`, `IF TYPEOF ausdruck IS klasse`, den
|
||||
Metabefehl `$FORM` und die Datentypen `FORM` und `CONTROL` als
|
||||
Parametertypen erkennen. Die Semantik SHALL Eigenschaften gegen die
|
||||
Klasse des Objekts prüfen und ihren Typ bestimmen. Ein Punktpfad, dessen
|
||||
Basis weder ein benutzerdefinierter Typ noch ein bekanntes Objekt ist,
|
||||
MUST eine Diagnose erzeugen, die Basis und Eigenschaft namentlich nennt;
|
||||
er MUST NOT zu einer impliziten Variablen mit Punkt im Namen werden.
|
||||
|
||||
#### Scenario: Unbekanntes Objekt wird benannt
|
||||
- **WHEN** ein Modul `Text9.Text = "a"` ohne Steuerelement `Text9` enthält
|
||||
- **THEN** nennt die Diagnose `Text9` als unbekanntes Objekt statt „Type mismatch" zu melden
|
||||
|
||||
#### Scenario: Unbekannte Eigenschaft wird benannt
|
||||
- **WHEN** ein Modul `Text1.Farbe = 3` enthält
|
||||
- **THEN** nennt die Diagnose `Farbe` als unbekannte Eigenschaft von `TextBox`
|
||||
|
||||
#### Scenario: Eigenschaftstyp wird geprüft
|
||||
- **WHEN** ein Modul `Text1.Text = 5` enthält
|
||||
- **THEN** meldet die Semantik einen Typkonflikt mit Nennung der Eigenschaft
|
||||
|
||||
#### Scenario: TYPEOF
|
||||
- **WHEN** ein Modul `IF TYPEOF Ziel IS CommandButton THEN` enthält
|
||||
- **THEN** wird es übersetzt und die Klasse gegen die bekannten Steuerelementklassen geprüft
|
||||
|
||||
### Requirement: Bang-Operator neben dem SINGLE-Suffix
|
||||
Der Lexer SHALL `!` als Container-Operator erkennen, wenn ihm ein
|
||||
Bezeichner folgt, und andernfalls als Typsuffix für SINGLE. Beide Formen
|
||||
MUST im selben Modul nebeneinander verwendbar sein.
|
||||
|
||||
#### Scenario: Beide Bedeutungen im selben Modul
|
||||
- **WHEN** ein Modul `Wert! = 1.5` und `Form1!Text1.Text = "a"` enthält
|
||||
- **THEN** wird die erste Zeile als SINGLE-Variable und die zweite als Containerzugriff übersetzt
|
||||
|
||||
### Requirement: Ereignisprozeduren mit festen Signaturen
|
||||
Die Semantik SHALL eine `SUB`, deren Name der Form `Objekt_Ereignis`
|
||||
entspricht und deren Objekt bekannt ist, als Ereignisprozedur erkennen
|
||||
und ihre Parameterliste gegen die Signatur der Forms-Referenz prüfen.
|
||||
Eine abweichende Parameterliste MUST namentlich gemeldet werden. Eine
|
||||
`SUB` mit Unterstrich im Namen ohne bekanntes Objekt MUST weiterhin eine
|
||||
gewöhnliche Prozedur sein.
|
||||
|
||||
#### Scenario: Falsche Signatur
|
||||
- **WHEN** ein Modul `SUB Form_MouseDown (X AS SINGLE)` enthält
|
||||
- **THEN** nennt die Diagnose die erwartete Parameterliste der Ereignisprozedur
|
||||
|
||||
#### Scenario: Gewöhnliche SUB mit Unterstrich
|
||||
- **WHEN** ein Modul `SUB Zins_Berechnen (n AS INTEGER)` ohne Objekt `Zins` enthält
|
||||
- **THEN** wird sie als gewöhnliche Prozedur übersetzt
|
||||
@@ -0,0 +1,29 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Vollständiges Inventar der Original-Hilfe
|
||||
`docs/inventar.md` SHALL jede in der Original-Hilfe des Vorbilds
|
||||
dokumentierte Anweisung, Funktion, Metabefehl, Operator-Schlüsselwort und
|
||||
Klausel als eigenen Eintrag führen. Darüber hinaus SHALL es das
|
||||
**Forms-Objektmodell** führen: je Klasse ihre Eigenschaften, Methoden und
|
||||
Ereignisse als eigene Einträge. Ein dokumentiertes Element MUST NOT
|
||||
fehlen; Vollständigkeit gegenüber der Quelle geht der Kürze vor. Jeder
|
||||
Eintrag SHALL Name, Art (Anweisung/Funktion/Metabefehl/Eigenschaft/
|
||||
Methode/Ereignis), Themengruppe, Status, Fundstelle und Quelle tragen.
|
||||
Für Einträge des Objektmodells SHALL die Quelle die Forms-Referenz sein,
|
||||
weil die Themenliste der Original-Hilfe sie nicht führt.
|
||||
|
||||
#### Scenario: Dokumentiertes Element ist erfasst
|
||||
- **WHEN** die Original-Hilfe die Anweisung `VIEW PRINT` dokumentiert
|
||||
- **THEN** enthält das Inventar einen Eintrag `VIEW PRINT` mit Art, Gruppe, Status und Fundstelle
|
||||
|
||||
#### Scenario: Non-Feature ist erfasst, nicht weggelassen
|
||||
- **WHEN** `PEEK` als Non-Feature deklariert ist
|
||||
- **THEN** steht `PEEK` mit Status `Non-Feature` im Inventar und die Fundstelle verweist auf den Abschnitt „Abweichungen" der Sprachreferenz
|
||||
|
||||
#### Scenario: Eigenschaft einer Steuerelementklasse ist erfasst
|
||||
- **WHEN** die Forms-Referenz für `TextBox` die Eigenschaft `SelStart` führt
|
||||
- **THEN** enthält das Inventar einen Eintrag für `TextBox.SelStart` mit Art `Eigenschaft`, Status und Quelle Forms-Referenz
|
||||
|
||||
#### Scenario: Ereignis einer Klasse ist erfasst
|
||||
- **WHEN** die Forms-Referenz für `Form` das Ereignis `Resize` führt
|
||||
- **THEN** enthält das Inventar einen Eintrag für `Form_Resize` mit Art `Ereignis`
|
||||
@@ -0,0 +1,42 @@
|
||||
## 1. Belege
|
||||
|
||||
- [x] 1.1 Eigenschaften, Methoden und Ereignisse je Klasse aus Original-Hilfe, `CONSTANT.BI` und den Beispielprojekten in `klassen-und-eigenschaften.md` dieses Changes zusammenstellen (Typ, Wertebereich, Vorgabewert, Schreibbarkeit zur Laufzeit); verifiziert durch die Datei mit Quellenangabe je Zeile
|
||||
- [x] 1.2 Signaturen der Ereignisprozeduren und die Regeln für Steuerelement-Arrays belegen; verifiziert durch einen Abschnitt in derselben Datei, der jede Signatur der Forms-Referenz gegenüberstellt
|
||||
|
||||
## 2. Frontend: Objektsprache
|
||||
|
||||
- [x] 2.1 `!` am folgenden Token entscheiden (D3); verifiziert durch Lexer-Tests für beide Bedeutungen im selben Modul
|
||||
- [x] 2.2 Punktpfade in der Semantik nach D2 auflösen, mit Diagnose statt impliziter Variablen; verifiziert durch Sema-Tests für unbekanntes Objekt, unbekannte Eigenschaft und unveränderten UDT-Feldzugriff
|
||||
- [x] 2.3 Eigenschaftstabelle je Klasse aus 1.1 im Frontend führen und Typ/Schreibbarkeit prüfen; verifiziert durch Sema-Tests für Typkonflikt und schreibgeschützte Eigenschaft
|
||||
- [x] 2.4 `IF TYPEOF … IS klasse`, Datentypen `FORM`/`CONTROL` als Parametertypen, Metabefehl `$FORM`; verifiziert durch Parser- und Sema-Tests je Konstrukt
|
||||
- [x] 2.5 Ereignisprozeduren erkennen und ihre Parameterlisten prüfen; verifiziert durch Sema-Tests für richtige Signatur, falsche Signatur und gewöhnliche `SUB` mit Unterstrich
|
||||
- [x] 2.6 Absenkung ins HIR: Objektzugriff als Objekt-/Eigenschaftsindex, Ereignisprozeduren mit ihrer Zuordnung; verifiziert durch `cargo test -p tb-frontend`
|
||||
|
||||
## 3. Laufzeit: Formularmodell
|
||||
|
||||
- [x] 3.1 Formular- und Steuerelementobjekte mit Eigenschaftsspeicher und Vorgabewerten aus 1.1; verifiziert durch Unit-Tests für Vorgabewerte und Wertebereichsprüfung (Fehler 5)
|
||||
- [x] 3.2 Lade-/Entladezyklus mit `Form_Load` und `Form_Unload (Cancel)`; verifiziert durch Unit-Tests, darunter das verhinderte Entladen
|
||||
- [x] 3.3 `SHOW`/`HIDE`, Sichtbarkeit, implizites Laden beim Zugriff; verifiziert durch Unit-Tests für „HIDE entlädt nicht" und implizites Laden
|
||||
- [x] 3.4 Steuerelement-Arrays: `LOAD ctl(i)`, `UNLOAD ctl(i)`, Fehler 362 für Entwurfszeit-Elemente; verifiziert durch Unit-Tests je Fall
|
||||
- [x] 3.5 `SCREEN`-Objekt mit `ActiveForm`, `ActiveControl`, `Height`/`Width` aus der aktuellen Terminalgröße; verifiziert durch Unit-Tests, darunter einer nach Größenänderung
|
||||
|
||||
## 4. Modalität und Dispatch
|
||||
|
||||
- [x] 4.1 Modales `SHOW` als Frame-Marke (D4) mit Fortsetzung nach dem Entladen; verifiziert durch einen VM-Test, der die Anweisung nach `SHOW 1` erst nach dem Entladen ausführt
|
||||
- [x] 4.2 Stapelregeln der Modalität mit den Fehlern 400–403; verifiziert durch Unit-Tests je Fehlercode
|
||||
- [x] 4.3 Ereignisdispatch: Ereignis der Warteschlange auf `Name_Ereignis` abbilden und mit Argumenten aufrufen (D5); verifiziert durch VM-Tests für vorhandene und fehlende Prozedur
|
||||
- [x] 4.4 Mausereignisse aus `phase-4-ereignisschleife` an die Formularereignisse übergeben (Taste, Umschaltzustand, Position in Zellen); verifiziert durch einen Test, der `Form_MouseDown` mit den erwarteten Argumenten sieht
|
||||
- [x] 4.5 Nachweis, dass keine neuen Zustellpunkte entstehen; verifiziert durch einen Test, der die Zustellpunkte der Ereignissteuerung gegenprüft
|
||||
|
||||
## 5. Inventar und Dokumentation
|
||||
|
||||
- [x] 5.1 `docs/inventar.md` um Art `Eigenschaft`/`Methode`/`Ereignis` und die Spalte Quelle erweitern, Einträge aus 1.1 eintragen (Status zunächst `offen`); verifiziert durch `inventar_stimmt_mit_code_ueberein`
|
||||
- [x] 5.2 Abgleichstest auf die neuen Arten ausdehnen; verifiziert durch einen Testfall, der eine falsch gesetzte Eigenschaft im Inventar erkennt
|
||||
- [x] 5.3 Einträge dieses Changes auf `implementiert` setzen; verifiziert durch denselben Test
|
||||
- [x] 5.4 `docs/sprachreferenz.md` §12 ausformulieren und `docs/forms-referenz.md` um die Belege aus 1.1 ergänzen; verifiziert durch die Abschnitte
|
||||
- [x] 5.5 PLAN.md: Punkte „Formular-Modell" und „Ereignisdispatch" abhaken; verifiziert durch die aktualisierte Liste
|
||||
|
||||
## 6. Abnahme
|
||||
|
||||
- [x] 6.1 Der Dreizeiler aus proposal.md wird namentlich abgewiesen statt falsch übersetzt; verifiziert durch einen Test, der die drei Diagnosen prüft
|
||||
- [x] 6.2 `cargo test` grün und `openspec validate phase-4-objektmodell --strict` ohne Befund; verifiziert durch beide Kommandos
|
||||
138
openspec/specs/forms-objektmodell/spec.md
Normal file
138
openspec/specs/forms-objektmodell/spec.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# forms-objektmodell Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Das Forms-Objektmodell macht Formulare und Steuerelemente zu benannten
|
||||
Objekten des laufenden Programms: Eigenschaften lesen und schreiben,
|
||||
Formulare laden, zeigen, verbergen und entladen — modal wie nicht-modal —,
|
||||
Steuerelement-Arrays führen und Ereignisse an die Prozeduren
|
||||
`Name_Ereignis` zustellen. Es ist die Grundlage der Steuerelemente und der
|
||||
vordefinierten Dialoge.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Formulare und Steuerelemente als benannte Objekte
|
||||
Ein Formular SHALL unter seinem Namen ansprechbar sein, ein
|
||||
Steuerelement unter seinem Namen innerhalb seines Formulars. Eigenschaften
|
||||
SHALL über `objekt.eigenschaft` gelesen und geschrieben werden, mit dem
|
||||
in der Forms-Referenz festgelegten Typ und Wertebereich. Es SHALL keine
|
||||
Standardeigenschaft geben: `Text1 = "x"` MUST abgewiesen werden. Das
|
||||
Schreiben eines Wertes außerhalb des Wertebereichs MUST Laufzeitfehler 5
|
||||
auslösen; eine zur Laufzeit schreibgeschützte Eigenschaft MUST bei
|
||||
Zuweisung einen Fehler auslösen.
|
||||
|
||||
#### Scenario: Eigenschaft lesen und schreiben
|
||||
- **WHEN** `Text1.Text = "hallo"` ausgeführt und danach `Text1.Text` gelesen wird
|
||||
- **THEN** liefert der Zugriff `hallo`
|
||||
|
||||
#### Scenario: Keine Standardeigenschaft
|
||||
- **WHEN** ein Programm `Text1 = "x"` enthält
|
||||
- **THEN** wird es abgewiesen und die Meldung nennt `Text1`
|
||||
|
||||
#### Scenario: Wert außerhalb des Bereichs
|
||||
- **WHEN** `Check1.Value = 3` ausgeführt wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: Container-Zugriff über den Bang-Operator
|
||||
`form!steuerelement.eigenschaft` SHALL ein Steuerelement eines benannten
|
||||
Formulars ansprechen. Die Schreibweise MUST NOT mit dem Typsuffix `!`
|
||||
für SINGLE verwechselt werden; eine Variable `Form1!` und ein Zugriff
|
||||
`Form1!Text1` MUST unterscheidbar bleiben.
|
||||
|
||||
#### Scenario: Zugriff über den Container
|
||||
- **WHEN** `Form1!Text1.Text = "a"` ausgeführt wird
|
||||
- **THEN** trägt die Eigenschaft `Text` des Steuerelements `Text1` in `Form1` den Wert `a`
|
||||
|
||||
#### Scenario: Typsuffix bleibt Typsuffix
|
||||
- **WHEN** ein Programm die Variable `Wert!` verwendet
|
||||
- **THEN** bleibt sie eine SINGLE-Variable und wird nicht als Containerzugriff gelesen
|
||||
|
||||
### Requirement: Lade- und Entladezyklus
|
||||
`LOAD form` SHALL das Formular anlegen und `Form_Load` ausführen, bevor
|
||||
es sichtbar wird. `UNLOAD form` SHALL `Form_Unload (Cancel)` ausführen;
|
||||
ein von der Prozedur gesetzter Wert `Cancel <> 0` MUST das Entladen
|
||||
verhindern. `HIDE` SHALL nur die Sichtbarkeit zurücknehmen und das
|
||||
Formular geladen lassen. Der Zugriff auf ein nicht geladenes Formular
|
||||
SHALL es implizit laden.
|
||||
|
||||
#### Scenario: Load läuft vor der Anzeige
|
||||
- **WHEN** ein Formular gezeigt wird, dessen `Form_Load` eine Eigenschaft setzt
|
||||
- **THEN** ist der gesetzte Wert bereits in der ersten Anzeige sichtbar
|
||||
|
||||
#### Scenario: Entladen abbrechen
|
||||
- **WHEN** `Form_Unload` den Parameter `Cancel` auf 1 setzt und `UNLOAD Form1` ausgeführt wird
|
||||
- **THEN** bleibt das Formular geladen und sichtbar
|
||||
|
||||
#### Scenario: HIDE entlädt nicht
|
||||
- **WHEN** `Form1.HIDE` ausgeführt und danach eine Eigenschaft von `Form1` gelesen wird
|
||||
- **THEN** ist der zuvor gesetzte Wert erhalten und `Form_Load` läuft nicht erneut
|
||||
|
||||
### Requirement: Modales und nicht-modales Anzeigen
|
||||
`form.SHOW [style%]` SHALL das Formular anzeigen; `style%` 0 (Vorgabe)
|
||||
zeigt es nicht-modal, 1 modal. Bei modaler Anzeige MUST die Anweisung
|
||||
nach `SHOW` erst ausgeführt werden, wenn das Formular entladen oder
|
||||
verborgen wurde; die Ereignisschleife MUST währenddessen weiterlaufen.
|
||||
Verstöße gegen die Stapelregeln des Vorbilds MUST die Fehler 400–403
|
||||
auslösen.
|
||||
|
||||
#### Scenario: Modal hält den Ablauf an
|
||||
- **WHEN** ein Programm `Form1.SHOW 1 : PRINT "danach"` ausführt und ein Ereignis `Form1` entlädt
|
||||
- **THEN** erscheint `danach` erst nach dem Entladen
|
||||
|
||||
#### Scenario: Ereignisse laufen während der Modalität
|
||||
- **WHEN** ein modales Formular angezeigt wird und ein Ereignis eintrifft
|
||||
- **THEN** läuft die zugehörige Ereignisprozedur
|
||||
|
||||
### Requirement: Steuerelement-Arrays
|
||||
Steuerelemente mit gleichem Namen und `Index` SHALL als Array
|
||||
ansprechbar sein. `LOAD ctl(index%)` SHALL ein weiteres Element zur
|
||||
Laufzeit anlegen, `UNLOAD ctl(index%)` es entfernen. Der Versuch, ein zur
|
||||
Entwurfszeit angelegtes Element zu entladen, MUST Fehler 362 auslösen.
|
||||
Bei einem Array MUST `Index AS INTEGER` als erster Parameter der
|
||||
Ereignisprozedur übergeben werden.
|
||||
|
||||
#### Scenario: Element zur Laufzeit anlegen
|
||||
- **WHEN** `LOAD Command1(3)` ausgeführt wird
|
||||
- **THEN** existiert `Command1(3)` mit den Eigenschaften des Elements 0 als Vorlage
|
||||
|
||||
#### Scenario: Entwurfszeit-Element ist nicht entladbar
|
||||
- **WHEN** `UNLOAD Command1(0)` für ein zur Entwurfszeit angelegtes Element ausgeführt wird
|
||||
- **THEN** tritt Fehler 362 auf
|
||||
|
||||
#### Scenario: Index als erster Parameter
|
||||
- **WHEN** ein Klick auf `Command1(3)` zugestellt wird
|
||||
- **THEN** wird `Command1_Click` mit `Index = 3` aufgerufen
|
||||
|
||||
### Requirement: Ereignisdispatch an Name_Ereignis
|
||||
Ein Ereignis SHALL die Prozedur `Name_Ereignis` des betroffenen Objekts
|
||||
aufrufen und ihr die Argumente der in der Forms-Referenz festgelegten
|
||||
Signatur übergeben. Fehlt die Prozedur, MUST das Ereignis wirkungslos
|
||||
verfallen, ohne Fehler. Die Zustellung SHALL an den Zustellpunkten der
|
||||
Ereignissteuerung erfolgen; es MUST NOT neue Zustellpunkte geben.
|
||||
|
||||
#### Scenario: Ereignisprozedur wird gerufen
|
||||
- **WHEN** ein Klick auf `Command1` zugestellt wird und `SUB Command1_Click` existiert
|
||||
- **THEN** läuft die Prozedur genau einmal
|
||||
|
||||
#### Scenario: Fehlende Ereignisprozedur
|
||||
- **WHEN** ein Klick auf ein Steuerelement ohne passende Prozedur zugestellt wird
|
||||
- **THEN** läuft das Programm unverändert weiter und es entsteht kein Fehler
|
||||
|
||||
#### Scenario: Signatur mit Mausargumenten
|
||||
- **WHEN** `Form_MouseDown` definiert ist und ein Mausereignis in Zeile 5, Spalte 7 zugestellt wird
|
||||
- **THEN** erhält die Prozedur Taste, Umschaltzustand und die Position in Zellen
|
||||
|
||||
### Requirement: SCREEN-Objekt
|
||||
`SCREEN` SHALL als Objekt ansprechbar sein mit `ActiveControl`,
|
||||
`ActiveForm`, `Height`, `Width`, `MousePointer` und `ControlPanel` sowie
|
||||
den Methoden `SHOW` und `HIDE`. `Height` und `Width` SHALL die
|
||||
**aktuelle** Terminalgröße in Zeichen liefern und schreibgeschützt sein;
|
||||
die feste Größe des Vorbilds MUST als Abweichung dokumentiert sein.
|
||||
|
||||
#### Scenario: Größe folgt dem Terminal
|
||||
- **WHEN** die Darstellungsfläche 120×40 misst und `SCREEN.Width` gelesen wird
|
||||
- **THEN** liefert es 120
|
||||
|
||||
#### Scenario: Aktives Formular
|
||||
- **WHEN** zwei Formulare geladen sind und eines den Fokus hat
|
||||
- **THEN** liefert `SCREEN.ActiveForm` dieses Formular
|
||||
@@ -247,3 +247,55 @@ und nennt dabei das Label.
|
||||
#### Scenario: Unbekanntes Trap-Ziel
|
||||
- **WHEN** ein Modul `ON TIMER(1) GOSUB Fehlt` ohne Label `Fehlt` enthält
|
||||
- **THEN** meldet die Übersetzung ein undefiniertes Sprungziel und nennt `Fehlt`
|
||||
|
||||
### Requirement: Objektzugriffe in Grammatik und Semantik
|
||||
Der Parser SHALL Punktpfade auf Formulare und Steuerelemente
|
||||
(`objekt.eigenschaft`), den Container-Operator
|
||||
`form!steuerelement[.eigenschaft]`, `IF TYPEOF ausdruck IS klasse`, den
|
||||
Metabefehl `$FORM` und die Datentypen `FORM` und `CONTROL` als
|
||||
Parametertypen erkennen. Die Semantik SHALL Eigenschaften gegen die
|
||||
Klasse des Objekts prüfen und ihren Typ bestimmen. Ein Punktpfad, dessen
|
||||
Basis weder ein benutzerdefinierter Typ noch ein bekanntes Objekt ist,
|
||||
MUST eine Diagnose erzeugen, die Basis und Eigenschaft namentlich nennt;
|
||||
er MUST NOT zu einer impliziten Variablen mit Punkt im Namen werden.
|
||||
|
||||
#### Scenario: Unbekanntes Objekt wird benannt
|
||||
- **WHEN** ein Modul `Text9.Text = "a"` ohne Steuerelement `Text9` enthält
|
||||
- **THEN** nennt die Diagnose `Text9` als unbekanntes Objekt statt „Type mismatch" zu melden
|
||||
|
||||
#### Scenario: Unbekannte Eigenschaft wird benannt
|
||||
- **WHEN** ein Modul `Text1.Farbe = 3` enthält
|
||||
- **THEN** nennt die Diagnose `Farbe` als unbekannte Eigenschaft von `TextBox`
|
||||
|
||||
#### Scenario: Eigenschaftstyp wird geprüft
|
||||
- **WHEN** ein Modul `Text1.Text = 5` enthält
|
||||
- **THEN** meldet die Semantik einen Typkonflikt mit Nennung der Eigenschaft
|
||||
|
||||
#### Scenario: TYPEOF
|
||||
- **WHEN** ein Modul `IF TYPEOF Ziel IS CommandButton THEN` enthält
|
||||
- **THEN** wird es übersetzt und die Klasse gegen die bekannten Steuerelementklassen geprüft
|
||||
|
||||
### Requirement: Bang-Operator neben dem SINGLE-Suffix
|
||||
Der Lexer SHALL `!` als Container-Operator erkennen, wenn ihm ein
|
||||
Bezeichner folgt, und andernfalls als Typsuffix für SINGLE. Beide Formen
|
||||
MUST im selben Modul nebeneinander verwendbar sein.
|
||||
|
||||
#### Scenario: Beide Bedeutungen im selben Modul
|
||||
- **WHEN** ein Modul `Wert! = 1.5` und `Form1!Text1.Text = "a"` enthält
|
||||
- **THEN** wird die erste Zeile als SINGLE-Variable und die zweite als Containerzugriff übersetzt
|
||||
|
||||
### Requirement: Ereignisprozeduren mit festen Signaturen
|
||||
Die Semantik SHALL eine `SUB`, deren Name der Form `Objekt_Ereignis`
|
||||
entspricht und deren Objekt bekannt ist, als Ereignisprozedur erkennen
|
||||
und ihre Parameterliste gegen die Signatur der Forms-Referenz prüfen.
|
||||
Eine abweichende Parameterliste MUST namentlich gemeldet werden. Eine
|
||||
`SUB` mit Unterstrich im Namen ohne bekanntes Objekt MUST weiterhin eine
|
||||
gewöhnliche Prozedur sein.
|
||||
|
||||
#### Scenario: Falsche Signatur
|
||||
- **WHEN** ein Modul `SUB Form_MouseDown (X AS SINGLE)` enthält
|
||||
- **THEN** nennt die Diagnose die erwartete Parameterliste der Ereignisprozedur
|
||||
|
||||
#### Scenario: Gewöhnliche SUB mit Unterstrich
|
||||
- **WHEN** ein Modul `SUB Zins_Berechnen (n AS INTEGER)` ohne Objekt `Zins` enthält
|
||||
- **THEN** wird sie als gewöhnliche Prozedur übersetzt
|
||||
|
||||
@@ -12,10 +12,14 @@ jederzeit prüfen lässt.
|
||||
### Requirement: Vollständiges Inventar der Original-Hilfe
|
||||
`docs/inventar.md` SHALL jede in der Original-Hilfe des Vorbilds
|
||||
dokumentierte Anweisung, Funktion, Metabefehl, Operator-Schlüsselwort und
|
||||
Klausel als eigenen Eintrag führen. Ein dokumentiertes Element MUST NOT
|
||||
Klausel als eigenen Eintrag führen. Darüber hinaus SHALL es das
|
||||
**Forms-Objektmodell** führen: je Klasse ihre Eigenschaften, Methoden und
|
||||
Ereignisse als eigene Einträge. Ein dokumentiertes Element MUST NOT
|
||||
fehlen; Vollständigkeit gegenüber der Quelle geht der Kürze vor. Jeder
|
||||
Eintrag SHALL Name, Art (Anweisung/Funktion/Metabefehl), Themengruppe,
|
||||
Status und Fundstelle tragen.
|
||||
Eintrag SHALL Name, Art (Anweisung/Funktion/Metabefehl/Eigenschaft/
|
||||
Methode/Ereignis), Themengruppe, Status, Fundstelle und Quelle tragen.
|
||||
Für Einträge des Objektmodells SHALL die Quelle die Forms-Referenz sein,
|
||||
weil die Themenliste der Original-Hilfe sie nicht führt.
|
||||
|
||||
#### Scenario: Dokumentiertes Element ist erfasst
|
||||
- **WHEN** die Original-Hilfe die Anweisung `VIEW PRINT` dokumentiert
|
||||
@@ -25,6 +29,14 @@ Status und Fundstelle tragen.
|
||||
- **WHEN** `PEEK` als Non-Feature deklariert ist
|
||||
- **THEN** steht `PEEK` mit Status `Non-Feature` im Inventar und die Fundstelle verweist auf den Abschnitt „Abweichungen" der Sprachreferenz
|
||||
|
||||
#### Scenario: Eigenschaft einer Steuerelementklasse ist erfasst
|
||||
- **WHEN** die Forms-Referenz für `TextBox` die Eigenschaft `SelStart` führt
|
||||
- **THEN** enthält das Inventar einen Eintrag für `TextBox.SelStart` mit Art `Eigenschaft`, Status und Quelle Forms-Referenz
|
||||
|
||||
#### Scenario: Ereignis einer Klasse ist erfasst
|
||||
- **WHEN** die Forms-Referenz für `Form` das Ereignis `Resize` führt
|
||||
- **THEN** enthält das Inventar einen Eintrag für `Form_Resize` mit Art `Ereignis`
|
||||
|
||||
### Requirement: Festes Statusvokabular
|
||||
Der Status eines Eintrags SHALL genau einen von drei Werten tragen:
|
||||
`implementiert` (Frontend senkt ab und die Laufzeit führt aus),
|
||||
|
||||
Reference in New Issue
Block a user