Summary
The MKBTT/TRS parser interns every symbol after applying string-upcase. As a result, function symbols that differ only in letter case (e.g. T and t) are folded into a single symbol. Inputs that rely on case to distinguish function symbols are silently mis-parsed, and completion can report a "success" for a degenerate system that is not the one given.
This is not caused by the Common Lisp reader's default case conversion; it is an explicit string-upcase call in the parser.
Facts
1. All parsed symbols (function symbols, constants, variables) are interned through a single helper that upcases the name.
src/parser.lisp:30-33
(defun %intern-symbol-to-specified-package (string)
(intern
(string-upcase string)
*parsed-symbol-intern-package*))
2. The lexer itself preserves case.
The MKBTT rules lexer accepts both cases as a single token class, so the original case is present in the token string and is only lost at intern time.
src/parser.lisp:108-112 (mkbtt-rules-lexer)
("[a-zA-Z0-9_%+%*%.%-/@&\\]+" (values :rules-symbol $$))
3. The printer additionally lowercases names on output, which hides the collapse.
src/types.lisp:179 — variable: (string-downcase (symbol-name ...))
src/types.lisp:197-198 — fterm: 0-arity printed with string-upcase, application printed with string-downcase
src/types.lisp:260 — predicate: (string-downcase (symbol-name ...))
So source T -> interned |T| -> printed t and source t -> interned |T| -> printed t: both render as t.
4. Empirical reproduction.
Using tests/resources/eq_systems/Sim91_sims2.trs, whose source uses 6 function symbols (T t S s R r):
(let ((eqs (clover.parser:parse-mkbtt-expression <Sim91_sims2 source>)))
(multiple-value-bind (consts funs) (clover.multicompletion::collect-symbol eqs)
(print funs))) ; => (R T S) ;
- Collected function symbols:
(R T S) — 3 instead of 6.
- The first two rules
T(t(x)) -> x and t(T(x)) -> x both become the same rule t(t(x)) = x (i.e. duplicates).
- Completion then reports success with a trivial system (x) => x
), which is **not** a completion of the intended Sim91_sims2`.
5. Blast radius in the bundled JAIST completion corpus.
4 of 115 files use function symbols that collide under upcasing:
| File |
Colliding symbols |
ASK93_5.trs |
A/a, E/e, H/h, Q/q, W/w |
LS94_G1.trs |
A/a, B/b, C/c |
LS94_P1.trs |
R/r, S/s, T/t |
Sim91_sims2.trs |
R/r, S/s, T/t |
These are group/monoid presentations where the uppercaseenerator (e.g. T = t^-1); the presence of both T(t(x))-> x and t(T(x)) -> x confirms the two are intended t
Impact
- Any input that distinguishes function symbols by case is mis-parsed.
- Completion may report a spurious "success" for a collahe affected inputs are not trustworthy.
Notes / possible directions (not yet decided)
- Primary locus is the
string-upcase in %intern-symbo/parser.lisp:30). Preserving case there would require acoordinated change to the printer (src/types.lisp:179,it of:
- hardcoded literal interns such as
"NIL" / "CONS" (src/parser.lisp:193,197-198,306,310-311),
- the premise/conseq grammars where case is used at the lexer level to classify
[A-Z]+ as constants vs [a-z]+ as symbols,
- any symbol-name comparisons that assume upper case.
- Alternatively, case-insensitivity could be documented as an intentional limitation, in which case the affected inputs should be marked out of scope.
- Suggested regression tests: parse->print->parse round-trip stability, and an assertion that
T and t parse to distinct symbols.
Summary
The MKBTT/TRS parser interns every symbol after applying
string-upcase. As a result, function symbols that differ only in letter case (e.g.Tandt) are folded into a single symbol. Inputs that rely on case to distinguish function symbols are silently mis-parsed, and completion can report a "success" for a degenerate system that is not the one given.This is not caused by the Common Lisp reader's default case conversion; it is an explicit
string-upcasecall in the parser.Facts
1. All parsed symbols (function symbols, constants, variables) are interned through a single helper that upcases the name.
src/parser.lisp:30-332. The lexer itself preserves case.
The MKBTT rules lexer accepts both cases as a single token class, so the original case is present in the token string and is only lost at intern time.
src/parser.lisp:108-112(mkbtt-rules-lexer)3. The printer additionally lowercases names on output, which hides the collapse.
src/types.lisp:179— variable:(string-downcase (symbol-name ...))src/types.lisp:197-198— fterm: 0-arity printed withstring-upcase, application printed withstring-downcasesrc/types.lisp:260— predicate:(string-downcase (symbol-name ...))So
source T -> interned |T| -> printed tandsource t -> interned |T| -> printed t: both render ast.4. Empirical reproduction.
Using
tests/resources/eq_systems/Sim91_sims2.trs, whose source uses 6 function symbols (T t S s R r):(R T S)— 3 instead of 6.T(t(x)) -> xandt(T(x)) -> xboth become the same rulet(t(x)) = x(i.e. duplicates).), which is **not** a completion of the intendedSim91_sims2`.5. Blast radius in the bundled JAIST completion corpus.
4 of 115 files use function symbols that collide under upcasing:
ASK93_5.trsLS94_G1.trsLS94_P1.trsSim91_sims2.trsThese are group/monoid presentations where the uppercaseenerator (e.g.
T = t^-1); the presence of bothT(t(x))-> xandt(T(x)) -> xconfirms the two are intended tImpact
Notes / possible directions (not yet decided)
string-upcasein%intern-symbo/parser.lisp:30). Preserving case there would require acoordinated change to the printer (src/types.lisp:179,it of:"NIL"/"CONS"(src/parser.lisp:193,197-198,306,310-311),[A-Z]+as constants vs[a-z]+as symbols,Tandtparse to distinct symbols.