Ajout de Définitions et appels de fonctions simples dans un article précédent. A cette époque, il n'y avait qu'un seul argument de fonction. Cette fois, il ne prend en charge aucun argument ou deux arguments ou plus.
Assurez-vous de vouloir le faire facilement.
Par exemple, si vous avez un programme comme celui ci-dessous, la fonction ʻadd3 () est définie et nous visons à afficher
6` sur la sortie standard.
v = 0
function add3(a1, a2, a3) {
v = a1 + a2 + a3
}
add3(1,2,3)
println(v)
C'est toujours dans l'article précédent qu'il ne correspond pas à la valeur de retour de la fonction ou à la portée de la variable.
Nous examinerons comment l'implémenter dans l'ordre de l'analyse des phrases (Lexer), de l'analyse syntaxique (Parser) et de l'interpréteur (Interpreter).
Puisqu'il n'y a pas de fonction pour analyser les virgules et ,
dans la phrase d'analyse de l'implémentation jusqu'à présent, nous allons l'implémenter.
Apportez des modifications à la partie implémentation de l'analyse syntaxique des définitions de fonction et des appels de fonction qui a été ajoutée dans l'article précédent. Les deux ne considèrent le cas qu'avec un seul argument, donc Modifiez l'implémentation de l'analyse pour ne prendre en compte aucun argument ou plusieurs arguments.
C'est aussi une classe qui représente la fonction ajoutée dans l'article précédent, Utilisez-le pour apporter des modifications à la partie qui définit et appelle la fonction. Tous ne considèrent que le cas d'un seul argument, donc Modifiez pour ne prendre en compte aucun argument ou plusieurs arguments.
Passez à la mise en œuvre. À propos de l'analyse de phrases (Lexer), de l'analyse syntaxique (Parser), de l'interpréteur (Interpreter) Jetons un coup d'œil aux modifications et aux ajouts dans l'ordre.
Lexer.java
Une implémentation de Lexer.java.
Tout d'abord, ajoutez la fonction pour analyser les virgules et ,
.
Lexer.java
private boolean isSymbolStart(char c) {
return c == ',';
}
private Token symbol() throws Exception {
Token t = new Token();
t.kind = "symbol";
t.value = Character.toString(next());
return t;
}
J'ajouterai la partie appel de l'analyse des virgules. L'implémentation de Lexer.java est terminée.
Lexer.java
public Token nextToken() throws Exception {
skipSpace();
if (isEOT()) {
return null;
} else if (isSignStart(c())) {
return sign();
} else if (isDigitStart(c())) {
return digit();
} else if (isIdentStart(c())) {
return ident();
} else if (isParenStart(c())) {
return paren();
} else if (isCurlyStart(c())) {
return curly();
} else if (isSymbolStart(c())) {
return symbol();
} else {
throw new Exception("Not a character for tokens");
}
}
Parser.java
Changement de la méthode func ()
pour analyser la définition de la fonction.
Avant d'expliquer le changement de méthode func ()
, car le changement de classe Token
est pertinent
Je vais vous expliquer comment changer la classe Token
.
Le résultat de l'analyse de la définition de fonction est résumé dans l'argument token
.
Dans l'article précédent, j'ai ajouté une variable de champ appelée param
qui représente un argument formel de la classe Token
.
Cette fois, afin de prendre en charge plusieurs arguments, le champ param
est supprimé et une variable de champ appelée params
de typeList <Token>
est ajoutée à la classe Token
.
Vient ensuite l'explication de la modification de la méthode func ()
.
Les principaux changements sont «<-Update».
L'analyse syntaxique y est la suivante:
(Après le
jeton, après
)
arrive bientôt, il n'y a pas d'argument)
tout de suite, vous avez au moins un argument en premier)
ne vient pas, deux arguments ou plus continueront.Et analysez.
Parser.java
private Token func(Token token) throws Exception {
token.kind = "func";
token.ident = ident();
consume("(");
token.params = new ArrayList<Token>();
if (!token().value.equals(")")) { // <-- Update
token.params.add(ident());
while (!token().value.equals(")")) {
consume(",");
token.params.add(ident());
}
}
consume(")");
consume("{");
token.block = block();
consume("}");
return token;
}
Une implémentation de l'analyse de syntaxe pour la partie qui effectue l'appel de fonction.
Le principal changement est <-Update
, et vous pouvez voir que la méthode d'implémentation est presque la même que l'analyse syntaxique de la définition de fonction précédente.
Parser.java
private Token bind(Token left, Token operator) throws Exception {
if (binaryKinds.contains(operator.kind)) {
operator.left = left;
int leftDegree = degree(operator);
if (rightAssocs.contains(operator.value)) {
leftDegree -= 1;
}
operator.right = expression(leftDegree);
return operator;
} else if (operator.kind.equals("paren") && operator.value.equals("(")) {
operator.left = left;
operator.params = new ArrayList<Token>();
if (!token().value.equals(")")) { // <-- Update
operator.params.add(expression(0));
while (!token().value.equals(")")) {
consume(",");
operator.params.add(expression(0));
}
}
consume(")");
return operator;
} else {
throw new Exception("The token cannot place there.");
}
}
Interpreter.java
Une implémentation d'Interpreter.java.
Une modification de la classe abstraite de la classe qui représente la fonction.
Argument de méthode ʻInvoke () selon plusieurs arguments, Changé de ʻObject arg
en List <Object> args
.
Interpreter.java
public static abstract class Func {
public String name;
abstract public Object invoke(List<Object> args) throws Exception;
}
J'ai changé la méthode ʻinvoke () de la classe
Println` pour correspondre au changement de la classe abstraite.
Interpreter.java
public static class Println extends Func {
public Println() {
name = "println";
}
@Override
public Object invoke(List<Object> args) throws Exception {
Object arg = args.size() > 0 ? args.get(0) : null;
System.out.println(arg);
return null;
}
}
De même, la méthode ʻinvoke () de la classe
DynamicFunc` a été modifiée pour correspondre au changement de la classe abstraite.
Changer.
De plus, la variable de champ «param» a été supprimée afin que plusieurs arguments puissent être exprimés, et «params» de type «List
Interpreter.java
public static class DynamicFunc extends Func {
public Interpreter context;
public List<Token> params;
public List<Token> block;
@Override
public Object invoke(List<Object> args) throws Exception {
for (int i = 0; i < params.size(); ++i) {
Token param = params.get(i);
Variable v = context.variable(context.ident(param));
if (i < args.size()) {
v.value = context.value(args.get(i));
} else {
v.value = null;
}
}
context.body(block);
return null;
}
}
C'est un changement de la partie définition de la fonction. Les principaux changements sont «<-Add» et «<-Update». Il est répété dans l'instruction for pour traiter plusieurs arguments.
Interpreter.java
public Object func(Token token) throws Exception {
String name = token.ident.value;
if (functions.containsKey(name)) {
throw new Exception("Name was used");
}
if (variables.containsKey(name)) {
throw new Exception("Name was used");
}
List<String> paramCheckList = new ArrayList<String>(); // <-- Add
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 = this;
func.name = name;
func.params = token.params; // <-- Update
func.block = token.block;
functions.put(name, func);
return null;
}
Le programme ci-dessous utilisant l'implémentation ci-dessus
v = 0
function add3(a1, a2, a3) {
v = a1 + a2 + a3
}
add3(1,2,3)
println(v)
Pour imprimer la valeur «6» affectée à la variable «v» sur la sortie standard.
Interpreter.java
public static void main(String[] args) throws Exception {
String text = "";
text += "v = 0";
text += "function add3(a1, a2, a3) {";
text += " v = a1 + a2 + a3";
text += "}";
text += "add3(1,2,3)";
text += "println(v)";
List<Token> tokens = new Lexer().init(text).tokenize();
List<Token> blk = new Parser().init(tokens).block();
new Interpreter().init(blk).run();
// --> 6
}
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-8-multiple-arguments-r2/Calc/src/main/java
Il y a un article de suite.
** Correspond à la valeur de retour ** http://qiita.com/quwahara/items/1db9a5b880fd36dcfd3c
Recommended Posts