Нужна помощь на парсере LALR парсере - PullRequest
0 голосов
/ 04 июля 2018

Я пытаюсь собрать инструмент-скрипт с ply. Однако я заблокирован некоторыми ошибками синтаксического анализа. Перемещая эти функции p_xxx в разные места, я получаю разные ошибки синтаксиса. Может ли кто-нибудь оказать добрую помощь?

например. Если я перемещу p_funcall после других функций p_xxx, то получу:

Синтаксическая ошибка в '(', lineno 4, pos 8, data ' весело (arg1, arg2, arg3) «

Ниже приведен исходный код:

    #!/usr/bin/python
# -*- coding: UTF-8 -*-

__version__ = '3.16'

import sys
sys.path.insert(0, "..")

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

import dumper

class TdsParser(object):
  '''
  Base class for a lexer/parser that has the rules defined as methods
  '''
  def __init__(self, **kw):
    self.debug = kw.get('debug', 0)
    self.names = {}
    try:
      modname = os.path.split(os.path.splitext(__file__)[0])[
        1] + "_" + self.__class__.__name__
    except:
      modname = "parser" + "_" + self.__class__.__name__
    self.debugfile = modname + ".dbg"
    self.tabmodule = modname + "_" + "parsetab"
    print(self.debugfile, self.tabmodule)

    # Build the lexer and parser
    self.lexer = lex.lex(module=self, debug=self.debug)
    self.yaccer = yacc.yacc(module=self,
          debug=self.debug,
          debugfile=self.debugfile,
          tabmodule=self.tabmodule)

  tokens = (   
    'COMMA', 'COLON', 
    'LPAREN', 'RPAREN', 'LBRACE', 'RBRACE',
    'ATOM', 'NUMBER',
  )

  # Tokens

  # Tokens

  t_COMMA = r','
  t_COLON = r':'

  def t_LPAREN(self, t):
    r'\('
    return t

  def t_RPAREN(self, t):
    r'\)'
    return t

  def t_ATOM(self, t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    return t

  def t_NUMBER(self, t):
    r'\d+|0[Bb][01]+|0[oO][0-7]|0[xX][a-f0-9]'
    return t

  def t_newline(self, t):
    r'\n+'
    t.lexer.lineno += len(t.value)

  t_ignore  = ' \t'

  def t_error(self, t):
    print("Illegal character '%s', lineno %d, pos %d, data '%s'" 
           % (t.value, t.lexer.lineno, t.lexer.lexpos, t.lexer.lexdata))
    t.lexer.skip(1)

  def t_COMMENT(self, t):
    r'\#.*'
    pass

  #yacc parser


  def p_funcall(self, p):
    '''
    funcall : ATOM LPAREN arglist RPAREN
         | ATOM LPAREN RPAREN
    '''
    print("\n###", sys._getframe().f_code.co_name, "###\n")
    pass

  def p_varlist(self, p) :
    '''          
    varlist : varlist expression
         | expression
    '''
    print("\n###", sys._getframe().f_code.co_name, "###\n")
    pass    

  def p_arglist(self, p) :
    '''          
    arglist : arglist COMMA expression
         | expression
    '''    
    print("\n###", sys._getframe().f_code.co_name, "###\n")
    pass


  def p_expression(self, p) :
    '''
    expression : ATOM
         | NUMBER
    '''
    print("\n###", sys._getframe().f_code.co_name, "###\n")
    dumper.dump(p[1])
    pass

  def p_error(self, p):
    if p:
      #dumper.dump(p)
      print("Syntax error at '%s', lineno %d, pos %d, data '%s'" 
           % (p.value, p.lexer.lineno, p.lexer.lexpos, p.lexer.lexdata))
    else:
      print("Syntax error at EOF")

  def yacc_input(self, data):
    self.yaccer.parse(data)

if __name__ == "__main__":      
  data = 'fun(arg1,arg2,arg3)'  
  tdsparser = TdsParser()  
  tdsparser.yacc_input(data)

Спасибо.

1 Ответ

0 голосов
/ 04 июля 2018

Первая продукция в первой функции синтаксического анализатора в вашем файле определяет цель синтаксического анализа: при каждом вызове анализатор будет пытаться проанализировать один экземпляр начального нетерминала.

Как написано в вопросе, парсер распознает (один) funcall. Но если вы переместите p_funcall в другое место, вы в конечном итоге создадите парсер, который пытается найти другой начальный символ. Если вы больше ничего не двигаете, это будет varlist, а ваш ввод совсем не похож на varlist.

Вы также можете явно объявить начальный символ. Подробнее см. Руководство Ply .

...