Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/GRAMMAR/ADR-records/ADR_20261111_UNTERMINATED_LAST_LINE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Keep the last line of a C code file that lacks a final newline

## Status

proposed

## Context

Inside `%{ ... %}` blocks, and in files embedded there with `%include`, the lexer (`instrument.l`, state `<ccode>`) only knew newline-terminated lines. A last line without a newline matched no rule, so flex echoed it to stdout and it was missing from the generated C.

Seen with the ESS BIFROST model, where `rfc-lib.h` and `rfc-lib.c` (mcstas-radial-filter-collimator) end in `#endif // RFC_LIB_H` and `}` without a newline. McStas printed `#endif // RFC_LIB_H}` and the compiler reported `function definition is not allowed here` for everything that followed.

mccode-antlr accepts such files, so they are easily produced and go unnoticed.

## Decision

Add one `<ccode>` rule matching a line without a newline. It can only be the longest match on the last line of a file. The rule pushes the line back with a newline appended, so it is rescanned by the existing rules (`%}`, `%include`, plain code line).

## Consequences

Library and code files no longer need to end with a newline. No new syntax, and nothing changes for files that do end with one.

## Behaviour

- An unterminated last line is kept, and gets a newline in the generated C.
- Generated C was compared before/after for the 369 McStas example instruments: 365 identical, 2 fail to generate both before and after. 2 differ by one restored line (`Supermirror_thin_substrate`, `Test_lib_supermirror_lib`): the closing comment of `supermirror-lib.h`, which also lacks a final newline and was silently dropped until now.
- The lexer is shared with McXtrace; only McStas was built and tested (macOS arm64).
9 changes: 9 additions & 0 deletions mccode/src/instrument.l
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,15 @@ METADATA return TOK_METADATA;
yylval.string = str_dup(yytext);
return TOK_CODE_LINE;
}
/* last line of a file, lacking its newline (only then can this rule be
the longest match): rescan it with a newline appended */
[^\n]+ {
char *line = str_dup(yytext);
int i = yyleng;
unput('\n');
while (i > 0) unput(line[--i]);
str_free(line);
}
} /* end ccode */

/* C character literals, e.g. 'a' or '\n', kept verbatim as a single token in
Expand Down
Loading