2017-05-23 1 views
0

J'ai trouvé un site web [Python: Writing a Compiler and Interpreter in 160 lines of code] qui mettent en œuvre un simple interprète, je suis très impressionné par le code et que vous souhaitez améliorer, ci-dessous est la version modifiée de la source:Un simple interprète ne fonctionne pas bien

#****************** Grammer ************************ 
# stmtlist := (statement)* 
# 
# statement := 'if' condition ('{')? stmtlist ['else' stmtlist] '}' 
#    | 'while' condition ('{')? stmtlist '}' 
#    | variable '=' expression 
#    | variable '+=' expression 
#    | variable '-=' expression 
#    | variable '*=' expression 
#    | variable '/=' expression 
#    | 'print' expression 
# 
# condition := expression ('=='|'!='|'>'|'<'|'>='|'<=') expression 
# 
# expression := term ('+'|'-' term)* 
# 
# term  := factor ('*'|'/' factor)* 
# 
# factor  := variable|digit 

#****************** Lexer ************************ 

tokenlist = [] 
currtoken = ("", "", 0) # token, identifier name, value 
keywords = set(["while", "if", "else", "print", 
       "=", "==", "!=", ">", ">=", "<", "<=", 
       "+", "-", "*", "/", "+=", "-=", "*=", "/=", 
       "{", "}"]) # '}' end 'while' and 'if' 
symboltable = dict() 

def nextToken(): 
    global currtoken, symboltable 
    if(len(tokenlist) > 0): 
     s = tokenlist.pop(0) 
     if s in keywords: 
      currtoken = (s, "", 0) 
     elif s.isdigit(): 
      #currtoken = ("digit", "", int(s)) 
      currtoken = ("digit", "", float(s)) 
     elif s.isalnum(): 
      symboltable[s] = 0 
      currtoken = ("variable", s, 0) 
     else: 
      print("syntax error: " + s) 
    else: 
     currtoken = ("", "", 0) 

def consume(expected): 
    if currtoken[0] == expected: 
     nextToken() 
    else: 
     print("expected " + expected + " not found") 

#****************** Parser ************************ 

def parseFile(filename): 
    inputfile = open(filename, "r") 
    inputstring = inputfile.read() 
    global tokenlist 
    tokenlist = inputstring.split() 
    nextToken() 

    pgm = doStatementList() 
    return execStatementList(pgm) 

def doStatementList(): 
    stmts = [] 
    newstmt = [] 

    while currtoken[0] in ["while", "if", "print", "variable"]: 
     if currtoken[0] == "while": 
      # ["while", [condition], ['{'] [statementlist], '}'] 
      consume("while") 
      newstmt = ["while"] 
      newstmt.append(doCondition()) 
      if currtoken[0] == "{": 
       consume("{") 
      newstmt.append(doStatementList()) 
      #consume("endwhile") 
      consume("}") 
     elif currtoken[0] == "if": 
      # ['if' [condition] [statementlist] ['{'] [['else' statementlist]] '}'] 
      consume("if") 
      newstmt = ["if"] 
      newstmt.append(doCondition()) 
      if currtoken[0] == "{": 
       consume("{") 
      newstmt.append(doStatementList()) 
      if currtoken[0] == "else": 
       consume("else") 
       newstmt.append(doStatementList()) 
      #consume("endif") 
      consume("}") 
     elif currtoken[0] == "print": 
      # ["print", [expression]] 
      consume("print") 
      newstmt = ["print"] 
      newstmt.append(doExpression()) 
     elif currtoken[0] == "variable": 
      # ["=", [variable], [expression]] 
      variable = [currtoken[1]] 
      nextToken() 
      if currtoken[0] == "=": 
       consume("=") 
       newstmt = ["="] 
      elif currtoken[0] == "+=": 
       consume("+=") 
       newstmt = ["+="] 
      elif currtoken[0] == "-=": 
       consume("-=") 
       newstmt = ["-="] 
      elif currtoken[0] == "*=": 
       consume("*=") 
       newstmt = ["*="] 
      elif currtoken[0] == "/=": 
       consume("/=") 
       newstmt = ["/="] 
      newstmt.append(variable) 
      newstmt.append(doExpression()) 
     else: 
      print("invalid statement: " + currtoken[0]) 
     stmts.append(newstmt) 
    return stmts 

def doCondition(): 
    exp = doExpression() 
    # ["==|!=|>|<|>=|<=", [left side], [right side]] 
    if currtoken[0] in ["==", "!=", ">", ">=", "<", "<="]: 
     retval = [currtoken[0]] 
     retval.append(exp) 
     nextToken() 
     retval.append(doExpression()) 
    else: 
     print("expected == or != not found") 
    return retval 

def doExpression(): 
    term = doTerm() 
    # carry the term in case there's no +|- 
    exp = term 
    # ["+|-", [left side], [right side]] 
    while currtoken[0] in ["+", "-"]: 
     exp = [currtoken[0]] 
     nextToken() 
     exp.append(doExpression()) 
     exp.append(term) 
    print("exp=", exp) 
    return exp 

def doTerm(): 
    factor = doFactor() 
    term = factor 
    # ["*|/", [left side], [right side]] 
    while currtoken[0] in ["*", "/"]: 
     term = [currtoken[0]] 
     nextToken() 
     term.append(doTerm()) 
     term.append(factor) 
    print("term=", term) 
    return term 

def doFactor(): 
    if currtoken[0] == "variable": 
     retval = currtoken[1] 
     nextToken() 
    elif currtoken[0] == "digit": 
     retval = currtoken[2] 
     nextToken() 
    return [retval] 

#****************** Interpreter ************************ 

stack = [] 

def execStatementList(pgm): 
    for stmt in pgm: 
     execStatement(stmt) 

def execStatement(stmt): 
    if stmt[0] == "while": 
     # ["while", [condition], [statementlist]] 
     execCondition(stmt[1]) 
     while stack.pop(): 
      execStatementList(stmt[2]) 
      execCondition(stmt[1]) 
    elif stmt[0] == "if": 
     # ['if' [condition] [statementlist] [['else' statementlist]] 'endif'] 
     execCondition(stmt[1]) 
     if stack.pop(): 
      execStatementList(stmt[2]) 
     elif len(stmt) == 4: 
      execStatementList(stmt[3]) 
    elif stmt[0] == "=": 
     execExpression(stmt[2]) 
     symboltable[stmt[1][0]] = stack.pop() 
    elif stmt[0] == "+=": 
     execExpression(stmt[2]) 
     symboltable[stmt[1][0]] = symboltable[stmt[1][0]] + stack.pop() 
    elif stmt[0] == "-=": 
     execExpression(stmt[2]) 
     symboltable[stmt[1][0]] = symboltable[stmt[1][0]] - stack.pop() 
    elif stmt[0] == "*=": 
     execExpression(stmt[2]) 
     symboltable[stmt[1][0]] = symboltable[stmt[1][0]] * stack.pop() 
    elif stmt[0] == "/=": 
     execExpression(stmt[2]) 
     symboltable[stmt[1][0]] = symboltable[stmt[1][0]]/stack.pop() 
    elif stmt[0] == "print": 
     execExpression(stmt[1]) 
     print("output:" + str(stack.pop())) 
    else: 
     print("invalid statement") 

def execCondition(cond): 
    # ["==|!=|>|<|>=|<=", [left side], [right side]] 
    execExpression(cond[1]) 
    execExpression(cond[2]) 

    a = stack.pop() 
    b = stack.pop() 
    if cond[0] == "==": 
     stack.append(a == b) 
    elif cond[0] == "!=": 
     stack.append(a != b) 
    elif cond[0] == ">": 
     stack.append(b > a) 
    elif cond[0] == ">=": 
     stack.append(b >= a) 
    elif cond[0] == "<": 
     stack.append(b < a) 
    elif cond[0] == "<=": 
     stack.append(b <= a) 


def execExpression(exp): 
    if len(exp) == 3: 
     execExpression(exp[1]) 
     execExpression(exp[2]) 
     if exp[0] == "+": 
      a = stack.pop() 
      b = stack.pop() 
      stack.append(a + b) 
     elif exp[0] == "-": 
      a = stack.pop() 
      b = stack.pop() 
      stack.append(a - b) 
     if exp[0] == "*": 
      a = stack.pop() 
      b = stack.pop() 
      stack.append(a * b) 
     elif exp[0] == "/": 
      a = stack.pop() 
      b = stack.pop() 
      stack.append(a/b) 
    else: 
     #if type(exp[0]) == int: 
     if type(exp[0]) == float: 
      stack.append(exp[0]) 
     else: 
      stack.append(symboltable[exp[0]]) 

if __name__ == "__main__": 
    parseFile("./simpleScript.txt") 
Et

ci-dessous est le fichier simpleScript.txt:

d = 2 + 5 * 3 - 2 * 4 + 5 
print d 

le résultat attendu devrait être 14.0, mais je m'y suis 4.0. Je sais que le problème est dans la fonction doExpression et doTerm, mais je ne peux pas comprendre comment le corriger, n'importe qui s'il vous plaît aidez-moi.

+0

En (sortie): '('exp =', [ '+', ['-', ['+', [5.0], ['*', [4.0], [2.0]]], ['*', [3.0], [5.0]], [2.0]]) ' vous voyez observer que le 2 * 4 est ajouté à 5 avant de faire la soustraction. – Nuageux

+0

Je vois le résultat, mais je n'arrive pas à comprendre comment le corriger. –

Répondre

1

La seule idée, j'ai obtenu en ce moment consiste à faire de cas spécial pour -. Ce que je veux dire, c'est que vous pouvez le considérer comme un +, si vous multipliez par -1 l'expression à l'intérieur de celui-ci (vous devez le faire dans le prochain doExpression, donc je passe un booléen).

Changer votre doExpression() pour (je suis désolé c'est un peu laid, peut-être quelqu'un proposera une autre solution):

def doExpression(_b = True): 
    term = doTerm() 
    if _b == False: 
     tp = ['*'] 
     tp.append([-1.0]) 
     tp.append(term) 
     term = tp 
    # carry the term in case there's no +|- 
    exp = term 
    # ["+|-", [left side], [right side]] 
    while currtoken[0] in ["+", "-"]: 
     exp = ['+'] 
     if currtoken[0] == '+': 
      nextToken() 
      exp.append(doExpression()) 
     else: # '-' 
      nextToken() 
      exp.append(doExpression(_b = False)) 
     exp.append(term) 

    print("exp=", exp) 
    return exp 
+0

Vous avez raison, de cette façon l'expression peut être interprétée sans problème, merci beaucoup! –