Hier (6ème jour) était seiketkm's "[J'ai développé une application Robophone qui vient du futur](http://qiita.com/seiketkm/items/ C'était 46992f933294a7668dba) ". Cet article est l'article du 7ème jour du Tech-Circle Hands on Advent Calendar 2016.
Cette fois, je voudrais créer un langage original en utilisant PLY (lex + yacc), qui est une bibliothèque d'analyse de phrases / d'analyse syntaxique de Python.
En parlant du langage original, TrumpScript, un langage de programmation inspiré de Donald Trump, a déjà été publié. https://github.com/samshadwell/TrumpScript
TrumpScript a les fonctionnalités suivantes.
Tel…. De cette manière, c'est un langage plein de sens qui reproduit fidèlement M. Donald Trump.
Donc, cette fois, contrairement à TrumpScript, "[PPAPScript](https://github.com/sakaro01/PPAPScript." Je vais créer "git)".
Les spécifications que j'ai proposées sont comme ça.
Avant d'implémenter PPAPScript, j'expliquerai le ply utilisé cette fois. ply est une bibliothèque Python qui implémente lex et yacc en Python et les rassemble sous forme de module.
L'installation du pli peut être faite avec pip. Il prend également en charge python3.
$ pip install ply
À partir de là, j'expliquerai l'utilisation minimale dans lex.py et yacc.py.
Ceci est une explication de lex.py, qui est responsable de l'analyse des phrases.
import ply.lex as lex
tokens = (
'NUMBER',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'LPAREN',
'RPAREN',
)
Il y a deux manières de le définir. Dans les deux méthodes, la convention de dénomination des noms de variables et des noms de fonctions est définie sous la forme t_ (nom de jeton).
t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'
Définissez l'expression régulière sur la première ligne de la fonction. Un objet LexToken est toujours passé en argument. Ce sera l'objet de la phrase correspondante. Dans l'exemple suivant, la valeur de jeton qui correspond à la règle d'expression régulière est convertie en type int.
def t_NUMBER(t):
r'\d+'
t.value = int(t.value)
return t
Une variable spéciale appelée t_ignore vous permet de sauter certaines chaînes. Dans l'exemple ci-dessous, les espaces et les tabulations sont ignorés.
t_ignore = ' \t'
Vous pouvez définir des règles d'expression régulière de commentaire à l'aide d'une variable spéciale appelée t_ignore_COMMENT.
t_ignore_COMMENT = r'\#.*'
La fonction t_error sera appelée si aucune phrase ne correspond.
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(t)
Construisez avec lex (). Ceci termine la préparation de l'analyse des phrases.
lex.lex()
Ceci est une description de yacc.py, qui est responsable de l'analyse syntaxique.
import ply.yacc as yacc
L'exemple suivant définit une règle de syntaxe pour l'ajout.
def p_expression_minus(p):
'expression : expression PLUS term'
p[0] = p[1] - p[3]
def p_expression_minus(p):
'expression : expression MINUS term'
#Symbole sans terminaison:Symbole sans terminaison終端記号 非終端記号
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]
Des règles de syntaxe similaires peuvent être regroupées, comme indiqué ci-dessous.
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]
Similaire à lex, il est appelé lorsqu'aucune règle de syntaxe ne correspond.
def p_error(p):
print "Syntax error in input"
Créez un objet paraser avec yacc () et effectuez une analyse de syntaxe avec parser.parse (). Passez la chaîne de caractères que vous souhaitez analyser comme argument.
parser = yacc.yacc()
parser.parse(data)
L'implémentation sera créée sur la base de l'exemple README dans le référentiel ply. https://github.com/dabeaz/ply/blob/master/README.md
L'indicateur est contrôlé par la partie qui exécute yacc.parse ().
# 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
Créez un contournement qui ignore "PPAP" dans l'expression régulière car "PPAP" est intercepté par l'analyse de la phrase de la variable (t_NAME).
def t_NAME(t):
r"""(?!PPAP)[a-zA-Z_][a-zA-Z0-9_]*"""
return t
Vous pouvez limiter le nom de la variable avec l'expression régulière lex, mais puisque vous souhaitez émettre un message d'erreur dédié, utilisez le module re pour gérer l'erreur.
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)
La raison de définir avec def est de donner la priorité à l'analyse des phrases. (Lex a priorité dans l'ordre défini par def) Dans ce cas, la définition est requise avant t_NAME.
def t_DECLARE(t):
r"""I_have_(an|a)"""
return t
Lex et yacc ont tous deux des définitions ordinaires.
def t_PRINT(t):
r"""Ah!"""
return t
def p_statement_print_expr(p):
"""statement : PRINT expression"""
print(p[2])
Le produit fini est publié dans le référentiel suivant, je vais donc le cloner. PPAPScript
$ git clone https://github.com/sakaro01/PPAPScript.git
Installez le pli.
$ pip install -r requirements.txt
Exécutez PPAPScript.
$ python ppapscript.py
Jouons de manière interactive. (Actuellement uniquement interactif)
Le prochain Tech-Circle Hands on Advent Calendar 2016 sera en charge de mon synchrone Koga Yuta est. Probablement un robot. Il peut être intéressant d'appliquer cet article pour créer un langage de commande de robot original.
Recommended Posts