From 79660fa75bbeeafb905c7ac9130d50d409dd2735 Mon Sep 17 00:00:00 2001 From: Tomas Mysik Date: Mon, 31 Oct 2016 13:09:15 +0100 Subject: [PATCH] Add support for pow (^) --- .../sl/nodes/expression/SLPowNode.java | 42 ++++++++++ .../com/oracle/truffle/sl/parser/Parser.java | 51 +++++++------ .../truffle/sl/parser/SLNodeFactory.java | 4 + .../com/oracle/truffle/sl/parser/Scanner.java | 29 +++---- .../truffle/sl/parser/SimpleLanguage.atg | 2 +- .../com/oracle/truffle/sl/test/SLPowTest.java | 76 +++++++++++++++++++ tests/Pow.sl | 9 +++ 7 files changed, 175 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/oracle/truffle/sl/nodes/expression/SLPowNode.java create mode 100644 src/test/java/com/oracle/truffle/sl/test/SLPowTest.java create mode 100644 tests/Pow.sl diff --git a/src/main/java/com/oracle/truffle/sl/nodes/expression/SLPowNode.java b/src/main/java/com/oracle/truffle/sl/nodes/expression/SLPowNode.java new file mode 100644 index 000000000..47580f04a --- /dev/null +++ b/src/main/java/com/oracle/truffle/sl/nodes/expression/SLPowNode.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.oracle.truffle.sl.nodes.expression; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.sl.nodes.SLBinaryNode; +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * Performs the "^" operation - returns the value of the first argument + * raised to the power of the second argument. + */ +@NodeInfo(shortName = "^") +public abstract class SLPowNode extends SLBinaryNode { + + private final BranchProfile bigNumbers = BranchProfile.create(); + + + @Specialization(rewriteOn = ArithmeticException.class) + protected long pow(long left, long right) { + double pow = Math.pow(left, right); + if (pow > Long.MAX_VALUE) { + bigNumbers.enter(); + throw new ArithmeticException(); + } + return (long) pow; + } + + @Specialization + @CompilerDirectives.TruffleBoundary + protected BigInteger pow(BigInteger left, BigInteger right) { + return new BigDecimal(Math.pow(left.doubleValue(), right.doubleValue())).toBigIntegerExact(); + } + +} diff --git a/src/main/java/com/oracle/truffle/sl/parser/Parser.java b/src/main/java/com/oracle/truffle/sl/parser/Parser.java index 8600608f1..78994b782 100644 --- a/src/main/java/com/oracle/truffle/sl/parser/Parser.java +++ b/src/main/java/com/oracle/truffle/sl/parser/Parser.java @@ -60,7 +60,7 @@ public class Parser { public static final int _identifier = 1; public static final int _stringLiteral = 2; public static final int _numericLiteral = 3; - public static final int maxT = 34; + public static final int maxT = 35; static final boolean _T = true; static final boolean _x = false; @@ -227,7 +227,7 @@ SLStatementNode Statement(boolean inLoop) { Expect(11); break; } - default: SynErr(35); break; + default: SynErr(36); break; } return result; } @@ -354,9 +354,11 @@ SLExpressionNode Arithmetic() { SLExpressionNode Term() { SLExpressionNode result; result = Factor(); - while (la.kind == 28 || la.kind == 29) { + while (la.kind == 28 || la.kind == 29 || la.kind == 30) { if (la.kind == 28) { Get(); + } else if (la.kind == 29) { + Get(); } else { Get(); } @@ -377,7 +379,7 @@ SLExpressionNode Factor() { result = MemberExpression(null, null, assignmentName); } else if (StartOf(5)) { result = factory.createRead(assignmentName); - } else SynErr(36); + } else SynErr(37); } else if (la.kind == 2) { Get(); result = factory.createStringLiteral(t, true); @@ -392,7 +394,7 @@ SLExpressionNode Factor() { Expect(7); int length = (t.charPos + t.val.length()) - start; result = factory.createParenExpression(expr, start, length); - } else SynErr(37); + } else SynErr(38); return result; } @@ -420,7 +422,7 @@ SLExpressionNode MemberExpression(SLExpressionNode r, SLExpressionNode assignme Expect(7); Token finalToken = t; result = factory.createCall(receiver, parameters, finalToken); - } else if (la.kind == 30) { + } else if (la.kind == 31) { Get(); SLExpressionNode value = Expression(); if (assignmentName == null) { @@ -430,7 +432,7 @@ SLExpressionNode MemberExpression(SLExpressionNode r, SLExpressionNode assignme } else { result = factory.createWriteProperty(assignmentReceiver, assignmentName, value); } - } else if (la.kind == 31) { + } else if (la.kind == 32) { Get(); if (receiver == null) { receiver = factory.createRead(assignmentName); @@ -438,15 +440,15 @@ SLExpressionNode MemberExpression(SLExpressionNode r, SLExpressionNode assignme Expect(1); nestedAssignmentName = factory.createStringLiteral(t, false); result = factory.createReadProperty(receiver, nestedAssignmentName); - } else if (la.kind == 32) { + } else if (la.kind == 33) { Get(); if (receiver == null) { receiver = factory.createRead(assignmentName); } nestedAssignmentName = Expression(); result = factory.createReadProperty(receiver, nestedAssignmentName); - Expect(33); - } else SynErr(38); + Expect(34); + } else SynErr(39); if (StartOf(4)) { result = MemberExpression(result, receiver, nestedAssignmentName); } @@ -465,12 +467,12 @@ public void Parse() { } private static final boolean[][] set = { - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _x,_T,_x,_x, _x,_x,_T,_x, _T,_T,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x}, - {_x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x} + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_T,_T, _x,_T,_x,_x, _x,_x,_T,_x, _T,_T,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_T,_T,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x}, + {_x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x}, + {_x,_x,_x,_x, _x,_T,_T,_T, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x} }; @@ -544,15 +546,16 @@ public void SynErr(int line, int col, int n) { case 27: s = "\"-\" expected"; break; case 28: s = "\"*\" expected"; break; case 29: s = "\"/\" expected"; break; - case 30: s = "\"=\" expected"; break; - case 31: s = "\".\" expected"; break; - case 32: s = "\"[\" expected"; break; - case 33: s = "\"]\" expected"; break; - case 34: s = "??? expected"; break; - case 35: s = "invalid Statement"; break; - case 36: s = "invalid Factor"; break; + case 30: s = "\"^\" expected"; break; + case 31: s = "\"=\" expected"; break; + case 32: s = "\".\" expected"; break; + case 33: s = "\"[\" expected"; break; + case 34: s = "\"]\" expected"; break; + case 35: s = "??? expected"; break; + case 36: s = "invalid Statement"; break; case 37: s = "invalid Factor"; break; - case 38: s = "invalid MemberExpression"; break; + case 38: s = "invalid Factor"; break; + case 39: s = "invalid MemberExpression"; break; default: s = "error " + n; break; diff --git a/src/main/java/com/oracle/truffle/sl/parser/SLNodeFactory.java b/src/main/java/com/oracle/truffle/sl/parser/SLNodeFactory.java index 783226951..af6a21c31 100644 --- a/src/main/java/com/oracle/truffle/sl/parser/SLNodeFactory.java +++ b/src/main/java/com/oracle/truffle/sl/parser/SLNodeFactory.java @@ -79,6 +79,7 @@ import com.oracle.truffle.sl.nodes.expression.SLLongLiteralNode; import com.oracle.truffle.sl.nodes.expression.SLMulNodeGen; import com.oracle.truffle.sl.nodes.expression.SLParenExpressionNode; +import com.oracle.truffle.sl.nodes.expression.SLPowNodeGen; import com.oracle.truffle.sl.nodes.expression.SLStringLiteralNode; import com.oracle.truffle.sl.nodes.expression.SLSubNodeGen; import com.oracle.truffle.sl.nodes.local.SLReadArgumentNode; @@ -351,6 +352,9 @@ public SLExpressionNode createBinary(Token opToken, SLExpressionNode leftNode, S case "||": result = SLLogicalOrNodeGen.create(leftNode, rightNode); break; + case "^": + result = SLPowNodeGen.create(leftNode, rightNode); + break; default: throw new RuntimeException("unexpected operation: " + opToken.val); } diff --git a/src/main/java/com/oracle/truffle/sl/parser/Scanner.java b/src/main/java/com/oracle/truffle/sl/parser/Scanner.java index 474778af2..ca039c882 100644 --- a/src/main/java/com/oracle/truffle/sl/parser/Scanner.java +++ b/src/main/java/com/oracle/truffle/sl/parser/Scanner.java @@ -328,8 +328,8 @@ public class Scanner { static final char EOL = '\n'; static final int eofSym = 0; - static final int maxT = 34; - static final int noSym = 34; + static final int maxT = 35; + static final int noSym = 35; public Buffer buffer; // scanner buffer @@ -366,17 +366,18 @@ public class Scanner { start.set(59, 11); start.set(124, 12); start.set(38, 14); - start.set(60, 28); - start.set(62, 29); - start.set(61, 30); + start.set(60, 29); + start.set(62, 30); + start.set(61, 31); start.set(33, 19); start.set(43, 21); start.set(45, 22); start.set(42, 23); start.set(47, 24); - start.set(46, 25); - start.set(91, 26); - start.set(93, 27); + start.set(94, 25); + start.set(46, 26); + start.set(91, 27); + start.set(93, 28); start.set(Buffer.EOF, -1); literals.put("function", new Integer(4)); literals.put("break", new Integer(10)); @@ -597,23 +598,25 @@ Token NextToken() { case 24: {t.kind = 29; break loop;} case 25: - {t.kind = 31; break loop;} + {t.kind = 30; break loop;} case 26: {t.kind = 32; break loop;} case 27: {t.kind = 33; break loop;} case 28: + {t.kind = 34; break loop;} + case 29: recEnd = pos; recKind = 20; if (ch == '=') {AddCh(); state = 16; break;} else {t.kind = 20; break loop;} - case 29: + case 30: recEnd = pos; recKind = 22; if (ch == '=') {AddCh(); state = 17; break;} else {t.kind = 22; break loop;} - case 30: - recEnd = pos; recKind = 30; + case 31: + recEnd = pos; recKind = 31; if (ch == '=') {AddCh(); state = 18; break;} - else {t.kind = 30; break loop;} + else {t.kind = 31; break loop;} } } diff --git a/src/main/java/com/oracle/truffle/sl/parser/SimpleLanguage.atg b/src/main/java/com/oracle/truffle/sl/parser/SimpleLanguage.atg index 5b4fab1ae..759fd8ad3 100644 --- a/src/main/java/com/oracle/truffle/sl/parser/SimpleLanguage.atg +++ b/src/main/java/com/oracle/truffle/sl/parser/SimpleLanguage.atg @@ -214,7 +214,7 @@ Term = Factor { - ("*" | "/") (. Token op = t; .) + ("*" | "/" | "^") (. Token op = t; .) Factor (. result = factory.createBinary(op, result, right); .) } . diff --git a/src/test/java/com/oracle/truffle/sl/test/SLPowTest.java b/src/test/java/com/oracle/truffle/sl/test/SLPowTest.java new file mode 100644 index 000000000..640924717 --- /dev/null +++ b/src/test/java/com/oracle/truffle/sl/test/SLPowTest.java @@ -0,0 +1,76 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.oracle.truffle.sl.test; + +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; +import java.math.BigDecimal; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SLPowTest { + + private PolyglotEngine engine; + private PolyglotEngine.Value pow; + + @Before + public void initEngine() throws Exception { + engine = PolyglotEngine.newBuilder().build(); + engine.eval( + Source.newBuilder("\n" + + "function pow(a, b) {\n" + + " return a ^ b;\n" + + "}\n"). + name("pow.sl"). + mimeType("application/x-sl"). + build() + ); + pow = engine.findGlobalSymbol("pow"); + } + + @After + public void dispose() { + engine.dispose(); + } + + @Test + public void powOf2And2() throws Exception { + Number ret = pow.execute(2, 2).as(Number.class); + assertEquals(4, ret.intValue()); + } + + @Test + public void pow2And3() throws Exception { + Number ret = pow.execute(2, 3).as(Number.class); + assertEquals(8, ret.intValue()); + } + + @Test + public void powBigIntegers() throws Exception { + Number ret = pow.execute(Long.MAX_VALUE, 2).as(Number.class); + assertEquals(new BigDecimal(Math.pow(Long.MAX_VALUE, 2)).toBigIntegerExact(), ret); + } + + @Test + public void pow2AndString() throws Exception { + Exception exc = null; + try { + pow.execute(2, "test"); + Assert.fail("should not get here"); + } catch (RuntimeException ex) { + Throwable cause = ex.getCause(); + Assert.assertTrue(cause.getClass().getName(), cause instanceof UnsupportedTypeException); + exc = ex; + } + Assert.assertNotNull(exc); + } + +} diff --git a/tests/Pow.sl b/tests/Pow.sl new file mode 100644 index 000000000..0be23a1ca --- /dev/null +++ b/tests/Pow.sl @@ -0,0 +1,9 @@ +function main() { + println(2 ^ 2); + println(2 ^ 3); + println(2 ^ 2 ^ 2); + println(3 + 2 ^ 2); + println(6465467984651316454646 ^ 4); + //println(2 ^ "test"); + //println(6465467984651316454646 ^ "test"); +}