3

我正在为 VBScript 编写 GOLD Parser 语法。这是一个摘录:

<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)

4

1 回答 1

2

您遇到的问题是 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> 
于 2014-05-26T17:25:40.997 回答