解析PHP语法的语法,它可以处理语法中的PHP开始和结束标记(“<! - ?”和“? - >”)

时间:2015-05-26 17:18:26

标签: parsing grammar context-free-grammar lexical-analysis

我试图理解如何定义类似PHP的语法。在PHP中,可以退出PHP模式进入H​​TML模式,然后再进入PHP模式。

为了提出这个问题,我正在定义类似PHP的语言 太荒谬了。在下面这个问题的其余部分中,这种语言将被称为“类似PHP”。

它只包含一个构造:if (expression) { block_list },即 if语句。 block_list是嵌套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>

这是另一个例子,展示了我们如何在if语句中脱离类似PHP的模式进入H​​TML模式。

<?  if (expression1) {     ?>
        some html here
<?      if (expession2) { ?>
            some html here
<?      } 
    }
?>

为了实现这一点,我有一个能够识别以下标记的词法分析器。

HTML       = All characters from the beginning of the code or the last
             occurrence of "?>" to the end of the code or the next
             occurrence of "<?". Zero length string is allowed.

IDENTIFIER = [_a-zA-Z][_a-zA-Z0-9]*  i.e. C identifier, a sequence
                                     of underscores, letters and
                                     digits such that the first
                                     character is not a digit.

WHITESPACE = [ \t\r\n]+              i.e. a sequence of spaces, tabs
                                     and newlines.

BEGIN      = "<?"

END        = "?>"

IF         = "if"

LPAREN     = "("

RPAREN     = ")"

LBRACE     = "{"

RBRACE     = "}"
词法分析器输出每个HTML块(即类似PHP的模式之外的东西)作为标记,即整个HTML块是单个标记。它不输出WHITESPACE。它不会在每个类似PHP的模式下输出开始<?和结束?>,即它不输出第一次出现的BEGIN和下一次出现的END。一旦达到END,其后面的任何内容将再次被解析为HTML,直到下一次出现BEGIN。

因此,对于这个问题中的第二个代码示例,词法分析器输出它。

代码:

<?  if (expression1) {     ?>
        some html here
<?      if (expession2) { ?>
            some html here
<?      } 
    }
?>

Lexer输出:

HTML        ""
IF          "if" 
LPAREN      "("
IDENTIFIER  "expression1" 
RPAREN      ")"
LBRACE      "{"
HTML        "\n        some html here\n"
IF          "if"
LPAREN      "("
...

不输出BEGIN和END标记使解析器语法简单。现在我可以使用以下语法解析这些令牌。由于解析器不必处理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;

但是,我想在词法分析器中输出BEGIN和END标记。有没有一种很好的方法来为它编写语法,以便它处理嵌套的if语句,这些语句中也可能包含HTML?

我正在尝试在以下语法中处理词法分析器输出中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
    }
?>

我有两个问题。

  1. 如果词法分析器输出BEGIN和END标记,我该如何编写语法来处理它们?
  2. 最好不输出BEGIN和END令牌,以便语法保持简单吗?

1 个答案:

答案 0 :(得分:2)

假设你的词法分析器仍然是有状态的,那么HTMLEND之间的文本会发出一个BEGIN标记,语法几乎没有差别。< / p>

除了第一个和最后一个HTML令牌之外,每个其他HTML令牌前面都会有END,后面跟BEGIN。换句话说,我们有:

html: END HTML BEGIN;

轻微的复杂性是我们需要处理第一个和最后一个HTML令牌,这意味着我们需要一个新的非终端(它将是起始符号):

program: HTML BEGIN block_list END HTML;

语法的其余部分与原始语法相同,只是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;

如果关联文本为空字符串,新词法分子不再发出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 */