From 5e246adccf986af9dd032d6e8645ddd5d2a577d4 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Fri, 24 Feb 2017 04:15:20 -0500 Subject: Work on unary operators Specifically, the coercive operator for allowing mixing floats and ints --- dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java | 25 +-- dice-lang/src/bjc/dicelang/v2/Evaluator.java | 24 ++- dice-lang/src/bjc/dicelang/v2/Parser.java | 12 ++ dice-lang/src/bjc/dicelang/v2/Shunter.java | 200 ++++++++++++---------- dice-lang/src/bjc/dicelang/v2/Token.java | 37 ++-- 5 files changed, 179 insertions(+), 119 deletions(-) diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java index d90ce9d..220fd3f 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java @@ -100,17 +100,18 @@ public class DiceLangEngine { litTokens = new FunctionalMap<>(); - litTokens.put("+", ADD); - litTokens.put("-", SUBTRACT); - litTokens.put("*", MULTIPLY); - litTokens.put("/", DIVIDE); - litTokens.put("//", IDIVIDE); - litTokens.put("dg", DICEGROUP); - litTokens.put("dc", DICECONCAT); - litTokens.put("dl", DICELIST); - litTokens.put("=>", LET); - litTokens.put(":=", BIND); - litTokens.put(",", GROUPSEP); + litTokens.put("+", ADD); + litTokens.put("-", SUBTRACT); + litTokens.put("*", MULTIPLY); + litTokens.put("/", DIVIDE); + litTokens.put("//", IDIVIDE); + litTokens.put("dg", DICEGROUP); + litTokens.put("dc", DICECONCAT); + litTokens.put("dl", DICELIST); + litTokens.put("=>", LET); + litTokens.put(":=", BIND); + litTokens.put(",", GROUPSEP); + litTokens.put("crc", COERCE); nextLiteral = 1; @@ -378,6 +379,8 @@ public class DiceLangEngine { IList readyTokens = shuntedTokens.flatMap(tk -> { if(tk.type == Token.Type.TOKGROUP) { return tk.tokenValues; + } else if(tk.type == Token.Type.TAGOP || tk.type == Token.Type.TAGOPR) { + return tk.tokenValues; } else { return new FunctionalList<>(tk); } diff --git a/dice-lang/src/bjc/dicelang/v2/Evaluator.java b/dice-lang/src/bjc/dicelang/v2/Evaluator.java index 72d7779..969b2d5 100644 --- a/dice-lang/src/bjc/dicelang/v2/Evaluator.java +++ b/dice-lang/src/bjc/dicelang/v2/Evaluator.java @@ -158,6 +158,13 @@ public class Evaluator { private TopDownTransformResult pickEvaluationType(Node nd) { switch(nd.type) { + case UNARYOP: + switch(nd.operatorType) { + case COERCE: + return TopDownTransformResult.PULLUP; + default: + return TopDownTransformResult.PUSHDOWN; + } default: return TopDownTransformResult.PUSHDOWN; } @@ -166,8 +173,7 @@ public class Evaluator { private ITree evaluateNode(ITree ast, Context ctx) { switch(ast.getHead().type) { case UNARYOP: - System.out.println("\tEVALUATOR ERROR: Unary operator evaluation isn't supported yet"); - return new Tree<>(FAIL(ast)); + return evaluateUnaryOp(ast, ctx); case BINOP: return evaluateBinaryOp(ast, ctx); case TOKREF: @@ -180,6 +186,20 @@ public class Evaluator { } } + private ITree evaluateUnaryOp(ITree ast, Context ctx) { + switch(ast.getHead().operatorType) { + case COERCE: + if(ast.getChildrenCount() != 1) { + Errors.inst.printError(EK_EVAL_NOTUNARY, ast.getChildrenCount()); + return new Tree<>(FAIL(AST)); + } + break; + default: + Errors.inst.printError(EK_EVAL_INVUNARY, ast.getHead().operatorType.toString()); + return new Tree<>(FAIL(ast)); + } + } + private ITree evaluateBinaryOp(ITree ast, Context ctx) { Token.Type binOp = ast.getHead().operatorType; diff --git a/dice-lang/src/bjc/dicelang/v2/Parser.java b/dice-lang/src/bjc/dicelang/v2/Parser.java index 9a873f1..7151e0e 100644 --- a/dice-lang/src/bjc/dicelang/v2/Parser.java +++ b/dice-lang/src/bjc/dicelang/v2/Parser.java @@ -127,6 +127,18 @@ public class Parser { working.push(opNode); } break; + case COERCE: + if(working.size() == 0) { + Errors.inst.printError(EK_PARSE_UNOPERAND, tk.toString()); + } else { + ITree operand = working.pop(); + ITree opNode = new Tree<>(new Node(UNARYOP, tk.type)); + + opNode.addChild(operand); + + working.push(opNode); + } + break; case INT_LIT: case FLOAT_LIT: case STRING_LIT: diff --git a/dice-lang/src/bjc/dicelang/v2/Shunter.java b/dice-lang/src/bjc/dicelang/v2/Shunter.java index 014be7d..37da14c 100644 --- a/dice-lang/src/bjc/dicelang/v2/Shunter.java +++ b/dice-lang/src/bjc/dicelang/v2/Shunter.java @@ -19,13 +19,17 @@ public class Shunter { private IMap ops; // Unary operators that can only be - // applied to non-operator tokens + // applied to non-operator tokens and yield operator tokens private Set unaryAdjectives; - // Unary operators that con only be - // applied to operator tokens + // Unary operators that can only be + // applied to operator tokens and yield operator tokens private Set unaryAdverbs; + // Unary operators that can only be + // applied to operator tokens and yield data tokens + private Set unaryGerunds; + private final int MATH_PREC = 20; private final int DICE_PREC = 10; private final int EXPR_PREC = 0; @@ -35,6 +39,9 @@ public class Shunter { unaryAdjectives = new HashSet<>(); unaryAdverbs = new HashSet<>(); + unaryGerunds = new HashSet<>(); + + unaryAdjectives.add(COERCE); ops.put(ADD, 0 + MATH_PREC); ops.put(SUBTRACT, 0 + MATH_PREC); @@ -51,6 +58,25 @@ public class Shunter { ops.put(BIND, 1 + EXPR_PREC); } + private boolean isUnary(Token ty) { + switch(ty.type) { + default: + return false; + } + } + + private boolean isOp(Token tk) { + Token.Type ty = tk.type; + + if(ops.containsKey(ty)) return true; + if(unaryAdjectives.contains(ty)) return true; + if(unaryAdverbs.contains(ty)) return true; + if(unaryGerunds.contains(ty)) return true; + if(ty == TAGOPR) return true; + + return false; + } + public boolean shuntTokens(IList tks, IList returned) { Deque opStack = new LinkedList<>(); @@ -60,118 +86,103 @@ public class Shunter { Deque currReturned = new LinkedList<>(); for(Token tk : tks.toIterable()) { + Token.Type tkType = tk.type; + if(unaryMode) { - if(unaryAdjectives.contains(tk.type) || unaryAdverbs.contains(tk.type)) { - unaryOps.push(tk); + if(isUnary(tk)) { + unaryOps.add(tk); continue; } - Token currOperator = unaryOps.peek(); - - if(unaryAdjectives.contains(currOperator.type)) { - boolean isOp = ops.containsKey(tk.type) - || unaryAdverbs.contains(tk.type) - || unaryAdjectives.contains(tk.type); + Token unaryOp = unaryOps.pop(); - if(isOp) { - Errors.inst.printError(EK_SHUNT_NOTADV, currOperator.toString(), tk.toString()); - return false; - } + Token.Type unaryType = unaryOp.type; - currReturned.addLast(tk); - currReturned.addLast(unaryOps.pop()); - } else if (unaryAdverbs.contains(currOperator.type)) { - if(opStack.size() < 1) { - Errors.inst.printError(EK_SHUNT_NOOP, currOperator.toString()); + if(unaryAdjectives.contains(unaryType)) { + if(isOp(tk)) { + Errors.inst.printError(EK_SHUNT_NOTADV, unaryOp.toString(), tk.toString()); return false; } - Token currOperand = opStack.peek(); - - boolean isOp = ops.containsKey(currOperand.type) - || unaryAdverbs.contains(currOperand.type) - || unaryAdjectives.contains(currOperand.type); - - if(!isOp) { - Errors.inst.printError(EK_SHUNT_NOTADJ, - currOperator.toString(), - tk.toString()); - - return false; + Token newTok = new Token(TAGOPR); + + if(tk.type == TAGOP) { + newTok.tokenValues = tk.tokenValues; + } else { + newTok.tokenValues = new FunctionalList<>(); } - currReturned.addLast(tk); - currReturned.addLast(unaryOps.pop()); + newTok.tokenValues.add(unaryOp); + opStack.push(newTok); + } else if(unaryAdverbs.contains(unaryType)) { + // @TODO finish implementing unary operators + // this will require adding a 'backfeed' to the shunter to catch + // tokens we missed while parsing unary operators } + } - if(unaryOps.isEmpty()) unaryMode = false; - } else { - if(unaryAdjectives.contains(tk.type) || unaryAdverbs.contains(tk.type)) { - unaryMode = true; - - unaryOps.add(tk); - continue; - } else if(ops.containsKey(tk.type)) { - while(!opStack.isEmpty() - && isHigherPrec(tk.type, opStack.peek().type)) { - currReturned.addLast(opStack.pop()); - } + if(isUnary(tk)) { + unaryMode = true; - opStack.push(tk); - } else if(tk.type == OPAREN || tk.type == OBRACE) { - opStack.push(tk); - - if(tk.type == OBRACE) - currReturned.addLast(tk); - } else if(tk.type == CPAREN || tk.type == CBRACE) { - Token matching = null; - - switch(tk.type) { - case CPAREN: - matching = new Token(OPAREN, tk.intValue); - break; - case CBRACE: - matching = new Token(OBRACE, tk.intValue); - break; - default: - break; - } + unaryOps.add(tk); + continue; + } else if(isOp(tk)) { + while(!opStack.isEmpty() && isHigherPrec(tk, opStack.peek())) { + currReturned.addLast(opStack.pop()); + } - if(!opStack.contains(matching)) { - Errors.inst.printError(EK_SHUNT_NOGROUP, - tk.toString(), matching.toString()); - return false; - } + opStack.push(tk); + } else if(tk.type == OPAREN || tk.type == OBRACE) { + opStack.push(tk); + + if(tk.type == OBRACE) currReturned.addLast(tk); + } else if(tk.type == CPAREN || tk.type == CBRACE) { + Token matching = null; + + switch(tk.type) { + case CPAREN: + matching = new Token(OPAREN, tk.intValue); + break; + case CBRACE: + matching = new Token(OBRACE, tk.intValue); + break; + default: + break; + } - while(!opStack.peek().equals(matching)) { - currReturned.addLast(opStack.pop()); - } + if(!opStack.contains(matching)) { + Errors.inst.printError(EK_SHUNT_NOGROUP, tk.toString(), matching.toString()); + return false; + } - if(tk.type == CBRACE) { - currReturned.addLast(tk); - } + while(!opStack.peek().equals(matching)) { + currReturned.addLast(opStack.pop()); + } - opStack.pop(); - } else if(tk.type == GROUPSEP) { - IList group = new FunctionalList<>(); + if(tk.type == CBRACE) { + currReturned.addLast(tk); + } - while(currReturned.size() != 0 && !currReturned.peek().isGrouper()) { - group.add(currReturned.pop()); - } + opStack.pop(); + } else if(tk.type == GROUPSEP) { + IList group = new FunctionalList<>(); - while(opStack.size() != 0 && !opStack.peek().isGrouper()) { - group.add(opStack.pop()); - } + while(currReturned.size() != 0 && !currReturned.peek().isGrouper()) { + group.add(currReturned.pop()); + } - if(currReturned.size() == 0) { - Errors.inst.printError(EK_SHUNT_INVSEP); - return false; - } + while(opStack.size() != 0 && !opStack.peek().isGrouper()) { + group.add(opStack.pop()); + } - currReturned.addLast(new Token(TOKGROUP, group)); - } else { - currReturned.addLast(tk); + if(currReturned.size() == 0) { + Errors.inst.printError(EK_SHUNT_INVSEP); + return false; } + + currReturned.addLast(new Token(TOKGROUP, group)); + } else { + currReturned.addLast(tk); } } @@ -187,7 +198,10 @@ public class Shunter { return true; } - private boolean isHigherPrec(Token.Type left, Token.Type right) { + private boolean isHigherPrec(Token lft, Token rght) { + Token.Type left = lft.type; + Token.Type right = rght.type; + boolean exists = ops.containsKey(right); // If it doesn't, the left is higher precedence. diff --git a/dice-lang/src/bjc/dicelang/v2/Token.java b/dice-lang/src/bjc/dicelang/v2/Token.java index e6f22a5..ed5ee8b 100644 --- a/dice-lang/src/bjc/dicelang/v2/Token.java +++ b/dice-lang/src/bjc/dicelang/v2/Token.java @@ -20,24 +20,41 @@ public class Token { INT_LIT, FLOAT_LIT, STRING_LIT, VREF, DICE_LIT, DICEGROUP, DICECONCAT, DICELIST, - LET, BIND, + LET, BIND, COERCE, OPAREN, CPAREN, OBRACKET, CBRACKET, OBRACE, CBRACE, + // Synthetic tokens // These are produced when needed - NIL, PRESHUNT, GROUPSEP, - TOKGROUP + NIL, GROUPSEP, TOKGROUP, + TAGOP, TAGOPR, + + // Tag tokens + // These are used for the TAG* tokens + } - public final Type type; + public final Type type; - // At most one of these is valid - // based on the token type + // This is used for the following token types + // INT_LIT (int value) + // STRING_LIT (index into string table) + // VREF (index into sym table) + // O* and C* (sym-count of current token) public long intValue; + + // This is used for the following token types + // FLOAT_LIT (float value) public double floatValue; - public String stringValue; + + // This is used for the following token types + // DICE_LIT (dice value) public DiceBox.DieExpression diceValue; + + // This is used for the following token types + // TOKGROUP (the tokens in the group) + // TAG* (the tagged construct) public IList tokenValues; public Token(Type typ) { @@ -56,12 +73,6 @@ public class Token { floatValue = val; } - public Token(Type typ, String val) { - this(typ); - - stringValue = val; - } - public Token(Type typ, DiceBox.DieExpression val) { this(typ); -- cgit v1.2.3