{"id":49621778,"url":"https://github.com/ourplcc/plcc","last_synced_at":"2026-05-05T03:07:01.931Z","repository":{"id":44906254,"uuid":"263733165","full_name":"ourPLCC/plcc","owner":"ourPLCC","description":"A Programming Languages Compiler Compiler","archived":false,"fork":false,"pushed_at":"2026-04-12T19:05:44.000Z","size":2475,"stargazers_count":9,"open_issues_count":13,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-04-12T19:23:23.649Z","etag":null,"topics":["compiler-compiler","plcc"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ourPLCC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-05-13T20:12:46.000Z","updated_at":"2026-04-12T19:05:48.000Z","dependencies_parsed_at":"2024-02-28T19:27:02.577Z","dependency_job_id":"2b1cd4d4-0037-4d6f-a6a1-ec5b1a95f41b","html_url":"https://github.com/ourPLCC/plcc","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/ourPLCC/plcc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ourPLCC%2Fplcc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ourPLCC%2Fplcc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ourPLCC%2Fplcc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ourPLCC%2Fplcc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ourPLCC","download_url":"https://codeload.github.com/ourPLCC/plcc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ourPLCC%2Fplcc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32633447,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"online","status_checked_at":"2026-05-05T02:00:06.033Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler-compiler","plcc"],"created_at":"2026-05-05T03:06:59.651Z","updated_at":"2026-05-05T03:07:01.925Z","avatar_url":"https://github.com/ourPLCC.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PLCC - Programming Language Compiler Compiler\n\nPLCC is designed for teaching and learning programming language concepts.\n\n- [Licensed under GPL v3.0 or higher](LICENSE)\n- [Chat with us on Discord](https://discord.gg/EVtNSxS9E2)\n- [Report a problem or request a feature](https://github.com/ourPLCC/plcc/issues)\n- [Read the paper](docs/PLCC-paper.pdf)\n- [Become a developer for this project](docs/Developer.md)\n- [Become a maintain for this project](docs/Maintainer.md)\n\nRelated repositories:\n\n- [ourPLCC/languages](https://github.com/ourPLCC/languages): Languages implemented in PLCC.\n- [ourPLCC/course](https://github.com/ourPLCC/course): Course materials for\n  teaching a Programming Languages course the uses PLCC.\n\n## Options for Installation and Use\n\nPLCC can be installed and used in different environments. The table below\nmay help you determine which option is best for you and your class.\n\n| Option | Software Requirements | Non-Software Requirements | Consistent, Pre-configured Environment |\n| ------ | ------------------- | ----------------------- | ---------- |\n| Codespace |  Web Browser | GitHub Account | Yes |\n| Docker | Docker Desktop | Minimal understanding of Docker | Yes |\n| Native | * Bash/Linux-like environment \u003cbr\u003e * Java \u003e= 11 \u003cbr\u003e * Python \u003e= 3.9 | System administration knowledge | No |\n\nThe advantages of Codespace or Docker are (1) few or no software dependencies\nand (2) the ability to provide your class/developers a consistent development\nenvironment with no installation necessary.\n\nHaving your students/developers perform native installations on their\nindividual machines can lead to unexpected challenges due to the variety of\ndifferent environments this creates. This can be mitigated by having your\nIT staff install PLCC on a shared server or into a computer lab and having\nyour students/developers use those if their native install stops working\nfor them for some strange, inexplicable reason.\n\n## Install PLCC for Use in a Codespace\n\nThis is probably the easiest way to get started.\n\nTo any project hosted on GitHub, add `.devcontainer/devcontainer.json`\nwith the following contents:\n\n```json\n{\n  \"name\": \"your-repository-name\",\n  \"image\": \"ghcr.io/ourplcc/plcc-devcontainer:1\"\n}\n```\n\nCreate a Codespace on that repository, and PLCC, Java, and Python will\nbe installed and ready for use within the Codespace.\n\nSee [plcc-devcontainer](https://github.com/ourPLCC/plcc-devcontainer) for\nmore information about the devcontainer.\n\n## Install for Use in Docker\n\nInstall [Docker Desktop](https://www.docker.com/products/docker-desktop/).\n\nInstall `plcc-con` ...\n\n* On macOS\n\n  ```bash\n  /bin/bash -c \"$(curl -fsSL https://github.com/ourPLCC/plcc/raw/main/installers/plcc-con/install.bash)\" \u003e\u003e ~/.zshrc\n  ```\n\n* On Windows \u003e= 10, first\n  [install WSL](https://learn.microsoft.com/en-us/windows/wsl/), and then in\n  a Bash terminal in Ubuntu\n\n  ```bash\n  /bin/bash -c \"$(curl -fsSL https://github.com/ourPLCC/plcc/raw/main/installers/plcc-con/install.bash)\" \u003e\u003e ~/.bashrc\n  ```\n\n* On Linux\n\n  ```bash\n  /bin/bash -c \"$(curl -fsSL https://github.com/ourPLCC/plcc/raw/main/installers/plcc-con/install.bash)\" \u003e\u003e ~/.bashrc\n  ```\n\nAfter starting a new shell, you can start a shell inside a PLCC container\nthat has access to the files in your current directory.\n\n```bash\nplcc-con\n```\n\nInside this shell, all of PLCC's commands are available. When you are done,\ntype `exit`.\n\nYou can also run a single PLCC command inside a PLCC container by passing\nthe command to `plcc-con`. For example, let's find out what version of\nPLCC, Java, and Python are installed in the container.\n\n```bash\nplcc-con plcc --version\nplcc-con java -version\nplcc-con python3 --version\n```\n\n`plcc-con` is a Bash function that wraps the following Docker command\n\n```bash\ndocker run --rm -it -v \"${PWD}:/workdir\" --user \"$(id -u):$(id -g)\" ghcr.io/ourplcc/plcc:latest\n```\n\n## Native Install\n\n### Install a Bash/Linux environment\n\n* On Windows \u003e= 10,\nplease [install WSL](https://learn.microsoft.com/en-us/windows/wsl/). Then run\na Terminal and open Ubuntu from its dropdown menu. You are now running in\nBash inside an Ubuntu running inside (or next to) Windows. Use this environment to install\nand use PLCC. From now on, when an instruction refers to Linux, make sure\nyou are running in this environment. Including the next line.\n\n* On Linux, we assume you are running in Bash on a Debian-based Linux\ndistributed (this includes Ubuntu) which uses `apt-get` as its package\nmanager. If this is not your situation, you will have to adapt the instructions\nappropriately for your environment.\n\n* On macOS, please [install Homebrew](https://brew.sh/).\n\n### Install Java\n\nCheck if you have `java` and `javac` \u003e= 11\n\n```\njava -version\njavac -version\n```\n\nIf you are missing either, or if their versions don't match, or either is\noutdated, please [install SDKMAN!](https://sdkman.io/install),\nand use it to install Java.\n\n### Install Python\n\nCheck if you have Python \u003e= 3.9\n\n```bash\npython3 --version\n```\n\nIf not, then install Python.\n\n* On macOS\n\n  ```bash\n  brew install python3\n  ```\n\n* On Linux or Windows under [WSL](https://learn.microsoft.com/en-us/windows/wsl/)\n\n  ```bash\n  sudo apt-get update\n  sudo apt-get install python3\n  ```\n\n### Install PLCC\n\n* On macOS (remove \"`\u003e\u003e ~/.zshrc`\" if you would like to update this file manually)\n\n  ```bash\n  brew install curl git\n  /bin/bash -c \"$(curl -fsSL https://github.com/ourPLCC/plcc/raw/main/installers/plcc/install.bash)\" \\\n    \u003e\u003e ~/.zshrc\n  ```\n\n* On Linux or Windows under [WSL](https://learn.microsoft.com/en-us/windows/wsl/) (remove \"`\u003e\u003e ~/.bashrc`\" if you would like to update this file manually)\n\n  ```bash\n  sudo apt-get update\n  sudo apt-get install curl git\n  /bin/bash -c \"$(curl -fsSL https://github.com/ourPLCC/plcc/raw/main/installers/plcc/install.bash)\" \u003e\u003e ~/.bashrc\n  ```\n\n## Use\n\nNow that you have a Linux-like, Bash-like environment installed with\nPLCC and its dependencies, here's how you use it.\n\n```bash\nmkdir mylang\ncd mylang\nvim samples            # Write sample programs in your language.\nvim grammar            # Write a grammar file defining your language.\nplccmk -c grammar      # Compile grammar into a scanner, parser, and interpreter.\nscan \u003c samples         # Run the scanner on your samples.\nparse -n -t \u003c samples  # Run the parser on your samples.\nrep -n -t \u003c samples    # Run the interpreter on your samples.\n```\n\n### Example\n\nCreate a file `samples` with the following example programs.\n\n```\n3\n-(3,2)\n-(-(4,1), -(3,2))\n```\n\nWrite a `grammar` file.\n\n```java\nskip WHITESPACE '\\s+'\ntoken WHOLE '\\d+'\ntoken MINUS '\\-'\ntoken LP '\\('\ntoken RP '\\)'\ntoken COMMA ','\n%\n\u003cprog\u003e ::= \u003cexp\u003e\n\u003cexp\u003eWholeExp ::= \u003cWHOLE\u003e\n\u003cexp\u003eSubExp ::= MINUS LP \u003cexp\u003eexp1 COMMA \u003cexp\u003eexp2 RP\n%\nProg\n%%%\n  public void $run() {\n    System.out.println(exp.eval());\n  }\n%%%\n\nExp\n%%%\n  abstract public int eval();\n%%%\n\nSubExp\n%%%\n  public int eval() {\n    return exp1.eval() - exp2.eval();\n  }\n%%%\n\nWholeExp\n%%%\n  public int eval() {\n    return Integer.parseInt(whole.toString());\n  }\n%%%\n```\n\nCompile it.\n\n```bash\n$ plccmk -c grammar\nNonterminals (* indicates start symbol):\n  \u003cexp\u003e\n *\u003cprog\u003e\n\nAbstract classes:\n  Exp\n\nJava source files created:\n  Exp.java\n  Prog.java\n  SubExp.java\n  WholeExp.java\n```\n\nTest the scanner.\n\n```bash\n$ scan \u003c samples\n   1: WHOLE '3'\n   3: MINUS '-'\n   3: LP '('\n   3: WHOLE '3'\n   3: COMMA ','\n   3: WHOLE '2'\n   3: RP ')'\n   5: MINUS '-'\n   5: LP '('\n   5: MINUS '-'\n   5: LP '('\n   5: WHOLE '4'\n   5: COMMA ','\n   5: WHOLE '1'\n   5: RP ')'\n   5: COMMA ','\n   5: MINUS '-'\n   5: LP '('\n   5: WHOLE '3'\n   5: COMMA ','\n   5: WHOLE '2'\n   5: RP ')'\n   5: RP ')'\n```\n\nTest the parser.\n\n```bash\n$ parse -t -n \u003c samples\n   1: \u003cprog\u003e\n   1: | \u003cexp\u003eWholeExp\n   1: | | WHOLE \"3\"\nOK\n   3: \u003cprog\u003e\n   3: | \u003cexp\u003eSubExp\n   3: | | MINUS \"-\"\n   3: | | LP \"(\"\n   3: | | \u003cexp\u003eWholeExp\n   3: | | | WHOLE \"3\"\n   3: | | COMMA \",\"\n   3: | | \u003cexp\u003eWholeExp\n   3: | | | WHOLE \"2\"\n   3: | | RP \")\"\nOK\n   5: \u003cprog\u003e\n   5: | \u003cexp\u003eSubExp\n   5: | | MINUS \"-\"\n   5: | | LP \"(\"\n   5: | | \u003cexp\u003eSubExp\n   5: | | | MINUS \"-\"\n   5: | | | LP \"(\"\n   5: | | | \u003cexp\u003eWholeExp\n   5: | | | | WHOLE \"4\"\n   5: | | | COMMA \",\"\n   5: | | | \u003cexp\u003eWholeExp\n   5: | | | | WHOLE \"1\"\n   5: | | | RP \")\"\n   5: | | COMMA \",\"\n   5: | | \u003cexp\u003eSubExp\n   5: | | | MINUS \"-\"\n   5: | | | LP \"(\"\n   5: | | | \u003cexp\u003eWholeExp\n   5: | | | | WHOLE \"3\"\n   5: | | | COMMA \",\"\n   5: | | | \u003cexp\u003eWholeExp\n   5: | | | | WHOLE \"2\"\n   5: | | | RP \")\"\n   5: | | RP \")\"\nOK\n```\n\nTest the interpreter.\n\n```bash\n$ rep -n \u003c samples\n3\n1\n2\n$\n```\n\n## Commands\n\nThis section provides a brief reference to the commands PLCC provides.\n\n```\nplcc file\n\n  Run plcc.py on 'file' to generate code in a directory named 'Java/'.\n\n\nplccmk [-c] [--json_ast] [file]\n\n  Run plcc.py on 'file' and compile its results.\n\n  '-c' Removes 'Java/' before regenerating it.\n  '--json_ast' add support to print JSON ASTs.\n      Required if you want to call parse with --json_ast.\n  'file' defaults to 'grammar'\n\n\nscan [file...]\n\n    Run Java/Scan on each file and then stdin printing recognized tokens.\n\n\nparse [-t] [-n] [--json_ast] [file...]\n\n  Run Java/Parser on each file and then stdin,\n  printing OK for recognized programs and errors otherwise.\n\n  '-t' Print trace (i.e., parse tree).\n  '-n' Suppress prompt.\n  '--json_ast' print JSON AST to stdout.\n\n\nrep [-t] [-n] [file...]\n\n    Run Java/Rep on each file and then stdin.\n    REP Reads, Executes, and Prints each program in the input.\n\n    '-t' Print trace (i.e., parse tree).\n    '-n' Suppress prompt.\n```\n\n## Grammar Files\n\nA grammar file consist of three sections separated by a line containing\na single percent.\n\n```\n[Lexical specification]\n%\n[Syntactic specification]\n%\n[Semantic specification]\n```\n\nPLCC generates a different tool from each section.\n\n| Grammar Section         | Tool Generated         |\n| ----------------------- | ---------------------- |\n| Lexical Specification   | Scanner                |\n| Syntactic Specification | Parser                 |\n| Semantic Specification  | Interpreter            |\n\nThe tools are dependent on each other as follows:\n\n```\nInterpreter -\u003e Parser -\u003e Scanner\n```\n\nLikewise the corresponding sections are dependent on\neach other:\n\n```\nSemantic -\u003e Syntactic -\u003e Lexical\n```\n\nFor example, to build a parser, you don't need a semantic spec,\nbut you do need a lexical and syntactic specs.\n\nAn external file can be include from anywhere in the spec\n(replace FILENAME with the file you want to include).\n\n```\ninclude FILENAME\n```\n\n### Lexical Specification\n\nThe lexical specification contains `token` and `skip` rules,\none per line. Lines starting with `#` are comments.\nFor example,\n\n```\n# Skip rules discard the text they match.\nskip WHITESPACE '\\s+'\n\n# Token rules emits a Token containing their name and the match.\ntoken PLUS \"\\+\"\ntoken WORD \"\\w+\"\n```\n\n* Names must be all-caps and may have underscores.\n* Patterns are regular expressions (regex) enclosed in either single\nor double quotes. Here are some resources on regex.\n  * [java.util.regex.Pattern](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html):\n    Complete reference of regex syntax that PLCC accepts.\n  * [RegexOne](https://regexone.com/): Great set of interact lessons.\n  * [regex101](https://regex101.com/): Great tool for building, testing, and visualizing.\n\n#### Scan algorithm\n\nBelow is PLCC's scan algorithm in pseudo-code. For clarity and simplicity, a couple details related to advanced features have been omitted.\n\n##### **DEFINE:** *Scan input for tokens.*\nWhile there is more unscanned input ...\n1. Identify the specification rule to apply. (defined below)\n2. Remove the non-empty string matched by the rule from the start of unscanned input.\n3. If rule is not a \"skip rule\", create and emit a token.\n\n##### **DEFINE:** *Identify the specification rule to apply.*\n1. Identify rules that match a non-empty sequence of characters from the start of the unscanned input.\n2. If no such rule exists, emit a lexical error and stop scanning.\n3.  Otherwise, from the matching rules, identify the rule that appears first in the specification.\n4. If the matching rule that appears first is a skip rule, then return it as the rule to apply.\n5. Otherwise, from the matching rules, remove all skip rules, leaving only token rules.\n6. From the matching token rules, identify rules with the longest match.\n7. From the rules with the longest match, identify the rule that appears first in the lexical specification.\n8. Return this first, longest-match, token rule.\n\n##### Notes:\n\n* Rules do not match across newline characters.\n\n### Syntactic Specification\n\nA syntax specification is a flavor of\n[BNF (Backus-Naur From)](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form).\n\n```\n\u003cprog\u003e ::= \u003cexp\u003e\n\u003cexp\u003eWholeExp ::= \u003cWHOLE\u003e\n\u003cexp\u003eSubExp ::= MINUS LP \u003cexp\u003eexp1 COMMA \u003cexp\u003eexp2 RP\n```\n\n* Non-terminals are always enclosed in angles and start\n  with a lowercase. E.g., `\u003cexp\u003e`.\n* Each non-terminal must be defined by appearing on the left-hand-side\n  of at least one rule.\n* Terminals are always all-caps, and MAY be enclosed\n  in angles. E.g., `\u003cWHOLE\u003e` and `MINUS`.\n* Terminals represent tokens which are generated by the scanner from\n  the input program.\n* Any symbol enclosed in angles will be included in\n  the parse tree. So `\u003cWHOLE\u003e` will be included,\n  but `MINUS` will not.\n* When a symbol appears more than once on the right-hand\nside of a rule, each must be given a name to distinguish it from the others. For example, in `\u003cexp\u003eexp1` the distinguishing name is `exp1`. That name must start with a lower case.\n* When a non-terminal appears multiple times on the left-hand-side, each must be given a name to distinguish it\nfrom the others. The name must start with an upper case letter. For example, in `\u003cexp\u003eSubExp` the distinguishing name is `SubExp`.\n* Alternative definitions for a non-terminal is accomplished by\nproviding multiple rules that define the same non-terminal.\n\n#### Parse Tree Class Hierarchy\n\nPLCC translates semantic rules into a class hierarchy. For example:\n\n```\n\u003cprog\u003e ::= \u003cexp\u003e\n\u003cexp\u003eWholeExp ::= \u003cWHOLE\u003e\n\u003cexp\u003eSubExp ::= MINUS LP \u003cexp\u003eexp1 COMMA \u003cexp\u003eexp2 RP\n```\n\nbecomes (some details omitted):\n\n```Java\nclass Prog extends _Start { Exp exp; }\nabstract class Exp {}\nclass WholeExp extends Exp { Token whole; }\nclass SubExp extends Exp { Exp exp1; Exp exp2; }\n```\n\n* A class is generated for the non-terminal defined by a rule (the LHS) with instance variables defined for each captured symbols (within `\u003c\u003e`) on the RHS.\n* The first rule defines the start symbol,\nand its class inherits from the standard, built-in class _Start.\n* A non-terminal defined more than once becomes an abstract base class,\nand the distinguishing names become its subclasses.\n* Tokens always have the type of Token.\n\n#### Repetition Rule\n\nThe repetition rule simplifies defining a repeating structure.\n\n```\n\u003cpairs\u003e **= LP \u003cWHOLE\u003ex \u003cWHOLE\u003ey RP +COMMA\n```\n\n`\u003cpairs\u003e` matches zero or more (x,y) pairs separated by comma: e.g., `(3 4), (5 6), (7 8)`. The separator clause (e.g., `+COMMA`) is optional. For example,\n\nPLCC translates the above rule into:\n\n```java\nclass Pairs { List\u003cVal\u003e xList; List\u003cVal\u003e yList; }\n```\n\nThe captured symbols become parallel lists. That is, `xList.git(i)` and `yList.get(i)` correspond to the i\u003csup\u003eth\u003c/sup\u003e value pair.\n\n#### Parse Algorithm\n\nThe parsing algorithm is a recursive-descent parser that parses languages\nin LL(1). Let's take a look.\n\n#### Code Generated for Parser\n\nEach rule in the syntactic spec turns into a static parse method embedded\nin the class generated by the same rule. For example,\n\n```\n\u003cprog\u003e ::= \u003cexp\u003e\n\u003cexp\u003eWholeExp ::= \u003cWHOLE\u003e\n\u003cexp\u003eSubExp ::= MINUS LP \u003cexp\u003eexp1 COMMA \u003cexp\u003eexp2 RP\n```\n\nThis generates (slightly simplified):\n\n```java\nclass Prog{\n  static Prog parse(Scan scn$) {\n    Exp exp = Exp.parse(scn$);\n    return new Prog(exp);\n  }\n}\nclass Exp{\n  public static Exp parse(Scan scn$) {\n    Token t$ = scn$.cur();\n    Token.Match match$ = t$.match;\n    switch(match$) {\n    case WHOLE:\n      return WholeExp.parse(scn$);\n    case MINUS:\n      return SubExp.parse(scn$);\n    default:\n      throw new PLCCException(\"Parse error\");\n    }\n  }\n}\nclass WholeExp{\n  static WholeExp parse(Scan scn$) {\n    Token whole = scn$.match(Token.Match.WHOLE);\n    return new WholeExp(whole);\n  }\n}\nclass SubExp{\n  static SubExp parse(Scan scn$) {\n    scn$.match(Token.Match.MINUS);\n    scn$.match(Token.Match.LP);\n    Exp exp1 = Exp.parse(scn$);\n    scn$.match(Token.Match.COMMA);\n    Exp exp2 = Exp.parse(scn$);\n    scn$.match(Token.Match.RP);\n    return new SubExp(exp1, exp2);\n  }\n}\n```\n\nParsing starts with the start symbol: here `Prog`.\nEach parse method is responsible for matching the structure of the RHS of\nthe syntactic rule it represents. It does so from left to right.\nTokens are matched directly by telling the scanner what type of token\nit should match and consume. Non-terminals are matched by calling their\nparse method which matches its structure, returning an instance of that\nnon-terminal's class.\n\nThe parse function of an abstract base class (e.g., in `Exp` above)\ndetermines which subclass's method to call by looking at the next token.\n\n### Semantic specification\n\nThe semantic specification injects code into\npredefined locations (called hooks) within each class\ngenerated from the syntactic specification.\n\n```java\nProg\n%%%\n  public void $run() {\n    System.out.println(exp.eval());\n  }\n%%%\n\nExp\n%%%\n  abstract public int eval();\n%%%\n\nSubExp\n%%%\n  @Override\n  public int eval() {\n    return exp1.eval() - exp2.eval();\n  }\n%%%\n\nWholeExp\n%%%\n  @Override\n  public int eval() {\n    return Integer.parseInt(whole.toString());\n  }\n%%%\n```\n\n* The class representing the start symbol should override the `$run`\n  method. Execution of the interpreter begins here: see `Prog` above.\n* To enable polymorphism, add an abstract method to the abstract base\n  class representing a non-terminal that has alternatives: see `Exp` above.\n  Then override this method in the subclasses representing the\n  concrete alternatives: see `SubExp` and `WholeExp` above.\n\n\n#### Hooks\n\nBy default, you can only inject code at the end of a class.\nHooks allow you to inject code elsewhere.\n\n* `\u003cclassname\u003e:top` - Top of file.\n* `\u003cclassname\u003e:import` - Add `import`s.\n* `\u003cclassname\u003e:class` - Declare `extends` or `implements`.\n* `\u003cclassname\u003e:init` - Constructor.\n\nAs an example, we update our original example by replacing the\ndefinition for WholeExp with this.\n\n```java\nWholeExp:import\n%%%\nimport java.util.HashMap;\n%%%\n\nWholeExp\n%%%\n  public static HashMap\u003cInteger,Integer\u003e =\n    new HashMap\u003cInteger,Integer\u003e();\n\n  public int eval() {\n    int x = Integer.parseInt(whole.toString());\n    checkDuplicate(x);\n    return x;\n  }\n\n  public void checkDuplicate(int x) {\n    if (seen.containsKey(x)) {\n      System.out.println(\"Duplicate: \" + x);\n    } else {\n      seen.put(x, x);\n    }\n  }\n%%%\n```\n\nNow our interpreter reports when it sees a duplicate whole number.\n\n#### Adding additional Java files/classes\n\nEntire Java files can be added by naming,\nand providing a complete definition of,\na class that is not generated from the syntactic specification.\n\n```java\nHelper\n%%%\nimport java.util.List;\n\npublic class Helper {\n  \\\\ ...\n}\n%%%\n```\n\n## Serializing AST in JSON\n\nTo print a JSON AST for a program, pass `--json_ast` to both `plccmk`\nand `parse`, like so:\n\n```bash\nplccmk --json_ast -c YOUR_GRAMMAR_FILE\nparse --json_ast \u003c YOUR_PROGRAM_FILE\n```\n\nThis feature allows other tools to be written in different languages\nthat reads the JSON AST as input. In particular, there are plans to\nextend PLCC to allow semantics to be written in Python. This option\nallows the parser implemented in Java to be reused by and interpreter\nwritten in Python.\n\n## Copyright and Licensing\n\nourPLCC is a community of developers that maintain a number of projects\nrelated to PLCC. The contents of the projects were originally created\nby Timothy Fossum \u003cplcc@pithon.net\u003e.\n\nThank you Tim!\n\n* Copyright (C) 2023  Timothy Fossum \u003cplcc@pithon.net\u003e\n* Copyright (C) 2023- PLCC Community \u003chttps://discord.gg/EVtNSxS9E2\u003e\n* License: [GPL v3.0 or higher](LICENSES/GPL-3.0-or-later.txt).\n\nThis program is free software: you can redistribute it and/or modify it\nunder the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License,\nor (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\nSee the GNU General Public License for more details.\n\nYou should have received a copy of the\n[GNU General Public License](LICENSES/GPL-3.0-or-later.txt)\nalong with this program.  If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n## Third Party Libraries\n\nPLCC uses and distributes [Jackson JSON Processor](https://github.com/FasterXML),\nunder the [Apache 2.0](LICENSES/Apache-2.0.txt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fourplcc%2Fplcc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fourplcc%2Fplcc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fourplcc%2Fplcc/lists"}