Skip to content

An escaped backslash in a string literal emits a spurious "Unknown escape character" #64

Description

@gitosaurus

A doubled backslash in a string literal produces the single backslash it
should, but complains on the way there.

$ cat esc.arch
null main
methods
  'START' : write "back\\slash"
end

$ archetype --source=esc.arch
At esc.arch, line 3, column 27:
  'START' : write "back\\slash"
                          ^
Unknown escape character \\
back\slash

The output is right; the diagnostic is noise. Exit status is 0, so this is
cosmetic — but it appears on every correct use of an escaped backslash, and
\\ is documented behaviour going back to the 1995 manual.

Cause

The escape switch at TokenStream.cc:212-224 has cases for t, b, e, n,
and ", and no case for \:

switch (next_ch) {
    case 't' : next_ch = '\t'; break;
    case 'b' : next_ch = '\b'; break;
    case 'e' : next_ch = '\033'; break;
    case 'n' : next_ch = '\n'; break;
    case '"' : next_ch = '"'; break;
    default: {
        errorMessage(format("Unknown escape character \\{}", next_ch));
        break;
    }
}
s += next_ch;

A backslash falls to default, which complains and then falls through to
s += next_ch — appending the backslash, which is why the text comes out
correct anyway.

Fix

case '\\' : break;      // next_ch is already the backslash

A question to settle with it

The 1995 manual promised more than the strict set:

Any other character preceded by a backslash becomes simply that character
without the backslash. \m becomes m, for example.

That is no longer true — \m yields m and an error message. Two coherent
readings:

  1. Strict (what the code does today, minus this bug): only \\, \",
    \b, \e, \n, \t are legal and anything else is a typo worth
    reporting. This catches real mistakes, which is presumably why the default
    arm was written.
  2. Permissive (what the manual says): unknown escapes pass the character
    through silently.

The revised manual in docs/manual.md documents the strict reading and drops
the \m sentence, on the assumption that the error arm was deliberate and the
missing \\ case was not. Worth confirming that is the intent; if the
permissive rule is preferred instead, the fix is to delete the default arm's
errorMessage rather than to add a case.

Either way a test in TestTokenStream.cc should pin "a\\b" scanning to the
three characters a, \, b with no diagnostic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions