From cb07629f0622237761880604341220f6ef750543 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 13 Feb 2016 18:08:36 +0200 Subject: [PATCH] =?UTF-8?q?=D0=90=D1=80=D0=B3=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D1=8B=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=83=D0=BC=D0=BE=D0=BB=D1=87=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 8 ++- .../ownlang/lib/UserDefinedFunction.java | 29 +++++++--- src/com/annimon/ownlang/parser/Parser.java | 51 ++++++++++-------- .../annimon/ownlang/parser/ast/Argument.java | 29 ++++++++++ .../annimon/ownlang/parser/ast/Arguments.java | 53 +++++++++++++++++++ .../parser/ast/FunctionDefineStatement.java | 11 ++-- 6 files changed, 144 insertions(+), 37 deletions(-) create mode 100644 src/com/annimon/ownlang/parser/ast/Argument.java create mode 100644 src/com/annimon/ownlang/parser/ast/Arguments.java diff --git a/program.own b/program.own index 67bf576..b94c859 100644 --- a/program.own +++ b/program.own @@ -211,4 +211,10 @@ def arrayRecursive(arr) = match arr { case [head :: tail]: "[" + head + ", " + arrayRecursive(tail) + "]" case []: "[]" case last: "[" + last + ", []]" -} \ No newline at end of file +} + +def funcWithOptionalArgs(str, count = 5, prefix = "<", suffix = ">") = prefix + (str * count) + suffix + +println funcWithOptionalArgs("*") +println funcWithOptionalArgs("+", 2) +println funcWithOptionalArgs("*", 10, " argNames; + private final Arguments arguments; private final Statement body; - public UserDefinedFunction(List argNames, Statement body) { - this.argNames = argNames; + public UserDefinedFunction(Arguments arguments, Statement body) { + this.arguments = arguments; this.body = body; } public int getArgsCount() { - return argNames.size(); + return arguments.size(); } public String getArgsName(int index) { if (index < 0 || index >= getArgsCount()) return ""; - return argNames.get(index); + return arguments.get(index).getName(); } @Override public Value execute(Value... values) { final int size = values.length; - if (size != getArgsCount()) throw new ArgumentsMismatchException("Arguments count mismatch"); + final int requiredArgsCount = arguments.getRequiredArgumentsCount(); + if (size < requiredArgsCount) { + throw new ArgumentsMismatchException(String.format("Arguments count mismatch. %d < %d", size, requiredArgsCount)); + } + final int totalArgsCount = getArgsCount(); + if (size > totalArgsCount) { + throw new ArgumentsMismatchException(String.format("Arguments count mismatch. %d > %d", size, totalArgsCount)); + } try { Variables.push(); for (int i = 0; i < size; i++) { Variables.set(getArgsName(i), values[i]); } + // Optional args if exists + for (int i = size; i < totalArgsCount; i++) { + final Argument arg = arguments.get(i); + Variables.set(arg.getName(), arg.getValueExpr().eval()); + } body.execute(); return NumberValue.ZERO; } catch (ReturnStatement rt) { @@ -49,6 +62,6 @@ public final class UserDefinedFunction implements Function { @Override public String toString() { - return String.format("function %s %s", argNames.toString(), body.toString()); + return String.format("function %s %s", arguments.toString(), body.toString()); } } diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index fc68b9b..f3a982f 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -199,20 +199,38 @@ public final class Parser { } private FunctionDefineStatement functionDefine() { - // def name(arg1, arg2) { ... } || def name(args) = expr + // def name(arg1, arg2 = value) { ... } || def name(args) = expr final String name = consume(TokenType.WORD).getText(); + final Arguments arguments = arguments(); + final Statement body = statementBody(); + return new FunctionDefineStatement(name, arguments, body); + } + + private Arguments arguments() { + // (arg1, arg2, arg3 = expr1, arg4 = expr2) + final Arguments arguments = new Arguments(); + boolean startsOptionalArgs = false; consume(TokenType.LPAREN); - final List argNames = new ArrayList<>(); while (!match(TokenType.RPAREN)) { - argNames.add(consume(TokenType.WORD).getText()); + final String name = consume(TokenType.WORD).getText(); + if (match(TokenType.EQ)) { + startsOptionalArgs = true; + arguments.addOptional(name, variable()); + } else if (!startsOptionalArgs) { + arguments.addRequired(name); + } else { + throw new ParseException("Required argument cannot be after optional"); + } match(TokenType.COMMA); } - if (lookMatch(0, TokenType.EQ)) { - match(TokenType.EQ); - return new FunctionDefineStatement(name, argNames, new ReturnStatement(expression())); + return arguments; + } + + private Statement statementBody() { + if (match(TokenType.EQ)) { + return new ReturnStatement(expression()); } - final Statement body = statementOrBlock(); - return new FunctionDefineStatement(name, argNames, body); + return statementOrBlock(); } private FunctionalExpression function(Expression qualifiedNameExpr) { @@ -534,20 +552,9 @@ public final class Parser { return match(); } if (match(TokenType.DEF)) { - consume(TokenType.LPAREN); - final List argNames = new ArrayList<>(); - while (!match(TokenType.RPAREN)) { - argNames.add(consume(TokenType.WORD).getText()); - match(TokenType.COMMA); - } - Statement statement; - if (lookMatch(0, TokenType.EQ)) { - match(TokenType.EQ); - statement = new ReturnStatement(expression()); - } else { - statement = statementOrBlock(); - } - return new ValueExpression(new UserDefinedFunction(argNames, statement)); + final Arguments arguments = arguments(); + final Statement statement = statementBody(); + return new ValueExpression(new UserDefinedFunction(arguments, statement)); } return variable(); } diff --git a/src/com/annimon/ownlang/parser/ast/Argument.java b/src/com/annimon/ownlang/parser/ast/Argument.java new file mode 100644 index 0000000..d900fa1 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/Argument.java @@ -0,0 +1,29 @@ +package com.annimon.ownlang.parser.ast; + +public final class Argument { + + private final String name; + private final Expression valueExpr; + + public Argument(String name) { + this(name, null); + } + + public Argument(String name, Expression valueExpr) { + this.name = name; + this.valueExpr = valueExpr; + } + + public String getName() { + return name; + } + + public Expression getValueExpr() { + return valueExpr; + } + + @Override + public String toString() { + return name + (valueExpr == null ? "" : " = " + valueExpr); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Arguments.java b/src/com/annimon/ownlang/parser/ast/Arguments.java new file mode 100644 index 0000000..437db70 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/Arguments.java @@ -0,0 +1,53 @@ +package com.annimon.ownlang.parser.ast; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public final class Arguments implements Iterable { + + private final List arguments; + private int requiredArgumentsCount; + + public Arguments() { + arguments = new ArrayList<>(); + requiredArgumentsCount = 0; + } + + public void addRequired(String name) { + arguments.add(new Argument(name)); + requiredArgumentsCount++; + } + + public void addOptional(String name, Expression expr) { + arguments.add(new Argument(name, expr)); + } + + public Argument get(int index) { + return arguments.get(index); + } + + public int getRequiredArgumentsCount() { + return requiredArgumentsCount; + } + + public int size() { + return arguments.size(); + } + + @Override + public Iterator iterator() { + return arguments.iterator(); + } + + @Override + public String toString() { + final StringBuilder result = new StringBuilder(); + result.append('('); + for (Argument arg : arguments) { + result.append(arg).append(", "); + } + result.append(')'); + return result.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java b/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java index 0980c11..6f7ffaf 100644 --- a/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java +++ b/src/com/annimon/ownlang/parser/ast/FunctionDefineStatement.java @@ -2,7 +2,6 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.Functions; import com.annimon.ownlang.lib.UserDefinedFunction; -import java.util.List; /** * @@ -11,18 +10,18 @@ import java.util.List; public final class FunctionDefineStatement implements Statement { public final String name; - public final List argNames; + public final Arguments arguments; public final Statement body; - public FunctionDefineStatement(String name, List argNames, Statement body) { + public FunctionDefineStatement(String name, Arguments arguments, Statement body) { this.name = name; - this.argNames = argNames; + this.arguments = arguments; this.body = body; } @Override public void execute() { - Functions.set(name, new UserDefinedFunction(argNames, body)); + Functions.set(name, new UserDefinedFunction(arguments, body)); } @Override @@ -32,6 +31,6 @@ public final class FunctionDefineStatement implements Statement { @Override public String toString() { - return String.format("def %s(%s) %s", name, argNames, body); + return String.format("def %s%s %s", name, arguments, body); } }