From 499fc8457bd1dc38cee48f43e284a346954e9f5d Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 19 Jun 2015 16:24:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D1=80=D0=BE=D0=BA=2010.=20=D0=9F=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C?= =?UTF-8?q?=D1=81=D0=BA=D0=B8=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.txt | 18 ++++++++- .../ownlang/lib/UserDefinedFunction.java | 39 +++++++++++++++++++ src/com/annimon/ownlang/lib/Variables.java | 13 ++++++- src/com/annimon/ownlang/parser/Lexer.java | 2 + src/com/annimon/ownlang/parser/Parser.java | 19 +++++++++ src/com/annimon/ownlang/parser/TokenType.java | 2 + .../parser/ast/FunctionDefineStatement.java | 32 +++++++++++++++ .../parser/ast/FunctionalExpression.java | 19 ++++++++- .../ownlang/parser/ast/ReturnStatement.java | 32 +++++++++++++++ 9 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 src/com/annimon/ownlang/lib/UserDefinedFunction.java create mode 100644 src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java create mode 100644 src/com/annimon/ownlang/parser/ast/ReturnStatement.java diff --git a/program.txt b/program.txt index c1a59f2..eb2982a 100644 --- a/program.txt +++ b/program.txt @@ -32,4 +32,20 @@ if (40 < 50 || 50 > 60) { else print "false" print "sin(PI) = " + sin(PI/2) -echo(1,2,3,"4","5","text",sin(0),cos(0),sin(PI),cos(PI),PI) \ No newline at end of file +echo(1,2,3,"4","5","text",sin(0),cos(0),sin(PI),cos(PI),PI) + +a = "print" +print a + +def name(a,b) { + echo("a = ", a, " b = ", b) +} + +def sum(a,b) { + a = -60 + return a+b +} +name(1,"text") +print sum(10, 15) + +print a \ No newline at end of file diff --git a/src/com/annimon/ownlang/lib/UserDefinedFunction.java b/src/com/annimon/ownlang/lib/UserDefinedFunction.java new file mode 100644 index 0000000..8226a09 --- /dev/null +++ b/src/com/annimon/ownlang/lib/UserDefinedFunction.java @@ -0,0 +1,39 @@ +package com.annimon.ownlang.lib; + +import com.annimon.ownlang.parser.ast.ReturnStatement; +import com.annimon.ownlang.parser.ast.Statement; +import java.util.List; + +/** + * + * @author aNNiMON + */ +public final class UserDefinedFunction implements Function { + + private final List argNames; + private final Statement body; + + public UserDefinedFunction(List argNames, Statement body) { + this.argNames = argNames; + this.body = body; + } + + public int getArgsCount() { + return argNames.size(); + } + + public String getArgsName(int index) { + if (index < 0 || index >= getArgsCount()) return ""; + return argNames.get(index); + } + + @Override + public Value execute(Value... args) { + try { + body.execute(); + return NumberValue.ZERO; + } catch (ReturnStatement rt) { + return rt.getResult(); + } + } +} diff --git a/src/com/annimon/ownlang/lib/Variables.java b/src/com/annimon/ownlang/lib/Variables.java index 50d4aa4..99a4c67 100644 --- a/src/com/annimon/ownlang/lib/Variables.java +++ b/src/com/annimon/ownlang/lib/Variables.java @@ -2,6 +2,7 @@ package com.annimon.ownlang.lib; import java.util.HashMap; import java.util.Map; +import java.util.Stack; /** * @@ -9,9 +10,11 @@ import java.util.Map; */ public final class Variables { - private static final Map variables; + private static final Stack> stack; + private static Map variables; static { + stack = new Stack<>(); variables = new HashMap<>(); variables.put("PI", new NumberValue(Math.PI)); variables.put("ПИ", new NumberValue(Math.PI)); @@ -19,6 +22,14 @@ public final class Variables { variables.put("GOLDEN_RATIO", new NumberValue(1.618)); } + public static void push() { + stack.push(new HashMap<>(variables)); + } + + public static void pop() { + variables = stack.pop(); + } + public static boolean isExists(String key) { return variables.containsKey(key); } diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index 849f245..5ff60a7 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -153,6 +153,8 @@ public final class Lexer { case "do": addToken(TokenType.DO); break; case "break": addToken(TokenType.BREAK); break; case "continue": addToken(TokenType.CONTINUE); break; + case "def": addToken(TokenType.DEF); break; + case "return": addToken(TokenType.RETURN); break; default: addToken(TokenType.WORD, word); break; diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 34e5242..dfad154 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser; import com.annimon.ownlang.parser.ast.*; +import java.util.ArrayList; import java.util.List; /** @@ -62,9 +63,15 @@ public final class Parser { if (match(TokenType.CONTINUE)) { return new ContinueStatement(); } + if (match(TokenType.RETURN)) { + return new ReturnStatement(expression()); + } if (match(TokenType.FOR)) { return forStatement(); } + if (match(TokenType.DEF)) { + return functionDefine(); + } if (get(0).getType() == TokenType.WORD && get(1).getType() == TokenType.LPAREN) { return new FunctionStatement(function()); } @@ -117,6 +124,18 @@ public final class Parser { return new ForStatement(initialization, termination, increment, statement); } + private FunctionDefineStatement functionDefine() { + final String name = consume(TokenType.WORD).getText(); + consume(TokenType.LPAREN); + final List argNames = new ArrayList<>(); + while (!match(TokenType.RPAREN)) { + argNames.add(consume(TokenType.WORD).getText()); + match(TokenType.COMMA); + } + final Statement body = statementOrBlock(); + return new FunctionDefineStatement(name, argNames, body); + } + private FunctionalExpression function() { final String name = consume(TokenType.WORD).getText(); consume(TokenType.LPAREN); diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 4cfa1d1..71e6b7e 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -20,6 +20,8 @@ public enum TokenType { DO, BREAK, CONTINUE, + DEF, + RETURN, PLUS, MINUS, diff --git a/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java new file mode 100644 index 0000000..8c7489a --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -0,0 +1,32 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.UserDefinedFunction; +import java.util.List; + +/** + * + * @author aNNiMON + */ +public final class FunctionDefineStatement implements Statement { + + private final String name; + private final List argNames; + private final Statement body; + + public FunctionDefineStatement(String name, List argNames, Statement body) { + this.name = name; + this.argNames = argNames; + this.body = body; + } + + @Override + public void execute() { + Functions.set(name, new UserDefinedFunction(argNames, body)); + } + + @Override + public String toString() { + return "def (" + argNames.toString() + ") " + body.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java index 9cdeefc..8288779 100644 --- a/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java +++ b/src/com/annimon/ownlang/parser/ast/FunctionalExpression.java @@ -1,7 +1,10 @@ package com.annimon.ownlang.parser.ast; +import com.annimon.ownlang.lib.Function; import com.annimon.ownlang.lib.Functions; +import com.annimon.ownlang.lib.UserDefinedFunction; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; import java.util.ArrayList; import java.util.List; @@ -35,7 +38,21 @@ public final class FunctionalExpression implements Expression { for (int i = 0; i < size; i++) { values[i] = arguments.get(i).eval(); } - return Functions.get(name).execute(values); + + final Function function = Functions.get(name); + if (function instanceof UserDefinedFunction) { + final UserDefinedFunction userFunction = (UserDefinedFunction) function; + if (size != userFunction.getArgsCount()) throw new RuntimeException("Args count mismatch"); + + Variables.push(); + for (int i = 0; i < size; i++) { + Variables.set(userFunction.getArgsName(i), values[i]); + } + final Value result = userFunction.execute(values); + Variables.pop(); + return result; + } + return function.execute(values); } @Override diff --git a/src/com/annimon/ownlang/parser/ast/ReturnStatement.java b/src/com/annimon/ownlang/parser/ast/ReturnStatement.java new file mode 100644 index 0000000..7826dad --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ReturnStatement.java @@ -0,0 +1,32 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Value; + +/** + * + * @author aNNiMON + */ +public final class ReturnStatement extends RuntimeException implements Statement { + + private final Expression expression; + private Value result; + + public ReturnStatement(Expression expression) { + this.expression = expression; + } + + public Value getResult() { + return result; + } + + @Override + public void execute() { + result = expression.eval(); + throw this; + } + + @Override + public String toString() { + return "return"; + } +}