Как описать условное утверждение (if-then-else) с помощью PEG
Я работаю над анализатором файлов проекта Qmake (проект с открытым исходным кодом). И у меня возникли проблемы с описанием варианта условного оператора qmake, называемого "областью действия" в документации.
EBNF (упрощенно):
ScopeStatement -> Condition ScopeBody
Condition -> Identifier | TestFunctionCall | NotExpr | OrExpr | AndExpr
NotExpr -> "!" Condition
OrExpr -> Condition "|" Condition
AndExpr -> Condition ":" Condition
ScopeBody -> COLON Statement | BR_OPEN Statement:* BR_CLOSE
Statement -> AssignmentStatement
AssignmentStatement -> Identifier EQ String
// There are many others built-in boolean functions
TestFunctionCall -> ("defined" | ...) ARG_LIST_OPEN (String COMMA:?):* ARG_LIST_CLOSE
Identifier -> Letter (Letter | Digit | UNDERSCP):+ String -> (Letter | Digit | UNDERSCP):+
EQ -> "="
COLON -> ":"
COMMA -> ","
ARG_LIST_OPEN -> "("
ARG_LIST_CLOSE -> ")"
BLOCK_OPEN -> "{"
BLOCK_CLOSE -> "}"
UNDERSCP -> "_"
Первый вопрос: как отличить двоеточие AND-оператора от терминального условия? Является ли это возможным?
PS Мой проект грамматики (без поддержки вызова функции) не работает даже для простого случая, как
win32:xml: x = y
PEG.JS
Код:
Start
= ScopeStatement
// qmake scope statement
ScopeStatement
= BooleanExpression ws* ((":" ws* SingleLineStatement) / ("{" ws* MultiLineStatement ))
SingleLineStatement
= Identifier ws* "=" ws* Identifier lb*
MultiLineStatement
= (SingleLineStatement lb*)+
// qmake condition statement
BooleanExpression
= BooleanOrExpression
BooleanOrExpression
= left:BooleanAndExpression ws* "|" ws* right:BooleanOrExpression { return {type: "OR", left:left, right:right} }
/ BooleanAndExpression
BooleanAndExpression
= left:BooleanNotExpression ws* ":" ws* right:BooleanAndExpression { return {type: "AND", left:left, right:right} }
/ BooleanNotExpression
BooleanNotExpression
= "!" ws* operand:BooleanNotExpression { return {type: "NOT", operand: operand } }
/ BooleanComplexExpression
BooleanComplexExpression
= Identifier
/ "(" logical_or:BooleanOrExpression ")" { return logical_or; }
Identifier
= token:[a-zA-Z0-9_]+ { return token.join(""); }
ws
= [ \t]
lb
= [\r\n]
Спасибо!
1 ответ
Решение
Вы должны включить негативный прогноз после BooleanAndExpression
за все, что не является BooleanAndExpression
иначе он будет жадно потреблять дополнительные выражения "и".
Start
= ScopeStatement
// qmake scope statement
ScopeStatement
= bool:BooleanExpression ws* state:Statement { return {bool:bool, state:state} }
Statement
= ":" ws* state:SingleLineStatement { return state }
SingleLineStatement
= left:Identifier ws* "=" ws* right:Identifier lb* { return {type: "ASSIGN", left:left, right:right} }
MultiLineStatement
= (SingleLineStatement lb*)+
// qmake condition statement
BooleanExpression
= BooleanOrExpression
BooleanOrExpression
= left:BooleanAndExpression ws* "|" ws* right:BooleanOrExpression { return {type: "OR", left:left, right:right} }
/ BooleanAndExpression
BooleanAndExpression
= left:BooleanNotExpression ws* !(":" ws* SingleLineStatement) ":" ws* right:BooleanAndExpression { return {type: "AND", left:left, right:right} }
/ BooleanNotExpression
BooleanNotExpression
= "!" ws* operand:BooleanNotExpression { return {type: "NOT", operand: operand } }
/ BooleanComplexExpression
BooleanComplexExpression
= Identifier
/ "(" logical_or:BooleanOrExpression ")" { return logical_or; }
Identifier
= token:[a-zA-Z0-9_]+ { return token.join(""); }
ws
= [ \t]
lb
= [\r\n]