Урок 10. Пользовательские функции

This commit is contained in:
Victor 2015-06-19 16:24:22 +03:00
parent ebed2615d5
commit 499fc8457b
9 changed files with 173 additions and 3 deletions

View File

@ -33,3 +33,19 @@ else print "false"
print "sin(PI) = " + sin(PI/2) print "sin(PI) = " + sin(PI/2)
echo(1,2,3,"4","5","text",sin(0),cos(0),sin(PI),cos(PI),PI) 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

View File

@ -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<String> argNames;
private final Statement body;
public UserDefinedFunction(List<String> 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();
}
}
}

View File

@ -2,6 +2,7 @@ package com.annimon.ownlang.lib;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Stack;
/** /**
* *
@ -9,9 +10,11 @@ import java.util.Map;
*/ */
public final class Variables { public final class Variables {
private static final Map<String, Value> variables; private static final Stack<Map<String, Value>> stack;
private static Map<String, Value> variables;
static { static {
stack = new Stack<>();
variables = new HashMap<>(); variables = new HashMap<>();
variables.put("PI", new NumberValue(Math.PI)); variables.put("PI", new NumberValue(Math.PI));
variables.put("ПИ", 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)); 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) { public static boolean isExists(String key) {
return variables.containsKey(key); return variables.containsKey(key);
} }

View File

@ -153,6 +153,8 @@ public final class Lexer {
case "do": addToken(TokenType.DO); break; case "do": addToken(TokenType.DO); break;
case "break": addToken(TokenType.BREAK); break; case "break": addToken(TokenType.BREAK); break;
case "continue": addToken(TokenType.CONTINUE); break; case "continue": addToken(TokenType.CONTINUE); break;
case "def": addToken(TokenType.DEF); break;
case "return": addToken(TokenType.RETURN); break;
default: default:
addToken(TokenType.WORD, word); addToken(TokenType.WORD, word);
break; break;

View File

@ -1,6 +1,7 @@
package com.annimon.ownlang.parser; package com.annimon.ownlang.parser;
import com.annimon.ownlang.parser.ast.*; import com.annimon.ownlang.parser.ast.*;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -62,9 +63,15 @@ public final class Parser {
if (match(TokenType.CONTINUE)) { if (match(TokenType.CONTINUE)) {
return new ContinueStatement(); return new ContinueStatement();
} }
if (match(TokenType.RETURN)) {
return new ReturnStatement(expression());
}
if (match(TokenType.FOR)) { if (match(TokenType.FOR)) {
return forStatement(); return forStatement();
} }
if (match(TokenType.DEF)) {
return functionDefine();
}
if (get(0).getType() == TokenType.WORD && get(1).getType() == TokenType.LPAREN) { if (get(0).getType() == TokenType.WORD && get(1).getType() == TokenType.LPAREN) {
return new FunctionStatement(function()); return new FunctionStatement(function());
} }
@ -117,6 +124,18 @@ public final class Parser {
return new ForStatement(initialization, termination, increment, statement); return new ForStatement(initialization, termination, increment, statement);
} }
private FunctionDefineStatement functionDefine() {
final String name = consume(TokenType.WORD).getText();
consume(TokenType.LPAREN);
final List<String> 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() { private FunctionalExpression function() {
final String name = consume(TokenType.WORD).getText(); final String name = consume(TokenType.WORD).getText();
consume(TokenType.LPAREN); consume(TokenType.LPAREN);

View File

@ -20,6 +20,8 @@ public enum TokenType {
DO, DO,
BREAK, BREAK,
CONTINUE, CONTINUE,
DEF,
RETURN,
PLUS, PLUS,
MINUS, MINUS,

View File

@ -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<String> argNames;
private final Statement body;
public FunctionDefineStatement(String name, List<String> 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();
}
}

View File

@ -1,7 +1,10 @@
package com.annimon.ownlang.parser.ast; package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Function;
import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.Functions;
import com.annimon.ownlang.lib.UserDefinedFunction;
import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -35,7 +38,21 @@ public final class FunctionalExpression implements Expression {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
values[i] = arguments.get(i).eval(); 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 @Override

View File

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