Repository navigation
compiler-common rejects names that gren make compiles: x中, xʰ, xDž and Dža #38
Description
Found against: gren 0.6.6, gren-lang/compiler-common 3.0.0, gren-lang/core 7.4.2, node 22
Reproduction: in https://github.com/gilramir/gren-bug-reports, 2026-09-13-identifier-letters; ./run.sh prints the table below.
Summary
This compiles with gren make:
x中 : Int
x中 =
1
and compiler-common's parser cannot parse it. Compiler.Parse.Variable.lowerCase
stops at the 中 and gives back "x", and the declaration then fails to parse.
Anything built on compiler-common refuses a source file that the compiler accepts.
The two parsers disagree about which letters may appear in a name. Both
decide by a character's Unicode General Category, a two-letter code the
Unicode Character Database gives every character (UAX #44, General_Category values).
Letters fall into five categories:
| category | meaning | example |
|---|---|---|
Lu |
uppercase letter | A, É |
Ll |
lowercase letter | a, é |
Lt |
titlecase letter: a single character written as two letters, in the form that starts a capitalized word | Dž (U+01C5), between DŽ and dž |
Lm |
modifier letter | ʰ (U+02B0) |
Lo |
other letter, with no case | 中 (U+4E2D), א, ก |
In a regular expression, \p{Ll} matches one character of category Ll, and
\p{L} matches any of the five. What each parser accepts:
gren make |
compiler-common |
|
|---|---|---|
| first letter of a lower-case name | lowercase letter (Ll) |
\p{Ll} — the same |
| first letter of an upper-case name | uppercase or titlecase letter (Lu, Lt) |
\p{Lu} |
| every later character | ASCII letter, digit or _, or any letter (Lu, Ll, Lt, Lm, Lo) |
ASCII letter, digit or _, or \p{Ll} or \p{Lu} |
So the letters compiler-common is missing are titlecase (Lt, 31 characters,
such as Dž), modifier letters (Lm, such as ʰ) and "other" letters (Lo,
which is most of CJK, Arabic, Hebrew, Devanagari and Thai, among others).
Reproduction
./run.sh asks both questions about each name and prints the table below. For
gren make it compiles a module declaring the name, as a value (lower) or as
a type with one constructor of the same name (upper). For compiler-common it
runs lowerCase or upperCase followed by end, so a name counts as accepted
only if the parser consumed all of it.
| kind | name | gren make |
compiler-common |
|---|---|---|---|
| lower | xé |
accepts | accepts |
| lower | xDž (U+01C5, Lt) |
accepts | rejects |
| lower | xʰ (U+02B0, Lm) |
accepts | rejects |
| lower | x中 (U+4E2D, Lo) |
accepts | rejects |
| upper | Éa |
accepts | accepts |
| upper | Dža (U+01C5, Lt) |
accepts | rejects |
The two accepts rows are the control: an Ll or Lu letter, which both
parsers accept.
The cause and possible fixes
gren makeasks Haskell'sData.Char: isUpper(which isLuorLt) for the first letter of an upper-case name, isLowerfor a lower-case one, andisAlpha(everyL*category) after that —compiler/src/Parse/Variable.hs. compiler-common's Compiler/Parse/Variable.gren` uses two regexes instead:
lowerCaseLetterRegex = Regex.fromString "\\p{Ll}" ...
upperCaseLetterRegex = Regex.fromString "\\p{Lu}" ...
isInner char =
Char.isAlphaNum char
|| char == '_'
|| isLowerCaseLetter char
|| isUpperCaseLetter char
Matching gren make would need to change two things.
- An upper-case first letter becomes
[\p{Lu}\p{Lt}] - A later character may be any
\p{L}.
The lower-case first letter stays \p{Ll}, which already agrees.
Or, of course, document this as change in the language.
Activity
robinheghan commentedon Oct 6, 2026
Fixed on main