我正在使用PLY构建PHP词法分析器,以便我能理解lexing / parsing背后的概念。我决定从一个非常简单的PHP代码块开始:
<?php if (isset($_REQUEST['name'])){
$name = $_REQUEST['name'];
$msg = "Hello, " . $name . "!";
$encoded = htmlspecialchars($msg);
}
?>
最终我的目标是解析这个,以便我可以“追踪”用户输入并验证它确实落在htmlspecialchars
函数(或我选择的任何函数)中。但在我解析之前,我需要正确生成令牌。
我特意停留在对以下行进行标记:
$msg = "Hello, " . $name . "!";
将所有内容转换为令牌的最佳方法是什么?我当前的代码将$msg
识别为变量,将=
识别为正确的令牌,但将行"Hello, " . $name . "!"
的其余部分视为一个不正确的令牌。
我对这个痛苦的lexing / parsing世界很新,所以任何帮助都会受到赞赏。这是我目前的代码和结果:
import ply.lex as lex
states = (
)
delimeters = ('LPAREN', 'RPAREN', 'LBRACKET', 'RBRACKET')
tokens = delimeters + (
"CHAR",
"NUM",
"OPEN_TAG",
"CLOSE_TAG",
"php",
"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_QUOTE(t):
r'"'
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))
string = """<?php if (isset($_REQUEST['name'])){
$name = $_REQUEST['name'];
$msg = "Hello, " . $name . "!";
$encoded = htmlspecialchars($msg);
}
?>"""
lex.lex()
lex.input(string)
for tok in iter(lex.token, None):
print repr(tok.type), repr(tok.value)
结果:
'OPEN_TAG' '<?php '
'CHAR' 'i'
'CHAR' 'f'
'LPAREN' '('
'CHAR' 'i'
'CHAR' 's'
'CHAR' 's'
'CHAR' 'e'
'CHAR' 't'
'LPAREN' '('
'VARIABLE' '$_REQUEST'
'LBRACKET' '['
'CONSTANT_ENCAPSED_STRING' "'name'"
'RBRACKET' ']'
'RPAREN' ')'
'RPAREN' ')'
'LCURLYBRACKET' '{'
'VARIABLE' '$name'
'EQUALS' '='
'VARIABLE' '$_REQUEST'
'LBRACKET' '['
'CONSTANT_ENCAPSED_STRING' "'name'"
'RBRACKET' ']'
'SEMICOLON' ';'
'VARIABLE' '$msg'
'EQUALS' '='
'QUOTED_ENCAPSED_STRING' '"Hello, " . $name . "!"'
'SEMICOLON' ';'
'VARIABLE' '$encoded'
'EQUALS' '='
'CHAR' 'h'
'CHAR' 't'
'CHAR' 'm'
'CHAR' 'l'
'CHAR' 's'
'CHAR' 'p'
'CHAR' 'e'
'CHAR' 'c'
'CHAR' 'i'
'CHAR' 'a'
'CHAR' 'l'
'CHAR' 'c'
'CHAR' 'h'
'CHAR' 'a'
'CHAR' 'r'
'CHAR' 's'
'LPAREN' '('
'VARIABLE' '$msg'
'RPAREN' ')'
'SEMICOLON' ';'
'RCURLYBRACKET' '}'
'CLOSE_TAG' '?>'
所以除了QUOTED_ENCAPSED_STRING
令牌之外,所有令牌似乎都是正确的,但我不知道如何修复它。这是“州”派上用场吗?
具体问题:
如何解决此问题,以便正确分配令牌?
将令牌分配给函数/方法名称的正确方法是什么?例如,在上面的输出中,您会看到htmlspecialchars
和isset
函数只是被视为一堆单个字符,但最终当我解决这个问题时,我会希望我的标记能够识别函数名称“很容易”。
答案 0 :(得分:0)
事实证明,我的正则表达式中有一个拼写错误导致了这个问题。
将t_QUOUTED_ENCAPSED_STRING
更新为以下内容允许应用程序将每个元素分解为自己的标记:
def t_QUOTED_ENCAPSED_STRING(t):
r"\"([^\\"]|\\(.|\n))*\""
t.lexer.lineno += t.value.count("\n")
return t
现在我运行应用程序我得到以下输出:
'OPEN_TAG' '<?php '
'CHAR' 'i'
'CHAR' 'f'
'LPAREN' '('
'CHAR' 'i'
'CHAR' 's'
'CHAR' 's'
'CHAR' 'e'
'CHAR' 't'
'LPAREN' '('
'VARIABLE' '$_REQUEST'
'LBRACKET' '['
'CONSTANT_ENCAPSED_STRING' "'name'"
'RBRACKET' ']'
'RPAREN' ')'
'RPAREN' ')'
'LCURLYBRACKET' '{'
'VARIABLE' '$name'
'EQUALS' '='
'VARIABLE' '$_REQUEST'
'LBRACKET' '['
'CONSTANT_ENCAPSED_STRING' "'name'"
'RBRACKET' ']'
'SEMICOLON' ';'
'VARIABLE' '$msg'
'EQUALS' '='
'QUOTED_ENCAPSED_STRING' '"Hello, "'
'DOT' '.'
'VARIABLE' '$name'
'DOT' '.'
'QUOTED_ENCAPSED_STRING' '"!"'
'SEMICOLON' ';'
'VARIABLE' '$encoded'
'EQUALS' '='
'CHAR' 'h'
'CHAR' 't'
'CHAR' 'm'
'CHAR' 'l'
'CHAR' 's'
'CHAR' 'p'
'CHAR' 'e'
'CHAR' 'c'
'CHAR' 'i'
'CHAR' 'a'
'CHAR' 'l'
'CHAR' 'c'
'CHAR' 'h'
'CHAR' 'a'
'CHAR' 'r'
'CHAR' 's'
'LPAREN' '('
'VARIABLE' '$msg'
'RPAREN' ')'
'SEMICOLON' ';'
'RCURLYBRACKET' '}'
'CLOSE_TAG' '?>'