使用PLY.yacc进行适当的解析策略

时间:2016-06-25 00:25:36

标签: php python parsing lexical-analysis ply

我正在PLY中编写一个PHP解析器,以便自己学习lexing / parsing的概念。

我为一个非常简单的PHP代码片段创建了词法分析器代码,但我仍然坚持正确的解析方法。

以下是我尝试lex / parse的代码段:

  <?php if (isset($_REQUEST['name'])){
        $name = $_REQUEST['name'];
        $msg = "Hello, " . $name . "!";
        $encoded = htmlspecialchars($msg);
  }
  ?>

我的目标是跟踪用户输入以确定确实已达到htmlspecialchars()方法。我目前的解析策略让我解析到第2行

$name = $_REQUEST['name'];

但我不知道解析第3行的正确方法是什么:

$msg = "Hello, " . $name . "!";

复杂的是,我永远无法确定我的用户输入会发生多少次连接,而且我觉得这对于#34;硬编码&#34;是错误的。只是为了成功解析示例代码。例如,对于这一行,我感兴趣的是$msg变量包含我的用户提供的数据(来自$name变量)

我尝试以最糟糕的方式解析此令牌,只是为了测试我是否能够达到它但是当我运行我的脚本时它会显示WARNING: Symbol 'wrong' is unreachable

def p_wrong(p):
    '''wrong : VARIABLE EQUALS QUOTED_ENCAPSED_STRING DOT VARIABLE DOT QUOTED_ENCAPSED_STRING SEMICOLON'''
    print "wrong"

所以我希望得到指导我如何理解如何以这样的方式解析第3行,以至于它对我正在跟踪的变量进行了多少次连接或其他操作无关紧要。我有一种感觉,这是关于BNF语法或解析的奇妙痛苦复杂性的教训将开始的地方。但我想知道我不知道从哪里开始。

此处是我的完整代码:

import ply.lex as lex
import ply.yacc as yacc

string = """<?php if (isset($_REQUEST['name'])){
               $name = $_REQUEST['name'];
               $msg = "Hello, " . $name . "!";
               $encoded = htmlspecialchars($msg);
}
?>"""

delimeters = ('LPAREN', 'RPAREN', 'LBRACKET', 'RBRACKET')

tokens = delimeters + (
    "CHAR",
    "NUM",
    "OPEN_TAG",
    "CLOSE_TAG",
    "VARIABLE",
    "CONSTANT_ENCAPSED_STRING",
    "ENCAPSED_AND_WHITESPACE",
    "QUOTED_ENCAPSED_STRING",
    "LCURLYBRACKET",
    "RCURLYBRACKET",
    "EQUALS",
    "SEMICOLON",
    "QUOTE",
    "DOT",
    "IF"
)

t_ignore         = " \t"
t_CHAR           = r"[a-z]"
t_LPAREN         = r'\('
t_RPAREN         = r'\)'
t_RBRACKET       = r'\]'
t_LBRACKET       = r'\['
t_RCURLYBRACKET  = r'\}'
t_LCURLYBRACKET  = r'\{'
t_EQUALS         = r'='
t_SEMICOLON      = r';'
t_DOT            = r'\.'


def t_newline(t):
    r'\n+'
    t.lexer.lineno += t.value.count("\n")

def t_CONSTANT_ENCAPSED_STRING(t):
    r"'([^\\']|\\(.|\n))*'"
    t.lexer.lineno += t.value.count("\n")
    return t

def t_QUOTED_ENCAPSED_STRING(t):
    r"""\"([^\\"]|\\(.|\n))*\""""
    t.lexer.lineno += t.value.count("\n")
    return t

def t_OPEN_TAG(t):
    r'<[?%]((php[ \t\r\n]?)|=)?'
    if '=' in t.value: t.type = 'OPEN_TAG_WITH_ECHO'
    t.lexer.lineno += t.value.count("\n")
    return t

def t_CLOSE_TAG(t):
    r'[?%]>\r?\n?'
    t.lexer.lineno += t.value.count("\n")
    #t.lexer.begin('INITIAL')
    return t

def t_VARIABLE(t):
    r'\$[A-Za-z_][\w_]*'
    return t

def t_NUM(t):
    r"\d+"
    t.value = int(t.value)
    return t

def t_error(t):
    print t.lexer.current_state
    print dir(t.lexer)
    raise TypeError("unknown char '%s'"%(t.value))

lexer = lex.lex()

lex.input(string)
for tok in iter(lex.token, None):
    print repr(tok.type), repr(tok.value)


##now for the parsing

"""
$name = $_REQUEST['name'];
$msg = "Hello, " . $name . "!";    
"""

def p_assign(p):
    '''assign : VARIABLE EQUALS input'''
    print "assign rule"
    print p[1],p[2],p[3]
    p[0] = p[1]

def p_input(p):
    '''input : VARIABLE LBRACKET CONSTANT_ENCAPSED_STRING RBRACKET SEMICOLON
             | VARIABLE LBRACKET QUOTED_ENCAPSED_STRING RBRACKET SEMICOLON'''
    print "input rule"
    value =  p[1]+p[2]+p[3]+p[4]+p[5]
    p[0] = value

def p_wrong(p):
    '''wrong : VARIABLE EQUALS QUOTED_ENCAPSED_STRING DOT VARIABLE DOT QUOTED_ENCAPSED_STRING SEMICOLON'''
    print "wrong"    


yacc.yacc()
yacc.parse(string)

结果:

...
WARNING: There is 1 unused rule
WARNING: Symbol 'wrong' is unreachable
Generating LALR tables
yacc: Syntax error at line 6, token=OPEN_TAG
input rule
assign rule
$name = $_REQUEST['name'];
yacc: Syntax error at line 8, token=VARIABLE

我(不正确)尝试解析第3行(在解析器规则p_wrong中使用硬编码的格式)甚至不会被命中。但我想就如何继续解析这个简单的代码块提供一些指导。

所需的输出

理想情况下,我会得到允许我跟踪用户输入的结果:

user-input -> $name -> $msg -> htmlspecialchars($msg)

1 个答案:

答案 0 :(得分:0)

要解析具有任意数量的点的变量,我们可以使用如下的递归定义:

expression : VARIABLE
           | VARIABLE DOT expression