From 34c2efae1f2dac2449a52fd2913b4737111e692c Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 10 Aug 2026 14:40:04 -0700 Subject: Handle some escaped chars in strings Add a explicit "emit_main" flag in case you use no_run. Fix a make parallel bug --- .../main/java/com/seaofnodes/simple/Parser.java | 26 +++++++++++++++++----- .../java/com/seaofnodes/simple/Chapter25Test.java | 13 +++++++++-- .../test/java/com/seaofnodes/simple/Simple.java | 5 ++++- 3 files changed, 36 insertions(+), 8 deletions(-) (limited to 'chapter25/src') diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java index 98f8698f..4c2dfc84 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java @@ -2396,11 +2396,27 @@ public class Parser { private String parseString() { if( !peek('"') ) return null; _lexer.inc(); - int start = pos(); - while( !_lexer.isEOF() && _lexer.nextChar()!= '"' ) ; - if( _lexer.isEOF() ) - throw error("Unclosed string"); - return new String(_lexer._input,start,pos()-start-1); + StringBuilder sb = new StringBuilder(); + while( !_lexer.isEOF() ) { + char c = _lexer.nextChar(); + if( c=='"' ) return sb.toString(); + if( c!='\\' ) { + sb.append(c); + continue; + } + if( _lexer.isEOF() ) + throw error("Unclosed string"); + char esc = _lexer.nextChar(); + sb.append(switch( esc ) { + case 'n' -> '\n'; + case 't' -> '\t'; + case 'r' -> '\r'; + case '\\' -> '\\'; + case '"' -> '"'; + default -> throw error("Unknown string escape \\\\"+esc); + }); + } + throw error("Unclosed string"); } // Already parsed "'" diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java index cf9d764e..313ae1cf 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java @@ -38,6 +38,15 @@ public class Chapter25Test { assertEquals("1",Eval2.eval(code,0)); } + @Test + public void testStringEscapes() { + String src = "u8[~] s=\"A\\n\\t\\r\\\\\\\"B\"; " + + "return s#==7 && s[0]=='A' && s[1]==10 && s[2]==9 && s[3]==13 && " + + "s[4]==92 && s[5]==34 && s[6]=='B';"; + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("1",Eval2.eval(code,0)); + } + @Test @Ignore public void testModule0() throws IOException { @@ -197,7 +206,7 @@ public class Chapter25Test { File sys_file = buildTestSys(false); assertTrue("Missing "+sys_file+"; testHelloWorld depends on testSys building it", sys_file.exists()); - String expected = "Hello, World!"; + String expected = "Hello, World!\n"; String prog = "return sys.io.p(\""+expected+"\") - "+expected.length()+";"; TestC.run(prog,"helloWorld",new Ary<>(new String[]{SYS_BLDDIR}), TestC.CALL_CONVENTION, null, null, expected,0); @@ -223,7 +232,7 @@ public class Chapter25Test { assertTrue("Missing "+sys_file+"; testHelloWorldNoInline depends on testSys building it", sys_file.exists()); String base = "helloWorldNoInline"; - String expected = "Hello, World!"; + String expected = "Hello, World!\n"; String prog = "return sys.io.p_noInline(\""+expected+"\") - "+expected.length()+";"; CodeGen code = new CodeGen(null,"build/objs",new Ary<>(new String[]{SYS_BLDDIR}), base,prog,123L,TypeInteger.BOT); diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java index 870792c0..35b9b922 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java @@ -35,6 +35,7 @@ Options: -L path - add an external Simple object search path --eval - (slowly) evaluate the compiled code in emulator --run - run the compiled code natively; this is the default + --main - export the top-level initializer as C `main` --cpu - use specific CPU (x86_64_v2, riscv, arm) --abi - use specific ABI variant (SystemV) --target - print native CPU and ABI @@ -89,6 +90,7 @@ Options: boolean do_eval = false; boolean do_run = true; boolean do_codegen = false; + boolean emit_main = false; boolean dump_dot = false; boolean print_time = false; boolean print_size = false; @@ -129,6 +131,7 @@ Options: case "--eval": do_eval = true ; do_run = false; break; case "--run": do_run = true ; break; case "--norun": do_run = false; break; + case "--main": emit_main = true; break; case "--dump-size": print_size = true; break; case "--dump-time": print_time = true; break; case "-o": if (out != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); @@ -221,7 +224,7 @@ Options: // Compilation pipeline Ary externPaths = libPaths.isEmpty() ? null : new Ary<>(libPaths.toArray(new String[0])); CodeGen code = new CodeGen(modPath.toString(), outPath.toString(), externPaths, srcName, src, 456, TypeInteger.BOT ); - code.driver(Phase.LastPhase,cpu,abi, false, do_run || do_eval, dump); + code.driver(Phase.LastPhase,cpu,abi, false, emit_main || do_run || do_eval, dump); if( do_codegen && print_asm ) System.out.println(code.asm()); -- cgit v1.2.3