Preprocessor (internal/preproc)
Sits between the lexer and the parser. Its only job is to expand
include file splices and pass use (library) and import (module)
statements through unchanged.
Algorithm
- Strip trivia tokens (comments, blank lines) so the recognizers can rely on adjacent tokens being meaningful.
- Walk the token stream.
- When
INCLUDE STRING SEMIis found (a textual file splice):- Verify the string ends in
.j. - Resolve the path (relative to the current file’s directory, or
absolute if it starts with
/). - Reject if the path was already visited up the include chain (circular include).
- Read the file, lex it (with file-tagged tokens), recursively preprocess it.
- Splice the result (dropping the trailing
EOF) at this point.
- Verify the string ends in
- When
IMPORT ...is found (a module import): pass the tokens through unchanged.importis a real statement handled by the parser (ModuleImportStmt) and interpreter (the module loader), not a textual splice. The one thing checked here is the common unquoted mistake (validateModuleImport). - When
USE IDENT SEMIis found: pass through unchanged. The parser turns it into anImportStmtnode. - Helpful errors for common mistakes:
include IDENT;(library form withinclude) -> “useuse NAME;for system libraries”.include IDENT.j;(unquoted file form) -> “file splices take a string literal”.include "foo.go";(wrong extension) -> “include path must end with.j”.import IDENT;(library form withimport) -> “importtakes a quoted module path; for a system library useuse NAME;”.import IDENT.j;(unquoted module path) -> “module paths are quoted:import \"name.j\";”.use IDENT.j;(file form withuse) -> “for files useinclude \"name.j\";”.
Edge cases
- An
includepath must literally end in.j.include "foo.go";is rejected. includepaths may contain/for subdirectories. Absolute paths are accepted as-is; relative paths resolve from the including file’s directory.- Circular includes are detected by tracking absolute paths visited
along the current chain. (A module
importcycle is detected separately, by the loader ininternal/interpreter; the preprocessor never opens an imported module - it only forwards its tokens.)