2017-11-13 3 views
0

I das erste Beispiel am Lesen vonVerallgemeinernd integer calculator Beispiel aus PLY zu Gleitzahl

https://github.com/dabeaz/ply

Es ist ein Basisrechner für nur Expression ermöglicht Einbeziehung '(',')','+','-','*','/', ganzen Zahlen und assignement (zB x=3) und Werfen die Auswertung des Ausdrucks (auch wenn es sich dabei nicht um eine Ganzzahl handelt, zB '3/4').

Ich möchte für Gleitkommazahlen ermöglichen, so dass ich im Grunde den Code aus dem Beispiel wie folgt geändert, aber es funktioniert nicht:

# ----------------------------------------------------------------------------- 
# calc.py 
# 
# A simple calculator with variables. 
# ----------------------------------------------------------------------------- 

tokens = (
    'NAME','INTEGER', 'FLOAT', 
    'PLUS','MINUS','TIMES','DIVIDE','EQUALS', 
    'LPAREN','RPAREN', 
    ) 

# Tokens 

t_PLUS = r'\+' 
t_MINUS = r'-' 
t_TIMES = r'\*' 
t_DIVIDE = r'/' 
t_EQUALS = r'=' 
t_LPAREN = r'\(' 
t_RPAREN = r'\)' 
t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' 

def t_INTEGER(t): 
    r'\d+' 
    t.value = int(t.value) 
    return t 

def t_FLOAT(t): 
    r'/^(?!0\d)\d*(\.\d+)?$/mg' 
    t.value = float(t.value) 
    return t 

# Ignored characters 
t_ignore = " \t" 

def t_newline(t): 
    r'\n+' 
    t.lexer.lineno += t.value.count("\n") 

def t_error(t): 
    print("Illegal character '%s'" % t.value[0]) 
    t.lexer.skip(1) 

# Build the lexer 
import ply.lex as lex 
lex.lex() 

# Precedence rules for the arithmetic operators 
precedence = (
    ('left','PLUS','MINUS'), 
    ('left','TIMES','DIVIDE'), 
    ('right','UMINUS'), 
    ) 

# dictionary of names (for storing variables) 
names = { } 

def p_statement_assign(p): 
    'statement : NAME EQUALS expression' 
    names[p[1]] = p[3] 

def p_statement_expr(p): 
    'statement : expression' 
    print(p[1]) 

def p_expression_binop(p): 
    '''expression : expression PLUS expression 
        | expression MINUS expression 
        | expression TIMES expression 
        | expression DIVIDE expression''' 
    if p[2] == '+' : p[0] = p[1] + p[3] 
    elif p[2] == '-': p[0] = p[1] - p[3] 
    elif p[2] == '*': p[0] = p[1] * p[3] 
    elif p[2] == '/': p[0] = p[1]/p[3] 

def p_expression_uminus(p): 
    'expression : MINUS expression %prec UMINUS' 
    p[0] = -p[2] 

def p_expression_group(p): 
    'expression : LPAREN expression RPAREN' 
    p[0] = p[2] 

def p_expression_integer(p): 
    'expression : INTEGER' 
    p[0] = p[1] 

def p_expression_float(p): 
    'expression : FLOAT' 
    p[0] = p[1] 

def p_expression_name(p): 
    'expression : NAME' 
    try: 
     p[0] = names[p[1]] 
    except LookupError: 
     print("Undefined name '%s'" % p[1]) 
     p[0] = 0 

def p_error(p): 
    print("Syntax error at '%s'" % p.value) 

import ply.yacc as yacc 
yacc.yacc() 

while True: 
    try: 
     s = input('calc > ') 
    except EOFError: 
     break 
    yacc.parse(s) 

Ich habe Fehler:

calc > 3.14+1 
Illegal character '.' 
Syntax error at '14' 

Antwort

1

Ply analysiert die T_xxx Mitglieder in der Reihenfolge der Deklaration (mit Reflektion auf Ihrem Modul). Was hier passiert ist, dass T_INTEGER mit vorT_FLOAT übereinstimmt. Also wird der ganzzahlige Teil Ihres Floats geparst, dann ply Drosseln auf dem Punkt.

Das würde direkt funktionieren, wenn Ihre Regex für Floats nicht ausgeschaltet war (komplett verpasste diesen Punkt in meiner ersten Antwort, geblendet durch die offensichtliche falsche Reihenfolge).

habe ich es \d+\.\d+ vereinfacht (die nicht 1. oder .9 passt so nicht die beste Wahl), aber man kann ein besseres von einem ähnlichen Problem genommen borgen: PLY lexer for numbers always returns double

Sie haben T_FLOAT zu bekommen geparst vor . Nur die beiden Erklärungen tauschen, dies zu tun:

def t_FLOAT(t): 
    r'\d+\.\d+' 
    # a better regex taking exponents into account: 
    '[-+]?[0-9]+(\.([0-9]+)?([eE][-+]?[0-9]+)?|[eE][-+]?[0-9]+)'   
    t.value = float(t.value) 
    return t 

def t_INTEGER(t): 
    r'\d+' 
    t.value = int(t.value) 
    return t 

Als allgemeine Regel gilt für ply, tun dies für alle Muster, die länger/spezifischer sind als andere Konflikte zu vermeiden.

+0

Dies führt immer noch zu dem gleichen Fehler. Soll ich vielleicht den Punkt Token definieren oder? – EricFlorentNoube

+0

jetzt habe ich Ihren Code getestet und es funktioniert, siehe meine Bearbeitung. –

+0

Danke! Und meine schlechte – EricFlorentNoube

1

Sie haben zwei Probleme in Ihrer Lex-Datei. Erstens ist das Token, um so erklärt Jean-François: Je länger die Token müssen zuerst in lex definiert werden (aus Lagen doc ref.):

Wenn der reguläre Ausdruck Master Aufbau werden Regeln hinzugefügt in der folgenden Bestellung:

  1. Alle Token, die durch Funktionen definiert sind in der gleichen Reihenfolge hinzugefügt, wie sie in der Lexer Datei erscheinen.
  2. Token, die durch Strings definiert sind, werden als nächstes hinzugefügt, indem sie in der Reihenfolge der abnehmenden regulären Ausdrucklänge sortiert werden (längere Ausdrücke werden zuerst hinzugefügt).

Aber die Zeichenfolge das Token definieren, ist ein re kompatiblen String sein. Deine FLOAT-Definition ist hier furchtbar gebrochen.Wenn wir einen Schwimmer als die aus genau einem Punkt, und optional Stellen vor oder nach dem Punkt und nicht ein Punkt allein, eine akzeptable Definition definieren könnte:

r'(\d*\.\d+)|(\d+\.\d*)' 

Insbesondere die shlashes / ist, nicht einbezogen werden der String ...

+0

Noch einmal scheitere ich wegen einer Regex ... Thx viel! – EricFlorentNoube