VBScript语法:如何在没有括号的情况下建模子调用

时间:2014-05-23 13:31:27

标签: parsing vbscript gold-parser

我正在为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)的括号的子调用?

1 个答案:

答案 0 :(得分:2)

您遇到的问题是VBScript语法不明确。

哪个变体是obj.sub1 (1)?围绕论点的那个人,或者没有和第一个论点的那个人恰好是在parens?

如果我们无法分辨,那么解析器也不能......我们只能确定何时我们有多个参数,例如:一个逗号。因此,假设默认情况下,当我们遇到多个参数或根本没有参数时,我们选择仅使用parens作为参数。

在您努力解决问题的过程中,您已经开始制作过于称职的终端,其中也包括点。这是一个坏主意,因为它不起作用('。'周围的空格被允许但不被这些终端匹配)并且它可能导致意外的标记化行为和降低的性能。通常它表示你的语法有问题。

这是我一起攻击的语法,它可以很好地解析你的样本,实际上也应该正确地处理赋值和构造函数调用。请注意,<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>