VBScript Грамматика: Как смоделировать суб-вызов без скобок
Я пишу грамматику GOLD Parser для VBScript. Вот выдержка:
<CallStmt> ::= 'Call' <CallExpr>
| <CallExpr> <ParameterList>
!| <CallExpr> '(' <Expr> ')'
| <CallExpr> '(' ')'
<AssignStmt> ::= <CallExpr> '=' <Expr>
| 'Set' <CallExpr> '=' <Expr>
| 'Set' <CallExpr> '=' 'New' <CallExpr>
<CallExpr> ::= '.' <LeftExpr>
| <LeftExpr>
<LeftExpr> ::= ID
| IDDot <LeftExpr>
| ID '(' <ParameterList> ')'
| ID '(' <ParameterList> ').' <LeftExpr>
!VBScript allows to skip parameters a(1,,2)
<ParameterList> ::= <Expr> ',' <ParameterList>
| ',' <ParameterList>
| <Expr>
|
! Value can be reduced from <Expr>
<Value> ::= <ConstExpr>
| <CallExpr>
| '(' <Expr> ')'
У меня конфликт по поводу <CallStmt> ::= <CallExpr> <ParameterList>
править. Это правило описывает вызов sub без круглых скобок. Например, следующие утверждения синтаксически верны:
obj.sub1(1, 2).sub2 1, 2
obj.sub1(1, 2).sub2(1),(2)
Call obj.sub1(1, 2).sub2(1, 2)
Как я могу различить дополнительный вызов с окружающими скобками sub1(1, 2)
и дополнительный вызов с круглыми скобками вокруг аргументов sub2(1),(2)
?
1 ответ
Проблема в том, что синтаксис VBScript неоднозначен.
Какой вариант obj.sub1 (1)
? Тот, у кого есть спаррины вокруг аргументов, или тот, у кого нет и первый аргумент случается в паранах?
Если мы не можем сказать, то и парсер не может... мы можем точно сказать, только когда у нас есть несколько аргументов, например, запятая. Поэтому предположим, что по умолчанию мы выбираем использование аргументов в качестве аргументов только в тех случаях, когда встречаемся с более чем одним параметром или вообще не имеем параметров.
В ваших усилиях по решению этой проблемы вы начали создавать сверхкомпетентные терминалы, в которые также входит точка. Это плохая идея, так как она не работает (пробелы вокруг "." Разрешены, но не соответствуют этим терминалам), и это может привести к неожиданному поведению токенизации и снижению производительности. Обычно это указывает на проблему в вашей грамматике.
Вот грамматика, которую я взломал вместе, которая прекрасно разбирает ваши образцы, и фактически должна также правильно обрабатывать назначения и вызовы конструктора. Обратите внимание, что <ParameterList>
будет соответствовать только два или более параметров, но не ноль или один параметр; это ключ к решению проблемы, которая вызывает вашу проблему.
<CallStmt> ::= 'Call' <CallPath>
| <CallPath>
<AssignStmt> ::= <AssignPath> '=' <Expr>
| 'Set' <AssignPath> '=' <Expr>
| 'Set' <AssignPath> '=' 'New' <CtorPath>
<CtorPath> ::= ID '.' <CtorPath>
| ID
| <CallExpr>
| ID <ParameterList>
<CallExpr> ::= ID '(' ')'
| ID '(' <Expr> ')'
| ID '(' <ParameterList> ')'
<CallPath> ::= <Member> '.' <CallPath>
| ID
| <CallExpr>
| ID <ParameterList>
<Member> ::= <CallExpr>
| ID
<MemberPath> ::= <Member> '.' <MemberPath>
| <Member>
<AssignPath> ::= <Member> '.' <AssignPath>
| ID
!VBScript allows to skip parameters a(1,,2)
<ParameterList> ::= <Expr> ',' <ParameterList>
| <Expr> ',' <Expr>
| <Expr> ','
| ',' <ParameterList>
| ','
! Value can be reduced from <Expr>
<Value> ::= NumberLiteral
| StringLiteral
| <MemberPath>
| '(' <Expr> ')'
!--- The rest of the grammar ---
"Start Symbol" = <Start>
{WS} = {Whitespace} - {CR} - {LF}
{ID Head} = {Letter} + [_]
{ID Tail} = {Alphanumeric} + [_]
{String Chars} = {Printable} + {HT} - ["]
Whitespace = {WS}+
NewLine = {CR}{LF} | {CR} | {LF}
ID = {ID Head}{ID Tail}*
StringLiteral = ('"' {String Chars}* '"')+
NumberLiteral = {Number}+ ('.' {Number}+ )?
<nl> ::= NewLine <nl> !One or more
| NewLine
<nl Opt> ::= NewLine <nl Opt> !Zero or more
| !Empty
<Start> ::= <nl opt> <StmtList>
<StmtList> ::= <CallStmt> <nl> <StmtList>
| <AssignStmt> <nl> <StmtList>
|
<Expr> ::= <Add Exp>
<Add Exp> ::= <Add Exp> '+' <Mult Exp>
| <Add Exp> '-' <Mult Exp>
| <Mult Exp>
<Mult Exp> ::= <Mult Exp> '*' <Negate Exp>
| <Mult Exp> '/' <Negate Exp>
| <Negate Exp>
<Negate Exp> ::= '-' <Value>
| <Value>