[JAVA] 8 Entspricht mehreren Argumenten

Einführung

Einfache Funktionsdefinitionen und Aufrufe wurde in einem früheren Artikel hinzugefügt. Zu dieser Zeit gab es nur ein Funktionsargument. Dieses Mal werden keine Argumente oder zwei oder mehr Argumente unterstützt.

Was Sie mit mehreren Argumenten machen wollen

Stellen Sie sicher, dass Sie es einfach machen möchten. Wenn Sie beispielsweise ein Programm wie das folgende haben, ist die Funktion add3 () definiert und wir möchten 6 an die Standardausgabe ausgeben.

v = 0
function add3(a1, a2, a3) {
  v = a1 + a2 + a3
}
add3(1,2,3)
println(v)

Die mangelnde Unterstützung für Funktionsrückgabewerte und variable Bereiche bleibt in früheren Artikeln gleich.

Wie zu implementieren

Wir werden überlegen, wie es in der Reihenfolge der Phrasenanalyse (Lexer), Syntaxanalyse (Parser) und Interpreter (Interpreter) implementiert werden kann.

Implementierung in die Phrasenanalyse (Lexer)

Da es bisher keine Funktion zum Analysieren von Kommas und `` in der Phrasenanalyse der Implementierung gibt, werden wir sie implementieren.

So implementieren Sie Parser

Wir werden Änderungen am Implementierungsteil der Syntaxanalyse von Funktionsdefinitionen und Funktionsaufrufen vornehmen, die wir im vorherigen Artikel hinzugefügt haben. Beide betrachten den Fall also nur mit einem Argument Ändern Sie die Parsing-Implementierung so, dass keine oder mehrere Argumente berücksichtigt werden.

So implementieren Sie Interpreter

Dies ist auch eine Klasse, die die im vorherigen Artikel hinzugefügte Funktion darstellt. Verwenden Sie diese Option, um Änderungen an dem Teil vorzunehmen, der die Funktion definiert und aufruft. Alle betrachten nur den Fall eines Arguments Ändern Sie diese Option, um keine oder mehrere Argumente zu berücksichtigen.

Versuchen Sie, in Java zu implementieren

Fahren Sie mit der Implementierung fort. Informationen zur Phrasenanalyse (Lexer), Syntaxanalyse (Parser), Interpreter (Interpreter) Schauen wir uns die Änderungen und Ergänzungen der Reihe nach an.

Lexer.java

Eine Implementierung von Lexer.java. Fügen Sie zunächst die Funktion zum Analysieren von Kommas und , hinzu.

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;
    }

Ich werde den Aufrufteil der Kommaanalyse hinzufügen. Die Implementierung von Lexer.java ist abgeschlossen.

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

Die Methode func () wurde geändert, um die Funktionsdefinition zu analysieren. Vor der Erläuterung der Änderung der func () -Methode, da die Änderung der Token-Klasse relevant ist Ich werde erklären, wie man die Token-Klasse ändert. Das Analyseergebnis der Funktionsdefinition ist im Argument "Token" zusammengefasst. Im vorherigen Artikel habe ich eine Feldvariable namens "param" hinzugefügt, die ein formales Argument für die "Token" -Klasse darstellt. Um mehrere Argumente zu unterstützen, wird dieses Mal das Feld "param" abgeschafft und eine Feldvariable mit dem Namen "params" vom Typ "List " zur Klasse "Token" hinzugefügt. Als nächstes folgt die Erklärung zum Ändern der Methode func (). Die wichtigsten Änderungen sind "<-Update". Die Syntaxanalyse dort ist wie folgt: (Nach dem Token, nach

Und analysieren.

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;
    }

Eine Implementierung der Syntaxanalyse für den Teil, der den Funktionsaufruf ausführt. Die Hauptänderung ist "<-Update", und Sie können sehen, dass die Implementierungsmethode fast dieselbe ist wie die Syntaxanalyse der Funktionsdefinition zuvor.

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

Eine Implementierung von Interpreter.java.

Eine Modifikation der abstrakten Klasse der Klasse, die die Funktion darstellt. Das Argument der Methode "invoke ()" nach mehreren Argumenten, Geändert von Object arg zu List <Object> args.

Interpreter.java


    public static abstract class Func {
        public String name;

        abstract public Object invoke(List<Object> args) throws Exception;
    }

Ich habe die Methode "invoke ()" der Klasse "Println" geändert, um sie an die Änderung der abstrakten Klasse anzupassen.

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;
        }
    }

In ähnlicher Weise wurde die Methode "invoke ()" der Klasse "DynamicFunc" geändert, um sie an die Änderung der abstrakten Klasse anzupassen. Wechseln. Außerdem wurde die Feldvariable "param" abgeschafft, damit mehrere Argumente ausgedrückt werden können, und "params" vom Typ "List " wurde eingeführt.

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;
        }
    }

Es ist eine Änderung des Funktionsdefinitionsteils. Die Hauptänderungen sind "<-Add" und "<-Update". In der for-Anweisung wird wiederholt, um mehrere Argumente zu verarbeiten.

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;
    }

Das folgende Programm verwendet die obige Implementierung

v = 0
function add3(a1, a2, a3) {
  v = a1 + a2 + a3
}
add3(1,2,3)
println(v)

Drucken des der Variablen "v" zugewiesenen Werts "6" an die Standardausgabe.

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
    }

Das ist alles für die Implementierung. Vielen Dank.

abschließend

Die vollständige Quelle finden Sie hier.

Calc https://github.com/quwahara/Calc/tree/article-8-multiple-arguments-r2/Calc/src/main/java

Es gibt einen Folgeartikel.

** Entspricht dem Rückgabewert ** http://qiita.com/quwahara/items/1db9a5b880fd36dcfd3c

Recommended Posts

8 Entspricht mehreren Argumenten
17 Entspricht einem Array
Entspricht dem Geltungsbereich
Entspricht 15 Zeichenfolgen
10 Entspricht der if-Anweisung
14 Entspricht einem Funktionsausdruck
5 Entspricht priorisierten Klammern
19 Entspricht der Objekterstellung
16 Entspricht dem Methodenaufruf
9 Entspricht dem Rückgabewert
12 Entspricht der while-Anweisung
18 Entspricht der JSON-ähnlichen Objektdefinition
20 Entspricht statischen Methodenaufrufen
11 Entspricht Vergleichs- und logischen Operatoren
Vorgang zum Verbinden mehrerer Streams @Java
[Swift] So ersetzen Sie mehrere Zeichenfolgen
to_ ○
Notiz, um mehrere Werte in HashMap zu haben
Zusammenfassung zum Schreiben von Anmerkungsargumenten
Mikrobanking beim Ändern von VM-Argumenten
So wechseln Sie zwischen mehreren Java-Versionen