mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 08:44:20 +03:00
Merge chain-functions into latest
This commit is contained in:
commit
890d2aa0c4
@ -28,23 +28,23 @@ public final class Parser {
|
||||
|
||||
private static final Token EOF = new Token(TokenType.EOF, "", -1, -1);
|
||||
|
||||
private static final EnumMap<TokenType, BinaryExpression.Operator> assignOperator;
|
||||
private static final EnumMap<TokenType, BinaryExpression.Operator> ASSIGN_OPERATORS;
|
||||
static {
|
||||
assignOperator = new EnumMap(TokenType.class);
|
||||
assignOperator.put(TokenType.EQ, null);
|
||||
assignOperator.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
|
||||
assignOperator.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
|
||||
assignOperator.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
|
||||
assignOperator.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
|
||||
assignOperator.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
|
||||
assignOperator.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
|
||||
assignOperator.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
|
||||
assignOperator.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
|
||||
assignOperator.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
|
||||
assignOperator.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
|
||||
assignOperator.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
|
||||
assignOperator.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
|
||||
assignOperator.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
|
||||
ASSIGN_OPERATORS = new EnumMap(TokenType.class);
|
||||
ASSIGN_OPERATORS.put(TokenType.EQ, null);
|
||||
ASSIGN_OPERATORS.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
|
||||
ASSIGN_OPERATORS.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
|
||||
ASSIGN_OPERATORS.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
|
||||
ASSIGN_OPERATORS.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
|
||||
ASSIGN_OPERATORS.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
|
||||
ASSIGN_OPERATORS.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
|
||||
ASSIGN_OPERATORS.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
|
||||
ASSIGN_OPERATORS.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
|
||||
ASSIGN_OPERATORS.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
|
||||
ASSIGN_OPERATORS.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
|
||||
ASSIGN_OPERATORS.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
|
||||
ASSIGN_OPERATORS.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
|
||||
ASSIGN_OPERATORS.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
|
||||
}
|
||||
|
||||
private final List<Token> tokens;
|
||||
@ -156,10 +156,10 @@ public final class Parser {
|
||||
return functionDefine();
|
||||
}
|
||||
if (match(TokenType.MATCH)) {
|
||||
return new ExprStatement(match());
|
||||
return match();
|
||||
}
|
||||
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
|
||||
return new ExprStatement(function(qualifiedName()));
|
||||
return new ExprStatement(functionChain(qualifiedName()));
|
||||
}
|
||||
return assignmentStatement();
|
||||
}
|
||||
@ -185,7 +185,7 @@ public final class Parser {
|
||||
} else {
|
||||
variables.add(null);
|
||||
}
|
||||
match(TokenType.COMMA);
|
||||
consume(TokenType.COMMA);
|
||||
}
|
||||
consume(TokenType.EQ);
|
||||
return new DestructuringAssignmentStatement(variables, expression());
|
||||
@ -227,36 +227,37 @@ public final class Parser {
|
||||
// for key, value : arr || for (key, value : arr)
|
||||
return foreachMapStatement();
|
||||
}
|
||||
|
||||
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
|
||||
|
||||
// for (init, condition, increment) body
|
||||
boolean optParentheses = match(TokenType.LPAREN);
|
||||
final Statement initialization = assignmentStatement();
|
||||
consume(TokenType.COMMA);
|
||||
final Expression termination = expression();
|
||||
consume(TokenType.COMMA);
|
||||
final Statement increment = assignmentStatement();
|
||||
if (openParen) consume(TokenType.RPAREN); // скобки
|
||||
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
|
||||
final Statement statement = statementOrBlock();
|
||||
return new ForStatement(initialization, termination, increment, statement);
|
||||
}
|
||||
|
||||
private ForeachArrayStatement foreachArrayStatement() {
|
||||
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
|
||||
boolean optParentheses = match(TokenType.LPAREN);
|
||||
final String variable = consume(TokenType.WORD).getText();
|
||||
consume(TokenType.COLON);
|
||||
final Expression container = expression();
|
||||
if (openParen) consume(TokenType.RPAREN); // скобки
|
||||
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
|
||||
final Statement statement = statementOrBlock();
|
||||
return new ForeachArrayStatement(variable, container, statement);
|
||||
}
|
||||
|
||||
private ForeachMapStatement foreachMapStatement() {
|
||||
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
|
||||
boolean optParentheses = match(TokenType.LPAREN);
|
||||
final String key = consume(TokenType.WORD).getText();
|
||||
consume(TokenType.COMMA);
|
||||
final String value = consume(TokenType.WORD).getText();
|
||||
consume(TokenType.COLON);
|
||||
final Expression container = expression();
|
||||
if (openParen) consume(TokenType.RPAREN); // скобки
|
||||
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
|
||||
final Statement statement = statementOrBlock();
|
||||
return new ForeachMapStatement(key, value, container, statement);
|
||||
}
|
||||
@ -296,6 +297,26 @@ public final class Parser {
|
||||
return statementOrBlock();
|
||||
}
|
||||
|
||||
private Expression functionChain(Expression qualifiedNameExpr) {
|
||||
// f1()()() || f1().f2().f3() || f1().key
|
||||
final Expression expr = function(qualifiedNameExpr);
|
||||
if (lookMatch(0, TokenType.LPAREN)) {
|
||||
return functionChain(expr);
|
||||
}
|
||||
if (lookMatch(0, TokenType.DOT)) {
|
||||
final List<Expression> indices = variableSuffix();
|
||||
if (indices == null | indices.isEmpty()) return expr;
|
||||
|
||||
if (lookMatch(0, TokenType.LPAREN)) {
|
||||
// next function call
|
||||
return functionChain(new ContainerAccessExpression(expr, indices));
|
||||
}
|
||||
// container access
|
||||
return new ContainerAccessExpression(expr, indices);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
private FunctionalExpression function(Expression qualifiedNameExpr) {
|
||||
// function(arg1, arg2, ...)
|
||||
consume(TokenType.LPAREN);
|
||||
@ -426,13 +447,13 @@ public final class Parser {
|
||||
}
|
||||
|
||||
final TokenType currentType = get(0).getType();
|
||||
if (!assignOperator.containsKey(currentType)) {
|
||||
if (!ASSIGN_OPERATORS.containsKey(currentType)) {
|
||||
pos = position;
|
||||
return null;
|
||||
}
|
||||
match(currentType);
|
||||
|
||||
final BinaryExpression.Operator op = assignOperator.get(currentType);
|
||||
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
|
||||
final Expression expression = expression();
|
||||
|
||||
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
|
||||
@ -683,18 +704,18 @@ public final class Parser {
|
||||
}
|
||||
return variable();
|
||||
}
|
||||
|
||||
|
||||
private Expression variable() {
|
||||
// function(...
|
||||
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
|
||||
return function(new ValueExpression(consume(TokenType.WORD).getText()));
|
||||
return functionChain(new ValueExpression(consume(TokenType.WORD).getText()));
|
||||
}
|
||||
|
||||
final Expression qualifiedNameExpr = qualifiedName();
|
||||
if (qualifiedNameExpr != null) {
|
||||
// variable(args) || arr["key"](args) || obj.key(args)
|
||||
if (lookMatch(0, TokenType.LPAREN)) {
|
||||
return function(qualifiedNameExpr);
|
||||
return functionChain(qualifiedNameExpr);
|
||||
}
|
||||
// postfix increment/decrement
|
||||
if (match(TokenType.PLUSPLUS)) {
|
||||
@ -726,7 +747,7 @@ public final class Parser {
|
||||
}
|
||||
return new ContainerAccessExpression(current.getText(), indices);
|
||||
}
|
||||
|
||||
|
||||
private List<Expression> variableSuffix() {
|
||||
// .key1.arr1[expr1][expr2].key2
|
||||
if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) {
|
||||
|
@ -10,14 +10,28 @@ import java.util.List;
|
||||
*/
|
||||
public final class ContainerAccessExpression implements Expression, Accessible {
|
||||
|
||||
public final String variable;
|
||||
public final Expression root;
|
||||
public final List<Expression> indices;
|
||||
private boolean rootIsVariable;
|
||||
|
||||
public ContainerAccessExpression(String variable, List<Expression> indices) {
|
||||
this.variable = variable;
|
||||
this(new VariableExpression(variable), indices);
|
||||
}
|
||||
|
||||
public ContainerAccessExpression(Expression root, List<Expression> indices) {
|
||||
rootIsVariable = root instanceof VariableExpression;
|
||||
this.root = root;
|
||||
this.indices = indices;
|
||||
}
|
||||
|
||||
|
||||
public boolean rootIsVariable() {
|
||||
return rootIsVariable;
|
||||
}
|
||||
|
||||
public Expression getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value eval() {
|
||||
return get();
|
||||
@ -60,7 +74,7 @@ public final class ContainerAccessExpression implements Expression, Accessible {
|
||||
}
|
||||
|
||||
public Value getContainer() {
|
||||
Value container = Variables.get(variable);
|
||||
Value container = root.eval();
|
||||
final int last = indices.size() - 1;
|
||||
for (int i = 0; i < last; i++) {
|
||||
final Value index = index(i);
|
||||
@ -108,6 +122,6 @@ public final class ContainerAccessExpression implements Expression, Accessible {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return variable + indices;
|
||||
return root.toString() + indices;
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +87,10 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
|
||||
@Override
|
||||
public Node visit(ContainerAccessExpression s, T t) {
|
||||
final Node root = s.root.accept(this, t);
|
||||
boolean changed = (root != s.root);
|
||||
|
||||
final List<Expression> indices = new ArrayList<>(s.indices.size());
|
||||
boolean changed = false;
|
||||
for (Expression expression : s.indices) {
|
||||
final Node node = expression.accept(this, t);
|
||||
if (node != expression) {
|
||||
@ -97,7 +99,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
indices.add((Expression) node);
|
||||
}
|
||||
if (changed) {
|
||||
return new ContainerAccessExpression(s.variable, indices);
|
||||
return new ContainerAccessExpression((Expression) root, indices);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@ -100,8 +100,11 @@ public class VariablesGrabber extends OptimizationVisitor<Map<String, VariableIn
|
||||
t.put(variableName, variableInfo(t, variableName));
|
||||
}
|
||||
if (s.expr1 instanceof ContainerAccessExpression) {
|
||||
final String variableName = ((ContainerAccessExpression) s.expr1).variable;
|
||||
t.put(variableName, variableInfo(t, variableName));
|
||||
ContainerAccessExpression conExpr = (ContainerAccessExpression) s.expr1;
|
||||
if (conExpr.rootIsVariable()) {
|
||||
final String variableName = ((VariableExpression) conExpr.root).name;
|
||||
t.put(variableName, variableInfo(t, variableName));
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visit(s, t);
|
||||
|
@ -47,6 +47,7 @@ public abstract class AbstractVisitor implements Visitor {
|
||||
|
||||
@Override
|
||||
public void visit(ContainerAccessExpression s) {
|
||||
s.root.accept(this);
|
||||
for (Expression index : s.indices) {
|
||||
index.accept(this);
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
|
||||
|
||||
@Override
|
||||
public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) {
|
||||
visitVariable(s.variable, t);
|
||||
s.root.accept(this, t);
|
||||
for (Expression index : s.indices) {
|
||||
t.append('[');
|
||||
index.accept(this, t);
|
||||
|
@ -14,12 +14,6 @@ public final class VariablePrinter extends AbstractVisitor {
|
||||
super.visit(s);
|
||||
Console.println(s.target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ContainerAccessExpression s) {
|
||||
super.visit(s);
|
||||
Console.println(s.variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(VariableExpression s) {
|
||||
|
13
src/test/resources/modules/std/indexOf.own
Normal file
13
src/test/resources/modules/std/indexOf.own
Normal file
@ -0,0 +1,13 @@
|
||||
use "std"
|
||||
|
||||
def testIndexOf() {
|
||||
assertEquals(3, indexOf("123/456/789", "/"))
|
||||
}
|
||||
|
||||
def testIndexOfIndex() {
|
||||
assertEquals(7, indexOf("123/456/789", "/", 4))
|
||||
}
|
||||
|
||||
def testIndexOfNonMatch() {
|
||||
assertEquals(-1, indexOf("123", "/"))
|
||||
}
|
13
src/test/resources/modules/std/lastIndexOf.own
Normal file
13
src/test/resources/modules/std/lastIndexOf.own
Normal file
@ -0,0 +1,13 @@
|
||||
use "std"
|
||||
|
||||
def testLastIndexOf() {
|
||||
assertEquals(8, lastIndexOf("/123/456/789", "/"))
|
||||
}
|
||||
|
||||
def testLastIndexOfIndex() {
|
||||
assertEquals(4, lastIndexOf("/123/456/789", "/", 6))
|
||||
}
|
||||
|
||||
def testLastIndexOfNonMatch() {
|
||||
assertEquals(-1, lastIndexOf("123", "/"))
|
||||
}
|
23
src/test/resources/other/functionChain.own
Normal file
23
src/test/resources/other/functionChain.own
Normal file
@ -0,0 +1,23 @@
|
||||
def f1() = {"func": ::f2}
|
||||
def f2() = {
|
||||
"functions" : {
|
||||
"add" : def(a, b) = a + b
|
||||
"mul" : def(a, b) = a * b
|
||||
"negate" : def(a) = {"result" : -a}
|
||||
}
|
||||
}
|
||||
def f3() = def() = def() = def() = "test"
|
||||
def f4() = def() = ::f1
|
||||
|
||||
def testFunctionChain() {
|
||||
assertEquals(5, f1().func().`functions`.add(2, 3))
|
||||
assertEquals(6, f1().func().`functions`.mul(2, 3))
|
||||
}
|
||||
|
||||
def testCallChain() {
|
||||
assertEquals("test", f3()()()())
|
||||
}
|
||||
|
||||
def testBoth() {
|
||||
assertEquals(-123, f4()()().func().`functions`.negate(123).result)
|
||||
}
|
Loading…
Reference in New Issue
Block a user