From 477c71d89d107936cd0c4776bb2d5ff37cb91539 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Wed, 13 Nov 2024 20:53:05 -0800 Subject: Allow oper-assign, += -= etc --- chapter17/README.md | 14 ++++--- .../main/java/com/seaofnodes/simple/Parser.java | 49 ++++++++++++++++++---- .../java/com/seaofnodes/simple/Chapter17Test.java | 22 ++++++---- 3 files changed, 64 insertions(+), 21 deletions(-) (limited to 'chapter17') diff --git a/chapter17/README.md b/chapter17/README.md index 1bac940b..d65ff879 100644 --- a/chapter17/README.md +++ b/chapter17/README.md @@ -12,6 +12,14 @@ Also allows `s.fld++` and `ary[idx]++`, and their `--` variants. Allow pre-increment for identifiers only, for now: `--pc`. +## Operator assignment + +Along the same lines as post-increment, we now allow `op=` assignment +with semantics similar to other languages. + +`x += y` + + ## var & val `var` can be used in the `type` position to indicate a "variable" (mutable @@ -125,11 +133,5 @@ return i; // ERROR: Undefined name 'i' -oper-assign +=, -= TODO switch - - - - - diff --git a/chapter17/src/main/java/com/seaofnodes/simple/Parser.java b/chapter17/src/main/java/com/seaofnodes/simple/Parser.java index 09ea21ac..47e7c3bb 100644 --- a/chapter17/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter17/src/main/java/com/seaofnodes/simple/Parser.java @@ -889,13 +889,30 @@ public class Parser { if( matchx("new" ) ) return alloc(); // Expect an identifier now ScopeMinNode.Var n = requireLookupId("an identifier or expression"); - Node rvalue = _scope.in(n).keep(); - if( matchx("++") || matchx("--") ) { - if( n._final ) - throw error("Cannot reassign final '"+n._name+"'"); - _scope.update(n,zsMask(peep(new AddNode(rvalue,con( _lexer.peek(-1)=='+' ? 1 : -1))),n.type())); - } - return rvalue.unkeep(); + Node rvalue = _scope.in(n); + // Check for assign-update, x += e0; + char ch = _lexer.matchOperAssign(); + if( ch==0 ) return rvalue; + if( n._final ) + throw error("Cannot reassign final '"+n._name+"'"); + Node op = switch(ch) { + case '+' -> new AddNode(rvalue,null); + case '-' -> new SubNode(rvalue,null); + case '*' -> new MulNode(rvalue,null); + case '/' -> new DivNode(rvalue,null); + case 1 -> new AddNode(rvalue,con( 1)); + case (char)-1 -> new AddNode(rvalue,con(-1)); + default -> throw Utils.TODO(); + }; + // Return pre-value (x+=1) or post-value (x++) + boolean pre = op.in(2)==null; + // Parse RHS argument as needed + if( pre ) + { op.keep().setDef(2,parseExpression()); op.unkeep(); } + else rvalue.keep(); // Keep post-value across peeps + op = zsMask(peep(op),n.type()); + _scope.update(n,op); + return pre ? op : rvalue.unkeep(); } ScopeMinNode.Var requireLookupId(String msg) { @@ -1344,12 +1361,28 @@ public class Parser { // private boolean isPunctuation(char ch) { - return "=;[]<>()+-/*".indexOf(ch) != -1; + return "=;[]<>()+-/*&|^".indexOf(ch) != -1; } private String parsePunctuation() { int start = _position; return new String(_input, start, 1); } + + // Next oper= character, or 0. + // As a convenience, mark "++" as a char 1 and "--" as char -1 (65535) + public char matchOperAssign() { + skipWhiteSpace(); + if( _position+2 >= _input.length ) return 0; + char ch0 = (char)_input[_position]; + if( "+-/*&|^".indexOf(ch0) == -1 ) return 0; + char ch1 = (char)_input[_position+1]; + if( ch1=='=' ) + { _position += 2; return ch0; } + if( isIdLetter((char)_input[_position+2]) ) return 0; // Stop --x + if( ch0 == '+' && ch1 == '+' ) { _position += 2; return (char) 1; } + if( ch0 == '-' && ch1 == '-' ) { _position += 2; return (char)-1; } + return 0; + } } } diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java index b1f7eb43..176a9236 100644 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java +++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java @@ -105,6 +105,14 @@ return s.x; assertEquals(255L, Evaluator.evaluate(stop, 0)); } + @Test + public void testInc8() { + Parser parser = new Parser("int x; x+=2; return x+=3;"); + StopNode stop = parser.parse().iterate(); + assertEquals("return 5;", stop.toString()); + assertEquals(5L, Evaluator.evaluate(stop, 0)); + } + // --------------------------------------------------------------- @Test public void testVar0() { Parser parser = new Parser("var d; return d;"); @@ -288,7 +296,7 @@ return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0 Parser parser = new Parser(""" int sum=0; for( int i=0; i=arg ) break; - sum = sum + i; + sum += i; } return sum; """); @@ -329,7 +337,7 @@ return sum; Parser parser = new Parser(""" int sum=0; for( int i=0; i