Skip to content

Migration from PLCC to PLCC-ng

PLCC-ng is a modern, pip-installable rewrite of PLCC. It adds Python semantics support, a cleaner CLI, and a more extensible architecture. It is not backward compatible — spec files and command invocations both require updates.

Breaking behavior changes

These two command-output changes are easy to miss in the checklist below and have caught instructors migrating workshop materials by surprise.

plcc-parse always prints the full parse tree. There is no -t flag for tracing and no OK output on success — the parse tree is the output.

plcc-scan output format changed. PLCC printed TOKEN_NAME(lexeme); PLCC-ng prints source:line:col TOKEN_NAME 'lexeme', where source is the filename (or - for stdin) and line/col are 1-indexed.

# PLCC
INTEGER(3)
ADDOP(+)

# PLCC-ng
-:1:1 INTEGER '3'
-:2:1 ADDOP '+'

Update any documentation, tests, or course materials that show the old TOKEN(lexeme) format — they will no longer match actual output.

The _run() entry point must return a string. This applies whether you're migrating an old $run() or have already ported to _run(). Java's _run() changed from void (print inside the method) to String (return the text) — a spec with public void _run() { System.out.println(...); } will fail to compile and must become public String _run() { return ...; }. Python and JavaScript's _run() must now return an actual str/string — a _run() that returns a non-string value (an int, a list, a bare number, ...) now fails with a specification_error instead of silently working; convert explicitly (str(x) / String(x)) if needed. No language's _run() may print or write to stdout directly — only plain-text plcc-rep sessions tolerated that before, and only by accident.

Migration checklist

1. Install PLCC-ng

Replace the PLCC shell script, Docker, or DevContainer install with:

pip install plcc-ng

See Installation for upgrade, pinning, and uninstall instructions.

2. Rename your grammar file

PLCC's default grammar file is named grammar. PLCC-ng's default is spec.plcc.

grammar  →  spec.plcc

3. Update regex patterns in the lexical section

PLCC uses Java regex (java.util.regex.Pattern). PLCC-ng uses Python regex (re).

Most common patterns are identical. The following Java-specific syntax must be rewritten:

Java pattern Python equivalent
\p{Alpha} [a-zA-Z]
(?<name>...) named group (?P<name>...)
\p{Digit} \d

Test your patterns with a Python regex tool (e.g. regex101.com set to Python flavor).

4. Check scan algorithm behavior

PLCC gives skip rules priority over all token rules whenever they match any input, regardless of match length. PLCC-ng uses pure first-longest-match — skip and token rules compete equally, with ties broken by declaration order.

If any of your skip rules are shorter than competing token rules, the behavior changes:

Input PLCC PLCC-ng
123 with skip ONE '1' before token NUMBER '\d+' skip wins (nothing emitted) NUMBER '123' (longer match wins)

Fix: If you relied on skip-first behavior, rewrite the skip pattern to match as many characters as the token it was shadowing. For most grammars (whitespace skips vs. identifier/number tokens), the longest match already produces the same result and no change is needed.

5. Update nonterminal names to PascalCase

PLCC nonterminals are lowercase; PLCC-ng uses PascalCase.

PLCC PLCC-ng
<prog> <Program>
<exp> <Expr>

Update every nonterminal on both the left-hand side and right-hand side of every rule.

6. Update alternative/subclass syntax

PLCC places the distinguishing name as a suffix after the closing bracket. PLCC-ng places it after a colon inside the brackets.

PLCC PLCC-ng
<exp>WholeExp ::= ... <Expr:WholeExp> ::= ...
<exp>SubExp ::= ... <Expr:SubExp> ::= ...

7. Update captured field syntax

PLCC places a field name as a suffix after the closing bracket. PLCC-ng places it after a colon inside the brackets.

PLCC PLCC-ng
<exp>exp1 (field named exp1) <Expr:exp1>
<exp>exp2 (field named exp2) <Expr:exp2>
<WHOLE> (auto-named field whole) <WHOLE> (same — auto-naming unchanged)
<WHOLE> with explicit name <WHOLE:whole>
<oneMore> (auto-named field oneMore) <OneMore> (auto-named field oneMore)

Auto-naming a bare nonterminal capture decapitalizes just the first letter of the nonterminal's (now-PascalCase) name — it doesn't lowercase the whole thing. For a single-word nonterminal this is invisible (<Expr> → field expr, same as PLCC's <exp>exp), but it matters once you apply step 5's PascalCase rename to a multi-word name: renaming PLCC's <oneMore> to PLCC-ng's <OneMore> and then auto-naming the field decapitalizes only the O, reproducing PLCC's original oneMore spelling — not onemore.

8. Add semantic section language header

PLCC only supports Java semantics and has no language header. PLCC-ng requires the first non-blank line of the semantic section to declare the language.

Before (PLCC):

%
Prog
%%%
  public void $run() { ... }
%%%

After (PLCC-ng):

%
Java

Prog
%%%
  public String _run() { ... }
%%%

The supported values are Java and Python.

9. Rename the entry point method

The method the start symbol's class uses as its execution entry point was renamed — and its contract changed, not just its name.

PLCC PLCC-ng
$run() _run()

$run() was void; it printed its output directly. _run() must return a string — the runtime prints it for you. See "Breaking behavior changes" above for what this means for each language.

Update this in the semantic section of your spec file.

10. Check %include paths

Both PLCC and PLCC-ng support %include FILENAME. The syntax is unchanged. If your include paths were relative to a file named grammar, they will work unchanged — paths are resolved relative to the file that contains the %include directive.

11. Replace commands

PLCC PLCC-ng Notes
plcc.py --version plcc-version
plccmk [-c] [file] (not needed) Top-level commands generate and compile automatically
scan [file...] plcc-scan [file...] Token output format changed (see below)
parse [-t] [-n] [file...] plcc-parse [file...] Always shows parse tree; no -t flag needed
rep [-t] [-n] [file...] plcc-rep [file...]
parse --json_ast plcc-tokens \| plcc-trees See below

Token output format change:

PLCC format:

   1: WHOLE '3'

PLCC-ng format (file:line:column):

-:1:1 NUM '42'

JSON parse trees:

PLCC's --json_ast flag (passed to plccmk and parse) is not available in PLCC-ng. To obtain a JSON parse tree, use the lower-level pipeline:

plcc-spec spec.plcc > plcc-ng/spec.json
plcc-spec spec.plcc | plcc-ll1 > plcc-ng/ll1.json
plcc-tokens plcc-ng/spec.json samples/ | plcc-trees --ll1=plcc-ng/ll1.json

See plcc-trees for details.

Features not yet in PLCC-ng

  • Docker installation — PLCC supported Docker via plcc-con. PLCC-ng is currently pip-only. Docker support is planned.
  • DevContainer — PLCC provided a ready-to-use devcontainer image. PLCC-ng does not yet provide one.