Phase 4: Steuerelemente implementieren
This commit is contained in:
@@ -480,6 +480,26 @@ pub enum Stmt {
|
||||
bottom: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
GraphicsLine {
|
||||
from: Option<(Expr, Expr)>,
|
||||
to: (Expr, Expr),
|
||||
relative: bool,
|
||||
color: Option<Expr>,
|
||||
fill: u8,
|
||||
pos: SourcePos,
|
||||
},
|
||||
GraphicsPaint {
|
||||
point: (Expr, Expr),
|
||||
paint: Option<Expr>,
|
||||
border: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
GraphicsView {
|
||||
rect: Option<(Expr, Expr, Expr, Expr)>,
|
||||
fill: Option<Expr>,
|
||||
border: Option<Expr>,
|
||||
pos: SourcePos,
|
||||
},
|
||||
/// `ON TIMER(n&) GOSUB ziel`, `ON KEY(n%) GOSUB ziel`,
|
||||
/// `ON UEVENT GOSUB ziel`, `ON SIGNAL(n%) GOSUB ziel` — die
|
||||
/// klassischen Ereignis-Traps (Sprachreferenz §8). `GOSUB 0` schaltet
|
||||
|
||||
@@ -21,10 +21,11 @@ pub enum ObjectClass {
|
||||
Timer,
|
||||
VScrollBar,
|
||||
Screen,
|
||||
Spin,
|
||||
}
|
||||
|
||||
impl ObjectClass {
|
||||
pub const ALL: [Self; 18] = [
|
||||
pub const ALL: [Self; 19] = [
|
||||
Self::Form,
|
||||
Self::CheckBox,
|
||||
Self::ComboBox,
|
||||
@@ -43,6 +44,7 @@ impl ObjectClass {
|
||||
Self::Timer,
|
||||
Self::VScrollBar,
|
||||
Self::Screen,
|
||||
Self::Spin,
|
||||
];
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
@@ -65,6 +67,7 @@ impl ObjectClass {
|
||||
Self::Timer => "TIMER",
|
||||
Self::VScrollBar => "VSCROLLBAR",
|
||||
Self::Screen => "SCREEN",
|
||||
Self::Spin => "SPIN",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +91,7 @@ impl ObjectClass {
|
||||
Self::Timer => "Timer",
|
||||
Self::VScrollBar => "VScrollBar",
|
||||
Self::Screen => "Screen",
|
||||
Self::Spin => "Spin",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +299,7 @@ pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
CheckBox => p.extend([string("CAPTION", ""), range("VALUE", 0, 0, 2)]),
|
||||
OptionButton => p.extend([string("CAPTION", ""), range("VALUE", 0, -1, 0)]),
|
||||
OptionButton => p.extend([string("CAPTION", ""), range("VALUE", 0, -1, 1)]),
|
||||
Frame => p.push(string("CAPTION", "")),
|
||||
Label => p.extend([
|
||||
range("ALIGNMENT", 0, 0, 2),
|
||||
@@ -311,6 +315,21 @@ pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
|
||||
range("SMALLCHANGE", 1, 1, 32767),
|
||||
range("VALUE", 0, -32768, 32767),
|
||||
]),
|
||||
Spin => {
|
||||
p.extend([
|
||||
ro("BORDERSTYLE", PropertyType::Integer),
|
||||
range("INTERVAL", 250, 0, 65535),
|
||||
range("MIN", 0, -32768, 32767),
|
||||
range("MAX", 32767, -32768, 32767),
|
||||
range("STYLE", 0, 0, 1),
|
||||
range("VALUE", 0, -32768, 32767),
|
||||
]);
|
||||
for name in ["HEIGHT", "WIDTH"] {
|
||||
if let Some(property) = p.iter_mut().find(|property| property.name == name) {
|
||||
property.writable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
PictureBox => p.extend([
|
||||
boolp("AUTOREDRAW", false),
|
||||
range("BORDERSTYLE", 1, 0, 2),
|
||||
@@ -337,10 +356,26 @@ pub fn properties(class: ObjectClass) -> Vec<PropertySpec> {
|
||||
boolp("SEPARATOR", false),
|
||||
string("TAG", ""),
|
||||
boolp("VISIBLE", true),
|
||||
string("SHORTCUT", ""),
|
||||
]),
|
||||
DirListBox => p.extend([
|
||||
ro("LIST", PropertyType::String),
|
||||
ro("LISTCOUNT", PropertyType::Integer),
|
||||
int("LISTINDEX", -1),
|
||||
string("PATH", ""),
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
DriveListBox => p.extend([
|
||||
ro("LIST", PropertyType::String),
|
||||
ro("LISTCOUNT", PropertyType::Integer),
|
||||
int("LISTINDEX", -1),
|
||||
string("DRIVE", ""),
|
||||
string("TEXT", ""),
|
||||
]),
|
||||
DirListBox => p.extend([string("PATH", ""), string("TEXT", "")]),
|
||||
DriveListBox => p.extend([string("DRIVE", ""), string("TEXT", "")]),
|
||||
FileListBox => p.extend([
|
||||
ro("LIST", PropertyType::String),
|
||||
ro("LISTCOUNT", PropertyType::Integer),
|
||||
int("LISTINDEX", -1),
|
||||
string("FILENAME", ""),
|
||||
string("PATH", ""),
|
||||
string("PATTERN", "*.*"),
|
||||
@@ -413,6 +448,7 @@ pub fn methods(class: ObjectClass) -> &'static [&'static str] {
|
||||
&["DRAG", "MOVE", "REFRESH", "SETFOCUS"]
|
||||
}
|
||||
Frame | Label | HScrollBar | VScrollBar => &["DRAG", "MOVE", "REFRESH"],
|
||||
Spin => &["DRAG", "REFRESH", "SETFOCUS"],
|
||||
PictureBox => &[
|
||||
"CLS",
|
||||
"DRAG",
|
||||
@@ -429,11 +465,7 @@ pub fn methods(class: ObjectClass) -> &'static [&'static str] {
|
||||
}
|
||||
|
||||
pub fn method_is_implemented(class: ObjectClass, name: &str) -> bool {
|
||||
matches!(
|
||||
(class, name),
|
||||
(ObjectClass::Form, "HIDE" | "LOAD" | "SHOW" | "UNLOAD")
|
||||
| (ObjectClass::Screen, "HIDE" | "SHOW")
|
||||
)
|
||||
methods(class).contains(&name)
|
||||
}
|
||||
|
||||
pub fn method_arity(class: ObjectClass, name: &str) -> Option<(usize, usize)> {
|
||||
@@ -441,6 +473,22 @@ pub fn method_arity(class: ObjectClass, name: &str) -> Option<(usize, usize)> {
|
||||
(ObjectClass::Form, "SHOW") => Some((0, 1)),
|
||||
(ObjectClass::Form, "HIDE" | "LOAD" | "UNLOAD")
|
||||
| (ObjectClass::Screen, "HIDE" | "SHOW") => Some((0, 0)),
|
||||
(_, "REFRESH" | "SETFOCUS" | "CLS" | "PRINTFORM") => Some((0, 0)),
|
||||
(_, "DRAG") => Some((1, 1)),
|
||||
(_, "MOVE") => Some((2, 4)),
|
||||
(ObjectClass::ListBox | ObjectClass::ComboBox, "ADDITEM") => Some((1, 2)),
|
||||
(ObjectClass::ListBox | ObjectClass::ComboBox, "REMOVEITEM") => Some((1, 1)),
|
||||
(ObjectClass::Form | ObjectClass::PictureBox, "PRINT") => Some((0, usize::MAX)),
|
||||
(ObjectClass::Form | ObjectClass::PictureBox, "TEXTWIDTH" | "TEXTHEIGHT") => Some((1, 1)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn method_return_type(class: ObjectClass, name: &str) -> Option<PropertyType> {
|
||||
match (class, name) {
|
||||
(ObjectClass::Form | ObjectClass::PictureBox, "TEXTWIDTH" | "TEXTHEIGHT") => {
|
||||
Some(PropertyType::Integer)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -558,6 +606,16 @@ pub fn events(class: ObjectClass) -> &'static [&'static str] {
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
Spin => &[
|
||||
"CUSTOM",
|
||||
"DRAGDROP",
|
||||
"DRAGOVER",
|
||||
"GOTFOCUS",
|
||||
"KEYDOWN",
|
||||
"KEYPRESS",
|
||||
"KEYUP",
|
||||
"LOSTFOCUS",
|
||||
],
|
||||
PictureBox => &[
|
||||
"CLICK",
|
||||
"DBLCLICK",
|
||||
@@ -608,6 +666,7 @@ pub fn event_params(event: &str) -> Option<&'static [(&'static str, EventParamTy
|
||||
("STATE", Integer),
|
||||
],
|
||||
"UNLOAD" => &[("CANCEL", Integer)],
|
||||
"CUSTOM" => &[("EVENTTYPE", Integer)],
|
||||
"CLICK" | "DBLCLICK" | "CHANGE" | "DROPDOWN" | "GOTFOCUS" | "LOSTFOCUS" | "LOAD"
|
||||
| "PAINT" | "RESIZE" | "TIMER" | "PATHCHANGE" | "PATTERNCHANGE" => &[],
|
||||
_ => return None,
|
||||
@@ -652,4 +711,18 @@ impl FormCatalog {
|
||||
.find(|(_, o)| o.name.eq_ignore_ascii_case(name))
|
||||
.map(|(i, o)| (i as u16, o))
|
||||
}
|
||||
|
||||
pub fn belongs_to(&self, object: &FormObject, form: &str) -> bool {
|
||||
let mut parent = object.parent_form.as_deref();
|
||||
for _ in 0..self.objects.len() {
|
||||
let Some(name) = parent else { return false };
|
||||
if name.eq_ignore_ascii_case(form) {
|
||||
return true;
|
||||
}
|
||||
parent = self
|
||||
.find(name)
|
||||
.and_then(|(_, object)| object.parent_form.as_deref());
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +369,13 @@ pub enum Builtin {
|
||||
IsamSavepoint,
|
||||
IsamSetmem,
|
||||
IsamBof,
|
||||
MsgBox,
|
||||
InputBoxS,
|
||||
ClipboardAdd,
|
||||
ClipboardGet,
|
||||
GraphicsLine,
|
||||
GraphicsPaint,
|
||||
GraphicsView,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -386,6 +393,20 @@ pub enum HExpr {
|
||||
property: u16,
|
||||
ty: HTy,
|
||||
},
|
||||
ObjectIndexedProperty {
|
||||
object: u16,
|
||||
object_index: Option<Box<HExpr>>,
|
||||
property: u16,
|
||||
index: Box<HExpr>,
|
||||
ty: HTy,
|
||||
},
|
||||
ObjectMethodCall {
|
||||
object: u16,
|
||||
index: Option<Box<HExpr>>,
|
||||
method: u16,
|
||||
args: Vec<HExpr>,
|
||||
ty: HTy,
|
||||
},
|
||||
DynamicObjectProperty {
|
||||
object: Box<HExpr>,
|
||||
property: String,
|
||||
@@ -511,6 +532,13 @@ pub enum HStmtKind {
|
||||
property: u16,
|
||||
value: HExpr,
|
||||
},
|
||||
SetObjectIndexedProperty {
|
||||
object: u16,
|
||||
object_index: Option<HExpr>,
|
||||
property: u16,
|
||||
index: HExpr,
|
||||
value: HExpr,
|
||||
},
|
||||
SetDynamicObjectProperty {
|
||||
object: HExpr,
|
||||
property: String,
|
||||
@@ -518,6 +546,7 @@ pub enum HStmtKind {
|
||||
},
|
||||
ObjectMethod {
|
||||
object: u16,
|
||||
index: Option<HExpr>,
|
||||
method: u16,
|
||||
args: Vec<HExpr>,
|
||||
},
|
||||
@@ -592,6 +621,10 @@ pub enum HStmtKind {
|
||||
targets: Vec<LabelId>,
|
||||
},
|
||||
ReturnGosub(Option<LabelId>),
|
||||
Run {
|
||||
target: Option<HExpr>,
|
||||
string: bool,
|
||||
},
|
||||
/// Ereignis-Trap erklären (Sprachreferenz §8). `art` ist die Quelle
|
||||
/// (0 KEY, 1 TIMER, 2 UEVENT, 3 SIGNAL), `index` ihre Kennung bzw. bei
|
||||
/// `TIMER` das Intervall in Sekunden. `ziel = None` = `GOSUB 0`.
|
||||
|
||||
@@ -750,8 +750,8 @@ mod tests {
|
||||
assert_eq!(kinds("40000")[0], TokenKind::Num(NumValue::Long(40000)));
|
||||
assert_eq!(kinds("1.5")[0], TokenKind::Num(NumValue::Single(1.5)));
|
||||
assert_eq!(
|
||||
kinds("3.14159265")[0],
|
||||
TokenKind::Num(NumValue::Double(3.14159265))
|
||||
kinds("1.23456789")[0],
|
||||
TokenKind::Num(NumValue::Double(1.23456789))
|
||||
);
|
||||
assert_eq!(kinds("1E3")[0], TokenKind::Num(NumValue::Single(1000.0)));
|
||||
assert_eq!(kinds("1D3")[0], TokenKind::Num(NumValue::Double(1000.0)));
|
||||
|
||||
@@ -28,7 +28,18 @@ pub fn parse(module_name: &str, tokens: &[Token]) -> ParseOutput {
|
||||
match p.k() {
|
||||
TokenKind::Eof => break,
|
||||
TokenKind::Kw(Kw::Sub) | TokenKind::Kw(Kw::Function) => {
|
||||
if let Some(proc) = p.parse_proc() {
|
||||
if let Some(proc) = p.parse_proc(false) {
|
||||
procs.push(proc);
|
||||
}
|
||||
}
|
||||
TokenKind::Kw(Kw::Static)
|
||||
if matches!(
|
||||
p.k_at(1),
|
||||
TokenKind::Kw(Kw::Sub) | TokenKind::Kw(Kw::Function)
|
||||
) =>
|
||||
{
|
||||
p.advance();
|
||||
if let Some(proc) = p.parse_proc(true) {
|
||||
procs.push(proc);
|
||||
}
|
||||
}
|
||||
@@ -244,10 +255,7 @@ impl<'a> P<'a> {
|
||||
self.advance();
|
||||
self.parse_input(true, pos)
|
||||
} else {
|
||||
// Grafikform LINE (x1,y1)-(x2,y2): deklariertes Non-Feature.
|
||||
self.err("Feature unavailable");
|
||||
self.sync();
|
||||
None
|
||||
self.parse_graphics_line(pos)
|
||||
}
|
||||
}
|
||||
TokenKind::Kw(Kw::If) => self.parse_if(pos),
|
||||
@@ -350,12 +358,8 @@ impl<'a> P<'a> {
|
||||
TokenKind::Kw(Kw::Erase) => {
|
||||
self.advance();
|
||||
let mut names = Vec::new();
|
||||
loop {
|
||||
if let Some(e) = self.parse_name_only() {
|
||||
names.push(e);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
while let Some(e) = self.parse_name_only() {
|
||||
names.push(e);
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
@@ -548,12 +552,8 @@ impl<'a> P<'a> {
|
||||
TokenKind::Kw(Kw::Read) => {
|
||||
self.advance();
|
||||
let mut vars = Vec::new();
|
||||
loop {
|
||||
if let Some(e) = self.parse_name_ref() {
|
||||
vars.push(e);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
while let Some(e) = self.parse_name_ref() {
|
||||
vars.push(e);
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
@@ -733,6 +733,14 @@ impl<'a> P<'a> {
|
||||
}
|
||||
Some(Stmt::ViewPrint { top, bottom, pos })
|
||||
}
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if name == "VIEW" => self.parse_graphics_view(pos),
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
} if name == "PAINT" => self.parse_graphics_paint(pos),
|
||||
TokenKind::Ident {
|
||||
ref name,
|
||||
suffix: None,
|
||||
@@ -832,6 +840,121 @@ impl<'a> P<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_graphics_point(&mut self) -> Option<(Expr, Expr)> {
|
||||
if !self.eat(&TokenKind::LParen) {
|
||||
self.err("Expected: (");
|
||||
return None;
|
||||
}
|
||||
let x = self.parse_expr()?;
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
self.err("Expected: ,");
|
||||
return None;
|
||||
}
|
||||
let y = self.parse_expr()?;
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
self.err("Expected: )");
|
||||
return None;
|
||||
}
|
||||
Some((x, y))
|
||||
}
|
||||
|
||||
fn parse_graphics_line(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
let from = if self.eat(&TokenKind::Minus) {
|
||||
None
|
||||
} else {
|
||||
let point = self.parse_graphics_point()?;
|
||||
if !self.eat(&TokenKind::Minus) {
|
||||
self.err("Expected: -");
|
||||
return None;
|
||||
}
|
||||
Some(point)
|
||||
};
|
||||
let relative = self.eat_kw(Kw::Step);
|
||||
let to = self.parse_graphics_point()?;
|
||||
let mut color = None;
|
||||
let mut fill = 0;
|
||||
if self.eat(&TokenKind::Comma) {
|
||||
if !matches!(
|
||||
self.k(),
|
||||
TokenKind::Comma | TokenKind::Colon | TokenKind::Eol | TokenKind::Eof
|
||||
) {
|
||||
color = Some(self.parse_expr()?);
|
||||
}
|
||||
if self.eat(&TokenKind::Comma) {
|
||||
if let TokenKind::Ident { name, .. } = self.k() {
|
||||
fill = match name.as_str() {
|
||||
"B" => 1,
|
||||
"BF" => 2,
|
||||
_ => {
|
||||
self.err("Expected: B or BF");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Stmt::GraphicsLine {
|
||||
from,
|
||||
to,
|
||||
relative,
|
||||
color,
|
||||
fill,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_graphics_paint(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
self.advance();
|
||||
let point = self.parse_graphics_point()?;
|
||||
let paint = self
|
||||
.eat(&TokenKind::Comma)
|
||||
.then(|| self.parse_expr())
|
||||
.flatten();
|
||||
let border = self
|
||||
.eat(&TokenKind::Comma)
|
||||
.then(|| self.parse_expr())
|
||||
.flatten();
|
||||
Some(Stmt::GraphicsPaint {
|
||||
point,
|
||||
paint,
|
||||
border,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_graphics_view(&mut self, pos: SourcePos) -> Option<Stmt> {
|
||||
self.advance();
|
||||
if self.at_stmt_end() {
|
||||
return Some(Stmt::GraphicsView {
|
||||
rect: None,
|
||||
fill: None,
|
||||
border: None,
|
||||
pos,
|
||||
});
|
||||
}
|
||||
let (x1, y1) = self.parse_graphics_point()?;
|
||||
if !self.eat(&TokenKind::Minus) {
|
||||
self.err("Expected: -");
|
||||
return None;
|
||||
}
|
||||
let (x2, y2) = self.parse_graphics_point()?;
|
||||
let fill = self
|
||||
.eat(&TokenKind::Comma)
|
||||
.then(|| self.parse_expr())
|
||||
.flatten();
|
||||
let border = self
|
||||
.eat(&TokenKind::Comma)
|
||||
.then(|| self.parse_expr())
|
||||
.flatten();
|
||||
Some(Stmt::GraphicsView {
|
||||
rect: Some((x1, y1, x2, y2)),
|
||||
fill,
|
||||
border,
|
||||
pos,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_print(&mut self, pos: SourcePos, printer: bool) -> Option<Stmt> {
|
||||
self.advance(); // PRINT bzw. LPRINT
|
||||
let file = if !printer && self.eat(&TokenKind::Hash) {
|
||||
@@ -1056,12 +1179,8 @@ impl<'a> P<'a> {
|
||||
}
|
||||
}
|
||||
let mut vars = Vec::new();
|
||||
loop {
|
||||
if let Some(e) = self.parse_name_ref() {
|
||||
vars.push(e);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
while let Some(e) = self.parse_name_ref() {
|
||||
vars.push(e);
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
@@ -1384,11 +1503,8 @@ impl<'a> P<'a> {
|
||||
return None;
|
||||
};
|
||||
let mut targets = Vec::new();
|
||||
loop {
|
||||
match self.parse_label_ref() {
|
||||
Some(t) => targets.push(t),
|
||||
None => break,
|
||||
}
|
||||
while let Some(t) = self.parse_label_ref() {
|
||||
targets.push(t);
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
@@ -1432,11 +1548,7 @@ impl<'a> P<'a> {
|
||||
let dims = if self.eat(&TokenKind::LParen) {
|
||||
let mut ds = Vec::new();
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
let a = match self.parse_expr() {
|
||||
Some(e) => e,
|
||||
None => break,
|
||||
};
|
||||
while let Some(a) = self.parse_expr() {
|
||||
if self.eat_kw(Kw::To) {
|
||||
match self.parse_expr() {
|
||||
Some(b) => ds.push((Some(a), b)),
|
||||
@@ -1602,44 +1714,42 @@ impl<'a> P<'a> {
|
||||
}
|
||||
};
|
||||
let mut params = Vec::new();
|
||||
if self.eat(&TokenKind::LParen) {
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
match self.k() {
|
||||
TokenKind::Ident {
|
||||
if self.eat(&TokenKind::LParen) && !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
match self.k() {
|
||||
TokenKind::Ident {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
} => {
|
||||
self.advance();
|
||||
let array = if self.eat(&TokenKind::LParen) {
|
||||
self.eat(&TokenKind::RParen);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let as_type = if self.eat_kw(Kw::As) {
|
||||
self.parse_type_name()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
params.push(Param {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
} => {
|
||||
self.advance();
|
||||
let array = if self.eat(&TokenKind::LParen) {
|
||||
self.eat(&TokenKind::RParen);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let as_type = if self.eat_kw(Kw::As) {
|
||||
self.parse_type_name()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
params.push(Param {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
array,
|
||||
as_type,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: identifier");
|
||||
break;
|
||||
}
|
||||
array,
|
||||
as_type,
|
||||
});
|
||||
}
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
_ => {
|
||||
self.err("Expected: identifier");
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.eat(&TokenKind::RParen);
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.eat(&TokenKind::RParen);
|
||||
}
|
||||
Some(ProcSig {
|
||||
kind,
|
||||
@@ -1649,10 +1759,10 @@ impl<'a> P<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_proc(&mut self) -> Option<Proc> {
|
||||
fn parse_proc(&mut self, leading_static: bool) -> Option<Proc> {
|
||||
let pos = self.pos();
|
||||
let sig = self.parse_proc_sig()?;
|
||||
let is_static = self.eat_kw(Kw::Static);
|
||||
let is_static = leading_static || self.eat_kw(Kw::Static);
|
||||
let end_kw = match sig.kind {
|
||||
ProcKind::Sub => Kw::Sub,
|
||||
ProcKind::Function => Kw::Function,
|
||||
@@ -1697,33 +1807,31 @@ impl<'a> P<'a> {
|
||||
}
|
||||
};
|
||||
let mut params = Vec::new();
|
||||
if self.eat(&TokenKind::LParen) {
|
||||
if !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
match self.k() {
|
||||
TokenKind::Ident {
|
||||
if self.eat(&TokenKind::LParen) && !self.eat(&TokenKind::RParen) {
|
||||
loop {
|
||||
match self.k() {
|
||||
TokenKind::Ident {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
} => {
|
||||
self.advance();
|
||||
params.push(Param {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
} => {
|
||||
self.advance();
|
||||
params.push(Param {
|
||||
name: pname,
|
||||
suffix: psfx,
|
||||
array: false,
|
||||
as_type: None,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
self.err("Expected: identifier");
|
||||
break;
|
||||
}
|
||||
array: false,
|
||||
as_type: None,
|
||||
});
|
||||
}
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
_ => {
|
||||
self.err("Expected: identifier");
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.eat(&TokenKind::RParen);
|
||||
if !self.eat(&TokenKind::Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.eat(&TokenKind::RParen);
|
||||
}
|
||||
if self.eat(&TokenKind::Eq) {
|
||||
let body = self.parse_expr()?;
|
||||
@@ -1818,8 +1926,8 @@ impl<'a> P<'a> {
|
||||
other => Expr::Paren(Box::new(other)),
|
||||
})
|
||||
.collect();
|
||||
if !self.at_stmt_end() && call_args.is_empty() {
|
||||
call_args = self.parse_arg_list_to_stmt_end();
|
||||
if !self.at_stmt_end() && (call_args.is_empty() || name.contains('.')) {
|
||||
call_args.extend(self.parse_arg_list_to_stmt_end());
|
||||
}
|
||||
Some(Stmt::Call {
|
||||
name,
|
||||
@@ -1842,7 +1950,7 @@ impl<'a> P<'a> {
|
||||
match self.k() {
|
||||
TokenKind::Ident { mut name, suffix } => {
|
||||
self.advance();
|
||||
let args = if self.eat(&TokenKind::LParen) {
|
||||
let mut args = if self.eat(&TokenKind::LParen) {
|
||||
let a = self.parse_arg_list(&TokenKind::RParen);
|
||||
self.eat(&TokenKind::RParen);
|
||||
Some(a)
|
||||
@@ -1858,6 +1966,11 @@ impl<'a> P<'a> {
|
||||
name.push('.');
|
||||
name.push_str(&member);
|
||||
self.advance();
|
||||
if self.eat(&TokenKind::LParen) {
|
||||
let member_args = self.parse_arg_list(&TokenKind::RParen);
|
||||
self.eat(&TokenKind::RParen);
|
||||
args.get_or_insert_with(Vec::new).extend(member_args);
|
||||
}
|
||||
}
|
||||
_ => self.err("Expected: property"),
|
||||
}
|
||||
@@ -1935,6 +2048,7 @@ impl<'a> P<'a> {
|
||||
args.push(Expr::Missing);
|
||||
continue;
|
||||
}
|
||||
self.eat(&TokenKind::Hash);
|
||||
match self.parse_expr() {
|
||||
Some(e) => args.push(e),
|
||||
None => break,
|
||||
@@ -1985,11 +2099,7 @@ impl<'a> P<'a> {
|
||||
|
||||
fn parse_bin(&mut self, min_prec: u8) -> Option<Expr> {
|
||||
let mut lhs = self.parse_prefix()?;
|
||||
loop {
|
||||
let (prec, op) = match self.infix_op() {
|
||||
Some(x) => x,
|
||||
None => break,
|
||||
};
|
||||
while let Some((prec, op)) = self.infix_op() {
|
||||
if prec < min_prec {
|
||||
break;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user