Gestern (6. Tag) war seiketkm "Ich habe eine Robophone-App entwickelt, die aus der Zukunft stammt Es war 46992f933294a7668dba) ". Dieser Artikel ist der 7. Tagesartikel von Tech-Circle Hands on Advent Calendar 2016.
Dieses Mal möchte ich eine Originalsprache mit PLY (lex + yacc) erstellen, einer Bibliothek für Phrasenanalyse / Syntaxanalyse von Python.
TrumpScript, eine von Donald Trump inspirierte Programmiersprache, wurde bereits veröffentlicht. https://github.com/samshadwell/TrumpScript
TrumpScript bietet die folgenden Funktionen.
Eine solche…. Auf diese Weise ist es eine Sprache voller Sinn, die Herrn Donald Trump getreu wiedergibt.
Diesmal also im Gegensatz zu TrumpScript "PPAPScript. Ich werde "git)" erstellen.
Die Spezifikationen, die ich mir ausgedacht habe, sind wie folgt.
Vor der Implementierung von PPAPScript werde ich die diesmal verwendete Lage erläutern. ply ist eine Python-Bibliothek, die Lex und Yacc in Python implementiert und als Modul zusammenfügt.
Die Installation der Lage kann mit pip erfolgen. Es unterstützt auch Python3.
$ pip install ply
Von hier aus werde ich die Mindestverwendung in lex.py und yacc.py erläutern.
Dies ist eine Erklärung von lex.py, das für die Phrasenanalyse verantwortlich ist.
import ply.lex as lex
tokens = (
'NUMBER',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'LPAREN',
'RPAREN',
)
Es gibt zwei Möglichkeiten, es zu definieren. In beiden Methoden wird die Namenskonvention für Variablennamen und Funktionsnamen in der Form t_ (Token-Name) definiert.
t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'
Definieren Sie den regulären Ausdruck in der ersten Zeile der Funktion. Ein LexToken-Objekt wird immer als Argument übergeben. Dies ist das Objekt der übereinstimmenden Phrase. Im folgenden Beispiel wird der Tokenwert, der der Regel für reguläre Ausdrücke entspricht, in den Typ int konvertiert.
def t_NUMBER(t):
r'\d+'
t.value = int(t.value)
return t
Mit einer speziellen Variablen namens t_ignore können Sie bestimmte Zeichenfolgen überspringen. Im folgenden Beispiel werden Leerzeichen und Tabulatoren übersprungen.
t_ignore = ' \t'
Sie können Regeln für kommentierende reguläre Ausdrücke mithilfe einer speziellen Variablen namens t_ignore_COMMENT definieren.
t_ignore_COMMENT = r'\#.*'
Die Funktion t_error wird aufgerufen, wenn keine Phrase übereinstimmt.
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(t)
Mit lex () erstellen. Damit ist die Vorbereitung für die Phrasenanalyse abgeschlossen.
lex.lex()
Dies ist eine Beschreibung von yacc.py, das für die Syntaxanalyse verantwortlich ist.
import ply.yacc as yacc
Das folgende Beispiel definiert eine Syntaxregel zum Hinzufügen.
def p_expression_minus(p):
'expression : expression PLUS term'
p[0] = p[1] - p[3]
def p_expression_minus(p):
'expression : expression MINUS term'
#Nicht abschließendes Symbol:Nicht abschließendes Symbol終端記号 非終端記号
def p_expression_minus(p):
'expression : expression MINUS term'
# p[0] p[1] p[2] p[3]
p[0] = p[1] - p[3]
def p_statement_assign(p):
"""statement : NAME EQUALS expression"""
names[p[1]] = p[3]
def p_expression_minus(p):
'expression : expression MINUS term'
p[0] = p[1] - p[3]
Ähnliche Syntaxregeln können wie unten gezeigt zusammengefasst werden.
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]
Ähnlich wie bei lex wird es aufgerufen, wenn keine Syntaxregel übereinstimmt.
def p_error(p):
print "Syntax error in input"
Erstellen Sie mit yacc () ein Paraser-Objekt und führen Sie mit parser.parse () eine Syntaxanalyse durch. Übergeben Sie die Zeichenfolge, die Sie analysieren möchten, als Argument.
parser = yacc.yacc()
parser.parse(data)
Die Implementierung wird basierend auf dem README-Beispiel im Repository von ply erstellt. https://github.com/dabeaz/ply/blob/master/README.md
Das Flag wird von dem Teil gesteuert, der yacc.parse () ausführt.
# Started flag is true by "PPAP" command
has_started = False
def parse(data, debug=0):
if data == "PPAP":
global has_started
has_started = True
print("Started PPAPScript!")
return
if has_started:
return yacc.parse(data, debug=debug)
else:
print('PPAPScript run by "PPAP" command.')
return
Erstellen Sie eine Umgehung, die "PPAP" im regulären Ausdruck ignoriert, da "PPAP" von der Phrasenanalyse der Variablen (t_NAME) erfasst wird.
def t_NAME(t):
r"""(?!PPAP)[a-zA-Z_][a-zA-Z0-9_]*"""
return t
Sie können den Variablennamen mit dem regulären Ausdruck lex einschränken. Da Sie jedoch eine dedizierte Fehlermeldung ausgeben möchten, verwenden Sie das Modul re zur Fehlerbehandlung.
def t_NAME(t):
r"""(?!PPAP)[a-zA-Z_][a-zA-Z0-9_]*"""
pattern = re.compile(r'^(apple|pineapple|pen)+', re.IGNORECASE)
if pattern.match(t.value):
return t
else:
print("This variable name can't be used '%s'.\n "
"Variable can use 'apple', 'pineapple', 'pen'." % t.value)
t.lexer.skip(t)
Der Grund für die Definition mit def ist die Priorisierung der Phrasenanalyse. (Lex hat Vorrang in der durch def definierten Reihenfolge) In diesem Fall ist die Definition vor t_NAME erforderlich.
def t_DECLARE(t):
r"""I_have_(an|a)"""
return t
Sowohl Lex als auch Yacc haben gewöhnliche Definitionen.
def t_PRINT(t):
r"""Ah!"""
return t
def p_statement_print_expr(p):
"""statement : PRINT expression"""
print(p[2])
Das fertige Produkt wird im folgenden Repository veröffentlicht, daher werde ich es klonen. PPAPScript
$ git clone https://github.com/sakaro01/PPAPScript.git
Lage installieren.
$ pip install -r requirements.txt
Führen Sie PPAPScript aus.
$ python ppapscript.py
Lass uns interaktiv spielen. (Derzeit nur interaktiv)
Der nächste Tech-Circle Hands on Advent Calendar 2016 wird für mein synchrones Koga Yuta verantwortlich sein. ist. Wahrscheinlich ein Roboter. Es kann interessant sein, diesen Artikel anzuwenden, um eine ursprüngliche Roboterbefehlssprache zu erstellen.