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 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 {
|
static {
|
||||||
assignOperator = new EnumMap(TokenType.class);
|
ASSIGN_OPERATORS = new EnumMap(TokenType.class);
|
||||||
assignOperator.put(TokenType.EQ, null);
|
ASSIGN_OPERATORS.put(TokenType.EQ, null);
|
||||||
assignOperator.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
|
ASSIGN_OPERATORS.put(TokenType.PLUSEQ, BinaryExpression.Operator.ADD);
|
||||||
assignOperator.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
|
ASSIGN_OPERATORS.put(TokenType.MINUSEQ, BinaryExpression.Operator.SUBTRACT);
|
||||||
assignOperator.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
|
ASSIGN_OPERATORS.put(TokenType.STAREQ, BinaryExpression.Operator.MULTIPLY);
|
||||||
assignOperator.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
|
ASSIGN_OPERATORS.put(TokenType.SLASHEQ, BinaryExpression.Operator.DIVIDE);
|
||||||
assignOperator.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
|
ASSIGN_OPERATORS.put(TokenType.PERCENTEQ, BinaryExpression.Operator.REMAINDER);
|
||||||
assignOperator.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
|
ASSIGN_OPERATORS.put(TokenType.AMPEQ, BinaryExpression.Operator.AND);
|
||||||
assignOperator.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
|
ASSIGN_OPERATORS.put(TokenType.CARETEQ, BinaryExpression.Operator.XOR);
|
||||||
assignOperator.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
|
ASSIGN_OPERATORS.put(TokenType.BAREQ, BinaryExpression.Operator.OR);
|
||||||
assignOperator.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
|
ASSIGN_OPERATORS.put(TokenType.COLONCOLONEQ, BinaryExpression.Operator.PUSH);
|
||||||
assignOperator.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
|
ASSIGN_OPERATORS.put(TokenType.LTLTEQ, BinaryExpression.Operator.LSHIFT);
|
||||||
assignOperator.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
|
ASSIGN_OPERATORS.put(TokenType.GTGTEQ, BinaryExpression.Operator.RSHIFT);
|
||||||
assignOperator.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
|
ASSIGN_OPERATORS.put(TokenType.GTGTGTEQ, BinaryExpression.Operator.URSHIFT);
|
||||||
assignOperator.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
|
ASSIGN_OPERATORS.put(TokenType.ATEQ, BinaryExpression.Operator.AT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<Token> tokens;
|
private final List<Token> tokens;
|
||||||
@ -156,10 +156,10 @@ public final class Parser {
|
|||||||
return functionDefine();
|
return functionDefine();
|
||||||
}
|
}
|
||||||
if (match(TokenType.MATCH)) {
|
if (match(TokenType.MATCH)) {
|
||||||
return new ExprStatement(match());
|
return match();
|
||||||
}
|
}
|
||||||
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
|
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
|
||||||
return new ExprStatement(function(qualifiedName()));
|
return new ExprStatement(functionChain(qualifiedName()));
|
||||||
}
|
}
|
||||||
return assignmentStatement();
|
return assignmentStatement();
|
||||||
}
|
}
|
||||||
@ -185,7 +185,7 @@ public final class Parser {
|
|||||||
} else {
|
} else {
|
||||||
variables.add(null);
|
variables.add(null);
|
||||||
}
|
}
|
||||||
match(TokenType.COMMA);
|
consume(TokenType.COMMA);
|
||||||
}
|
}
|
||||||
consume(TokenType.EQ);
|
consume(TokenType.EQ);
|
||||||
return new DestructuringAssignmentStatement(variables, expression());
|
return new DestructuringAssignmentStatement(variables, expression());
|
||||||
@ -228,35 +228,36 @@ public final class Parser {
|
|||||||
return foreachMapStatement();
|
return foreachMapStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
|
// for (init, condition, increment) body
|
||||||
|
boolean optParentheses = match(TokenType.LPAREN);
|
||||||
final Statement initialization = assignmentStatement();
|
final Statement initialization = assignmentStatement();
|
||||||
consume(TokenType.COMMA);
|
consume(TokenType.COMMA);
|
||||||
final Expression termination = expression();
|
final Expression termination = expression();
|
||||||
consume(TokenType.COMMA);
|
consume(TokenType.COMMA);
|
||||||
final Statement increment = assignmentStatement();
|
final Statement increment = assignmentStatement();
|
||||||
if (openParen) consume(TokenType.RPAREN); // скобки
|
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
|
||||||
final Statement statement = statementOrBlock();
|
final Statement statement = statementOrBlock();
|
||||||
return new ForStatement(initialization, termination, increment, statement);
|
return new ForStatement(initialization, termination, increment, statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ForeachArrayStatement foreachArrayStatement() {
|
private ForeachArrayStatement foreachArrayStatement() {
|
||||||
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
|
boolean optParentheses = match(TokenType.LPAREN);
|
||||||
final String variable = consume(TokenType.WORD).getText();
|
final String variable = consume(TokenType.WORD).getText();
|
||||||
consume(TokenType.COLON);
|
consume(TokenType.COLON);
|
||||||
final Expression container = expression();
|
final Expression container = expression();
|
||||||
if (openParen) consume(TokenType.RPAREN); // скобки
|
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
|
||||||
final Statement statement = statementOrBlock();
|
final Statement statement = statementOrBlock();
|
||||||
return new ForeachArrayStatement(variable, container, statement);
|
return new ForeachArrayStatement(variable, container, statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ForeachMapStatement foreachMapStatement() {
|
private ForeachMapStatement foreachMapStatement() {
|
||||||
boolean openParen = match(TokenType.LPAREN); // необязательные скобки
|
boolean optParentheses = match(TokenType.LPAREN);
|
||||||
final String key = consume(TokenType.WORD).getText();
|
final String key = consume(TokenType.WORD).getText();
|
||||||
consume(TokenType.COMMA);
|
consume(TokenType.COMMA);
|
||||||
final String value = consume(TokenType.WORD).getText();
|
final String value = consume(TokenType.WORD).getText();
|
||||||
consume(TokenType.COLON);
|
consume(TokenType.COLON);
|
||||||
final Expression container = expression();
|
final Expression container = expression();
|
||||||
if (openParen) consume(TokenType.RPAREN); // скобки
|
if (optParentheses) consume(TokenType.RPAREN); // close opt parentheses
|
||||||
final Statement statement = statementOrBlock();
|
final Statement statement = statementOrBlock();
|
||||||
return new ForeachMapStatement(key, value, container, statement);
|
return new ForeachMapStatement(key, value, container, statement);
|
||||||
}
|
}
|
||||||
@ -296,6 +297,26 @@ public final class Parser {
|
|||||||
return statementOrBlock();
|
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) {
|
private FunctionalExpression function(Expression qualifiedNameExpr) {
|
||||||
// function(arg1, arg2, ...)
|
// function(arg1, arg2, ...)
|
||||||
consume(TokenType.LPAREN);
|
consume(TokenType.LPAREN);
|
||||||
@ -426,13 +447,13 @@ public final class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final TokenType currentType = get(0).getType();
|
final TokenType currentType = get(0).getType();
|
||||||
if (!assignOperator.containsKey(currentType)) {
|
if (!ASSIGN_OPERATORS.containsKey(currentType)) {
|
||||||
pos = position;
|
pos = position;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
match(currentType);
|
match(currentType);
|
||||||
|
|
||||||
final BinaryExpression.Operator op = assignOperator.get(currentType);
|
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
|
||||||
final Expression expression = expression();
|
final Expression expression = expression();
|
||||||
|
|
||||||
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
|
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
|
||||||
@ -687,14 +708,14 @@ public final class Parser {
|
|||||||
private Expression variable() {
|
private Expression variable() {
|
||||||
// function(...
|
// function(...
|
||||||
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
|
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();
|
final Expression qualifiedNameExpr = qualifiedName();
|
||||||
if (qualifiedNameExpr != null) {
|
if (qualifiedNameExpr != null) {
|
||||||
// variable(args) || arr["key"](args) || obj.key(args)
|
// variable(args) || arr["key"](args) || obj.key(args)
|
||||||
if (lookMatch(0, TokenType.LPAREN)) {
|
if (lookMatch(0, TokenType.LPAREN)) {
|
||||||
return function(qualifiedNameExpr);
|
return functionChain(qualifiedNameExpr);
|
||||||
}
|
}
|
||||||
// postfix increment/decrement
|
// postfix increment/decrement
|
||||||
if (match(TokenType.PLUSPLUS)) {
|
if (match(TokenType.PLUSPLUS)) {
|
||||||
|
@ -10,14 +10,28 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public final class ContainerAccessExpression implements Expression, Accessible {
|
public final class ContainerAccessExpression implements Expression, Accessible {
|
||||||
|
|
||||||
public final String variable;
|
public final Expression root;
|
||||||
public final List<Expression> indices;
|
public final List<Expression> indices;
|
||||||
|
private boolean rootIsVariable;
|
||||||
|
|
||||||
public ContainerAccessExpression(String variable, List<Expression> indices) {
|
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;
|
this.indices = indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean rootIsVariable() {
|
||||||
|
return rootIsVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expression getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value eval() {
|
public Value eval() {
|
||||||
return get();
|
return get();
|
||||||
@ -60,7 +74,7 @@ public final class ContainerAccessExpression implements Expression, Accessible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Value getContainer() {
|
public Value getContainer() {
|
||||||
Value container = Variables.get(variable);
|
Value container = root.eval();
|
||||||
final int last = indices.size() - 1;
|
final int last = indices.size() - 1;
|
||||||
for (int i = 0; i < last; i++) {
|
for (int i = 0; i < last; i++) {
|
||||||
final Value index = index(i);
|
final Value index = index(i);
|
||||||
@ -108,6 +122,6 @@ public final class ContainerAccessExpression implements Expression, Accessible {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return variable + indices;
|
return root.toString() + indices;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,8 +87,10 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visit(ContainerAccessExpression s, T t) {
|
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());
|
final List<Expression> indices = new ArrayList<>(s.indices.size());
|
||||||
boolean changed = false;
|
|
||||||
for (Expression expression : s.indices) {
|
for (Expression expression : s.indices) {
|
||||||
final Node node = expression.accept(this, t);
|
final Node node = expression.accept(this, t);
|
||||||
if (node != expression) {
|
if (node != expression) {
|
||||||
@ -97,7 +99,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
|||||||
indices.add((Expression) node);
|
indices.add((Expression) node);
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
return new ContainerAccessExpression(s.variable, indices);
|
return new ContainerAccessExpression((Expression) root, indices);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,13 @@ public class VariablesGrabber extends OptimizationVisitor<Map<String, VariableIn
|
|||||||
t.put(variableName, variableInfo(t, variableName));
|
t.put(variableName, variableInfo(t, variableName));
|
||||||
}
|
}
|
||||||
if (s.expr1 instanceof ContainerAccessExpression) {
|
if (s.expr1 instanceof ContainerAccessExpression) {
|
||||||
final String variableName = ((ContainerAccessExpression) s.expr1).variable;
|
ContainerAccessExpression conExpr = (ContainerAccessExpression) s.expr1;
|
||||||
|
if (conExpr.rootIsVariable()) {
|
||||||
|
final String variableName = ((VariableExpression) conExpr.root).name;
|
||||||
t.put(variableName, variableInfo(t, variableName));
|
t.put(variableName, variableInfo(t, variableName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return super.visit(s, t);
|
return super.visit(s, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ public abstract class AbstractVisitor implements Visitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ContainerAccessExpression s) {
|
public void visit(ContainerAccessExpression s) {
|
||||||
|
s.root.accept(this);
|
||||||
for (Expression index : s.indices) {
|
for (Expression index : s.indices) {
|
||||||
index.accept(this);
|
index.accept(this);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ public class PrintVisitor implements ResultVisitor<StringBuilder, StringBuilder>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) {
|
public StringBuilder visit(ContainerAccessExpression s, StringBuilder t) {
|
||||||
visitVariable(s.variable, t);
|
s.root.accept(this, t);
|
||||||
for (Expression index : s.indices) {
|
for (Expression index : s.indices) {
|
||||||
t.append('[');
|
t.append('[');
|
||||||
index.accept(this, t);
|
index.accept(this, t);
|
||||||
|
@ -15,12 +15,6 @@ public final class VariablePrinter extends AbstractVisitor {
|
|||||||
Console.println(s.target);
|
Console.println(s.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ContainerAccessExpression s) {
|
|
||||||
super.visit(s);
|
|
||||||
Console.println(s.variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(VariableExpression s) {
|
public void visit(VariableExpression s) {
|
||||||
super.visit(s);
|
super.visit(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