Dans l'article précédent, Supports Scope. Cette fois, cela correspond à l'expression de la fonction. Je vais essayer de faire une fermeture en utilisant la portée qui a été prise en charge dans l'article précédent.
Confirmez ce que vous voulez faire avec la prise en charge des expressions de fonction.
Par exemple, il existe le programme suivant.
L'expression de fonction interne renvoyée par l'expression de fonction externe est affectée à la variable «counter».
Chaque fois que vous appelez counter
en tant que fonction
La valeur de la variable «c» définie dans l'expression de la fonction externe est ajoutée.
Le premier println (counter ())
imprime 1
,
Le prochain println (counter ())
affichera 2
.
counter = (function() {
var c = 0
return function() {
c = c + 1
return c
}
})()
println(counter())
println(counter())
Nous examinerons comment l'implémenter dans l'ordre de l'analyse syntaxique (Parser) et de l'interpréteur (Interpreter).
L'analyse syntaxique d'une expression de fonction est différente de l'analyse syntaxique d'une définition de fonction avec ou sans nom de fonction. La différence se reflète dans l'analyse syntaxique de la définition de fonction.
Introduit un objet qui représente une expression de fonction. L'objet peut être traité comme une valeur et il prend également en charge les appels de fonction. Jusqu'à présent, seul ʻInteger` était traité comme une valeur, Les expressions de fonction doivent également être traitées comme des valeurs.
Passez à la mise en œuvre. À propos de l'analyseur syntaxique (Parser) et de l'interpréteur (Interpreter) Jetons un coup d'œil aux modifications et aux ajouts dans l'ordre.
Parser.java
Une implémentation de Parser.java.
Une modification de la méthode func ()
qui analyse la définition de la fonction.
La partie modifiée est la première instruction if.
L'implémentation d'origine s'attendait à ce que le jeton «fonction» soit suivi du jeton «ident », qui est le nom de la fonction.
Dans le cas d'une expression de fonction, le jeton fonction
est suivi du jeton (
.
L'instruction if est jugée après que le jeton fonction
est (s'il s'agit d'un
jeton, l'expression de la fonction est analysée,
Sinon, je l'ai changé pour analyser la définition de la fonction.
Parser.java
private Token func(Token token) throws Exception {
if (token().value.equals("(")) {
token.kind = "fexpr";
} else {
token.kind = "func";
token.ident = ident();
}
consume("(");
token.params = new ArrayList<Token>();
if (!token().value.equals(")")) {
token.params.add(ident());
while (!token().value.equals(")")) {
consume(",");
token.params.add(ident());
}
}
consume(")");
token.block = body();
return token;
}
Interpreter.java
Une implémentation d'Interpreter.java.
Une modification de la classe «Variable» qui représente une variable.
Jusqu'à présent, la valeur ne traitait que de ʻInteger. Puisque nous introduisons une expression de fonction, l'expression de fonction doit également être traitée comme une valeur. Type de variable de champ
valeur à gérer, Changé de ʻInteger
en ʻObject`.
Interpreter.java
public static class Variable {
public String name;
public Object value;
@Override
public String toString() {
return name + " " + value;
}
}
Modification de la méthode ʻExpression () . C'est un processus qui se ramifie en fonction de la signification (sorte) du jeton qui représente l'expression. Ajout d'une branche sous
// Add pour le jeton
fexpr`, qui représente une expression de fonction.
Interpreter.java
public Object expression(Token expr) throws Exception {
if (expr.kind.equals("digit")) {
return digit(expr);
} else if (expr.kind.equals("ident")) {
return ident(expr);
} else if (expr.kind.equals("func")) {
return func(expr);
// Add
} else if (expr.kind.equals("fexpr")) {
return fexpr(expr);
} else if (expr.kind.equals("paren")) {
return invoke(expr);
} else if (expr.kind.equals("sign") && expr.value.equals("=")) {
return assign(expr);
} else if (expr.kind.equals("unary")) {
return unaryCalc(expr);
} else if (expr.kind.equals("sign")) {
return calc(expr);
} else {
throw new Exception("Expression error");
}
}
Ajout de la méthode fexpr ()
, qui est appelée sur la branche ajoutée par la méthode ʻexpression ci-dessus. Crée un objet qui représente une expression de fonction. Pour pouvoir maintenir la portée parente de l'expression de fonction, étant donné que l'expression de fonction est utilisée dans la fermeture Clonez et conservez le ʻInterpreter
actuel dans func.context
.
Interpreter.java
public Object fexpr(Token token) throws Exception {
List<String> paramCheckList = new ArrayList<String>();
for (Token p : token.params) {
String param = p.value;
if (paramCheckList.contains(param)) {
throw new Exception("Parameter name was used");
}
paramCheckList.add(param);
}
DynamicFunc func = new DynamicFunc();
func.context = new Interpreter();
func.context.global = global;
func.context.local = local;
func.context.body = body;
func.params = token.params;
func.block = token.block;
return func;
}
Une modification de la méthode func ()
qui crée une instance qui représente une définition de fonction.
Synchronisé avec la méthode fexpr ()
clonant le ʻInterpreteractuel, Je l'ai également changé pour cloner sous
// Update`.
Interpreter.java
public Object func(Token token) throws Exception {
String name = token.ident.value;
if (local.functions.containsKey(name)) {
throw new Exception("Name was used");
}
if (local.variables.containsKey(name)) {
throw new Exception("Name was used");
}
List<String> paramCheckList = new ArrayList<String>();
for (Token p : token.params) {
String param = p.value;
if (paramCheckList.contains(param)) {
throw new Exception("Parameter name was used");
}
paramCheckList.add(param);
}
DynamicFunc func = new DynamicFunc();
// Update
func.context = new Interpreter();
func.context.global = global;
func.context.local = local;
func.context.body = body;
func.name = name;
func.params = token.params;
func.block = token.block;
local.functions.put(name, func);
return null;
}
Changez la méthode value ()
et ajoutez la méthode ʻinteger () `.
La méthode value ()
renvoie la valeur si l'argument est la valeur elle-même, telle que "1".
S'il s'agit d'une variable, elle renvoie la valeur détenue par la variable.
Jusqu'à présent, nous n'avons traité de ʻIntegerque pour les valeurs. L'expression de fonction correspondante est également la valeur renvoyée par cette méthode, elle correspond donc. Changez le type de retour de la méthode
value () de ʻInteger
en ʻObject. Vous pouvez maintenant renvoyer à la fois ʻInteger
et l'expression de fonction avec la méthodevalue ()
.
La raison de l'ajout de la méthode ʻinteger () est Cela est dû au fait que la valeur de retour de la méthode
value () a été modifiée de ʻInteger
en ʻObject. Jusqu'à présent, la valeur de retour de la méthode
value () était garantie comme ʻInteger
,
Le changement de ʻInteger en ʻObject
ne garantit plus.
Au lieu de cela, la méthode ʻinteger () garantit que la valeur est ʻInteger
.
Pour influencer ces changements et s'assurer que la valeur est ʻInteger La méthode
value ()a été appelée, Même avec la méthode ʻunaryCalc ()
et la méthodecalc ()
Je change pour appeler la méthode ʻinteger () `à la place.
Interpreter.java
public Object value(Object value) throws Exception {
if (value instanceof Integer) {
return value;
} else if (value instanceof Func) {
return value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return value(v.value);
}
throw new Exception("right value error");
}
public Integer integer(Object value) throws Exception {
if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return integer(v.value);
}
throw new Exception("right value error");
}
Ceci est une modification de la méthode func ()
.
Une méthode qui garantit que l'argument est une fonction.
Pour ʻelse if, si l'argument est de type
Variable`
Traitement supplémentaire pour s'assurer que la valeur est une fonction.
Interpreter.java
public Func func(Object value) throws Exception {
if (value instanceof Func) {
return (Func) value;
} else if (value instanceof Variable) {
Variable v = (Variable) value;
return func(v.value);
} else {
throw new Exception("Not a function");
}
}
Changement de méthode «Est vrai».
La méthode ʻisTrue est le processus de résolution d'une valeur en une valeur booléenne. Jusqu'à présent, la valeur était uniquement ʻInteger
, mais comme les fonctions sont désormais traitées comme des valeurs,
Lorsque la valeur était une fonction, elle se résolvait simplement en «vrai».
Interpreter.java
public boolean isTrue(Object value) throws Exception {
if (value instanceof Integer) {
return 0 != ((Integer) value);
} else if (value instanceof Func) {
return true;
} else {
return false;
}
}
Le programme ci-dessous utilisant l'implémentation ci-dessus
counter = (function() {
var c = 0
return function() {
c = c + 1
return c
}
})()
println(counter())
println(counter())
Et
Le premier println (counter ())
imprime 1
,
Le prochain println (counter ())
affichera 2
.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "counter = (function() {";
text += " var c = 0";
text += " return function() {";
text += " c = c + 1";
text += " return c";
text += " }";
text += "})()";
text += "println(counter())";
text += "println(counter())";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 1
// --> 2
}
C'est tout pour la mise en œuvre. Merci beaucoup.
La source complète est disponible ici.
Calc https://github.com/quwahara/Calc/tree/article-14-function-expression-r3/Calc/src/main/java
Il y a un article de suite.
** Correspond à une chaîne de caractères ** http://qiita.com/quwahara/items/ddcbc8c37b9d442fe0f2
Recommended Posts