Parsing 用于解析类PHP语言的语法,以便它可以处理语法中的PHP开始和结束标记(“lt;”?“和“?”gt;”)

Parsing 用于解析类PHP语言的语法,以便它可以处理语法中的PHP开始和结束标记(“lt;”?“和“?”gt;”),parsing,grammar,context-free-grammar,lexical-analysis,Parsing,Grammar,Context Free Grammar,Lexical Analysis,我试图理解如何定义类似PHP的语法。在PHP中,可以从PHP模式进入HTML模式,然后再回到PHP模式 为了问这个问题,我定义了我的PHP语言 简单得可笑。在下面这个问题的剩余部分,这种语言将被称为“类似PHP” 它只包含一个构造:if(expression){block_list},即。 if语句。block_列表是一系列嵌套的if语句, 表达式或HTML。同样,为了保持语言的简单性,一个 表达式必须是标识符 下面是一个示例,显示了此语言中的有效代码。在这里,HTML后面跟着两个嵌套的if语句

我试图理解如何定义类似PHP的语法。在PHP中,可以从PHP模式进入HTML模式,然后再回到PHP模式

为了问这个问题,我定义了我的PHP语言 简单得可笑。在下面这个问题的剩余部分,这种语言将被称为“类似PHP”

它只包含一个构造:
if(expression){block_list}
,即。 if语句。block_列表是一系列嵌套的if语句, 表达式或HTML。同样,为了保持语言的简单性,一个 表达式必须是标识符

下面是一个示例,显示了此语言中的有效代码。在这里,HTML后面跟着两个嵌套的if语句,后面跟着另一个HTML

<body><p>Some HTML text here</p>
<?
    if (expression1) {
        if (expression2) {
            expression3
        }
    }
?>
</p>Some more HTML text here</p></body>
Lexer输出:

HTML        ""
IF          "if" 
LPAREN      "("
IDENTIFIER  "expression1" 
RPAREN      ")"
LBRACE      "{"
HTML        "\n        some html here\n"
IF          "if"
LPAREN      "("
...
不输出开始和结束标记使解析器语法保持简单。现在,我可以使用以下语法解析这些标记。由于解析器不必处理BEGIN和END标记,因此不必在语法中的任何地方提及它们。它使语法保持简单

block_list   = block | block_list block;
block        = HTML | if_statement | expression;
if_statement = IF LPAREN expression RPAREN LBRACE block_list RBRACE;
expression   = IDENTIFIER;
但是,比方说,我想在lexer中输出BEGIN和END标记。有没有一种好方法可以为它编写语法,这样它就可以处理嵌套的if语句,这些语句中可能也包含HTML

我试图在下面的语法中处理lexer输出中存在的BEGIN和END标记,但是我无法找到一个有效的语法

block_list   = block | block_list block;
block        = HTML | php_like | code;
php_like     = BEGIN code | BEGIN code END;
code         = if_statement | expression;
if_statement = IF LPAREN expression RPAREN LBRACE block_list RBRACE |
               IF LPAREN expression RPAREN LBRACE END block_list RBRACE |
               IF LPAREN expression RPAREN LBRACE END block_list BEGIN RBRACE
expression   = IDENTIFIER;
上述语法允许在这个问题中使用上述两个代码示例。但它也允许以下无效代码

<?
    if (expression1) {
        <? expression2
    }
?>

我有两个问题

  • 如果lexer输出BEGIN和END标记,我如何编写语法来处理它们
  • 最好不要输出BEGIN和END标记,以便语法保持简单

  • 假设您的lexer仍然是有状态的,因此将为
    END
    BEGIN
    之间的文本发出一个
    HTML
    标记,语法上几乎没有差异

    除了第一个和最后一个
    HTML
    标记外,每隔一个
    HTML
    标记前面都会有
    END
    ,后面是
    BEGIN
    。换言之,我们有:

    html: END HTML BEGIN;
    
    稍微复杂的是,我们需要处理第一个和最后一个
    HTML
    token,这意味着我们需要一个新的非终端(这将是开始符号):

    除了
    HTML
    变成
    HTML
    之外,其余语法与原始语法相同:

    block_list   = block | block_list block;
    block        = html /* Change is here */ | if_statement | expression;
    if_statement = IF LPAREN expression RPAREN LBRACE block_list RBRACE;
    expression   = IDENTIFIER;
    
    如果在关联文本为空字符串的情况下,您的新lexer不再发出
    HTML
    标记,则需要两个替代规则:

    program: leading_html block_list trailing_html;
    leading_html: HTML BEGIN | BEGIN;
    trailing_html: END HTML | END;
    html: END HTML BEGIN | END BEGIN;
     /* Remainder as above */
    

    谢谢你提出了一个聪明的解决方案。我喜欢把
    BEGIN
    END
    作为
    html
    的一部分,而不改变语法的其余部分。你能告诉我
    html:htmlbegin | END-htmlbegin | END-html
    是否也是一种解决方案吗?如果这也是一个正确的解决方案,那么就不需要一个名为
    program
    @LoneLearner的新的非终端:它可能会工作,但我觉得它错了,因为它允许不应该允许的令牌流(即使扫描器不会产生这样的流,除非它没有按照规范运行)。另外,我认为如果你还想让扫描器在没有HTML文本的情况下不生成HTML标记(但我没有检查,所以我可能错了),那么这将导致移位减少冲突。@LoneLearner:我没有在答案中提到这一点,但事实是,我认为你最初的解决方案更好。开始和结束标记不向语法添加任何内容;扫描器在任何情况下都需要跟踪这两个上下文;因此,上下文切换符号(开始和结束)属于扫描仪。至少,这是我的观点,这就是为什么我没有回答这个问题。
    html: END HTML BEGIN;
    
    program: HTML BEGIN block_list END HTML;
    
    block_list   = block | block_list block;
    block        = html /* Change is here */ | if_statement | expression;
    if_statement = IF LPAREN expression RPAREN LBRACE block_list RBRACE;
    expression   = IDENTIFIER;
    
    program: leading_html block_list trailing_html;
    leading_html: HTML BEGIN | BEGIN;
    trailing_html: END HTML | END;
    html: END HTML BEGIN | END BEGIN;
     /* Remainder as above */