-2

这是我当前的 Python 解释器,它使用解析规则来获取输入,然后打印出表达式。解释器工作正常,但我想对我当前的语法规则进行某些更改并将其添加到新的语法规则中。到目前为止,我只能得到一些我想要的语法更改。

这是我想从我当前的语法中做出的改变:

# <stmt-list> ::= empty | <stmt> <stmt-list>
to
# <stmt_list> ::= <stmt> | <stmt> <stmt_list>


# <factor> ::= id | intnum | ( <expr> )
to
# <base> ::= (<expr>) | id | number


<stmt> ::= id = <expr> ; | print <expr>;
to
<stmt> ::= id = <expr> ; | iprint <expr> ; | rprint <expr> ;

另外我不确定如何在我的解释器中实现下面的新语法规则,我想我可能已经有了它们?

<prog> ::= <decl_list> <stmt_list>
<decl-list> ::= <decl> | <decl> <decl_list>
<decl> ::= <type> <id_list> ;
<type> ::= int | real
<id_list> ::= id | id {, <id_list>}

这是我当前语法的当前代码:

import sys

global varTable
varTable = {}

def main():
    global itProgram, nextToken, nextChar, nextLex, flagEof, strStmt
    nextToken = ""
    nextChar = ""
    flagEof = False
    strStmt = ""

    try:
        fileProgram = open(sys.argv[1], "rt")
    except IndexError:
        print "Missing input file!"
        return
    except IOError:
        print "Could not open \'" + sys.argv[1] + "\'!"
        return

    strProgram = fileProgram.read()
    itProgram = iter(strProgram)

    if strProgram == "":
        nextChar = ""
    else:
        nextChar = itProgram.next()

    #while not flagEof:
    funcLex()

    stmtList()

def funcLex():
    global itProgram, nextToken, nextLex, nextChar, flagEof, strStmt
    nextToken = ""
    nextLex = ""

    isFloat = False

    try:
        while nextChar.isspace():
            nextChar = itProgram.next()
    except StopIteration:
        nextChar = ""
        funcLex()

        return

    try:
        if nextChar == "(":
            nextToken = "LPARA"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == ")":
            nextToken = "RPARA"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == "+":
            nextToken = "ADD"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == "-":
            nextToken = "SUB"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == "*":
            nextToken = "MULT"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == "/":
            nextToken = "DIV"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == "=":
            nextToken = "ASSIGN"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar == ";":
            nextToken = "SEMI"
            nextLex = nextChar
            nextChar = itProgram.next()
        elif nextChar.isalpha():
            nextLex = nextChar
            nextChar = itProgram.next()
            while nextChar.isalnum():
                nextLex += nextChar
                nextChar = itProgram.next()
            if nextLex == "print":
                nextToken = "PRINT"
            else:
                nextToken = "ID"
        elif nextChar.isalnum():
            nextLex = nextChar
            nextChar = itProgram.next()
            while nextChar.isalnum() or nextChar == ".":
                if nextChar == ".":
                    isFloat = True
                nextLex += nextChar
                nextChar = itProgram.next()
            if isFloat:
                nextToken = "FLOAT"
            else:
                nextToken = "INT"
        elif nextChar == "":
            nextLex = nextChar
            nextToken = "EMPTY"
            flagEof = True
        else:
            nextToken = "UNKNOWN"
            #print "Syntax error!"
    except StopIteration:
        nextChar = ""

    strStmt = strStmt + nextLex + " "
    if nextToken == "SEMI":
        print strStmt
        strStmt = ""

# <stmt-list> ::= empty | <stmt> <stmt-list>
def stmtList():
    global nextToken

    if nextToken == "EMPTY":
        print ">>> Empty .tiny file."
    else:
        while nextToken != "EMPTY":
            stmt()

# <stmt> ::= id = <expr> ; |
#            print <expr> ;
def stmt():
    global nextToken, nextLex

    if nextToken == "ID":
        varName = nextLex
        funcLex()
        if nextToken == "ASSIGN":
            funcLex()
            result = expr()
            if result[1] != "UNKNOWN":
                lookupVarTable(varName, result[0], result[1])
            else:
                printError("undefined variable.")
    elif nextToken == "PRINT":
        funcLex()
        result = expr()
        if result[1] != "UNKNOWN" and nextToken == "SEMI":
            print ">>> " + str(result[0])
        elif result[1] == "UNKNOWN":
            printError("undefined variable.")
    else:
        printError("<stmt> syntax error.")
        return

    if nextToken == "SEMI":
        funcLex()
    else:
        printError("<stmt> missing ';'")

# <expr> ::= <term> { + <term> | - <term> }
def expr():
    global nextToken, nextLex

    lResult = term()

    while nextToken == "ADD" or nextToken == "SUB":
        operator = nextToken
        funcLex()
        rResult = term()
        #Variable is not defined
        if lResult[1] == "UNKNOWN" or rResult[1] == "UNKNOWN":
            printError("Undefined variable!")
        if lResult[1] != rResult[1]:    #type mismatch
            printError("Type mismatch!")
        elif operator == "ADD":
            lResult = (lResult[0]+rResult[0], lResult[1])
        else:
            lResult = (lResult[0]-rResult[0], lResult[1])

    return lResult

# <term> ::= <factor> { * <factor> | / <factor> }
def term():
    global nextToken, nextLex

    lResult = factor()

    while nextToken == "MULT" or nextToken == "DIV":
        operator = nextToken
        funcLex()
        rResult = factor()
        #Variable is not defined
        if lResult[1] == "UNKNOWN" or rResult[1] == "UNKNOWN":
            printError("Undefined variable!")
        if lResult[1] != rResult[1]:    #type mismatch
            printError("Type mismatch!")
        elif operator == "MULT":
            lResult = (lResult[0]*rResult[0], lResult[1])
        else:
            lResult = (lResult[0]/rResult[0], lResult[1])

    return lResult

# <factor> ::= id | intnum | ( <expr> )
def factor():
    global nextToken, nextLex

    if nextToken == "ID":
        result = lookupVarTable(nextLex, 0, "UNKNOWN")
        funcLex()
    elif nextToken == "INT":
        result = (int(nextLex), "INT")
        funcLex()
    elif nextToken == "FLOAT":
        result = (float(nextLex), "FLOAT")
        funcLex()
    elif nextToken == "LPARA":
        funcLex()
        result = expr()
        if nextToken == "RPARA":
            funcLex()
        else:
            printError("<factor>")

    return result

def printError(strMessage):
    global strStmt

    if strStmt != "":
        print strStmt

    print ">>> Error: " + strMessage
    exit()

def lookupVarTable(varName, varValue, varType):

    #if varName not in varTable:
    # varValue == "UNKNOWN"
    if varType != "UNKNOWN":
        varTable[varName] = (varValue, varType)
        return varTable[varName]
    elif varName in varTable:
        return varTable[varName]
    else:
        return (varValue, varType)

if __name__ == "__main__":
    main()
4

1 回答 1

1

您应该考虑使用Antlr,有一个 Python 端口。

同时,这里是你如何设计你的词法分析器:

def parser_file(file_obj):
    for line in file_obj:
        for char in line:
            yield char


mapping = {'(': 'LPARA',
           ')': 'RPARA',
           '+': 'ADD',
           '-': 'SUB',
           '*': 'MUL',
           '/': 'DIV',
           '=': 'ASSIGN',
           ';': 'SEMI'}


def lexer(chars):
    it_char = iter(chars)

    char = next(it_char)

    while True:
        # skip spaces
        while char.isspace():
            char = next(it_char)

        # find simple tokens
        if char in mapping:
            yield mapping[char], char
            char = next(it_char)
            continue

        # find complex tokens
        if char.isalpha():
            lex = char
            char = next(it_char)
            while char.isalnum():
                lex += char
                char = next(it_char)
            if lex == "print":
                yield "PRINT", lex
            else:
                yield "ID", lex
            continue
        elif char.isdigit():
            lex = char
            char = next(it_char)
            while char.isdigit():
                lex += char
                char = next(it_char)
            if char == ".":
                lex += char
                char = next(it_char)
                while char.isdigit():
                    lex += char
                    char = next(it_char)
            if "." in lex:
                yield "FLOAT", lex
            else:
                yield "INT", lex
            continue
        else:
            raise SyntaxError(char)

要使用它,您可以进行如下处理:

import io

content = """\
10 + 12.5 / 18
(8 + 3.14)
"""

file_obj = io.BytesIO(content)

for token in lexer(parser_file(file_obj)):
    print(token)

你得到:

('INT', '10')
('ADD', '+')
('FLOAT', '12.5')
('DIV', '/')
('INT', '18')
('LPARA', '(')
('INT', '8')
('ADD', '+')
('FLOAT', '3.14')
('RPARA', ')')

您当然可以使用真实文件。

对于您的解析器:使用堆栈来构建抽象语法树并对其进行评估。

对不起,解释太长了,而且与 SO 无关,请考虑在Code Review上发帖。

于 2016-11-05T21:33:22.173 回答