用Python为新的编程语言编写词法分析器

时间:2019-04-08 10:15:39

标签: python programming-languages lexer

我不知道如何/从哪里开始。我应该使用python,更具体地说是ply库。到目前为止,我所做的所有工作都是创建将成为语言一部分的标记列表。该列表如下:


tokens = (
                                                         # OPERATORS #
    'PLUS' ,        # +
    'MINUS' ,       # -
    'MULTIPLY',     # *
    'DIVIDE',       # /
    'MODULO',       # %


    'NOT',          # ~
    'EQUALS',       # =

                                                         # COMPARATORS #
    'LT',           # <
    'GT',           # >
    'LTE',          # <=
    'GTE',          # >=
    'DOUBLEEQUAL',  # ==
    'NE',           # #


    'AND',          # &
    'OR',           # |
                                                    # CONDITIONS AND LOOPS #    

    'IF',           # if
    'ELSE',         # else
    'ELSEIF',       # elseif
    'WHILE',        # while
    'FOR',          # for
#   'DOWHILE',      # haven't thought about this yet
                                                          # BRACKETS #

    'LPAREN',       # (
    'RPAREN',       # )
    'LBRACE',       # [
    'RBRACE',       # ]
    'BLOCKSTART',   # {
    'BLOCKEND',     # }
                                                        # IDENTIFIERS #

    'INTEGER',      # int
    'DOUBLE',       # dbl
    'STRING',       # str
    'CHAR',         # char

    'SEMICOLON',    # ;
    'DOT',          # .
    'COMMA',        # ,
    'QUOTES',       # '
    'DOUBLEQUOTES', # "
    'COMMENTLINE',  # --

    'RETURN',       # return

)

显然,我还有很长的路要走,因为我还需要编写一个解析器和一个解释器。

我有几个问题:

  1. 如何使用层库?
  2. 这是一个良好的开端吗?如果是的话,我应该从这个开始做些什么?
  3. 有没有什么我可以用来帮助我的资源。

我已经尝试过使用谷歌搜索技术来编写新的编程语言,但还没有找到令人满意的方法

1 个答案:

答案 0 :(得分:1)

  

如何使用层库?

假设您已经安装了Ply,则应该从浏览official Ply website上的教程开始。它们写得很好并且易于遵循。

  

这是一个好的开始吗?如果是的话,我该怎么做?

Ply需要以令牌定义开头。您已经完成了。但是,当您的词法分析器必须区分诸如“ forget”之类的字符串与诸如for之类的保留关键字时,复杂性会增加。该库为变量优先级提供了良好的支持,以解决语法歧义。这就像定义元组的优先级一样容易:

precedence = (
 ('left', 'STRING', 'KEYWORD'),
 ('left', 'MULTIPLY', 'DIVIDE')
 )

但是,我建议您在深入了解Ply中的表达式优先级等更高级的功能之前,应该先阅读有关lexers and yacc的更多信息。首先,您应该构建一个简单的数字词法分析器,以成功地解析整数,运算符和方括号符号。我已经减少了令牌定义以适合此目的。以下示例已根据官方教程进行了修改。

  • 库导入和令牌定义

    import ply.lex as lex #library import
    # List of token names.   This is always required
    tokens = [
                                                         # OPERATORS #
    'PLUS' ,        # +
    'MINUS' ,       # -
    'MULTIPLY',     # *
    'DIVIDE',       # /
    'MODULO',       # %
    
    
    'NOT',          # ~
    'EQUALS',       # =
    
                                                         # COMPARATORS #
    'LT',           # <
    'GT',           # >
    'LTE',          # <=
    'GTE',          # >=
    'DOUBLEEQUAL',  # ==
    'NE',           # !=
    'AND',          # &
    'OR' ,          # |                                                
                                                          # BRACKETS #
    
    'LPAREN',       # (
    'RPAREN',       # )
    'LBRACE',       # [
    'RBRACE',       # ]
    'BLOCKSTART',   # {
    'BLOCKEND',     # }
                                                        # DATA TYPES#
    
    'INTEGER',      # int
    'FLOAT',       # dbl
    
    'COMMENT',  # --
    
    ]
    
  • 为简单标记定义正则表达式规则:Ply使用re Python库查找用于标记化的正则表达式匹配项。每个令牌都需要一个正则表达式定义。我们首先为简单标记定义正则表达式定义。每个规则声明都以特殊的前缀t_开头,以表明它定义了一个令牌。

    # Regular expression rules for simple tokens
    
    t_PLUS    = r'\+'
    t_MINUS   = r'-'
    t_MULTIPLY   = r'\*'
    t_DIVIDE  = r'/'
    t_MODULO = r'%'
    t_LPAREN  = r'\('
    t_RPAREN  = r'\)'
    t_LBRACE = r'\['
    t_RBRACE = r'\]'
    t_BLOCKSTART = r'\{'
    t_BLOCKEND = r'\}'
    t_NOT = r'\~'
    t_EQUALS = r'\='
    t_GT = r'\>'
    t_LT = r'\<'
    t_LTE = r'\<\='
    t_GTE = r'\>\='
    t_DOUBLEEQUAL = r'\=\='
    t_NE = r'\!\='
    t_AND = r'\&'
    t_OR = r'\|'
    t_COMMENT = r'\#.*'            
    t_ignore  = ' \t' ignore spaces and tabs
    
  • 为更复杂的令牌定义正则表达式规则,例如数据类型(例如int,float和换行符)以跟踪行号。您会注意到这些定义与上面的非常相似。

    #Rules for INTEGER and FLOAT tokens
    def t_INTEGER(t):
        r'\d+'
        t.value = int(t.value)    
        return t
    
    def t_FLOAT(t):
        r'(\d*\.\d+)|(\d+\.\d*)'
        t.value = float(t.value)
        return t        
    
    # Define a rule so we can track line numbers
    def t_newline(t):
        r'\n+'
        t.lexer.lineno += len(t.value)
    
  • 为无效字符添加一些错误处理

    # Error handling rule
    def t_error(t):
        print("Illegal character '%s'" % t.value[0])
        t.lexer.skip(1)
    
  • 构建词法分析器

    lexer = lex.lex()
    
  • 使用一些输入数据测试词法分析器,标记化并打印标记

    data = '''
    [25/(3*40) + {300-20} -16.5]
    {(300-250)<(400-500)}
    20 & 30 | 50
    # This is a comment
    '''
    
    # Give the lexer some input
    lexer.input(data)
    
    # Tokenize
    for tok in lexer:
        print(tok)
    

您可以将此示例代码添加到new_lexer.py之类的Python脚本文件中,然后像python new_lexer.py那样运行。您应该获得以下输出。请注意,输入数据由换行符('\n')组成,这些字符在输出中已被成功忽略。

    #Output
    LexToken(LBRACE,'[',2,1)
    LexToken(INTEGER,25,2,2)
    LexToken(DIVIDE,'/',2,4)
    LexToken(LPAREN,'(',2,5)
    LexToken(INTEGER,3,2,6)
    LexToken(MULTIPLY,'*',2,7)
    LexToken(INTEGER,40,2,8)
    LexToken(RPAREN,')',2,10)
    LexToken(PLUS,'+',2,12)
    LexToken(BLOCKSTART,'{',2,14)
    LexToken(INTEGER,300,2,15)
    LexToken(MINUS,'-',2,18)
    LexToken(INTEGER,20,2,19)
    LexToken(BLOCKEND,'}',2,21)
    LexToken(MINUS,'-',2,23)
    LexToken(INTEGER,16,2,24)
    LexToken(FLOAT,0.5,2,26)
    LexToken(RBRACE,']',2,28)
    LexToken(BLOCKSTART,'{',3,30)
    LexToken(LPAREN,'(',3,31)
    LexToken(INTEGER,300,3,32)
    LexToken(MINUS,'-',3,35)
    LexToken(INTEGER,250,3,36)
    LexToken(RPAREN,')',3,39)
    LexToken(LT,'<',3,40)
    LexToken(LPAREN,'(',3,41)
    LexToken(INTEGER,400,3,42)
    LexToken(MINUS,'-',3,45)
    LexToken(INTEGER,500,3,46)
    LexToken(RPAREN,')',3,49)
    LexToken(BLOCKEND,'}',3,50)
    LexToken(INTEGER,20,4,52)
    LexToken(AND,'&',4,55)
    LexToken(INTEGER,30,4,57)
    LexToken(OR,'|',4,60)
    LexToken(INTEGER,50,4,62)
    LexToken(COMMENT,'# This is a comment',5,65)

您还可以使用许多其他功能。例如,可以使用lex.lex(debug=True)启用调试。官方教程提供了有关这些功能的更多详细信息。

我希望这有助于您入门。您可以进一步扩展代码,使其包含诸如ifwhile之类的保留关键字,并以STRING标识字符串,以CHAR标识字符。这些教程通过定义如下的键值字典映射来介绍保留字的实现:

    reserved = {
    'if' : 'IF',
    'then' : 'THEN',
    'else' : 'ELSE',
    'while' : 'WHILE',
    ...
    }

通过将保留令牌type定义为'ID'并包括reserved字典值:tokens.append('ID')tokens = tokens + list(reserved.values()),进一步扩展令牌列表。然后,如上所述为t_ID添加一个定义。

  

我有什么资源可以帮助我解决这个问题。

有很多资源可用于学习词法分析器,解析器和编译器。您应该从一本涵盖理论和实现的好书开始。有很多书籍涵盖了这些主题。我喜欢这个oneHere's可能会有帮助的另一资源。如果您想探索类似的Python库或资源,此SO answer可能会有所帮助。