2016-02-28 3 views
0

Im new to Parsers и у меня проблема с моим парсером, особенно когда я называю себя, чтобы анализировать тело функции.Python Parser Recursion Infinite Reference

Когда он находит другую функцию, просто стал сумасшедшим и грязным.

В основном, при анализе этого кода

fn test (a, b, c): 
    fn test2 (c, b, a): 
     print("Hello world") 
    end 
end 

Он начинает указывать объект сам по себе, а не подфункции:

>>> print(ast[0].value.body[9]) 
<ast.VariableAST object at 0x7f6285540710> 
>>> print(ast[0].value.body[9].value.body[9]) 
<ast.VariableAST object at 0x7f6285540710> 

Это основной код парсера:

# Parser Loop 
def Parser(tokenList): 
    global tokens 
    global size 
    tokens = tokenList 
    size = len(tokens) 
    ast = [] 
    while i < size: 
     ast.append(MainParser()) 
    return ast 


# The Main Parser 
def MainParser(): 
    global i 
    if tokens[i] == 'fn': 
     i += 1 
     node = FunctionParser() 
    else: 
     node = tokens[i] 
     i += 1 
    return node 


# Parse a function 
def FunctionParser(): 
    global i 
    checkEnd("function") 
    if tokens[i][0].isalpha(): 
     node = VariableAST() 
     node.name = tokens[i] 
     i += 1 
     node.value = FunctionBodyParser() 
    elif tokens[i] == '(': 
     node = FunctionBodyParser() 
    else: 
     syntaxError("Expecting '(' or function name") 
    return node 


# Parse a function body 
def FunctionBodyParser(): 
    global i 
    i += 1 
    node = FunctionAST() 
    while True: 
     checkEnd("function") 
     if tokens[i][0].isalpha(): 
      node.args.append(tokens[i]) 
      i += 1 
     elif tokens[i] == ',': 
      i += 1 
     elif tokens[i] == ')': 
      break 
     else: 
      syntaxError("Expecting ')' or ',' in function declaration") 
    i += 1 
    checkEnd("function") 
    if tokens[i] != ':' and tokens[i] != '{': 
     syntaxError("Expecting '{' or ':'") 
    begin = tokens[i] 
    while True: 
     checkEnd("function") 
     if begin == ':' and tokens[i] == 'end': 
      break 
     elif begin == '{' and tokens[i] == '}': 
      break 
     else: 
      node.body.append(MainParser()) 
    i += 1 
    return node 

Редактировать: Я забыл упомянуть, что это прототип для версии C. Im избегая вещей, связанных с ориентацией объектов и некоторыми хорошими pratices в python, чтобы упростить перенос кода на C позже.

+1

Прежде чем идти дальше, и независимо от того, собираетесь ли вы использовать библиотеку синтаксического анализа или писать с нуля, напишите BNF языка, который вы планируете анализировать. Создайте серию тестовых примеров от очень простых до сложных и убедитесь, что BNF описывает их правильно. Это будет служить дорожной картой при реализации вашего синтаксического анализатора и поможет вам узнать, когда вы закончите (или, по крайней мере, готовы к дальнейшему расширению языка). – PaulMcG

ответ

0

Я нашел решение,

на самом деле это была глупая ошибка программирования на Python и выше анализатор работает нормально.

В AST я создавал классы, подобные структуре, чтобы упростить перенос языка на C.Но я делал это неправильно, например:

class VariableAST(NodeAST): 
    name = None 
    value = None 

Что я не знал о Python является то, что эти параметры внутри класса Арент атрибуты объекта, а статические переменные, которым только что сделал мою программу работы непредсказуемо, когда я Ассинг значение для объекта, так как im для статической переменной, я также рассматриваю все другие переменные, а theres - моя бесконечная рекурсия объектов.

+1

Чтобы они были переменными экземпляра, вам нужно создать метод, обычно 'def __init __ (self):', и назначить их как 'self.name = None' и' self.value = None'. Это должно быть рассмотрено в большинстве учебных пособий Python. – PaulMcG

1

В Python https://wiki.python.org/moin/LanguageParsing есть много парсера, например PyPEG, что позволяет вам описывать язык, который вы разбираете, вместо того, чтобы разбирать его самостоятельно, что более читаемо и менее подвержено ошибкам.

Также использование global обычно является источником проблем, так как вы не можете контролировать область действия переменной (есть только одна), что уменьшает возможность повторного использования ваших функций.

Это, вероятно, лучше использовать класс, который почти то же самое, но вы можете иметь несколько экземпляров одного и того же класса, выполняющихся в то же время без переменной colisions:

class Parser: 
    def __init__(self, tokenList): 
     self.tokens = tokenList 
     self.size = len(tokenList) 
     self.ast = [] 
     self.position = 0 

    def parse(tokenList): 
     while self.position < self.size: 
      self.ast.append(self.main()) 
     return self.ast 

    def main(self): 
     if self.tokens[self.position] == 'fn': 
      self.position += 1 
      node = self.function() 
     else: 
      node = self.tokens[self.position] 
      self.position += 1 
     return node 

    # And so on... 

С этого момента вы можете Дедуплицировать self.position + = 1 в главном:

def main(self): 
     self.position += 1 
     if self.tokens[self.position] == 'fn': 
      node = self.function() 
     else: 
      node = self.tokens[self.position] 
     return node 

Затем удалить бесполезный "узел" переменный:

def main(self): 
     self.position += 1 
     if self.tokens[self.position] == 'fn': 
      return self.function() 
     else: 
      return self.tokens[self.position] 

Но реальный способ сделать это - использовать парсер, взглянуть на pyPEG, он хороший, но другие тоже приятны.

Ну и последний пункт, избежать бесполезных комментариев типа:

# Parse a function 
def FunctionParser(): 

Мы знаем, что «FunctionParser» «Синтаксическая функция», спасибо, это не информационную. Самое главное - разумно выбирать имена своих функций (oh, PEP8 говорит нам не начинать имя метода с заглавными буквами), и если вы хотите добавить метаинформацию о функции, поместите ее в строку в качестве первого оператора в ваша функция, как:

def functionParser(): 
    "Delegates the body parsing to functionBodyParser" 
+0

Привет, мой друг, Я должен сказать это в теме (я отредактирую), это прототип для версии C, с которой у меня нет доступа к классам или любому конструктору парсеров (я просто избегаю этого, потому что разжигание язык). Но спасибо в любом случае, и я буду использовать некоторые советы, которые вы передали мне. – h0m3

+1

@ h0m3 Вы серьезно говорите мне, что в C нет парсера? Их много: https://en.wikipedia.org/wiki/Comparison_of_parser_generators, вероятно, в C больше парсеров, чем в Python. Некоторые (например, ANTLR3) могут даже производить из одного и того же описания C И Python, чтобы вы могли тестировать один и тот же код в своих тестах Python и реальном C impl. –

+0

Конечно, вы совершенно правы. Парсер, который я хочу сделать сам, для изучения пропусков и размера кода (язык довольно прост, и я хочу сохранить его как можно меньше. И я думаю, что не в первый раз, но когда я узнаю больше, парсер может фактически быть меньше, чем один, созданный генератором синтаксического анализатора). Я пытаюсь избежать использования чего-либо за пределами C lib. – h0m3