diff --git a/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4 b/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4 index cb06926..57fe47c 100644 --- a/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4 +++ b/antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4 @@ -91,7 +91,7 @@ multiplicationExpression ; unaryExpression - : ('-' | '!') unaryExpression + : ('-' | '!' | '#') unaryExpression | postfixExpression ; @@ -183,4 +183,4 @@ LINE_COMMENT ; -ANY: . ; \ No newline at end of file +ANY: . ; diff --git a/docs/ez-lang.rst b/docs/ez-lang.rst index 46e0756..9099ae6 100644 --- a/docs/ez-lang.rst +++ b/docs/ez-lang.rst @@ -222,6 +222,7 @@ Following table describes the available operators by their precedence (low to hi | ``%`` | | | +------------+-----------------+----------+ | ``-`` | negate | Unary | +| ``#`` | array length | Unary | | ``!`` | | | +------------+-----------------+----------+ | ``(...)``, | function call, | Postfix | diff --git a/lexer/src/main/java/com/compilerprogramming/ezlang/lexer/Lexer.java b/lexer/src/main/java/com/compilerprogramming/ezlang/lexer/Lexer.java index 295c7e1..d673446 100644 --- a/lexer/src/main/java/com/compilerprogramming/ezlang/lexer/Lexer.java +++ b/lexer/src/main/java/com/compilerprogramming/ezlang/lexer/Lexer.java @@ -154,6 +154,7 @@ public Token scan() { case ',': case '.': case '%': + case '#': case '+': case '*': case ';': diff --git a/lexer/src/test/java/com/compilerprogramming/ezlang/lexer/TestLexer.java b/lexer/src/test/java/com/compilerprogramming/ezlang/lexer/TestLexer.java index 32d1614..49bd2d8 100644 --- a/lexer/src/test/java/com/compilerprogramming/ezlang/lexer/TestLexer.java +++ b/lexer/src/test/java/com/compilerprogramming/ezlang/lexer/TestLexer.java @@ -12,7 +12,7 @@ public class TestLexer { public void testLexer() { String src = """ // A comment - Ident=,>=<>>=!=!1.5{0}11()[]+-*/ //Another comment + Ident=,>=<>>=!=!1.5{0}11()[]+-*/# //Another comment """; Lexer lexer = new Lexer(src); Token[] expected = new Token[]{ @@ -37,7 +37,8 @@ public void testLexer() { Token.newPunct("+", 0), Token.newPunct("-", 0), Token.newPunct("*", 0), - Token.newPunct("/", 0) + Token.newPunct("/", 0), + Token.newPunct("#", 0) }; List tokens = new ArrayList<>(); diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java index 14e1829..2992d17 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/compiler/SparseConditionalConstantPropagation.java @@ -452,6 +452,11 @@ private static EZType operandType(Operand operand) { } private static boolean evalUnary(LatticeElement cell, LatticeElement input, String unOp, EZType resultType) { + if (unOp.equals("#")) { + if (input.kind == F_TOP || input.kind == F_REF_TOP) + return false; + return cell.meetWithTypeBottom(resultType); + } if (input.isIntegerConstant()) { long value = input.kind == F_INT_ZERO ? 0 : input.intValue; if (unOp.equals("-")) diff --git a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java index a32834b..4798ddf 100644 --- a/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java +++ b/optvm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java @@ -153,6 +153,9 @@ else if (arg instanceof Operand.NullConstantOperand) { else if (unaryValue instanceof Value.FloatValue floatValue && unaryInst.unop.equals("-")) { execStack.stack[base + unaryInst.result().frameSlot()] = new Value.FloatValue(-floatValue.value); } + else if (unaryValue instanceof Value.ArrayValue arrayValue && unaryInst.unop.equals("#")) { + execStack.stack[base + unaryInst.result().frameSlot()] = new Value.IntegerValue(arrayValue.values.size()); + } else throw new IllegalStateException("Unexpected unary operand: " + unaryOperand); } diff --git a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java index 26d3174..7ffb987 100644 --- a/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java +++ b/optvm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java @@ -1031,4 +1031,18 @@ func main()->Int integerValue.value == 1); } + @Test + public void testArrayLengthUnaryOperator() { + String src = """ + func foo()->Int { + var a = new [Int] {1,2,3,4} + var b = new [Int] {len=0,value=0} + return #a + #b + } + """; + var value = compileAndRun(src, "foo"); + Assert.assertNotNull(value); + Assert.assertTrue(value instanceof Value.IntegerValue integerValue + && integerValue.value == 4); + } } diff --git a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java index d185a4a..b4b60ac 100644 --- a/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java +++ b/parser/src/main/java/com/compilerprogramming/ezlang/parser/Parser.java @@ -329,7 +329,8 @@ private AST.Expr parseMultiplication(Lexer lexer) { private AST.Expr parseUnary(Lexer lexer) { if (isToken(currentToken, "-") - || isToken(currentToken, "!")) { + || isToken(currentToken, "!") + || isToken(currentToken, "#")) { var tok = currentToken; nextToken(lexer); return new AST.UnaryExpr(tok, parseUnary(lexer), tok.lineNumber); diff --git a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java index 6faeace..518c84a 100644 --- a/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java +++ b/registervm/src/main/java/com/compilerprogramming/ezlang/interpreter/Interpreter.java @@ -152,6 +152,9 @@ else if (arg instanceof Operand.NullConstantOperand) { else if (unaryValue instanceof Value.FloatValue floatValue && unaryInst.unop.equals("-")) { execStack.stack[base + unaryInst.result().frameSlot()] = new Value.FloatValue(-floatValue.value); } + else if (unaryValue instanceof Value.ArrayValue arrayValue && unaryInst.unop.equals("#")) { + execStack.stack[base + unaryInst.result().frameSlot()] = new Value.IntegerValue(arrayValue.values.size()); + } else throw new IllegalStateException("Unexpected unary operand: " + unaryOperand); } diff --git a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java index ac15361..cdfedf1 100644 --- a/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java +++ b/registervm/src/test/java/com/compilerprogramming/ezlang/interpreter/TestInterpreter.java @@ -558,4 +558,18 @@ func foo()->Float { Assert.assertTrue(value instanceof Value.FloatValue floatValue && Math.abs(floatValue.value - 3.5) < 0.000001); } + @Test + public void testArrayLengthUnaryOperator() { + String src = """ + func foo()->Int { + var a = new [Int] {1,2,3} + var b = new [Int] {len=0,value=0} + return #a + #b + } + """; + var value = compileAndRun(src, "foo"); + Assert.assertNotNull(value); + Assert.assertTrue(value instanceof Value.IntegerValue integerValue + && integerValue.value == 3); + } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java index 6051d08..0f5b827 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/Compiler.java @@ -465,6 +465,25 @@ private Node compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) { return objPtr; } + // # operator + private Node compileArrayLengthExpr(AST.Expr arrayExpr, int lineNumber) { + Node objPtr = compileExpr(arrayExpr).keep(); + if( !(objPtr._type instanceof TypeMemPtr ptr) ) { + throw new CompilerException("Unexpected type " + objPtr._type.str(), lineNumber); + } + String name = "#"; + TypeStruct base = ptr._obj; + int fidx = base.find(name); + if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'", lineNumber); + Field f = base._fields[fidx]; + Node off = con(base.offset(fidx)).keep(); + Node load = new LoadNode(name, f._alias, f._type, memAlias(f._alias), objPtr, off); + load = peep(load); + objPtr.unkeep(); + off.unkeep(); + return load; + } + private Node compileArrayIndexExpr(AST.ArrayLoadExpr arrayLoadExpr) { Node objPtr = compileExpr(arrayLoadExpr.array).keep(); // Sanity check expr for being a reference @@ -531,12 +550,23 @@ private Node compileInitExpr(AST.InitExpr initExpr) { return objPtr.unkeep(); } - private Node newArray(TypeStruct ary, Node len) { + private Node newArray(TypeStruct ary, Node arrayLen) { int base = ary.aryBase (); - int scale= ary.aryScale(); - Node size = peep(new AddNode(con(base),peep(new ShlNode(len.keep(),con(scale))))); - return newStruct(ary,size); + int scale = ary.aryScale(); + // array size = base + len << scale + Node size = peep(new AddNode(con(base),peep(new ShlNode(arrayLen.keep(),con(scale))))); + Node ptr = newStruct(ary,size).keep(); + // arrays are structs with two fields + // first field is length + // second field is [] data + Field[] fs = ary._fields; + // store length as int32 + Node mem = memAlias(fs[0]._alias); + Node st = new StoreNode(fs[0]._fname,fs[0]._alias,fs[0]._type,mem,ptr,con(ary.offset(0)),arrayLen.unkeep(),true).peephole(); + memAlias(fs[0]._alias,st); + return ptr.unkeep(); } + /** * Return a NewNode initialized memory. * @param obj is the declared type, with GLB fields @@ -586,6 +616,7 @@ private Node compileUnaryExpr(AST.UnaryExpr unaryExpr) { } // Maybe below we should explicitly set Int case "!": return peep(new NotNode(compileExpr(unaryExpr.expr))); + case "#": return compileArrayLengthExpr(unaryExpr.expr, unaryExpr.lineNumber); default: throw new CompilerException("Invalid unary op", unaryExpr.lineNumber); } } diff --git a/seaofnodes/src/test/cases/array/array.ez b/seaofnodes/src/test/cases/array/array.ez new file mode 100644 index 0000000..eca7280 --- /dev/null +++ b/seaofnodes/src/test/cases/array/array.ez @@ -0,0 +1,3 @@ +func foo()->Int { + return #new [Int]{42,84} +} diff --git a/seaofnodes/src/test/cases/array/array.ez_ir b/seaofnodes/src/test/cases/array/array.ez_ir new file mode 100644 index 0000000..83a1f88 --- /dev/null +++ b/seaofnodes/src/test/cases/array/array.ez_ir @@ -0,0 +1,21 @@ +START: [[ ]] + 4 Start ____ ____ ____ [[ 7 18 3 ]] [ Ctrl, #TOP, int] + +L3: [[ START ]] + 3 foo ____ 4 [[ 6 17 11 10 14 8 13 5 16 2 ]] Ctrl + 6 $mem 3 7 [[ 5 10 10 ]] #BOT + 17 $rpc 3 18 [[ 2 ]] $[ALL] + 11 #24 3 [[ 10 ]] 24 + 10 new_ary 3 11 6 6 [[ 9 12 15 ]] [ Ctrl, *![int], #2:u32, #3:int] + 9 $2 10 [[ 8 ]] #2:u32 + 12 [int] 10 [[ 8 14 13 ]] *[int] + 15 $3 10 [[ 14 ]] #3:int + 14 st8 3 15 12 ____ ____ [[ 13 ]] #3:int + 13 st8 3 14 12 ____ ____ [[ 5 ]] #3:int + 8 st4 3 9 12 ____ ____ [[ 5 ]] #2:u32 + 16 #2 3 [[ 2 ]] 2 + 5 ALLMEM 3 6 8 13 [[ 2 ]] #BOT + 2 Return 3 5 16 17 [[ 1 ]] [ Ctrl, #BOT, 2] + +L1: [[ ]] + 1 Stop 2 [[ ]] Bot diff --git a/seaofnodes/src/test/cases/array/array.ez_out b/seaofnodes/src/test/cases/array/array.ez_out new file mode 100644 index 0000000..0530680 --- /dev/null +++ b/seaofnodes/src/test/cases/array/array.ez_out @@ -0,0 +1,15 @@ +---foo { -> int #0}--------------------------- +0000 4883EC08 subi rsp -= #8 +0004 BE18000000 ldi rsi = #24 +0009 BF01000000 alloc ldi rcx = #1 +000E E800000000 call #calloc +0013 48C740082A st8 [rax+8],#42 +0018 000000 +001B C700020000 st4 [rax],#2 +0020 00 +0021 48C7401054 st8 [rax+16],#84 +0026 000000 +0029 B802000000 ldi rax = #2 +002E 4883C408C3 addi rsp += #8 +0033 ret +---{ -> int #0}--------------------------- \ No newline at end of file diff --git a/seaofnodes/src/test/cases/array/array.smp b/seaofnodes/src/test/cases/array/array.smp new file mode 100644 index 0000000..d535448 --- /dev/null +++ b/seaofnodes/src/test/cases/array/array.smp @@ -0,0 +1,6 @@ +var foo = {-> + var a = new int[2]; + a[0] = 42; + a[1] = 84; + return a#; +}; \ No newline at end of file diff --git a/seaofnodes/src/test/cases/array/array.smp_ir b/seaofnodes/src/test/cases/array/array.smp_ir new file mode 100644 index 0000000..83a1f88 --- /dev/null +++ b/seaofnodes/src/test/cases/array/array.smp_ir @@ -0,0 +1,21 @@ +START: [[ ]] + 4 Start ____ ____ ____ [[ 7 18 3 ]] [ Ctrl, #TOP, int] + +L3: [[ START ]] + 3 foo ____ 4 [[ 6 17 11 10 14 8 13 5 16 2 ]] Ctrl + 6 $mem 3 7 [[ 5 10 10 ]] #BOT + 17 $rpc 3 18 [[ 2 ]] $[ALL] + 11 #24 3 [[ 10 ]] 24 + 10 new_ary 3 11 6 6 [[ 9 12 15 ]] [ Ctrl, *![int], #2:u32, #3:int] + 9 $2 10 [[ 8 ]] #2:u32 + 12 [int] 10 [[ 8 14 13 ]] *[int] + 15 $3 10 [[ 14 ]] #3:int + 14 st8 3 15 12 ____ ____ [[ 13 ]] #3:int + 13 st8 3 14 12 ____ ____ [[ 5 ]] #3:int + 8 st4 3 9 12 ____ ____ [[ 5 ]] #2:u32 + 16 #2 3 [[ 2 ]] 2 + 5 ALLMEM 3 6 8 13 [[ 2 ]] #BOT + 2 Return 3 5 16 17 [[ 1 ]] [ Ctrl, #BOT, 2] + +L1: [[ ]] + 1 Stop 2 [[ ]] Bot diff --git a/seaofnodes/src/test/cases/array/array.smp_out b/seaofnodes/src/test/cases/array/array.smp_out new file mode 100644 index 0000000..ad51d7b --- /dev/null +++ b/seaofnodes/src/test/cases/array/array.smp_out @@ -0,0 +1,15 @@ +---foo { -> 2 #0}--------------------------- +0000 4883EC08 subi rsp -= #8 +0004 BE18000000 ldi rsi = #24 +0009 BF01000000 alloc ldi rcx = #1 +000E E800000000 call #calloc +0013 48C740082A st8 [rax+8],#42 +0018 000000 +001B C700020000 st4 [rax],#2 +0020 00 +0021 48C7401054 st8 [rax+16],#84 +0026 000000 +0029 B802000000 ldi rax = #2 +002E 4883C408C3 addi rsp += #8 +0033 ret +---{ -> 2 #0}--------------------------- \ No newline at end of file diff --git a/seaofnodes/src/test/cases/array2/array.ez b/seaofnodes/src/test/cases/array2/array.ez new file mode 100644 index 0000000..af8ecee --- /dev/null +++ b/seaofnodes/src/test/cases/array2/array.ez @@ -0,0 +1,6 @@ +func foo()->[Float] { + return new [Float]{4.2,8.4} +} +func bar()->Int { + return #foo() +} \ No newline at end of file diff --git a/seaofnodes/src/test/cases/array2/array.ez_ir b/seaofnodes/src/test/cases/array2/array.ez_ir new file mode 100644 index 0000000..a08c246 --- /dev/null +++ b/seaofnodes/src/test/cases/array2/array.ez_ir @@ -0,0 +1,38 @@ +START: [[ ]] + 4 Start ____ ____ ____ [[ 7 19 24 3 ]] [ Ctrl, #TOP, int] + +L3: [[ START ]] + 3 foo ____ 4 [[ 6 18 11 16 17 10 14 13 8 5 2 ]] Ctrl + 6 $mem 3 7 [[ 5 10 10 ]] #BOT + 18 $rpc 3 19 [[ 2 ]] $[ALL] + 16 #4.2 3 [[ 14 ]] 4.2 + 11 #24 3 [[ 10 ]] 24 + 17 #8.4 3 [[ 13 ]] 8.4 + 14 st8 3 15 12 ____ 16 [[ 13 ]] #5:flt + 13 st8 3 14 12 ____ 17 [[ 5 ]] #5:flt + 10 new_ary 3 11 6 6 [[ 9 12 15 ]] [ Ctrl, *![flt], #4:u32, #5:flt] + 9 $4 10 [[ 8 ]] #4:u32 + 12 [flt] 10 [[ 8 14 13 2 ]] *[flt] + 15 $5 10 [[ 14 ]] #5:flt + 8 st4 3 9 12 ____ ____ [[ 5 ]] #4:u32 + 5 ALLMEM 3 6 ____ ____ 8 13 [[ 2 ]] #BOT + 2 Return 3 5 12 18 [[ 1 ]] [ Ctrl, #BOT, *![flt]] + +L24: [[ START ]] + 24 bar ____ 4 [[ 25 29 23 ]] Ctrl + 25 $mem 24 7 [[ 23 ]] #BOT + 29 $rpc 24 19 [[ 20 ]] $[ALL] + 23 call 24 25 [[ 22 ]] Ctrl + +L22: [[ L24 ]] + 22 CallEnd 23 [[ 21 26 28 ]] [ Ctrl, #BOT, *![flt]] + 28 #2 22 [[ 27 ]] *[flt] + 26 $mem 22 [[ 20 27 ]] #BOT + +L21: [[ L22 ]] + 21 $ctrl 22 [[ 27 20 ]] Ctrl + 27 ld4 21 26 28 ____ [[ 20 ]] u32 + 20 Return 21 26 27 29 [[ 1 ]] [ Ctrl, #BOT, u32] + +L1: [[ ]] + 1 Stop 2 20 [[ ]] Bot diff --git a/seaofnodes/src/test/cases/array2/array.ez_out b/seaofnodes/src/test/cases/array2/array.ez_out new file mode 100644 index 0000000..e7b9df2 --- /dev/null +++ b/seaofnodes/src/test/cases/array2/array.ez_out @@ -0,0 +1,24 @@ +---foo { -> *![flt] #0}--------------------------- +0000 4883EC08 subi rsp -= #8 +0004 BE18000000 ldi rsi = #24 +0009 BF01000000 alloc ldi rcx = #1 +000E E800000000 call #calloc +0013 F20F100500 ld8 xmm0 = #4.2 +0018 000000 +001B F20F114008 st8 [rax+8],xmm0 +0020 F20F100500 ld8 xmm0 = #8.4 +0025 000000 +0028 F20F114010 st8 [rax+16],xmm0 +002D C700020000 st4 [rax],#2 +0032 00 +0033 4883C408C3 addi rsp += #8 +0038 ret +---{ -> *![flt] #0}--------------------------- + +---bar { -> int #1}--------------------------- +0040 4883EC08 subi rsp -= #8 +0044 E8B7FFFFFF call foo +0049 8B00 ld4 rax,[rax] +004B 4883C408C3 addi rsp += #8 +0050 ret +---{ -> int #1}--------------------------- diff --git a/seaofnodes/src/test/cases/array2/array.smp b/seaofnodes/src/test/cases/array2/array.smp new file mode 100644 index 0000000..c9e7520 --- /dev/null +++ b/seaofnodes/src/test/cases/array2/array.smp @@ -0,0 +1,9 @@ +val foo = {-> + var a = new flt[2]; + a[0] = 4.2; + a[1] = 8.4; + return a; +}; +val bar = { -> + return foo()#; +}; \ No newline at end of file diff --git a/seaofnodes/src/test/cases/array2/array.smp_ir b/seaofnodes/src/test/cases/array2/array.smp_ir new file mode 100644 index 0000000..dfddaca --- /dev/null +++ b/seaofnodes/src/test/cases/array2/array.smp_ir @@ -0,0 +1,38 @@ +START: [[ ]] + 7 Start ____ ____ ____ [[ 9 25 11 6 ]] [ Ctrl, #TOP, int] + +L6: [[ START ]] + 6 bar ____ 7 [[ 8 30 5 ]] Ctrl + 8 $mem 6 9 [[ 5 ]] #BOT + 30 $rpc 6 25 [[ 2 ]] $[ALL] + 5 call 6 8 [[ 4 ]] Ctrl + +L11: [[ START ]] + 11 foo ____ 7 [[ 13 24 17 22 23 16 20 19 14 12 10 ]] Ctrl + 13 $mem 11 9 [[ 12 16 16 ]] #BOT + 24 $rpc 11 25 [[ 10 ]] $[ALL] + 22 #4.2 11 [[ 20 ]] 4.2 + 17 #24 11 [[ 16 ]] 24 + 23 #8.4 11 [[ 19 ]] 8.4 + 20 st8 11 21 18 ____ 22 [[ 19 ]] #3:flt + 19 st8 11 20 18 ____ 23 [[ 12 ]] #3:flt + 16 new_ary 11 17 13 13 [[ 15 18 21 ]] [ Ctrl, *![flt], #2:u32, #3:flt] + 15 $2 16 [[ 14 ]] #2:u32 + 18 [flt] 16 [[ 14 20 19 10 ]] *[flt] + 21 $3 16 [[ 20 ]] #3:flt + 14 st4 11 15 18 ____ ____ [[ 12 ]] #2:u32 + 12 ALLMEM 11 13 14 19 [[ 10 ]] #BOT + 10 Return 11 12 18 24 [[ 1 ]] [ Ctrl, #BOT, *![flt]] + +L4: [[ L6 ]] + 4 CallEnd 5 [[ 3 27 29 ]] [ Ctrl, #BOT, *![flt]] + 29 #2 4 [[ 28 ]] *[flt] + 27 $mem 4 [[ 2 28 ]] #BOT + +L3: [[ L4 ]] + 3 $ctrl 4 [[ 28 2 ]] Ctrl + 28 ld4 3 27 29 ____ [[ 2 ]] u32 + 2 Return 3 27 28 30 [[ 1 ]] [ Ctrl, #BOT, u32] + +L1: [[ ]] + 1 Stop 2 10 [[ ]] Bot diff --git a/seaofnodes/src/test/cases/array2/array.smp_out b/seaofnodes/src/test/cases/array2/array.smp_out new file mode 100644 index 0000000..a853e66 --- /dev/null +++ b/seaofnodes/src/test/cases/array2/array.smp_out @@ -0,0 +1,27 @@ +---bar { -> u32 #1}--------------------------- +0000 4883EC08 subi rsp -= #8 +0004 E807000000 call foo +0009 8B00 ld4 rax,[rax] +000B 4883C408C3 addi rsp += #8 +0010 ret +---{ -> u32 #1}--------------------------- + +---foo { -> *![flt] #0}--------------------------- +0010 4883EC08 subi rsp -= #8 +0014 BE18000000 ldi rsi = #24 +0019 BF01000000 alloc ldi rcx = #1 +001E E800000000 call #calloc +0023 F20F100500 ld8 xmm0 = #4.2 +0028 000000 +002B F20F114008 st8 [rax+8],xmm0 +0030 F20F100500 ld8 xmm0 = #8.4 +0035 000000 +0038 F20F114010 st8 [rax+16],xmm0 +003D C700020000 st4 [rax],#2 +0042 00 +0043 4883C408C3 addi rsp += #8 +0048 ret +---{ -> *![flt] #0}--------------------------- +--- Constant Pool ------ +0050 0000000000000000 4.2 +0058 0000000000000000 8.4 diff --git a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java index e7e293f..de1e2f7 100644 --- a/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java +++ b/seaofnodes/src/test/java/com/compilerprogramming/ezlang/compiler/TestSONTypes.java @@ -161,6 +161,15 @@ func foo()->Int { compileSrc(src); } + @Test + public void testArrayLengthUnaryOperator() { + String src = """ +func foo()->Int { + return #new [Int]{42,84}; +} +"""; + compileSrc(src); + } @Test public void test17() { String src = """ @@ -194,7 +203,7 @@ func foo()->Int { compileSrc(src); } - static void testCPU( String src, String cpu, String os, int spills, String stop ) { + static void testCPU( String src, String cpu, String os, int spills, String stop ) { CodeGen code = new CodeGen(src); code.parse().opto().typeCheck().loopTree().instSelect(cpu,os).GCM().localSched().regAlloc().encode(); int delta = spills>>3; diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/NullableAnalysis.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/NullableAnalysis.java index 8d98562..a5e604f 100644 --- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/NullableAnalysis.java +++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/NullableAnalysis.java @@ -199,6 +199,11 @@ else if (initExpr.newExpr.type instanceof EZType.EZTypeArray arrayType) { } if (e instanceof AST.UnaryExpr un) { + if (un.op.str.equals("#")) { + checkDereference(un.expr, facts); + return factFromType(un.type); + } + LatticeElement v = analyzeExpr(un.expr, facts); if (un.op.str.equals("-")) { diff --git a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java index 10c9c76..605c6de 100644 --- a/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java +++ b/semantic/src/main/java/com/compilerprogramming/ezlang/semantic/SemaAssignTypes.java @@ -98,7 +98,17 @@ public void exit(AST.UnaryExpr unaryExpr) { return; } validType(unaryExpr.expr.type, false, unaryExpr.lineNumber); - if (unaryExpr.expr.type instanceof EZType.EZTypeInteger) { + if (unaryExpr.op.str.equals("#")) { + if (unaryExpr.expr.type instanceof EZType.EZTypeArray || + (unaryExpr.expr.type instanceof EZType.EZTypeNullable nullable && nullable.baseType instanceof EZType.EZTypeArray)) { + unaryExpr.type = typeDictionary.INT; + } + else { + throw new CompilerException("Unary operator " + unaryExpr.op + " not supported for operand", unaryExpr.lineNumber); + } + } + else if (unaryExpr.expr.type instanceof EZType.EZTypeInteger && + (unaryExpr.op.str.equals("-") || unaryExpr.op.str.equals("!"))) { unaryExpr.type = unaryExpr.expr.type; } else if (unaryExpr.expr.type instanceof EZType.EZTypeFloat && unaryExpr.op.str.equals("-")) { diff --git a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestNullAnalysis.java b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestNullAnalysis.java index 860b1ec..2728b2f 100644 --- a/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestNullAnalysis.java +++ b/semantic/src/test/java/com/compilerprogramming/ezlang/semantic/TestNullAnalysis.java @@ -200,6 +200,28 @@ func test(arg: [Int]?)->Int { analyze(src, "test"); } + @Test(expected = CompilerException.class) + public void nullableArrayLength() { + String src = """ + func test(arg: [Int]?)->Int { + return #arg; + } +"""; + analyze(src, "test"); + } + + @Test + public void guardedNullableArrayLength() { + String src = """ + func test(arg: [Int]?)->Int { + if (arg != null) { + return #arg; + } + return 0; + } +"""; + analyze(src, "test"); + } @Test public void guardedNullableFieldStore() { String src = """ diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java index f6e7be2..6ef7919 100644 --- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java +++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/CompiledFunction.java @@ -418,6 +418,7 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) { switch (unaryExpr.op.str) { case "-" -> opCode = unaryExpr.type instanceof EZType.EZTypeFloat ? Instruction.NEG_F : Instruction.NEG_I; case "!" -> opCode = Instruction.NOT; + case "#" -> opCode = Instruction.LENGTH; default -> throw new CompilerException("Invalid binary op", unaryExpr.lineNumber); } code(new Instruction.UnaryOp(opCode)); diff --git a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java index a2fcbc2..670e2ad 100644 --- a/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java +++ b/stackvm/src/main/java/com/compilerprogramming/ezlang/compiler/Instruction.java @@ -36,6 +36,7 @@ public class Instruction { public static final int MUL_F = 28; public static final int DIV_F = 29; public static final int NEG_F = 30; + public static final int LENGTH = 31; static final String[] opNames = { "ret", @@ -68,7 +69,8 @@ public class Instruction { "subf", "mulf", "divf", - "negf" + "negf", + "length" }; public final int opcode; diff --git a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java index 2d828ee..ad7f28c 100644 --- a/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java +++ b/stackvm/src/test/java/com/compilerprogramming/ezlang/compiler/TestCompiler.java @@ -664,4 +664,20 @@ func foo()->Int """, result); } + @Test + public void testArrayLengthUnaryOperator() { + String src = """ + func foo(a: [Int])->Int { + return #a; + } + """; + String result = compileSrc(src); + Assert.assertEquals(""" +L0: + load 0 + length + jump L1 +L1: +""", result); + } }