Я пишу синтаксический анализатор PHP на языке PLY, чтобы научить себя принципам лексизации / разбора.
У меня есть токены лексера, созданные для очень простого фрагмента кода PHP, но я застрял на правильном способе анализа.
Вот фрагмент кода, который я пытаюсь проанализировать:
<?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)
Задача ещё не решена.
Других решений пока нет …