[英]Proper Parsing Strategy using PLY.yacc
我正在用 PLY 編寫一個 PHP 解析器,以便自學詞法/解析的概念。
我為一個非常簡單的 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 . "!";
復雜的是,我永遠無法確定在我的用戶輸入上會發生多少級聯,而且我覺得僅僅為了成功解析示例代碼而“硬編碼”是錯誤的。 例如,對於這一行,我對$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)
要解析具有任意數量點的變量,我們可以使用如下遞歸定義:
expression : VARIABLE
| VARIABLE DOT expression
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.