Ver Fonte

compiler: preserve long literals and diagnose export order

Eric Streit há 1 dia atrás
pai
commit
8527d2c65a

+ 5 - 4
bindings/sqlite3/TEST-INVENTORY.md

@@ -194,9 +194,10 @@ completed in this session and the current verification state.
 - A subset of m2SQLITE's `SQLiteUtils` now exists in the V3 binding;
   use it for common string, bind, exec and backup operations.
 - For a raw C pointer to a V3 array, use `ADR(a[0])`, not `ADR(a)`.
-- V3 string literals in the current compiler can be truncated for long
-  SQL text.  Keep individual literals short, or build the SQL through a
-  character buffer/concatenation.  Adjacent string literal concatenation
-  is not supported.
+- V3 now preserves string literals through the previous 64-byte
+  lowering limit; the practical limit is still the 255-byte literal
+  buffer.  Keep very long SQL below that limit or build it through a
+  character buffer.  Adjacent string literal concatenation is not
+  supported.
 - The upstream tests are specifications, not a runnable harness for this
   binding.

+ 2 - 0
compiler/run_tests.sh

@@ -48,6 +48,7 @@ expect_fail() {
 
 mkdir -p gen_ssa
 expect_run t_minimal.mod 0
+expect_run t_longstr.mod 42
 expect_run t_exit.mod 7
 expect_run t_arith.mod 25
 expect_run showcase1.mod 50
@@ -227,6 +228,7 @@ expect_fail t_bad_undecl.mod "undeclared identifier"
 expect_fail t_bad_dup.mod "duplicate identifier"
 expect_fail t_bad_mismatch.mod "module name mismatch"
 expect_fail t_bad_opaque.mod "opaque type outside definition"
+expect_fail d_bad_export_order.mod "EXPORT must precede declarations"
 expect_fail t_bad_while.mod "BOOLEAN condition required"
 expect_fail t_bad_exit.mod "not supported yet"
 expect_fail t_bad_return.mod "bad RETURN"

+ 14 - 11
compiler/src/Lower.mod

@@ -1633,6 +1633,7 @@ PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
   VAR et, et2, dt, vt: SymTab.TypeIndex; qa, qt, q2, qd, qr: QbeGen.QVal;
       k, lo, hi, c1, c2, ucp: INTEGER; qn, vn: SymTab.Name; sfx: BOOLEAN;
       isMax, isU, uok: BOOLEAN;
+      lit: ARRAY [0 .. 255] OF CHAR;
   BEGIN
     IF SymTab.Equal(nm, "ODD") THEN
       LowerExpr(AST.Child(n, 1), et, q);
@@ -1706,9 +1707,9 @@ PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
       IF AST.Kind(AST.Child(n, 1)) = AST.NkStrLit THEN
         (* LEN("literal"): the content length of the literal; a real
            string literal is still declared, as the grammar does *)
-        Name(AST.Child(n, 1), qn);
-        IF (qn[0] = "U") OR (qn[0] = "u") THEN
-          QbeGen.DeclUStr(qn, qd, isU, ucp, uok);
+        Name(AST.Child(n, 1), lit);
+        IF (lit[0] = "U") OR (lit[0] = "u") THEN
+          QbeGen.DeclUStr(lit, qd, isU, ucp, uok);
           IF NOT uok THEN t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
           ELSIF isU THEN
             IF SymTab.Equal(nm, "HIGH") THEN QbeGen.CopyOp("0", q)
@@ -1721,8 +1722,8 @@ PROCEDURE LowerBuiltinExpr (n: AST.Node; nm: SymTab.Name;
             QbeGen.CopyOp(qr, q); t := SymTab.IntType()
           END
         ELSE
-        IF SymTab.StrLen(qn) = 3 THEN c1 := 1
-        ELSE QbeGen.DeclStr(qn, qd); c1 := SymTab.StrLen(qn) - 2
+        IF SymTab.StrLen(lit) = 3 THEN c1 := 1
+        ELSE QbeGen.DeclStr(lit, qd); c1 := SymTab.StrLen(lit) - 2
         END;
         IF SymTab.Equal(nm, "HIGH") THEN DEC(c1) END;
         QbeGen.IntStr(c1, q); t := SymTab.IntType()
@@ -2195,6 +2196,7 @@ PROCEDURE LowerExprFull (n: AST.Node; VAR t: SymTab.TypeIndex;
       i: CARDINAL; el, seq, next: AST.Node;
       lw, rw, mw: CARDINAL; idx: INTEGER; fok, isL: BOOLEAN;
       lt, rt: SymTab.TypeIndex; qw: QbeGen.QVal;
+      lit: ARRAY [0 .. 255] OF CHAR;
   BEGIN
     IF AST.Kind(n) = AST.NkDesignator THEN
       LowerDesign(n, t, k, q, qn, sfx);
@@ -2227,10 +2229,10 @@ PROCEDURE LowerExprFull (n: AST.Node; VAR t: SymTab.TypeIndex;
     ELSIF AST.Kind(n) = AST.NkRealLit THEN
       Name(n, qn); QbeGen.NormReal(qn, q); t := SymTab.RealType()
     ELSIF AST.Kind(n) = AST.NkStrLit THEN
-      Name(n, qn);
-      IF (qn[0] = "U") OR (qn[0] = "u") THEN
+      Name(n, lit);
+      IF (lit[0] = "U") OR (lit[0] = "u") THEN
         (* U"..." / U'...': one codepoint -> UCHAR, else a ustring *)
-        QbeGen.DeclUStr(qn, q, isU, ucp, uok);
+        QbeGen.DeclUStr(lit, q, isU, ucp, uok);
         IF NOT uok THEN
           t := SymTab.InvalidType; QbeGen.CopyOp("0", q)
         ELSIF isU THEN
@@ -2238,10 +2240,11 @@ PROCEDURE LowerExprFull (n: AST.Node; VAR t: SymTab.TypeIndex;
         ELSE
           t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewUStr() END; QbeGen.NoteAddr(q, q)
         END
-      ELSIF SymTab.StrLen(qn) = 3 THEN
-        t := SymTab.CharType(); QbeGen.IntStr(QbeGen.CharVal(qn), q)
+      ELSIF SymTab.StrLen(lit) = 3 THEN
+        t := SymTab.CharType(); QbeGen.IntStr(QbeGen.CharVal(lit), q)
       ELSE
-        t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewStr() END; QbeGen.DeclStr(qn, q); QbeGen.NoteAddr(q, q)
+        t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewStr() END;
+        QbeGen.DeclStr(lit, q); QbeGen.NoteAddr(q, q)
       END
     ELSIF AST.Kind(n) = AST.NkSetLit THEN
       t := AST.Ty(n); IF t = -1 THEN t := SymTab.NewSet(SymTab.NewSubR(0, 255)) END;

+ 7 - 3
compiler/src/M2.atg

@@ -11,7 +11,7 @@ COMPILER M2
    Semantic errors reuse the V1/V2/Test2 family: 200 duplicate,
    201 undeclared, 202 module name mismatch, 210 bad assignment,
    211 bad arithmetic, 221 not a type, 230 not supported yet,
-   231 opaque type outside definition, 237 RETRY outside EXCEPT.
+   231 opaque type outside definition, 237 RETRY outside EXCEPT, 238 EXPORT after declarations.
    Scalar-phase TYPEs (named, integer subrange, enum) check fully;
    composite forms wait for step 3.    Procedure headings (formals,
    result, FORWARD) enter scopes now; bodies parse + check with one
@@ -914,11 +914,15 @@ PRODUCTIONS
                                         (. astSeq := AST.NoNode;
                                            astTail := AST.NoNode; .)
       {                                 (. ln[0] := CHR(0);
-                                           procIsExt := FALSE; .)
+                                           procIsExt := FALSE;
+                                           IF sym = 18 THEN
+                                             SemError(238)
+                                           END; .)
         ( ConstBlock                    (. AstAppend(AST.NkDeclSeq,
                                              astSeq, astTail, astDecl); .)
       | TypeBlock<TRUE>                 (. AstAppend(AST.NkDeclSeq,
-                                             astSeq, astTail, astDecl); .)
+                                             astSeq, astTail, astDecl);
+                                           IF sym = 18 THEN SemError(238) END; .)
       | VarBlock                        (. AstAppend(AST.NkDeclSeq,
                                              astSeq, astTail, astDecl); .)
       | ProcHeading<pn, SymTab.InvalidType> ";"

+ 1 - 0
compiler/src/compiler.frm

@@ -123,6 +123,7 @@ MODULE -->Grammar;
         | 235: Msg("implementation does not match definition")
         | 236: Msg("bad variant record")
         | 237: Msg("RETRY outside EXCEPT")
+        | 238: Msg("EXPORT must precede declarations")
         ELSE         Msg("Error: "); WriteInt(f, nr, 0);
         END
       END ErrText;

+ 4 - 0
compiler/tests/d_bad_export_order.mod

@@ -0,0 +1,4 @@
+DEFINITION MODULE DBadExportOrder;
+TYPE T = INTEGER;
+EXPORT UNQUALIFIED T;
+END DBadExportOrder.

+ 15 - 0
compiler/tests/t_longstr.mod

@@ -0,0 +1,15 @@
+MODULE TLongStr;
+(* String literals longer than 64 bytes must survive parsing and lowering. *)
+VAR ExitCode, i : INTEGER;
+    s : ARRAY [0..255] OF CHAR;
+BEGIN
+  s := "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+  i := 0;
+  WHILE (i <= HIGH(s)) AND (s[i] # CHR(0)) DO i := i + 1 END;
+  IF (i = 100) AND (LEN("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789") = 100)
+     AND (HIGH("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789") = 99) THEN
+    ExitCode := 42
+  ELSE
+    ExitCode := 1
+  END
+END TLongStr.

+ 68 - 0
docs/c-binding-compiler-notes.md

@@ -0,0 +1,68 @@
+# C binding compiler notes
+
+Date: 2026-10-27
+
+These notes record compiler-side decisions and fixes driven by the V3
+SQLite binding.
+
+## Long string literals
+
+The previous compiler path truncated string literals during lowering
+because `LowerExprFull` and `LowerBuiltinExpr` copied the AST text into
+`SymTab.Name` (64 chars).  Fixed in `compiler/src/Lower.mod` by using a
+255-byte literal buffer.
+
+Covered by:
+
+```text
+compiler/tests/t_longstr.mod
+```
+
+The practical limit is still set by the scanner/grammar buffers and
+`AST.MaxTxtLen` (255).  Keep very large SQL scripts below that limit or
+build them through a character buffer.
+
+## `ADR` on arrays
+
+Current behavior:
+
+```modula2
+ADR(fixedArray)       (* descriptor address *)
+ADR(fixedArray[0])    (* first data byte *)
+ADR(openArray)        (* payload base for a view *)
+ADR(multiDimArray)    (* payload base for the generated view *)
+```
+
+Decision: keep the existing `ADR` semantics and use:
+
+```modula2
+ADR(a[0])
+```
+
+when passing a fixed character array to C as a data pointer.
+
+Changing `ADR(a)` globally to mean the data address would also change
+pointer-to-array indexing (`p := ADR(a); p^[i]`), which currently relies
+on the descriptor representation.  A separate data-address intrinsic was
+prototyped but not landed in this slice; the C-binding convention remains
+`ADR(a[0])`.
+
+## `EXPORT` placement
+
+The definition-module grammar requires the export list before declaration
+blocks.  Placing `TYPE`/`CONST`/`VAR` before `EXPORT` now produces the
+specific diagnostic:
+
+```text
+EXPORT must precede declarations
+```
+
+Covered by:
+
+```text
+compiler/tests/d_bad_export_order.mod
+```
+
+The parser could be made permissive, but the obvious grammar change
+introduced significant parser/graph complexity, so the compiler now
+diagnoses the invalid ordering clearly instead.