{"id":25280134,"url":"https://github.com/dreden33773/pl_0","last_synced_at":"2025-07-07T21:03:37.159Z","repository":{"id":199678819,"uuid":"703457340","full_name":"DrEden33773/pl_0","owner":"DrEden33773","description":"a simple pl/0 compiler implemented in rust.","archived":false,"fork":false,"pushed_at":"2024-10-05T05:14:41.000Z","size":148,"stargazers_count":36,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T23:18:32.084Z","etag":null,"topics":["compiler","compiler-design","compiler-principles","curriculum-design","nuaa","pl0","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DrEden33773.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,"publiccode":null,"codemeta":null}},"created_at":"2023-10-11T09:27:59.000Z","updated_at":"2025-03-06T10:16:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"e85d1319-c616-4e0a-a598-461e9613d642","html_url":"https://github.com/DrEden33773/pl_0","commit_stats":null,"previous_names":["dreden33773/pl_0"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrEden33773%2Fpl_0","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrEden33773%2Fpl_0/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrEden33773%2Fpl_0/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DrEden33773%2Fpl_0/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DrEden33773","download_url":"https://codeload.github.com/DrEden33773/pl_0/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125592,"owners_count":21051771,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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-design","compiler-principles","curriculum-design","nuaa","pl0","rust"],"created_at":"2025-02-12T18:38:33.658Z","updated_at":"2025-04-09T23:18:37.044Z","avatar_url":"https://github.com/DrEden33773.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PL/0 (aka. PL_0)\n\n\u003e ## ❤️ Please give me a `Star` / `Follow` if you like this project! ❤️\n\n## To begin with\n\nThis is the `curriculum design` of `Compiler Principle` course in `Nanjing University of Aeronautics and Astronautics` (\naka. `NUAA`).\n\n## Introduction\n\n`PL/0` is a `subset language` of `Pascal`.\n\nThis is a simple `Rust` implementation of `PL/0` compiler.\n\n## BNF\n\n```bnf\n\u003cprog\u003e -\u003e program \u003cid\u003e ; \u003cblock\u003e\n\u003cblock\u003e -\u003e [\u003cconst-decl\u003e][\u003cvar-decl\u003e][\u003cproc\u003e]\u003cbody\u003e\n\u003cconst-decl\u003e -\u003e const \u003cconst\u003e {, \u003cconst\u003e} ;\n\u003cconst\u003e -\u003e \u003cid\u003e := \u003cinteger\u003e\n\u003cvar-decl\u003e -\u003e var \u003cid\u003e {, \u003cid\u003e} ;\n\u003cproc\u003e -\u003e procedure \u003cid\u003e ([\u003cid\u003e {, \u003cid\u003e}]) ; \u003cblock\u003e {; \u003cproc\u003e}\n\u003cbody\u003e -\u003e begin \u003cstatement\u003e {; \u003cstatement\u003e} end\n\u003cstatement\u003e -\u003e \u003cid\u003e := \u003cexp\u003e\n              | if \u003cl-exp\u003e then \u003cstatement\u003e [else \u003cstatement\u003e]\n              | while \u003cl-exp\u003e do \u003cstatement\u003e\n              | call \u003cid\u003e ([\u003cexp\u003e {, \u003cexp\u003e}])\n              | \u003cbody\u003e\n              | read (\u003cid\u003e {, \u003cid\u003e})\n              | write (\u003cexp\u003e {, \u003cexp\u003e})\n\u003cl-exp\u003e -\u003e \u003cexp\u003e \u003clop\u003e \u003cexp\u003e | odd \u003cexp\u003e\n\u003cexp\u003e -\u003e [+|-] \u003cterm\u003e {\u003caop\u003e \u003cterm\u003e}\n\u003cterm\u003e -\u003e \u003cfactor\u003e {\u003cmop\u003e \u003cfactor\u003e}\n\u003cfactor\u003e -\u003e \u003cid\u003e | \u003cinteger\u003e | (\u003cexp\u003e)\n\u003clop\u003e -\u003e = | \u003c\u003e | \u003c | \u003c= | \u003e | \u003e=\n\u003caop\u003e -\u003e + | -\n\u003cmop\u003e -\u003e * | /\n\u003cid\u003e -\u003e \u003cletter\u003e {\u003cletter\u003e | \u003cdigit\u003e}\n\u003cinteger\u003e -\u003e \u003cdigit\u003e {\u003cdigit\u003e}\n\u003cletter\u003e -\u003e a | b | ... | z | A | B | ... | Z\n\u003cdigit\u003e -\u003e 0 | 1 | ... | 9\n```\n\n## Structure\n\n$$\n\\set{Source Code} \\Longrightarrow \\textbf{Lexer} \\stackrel{Token}{\\Longrightarrow} \\textbf{Parser} \\stackrel{AST}{\\Longrightarrow} \\textbf{CodeGen} \\Longrightarrow \\set{PCode} \\longrightarrow \\textbf{VM} \\longrightarrow \\set{Result}\n$$\n\n|  Part   |    Analysis List    |\n|:-------:|:-------------------:|\n|  Lexer  | `Lexical Analysis`  |\n| Parser  |  `Syntax Analysis`  |\n| CodeGen | `Semantic Analysis` |\n\n## Overview\n\n### Lexer/Tokenizer\n\nThis part is extreme easy, I've implemented it in my own hand without using any other tools.\n\n(However, if you'd love to, you could use tools like `flex` or `pest` to generate `lexer/tokenizer` automatically)\n\n### Parser\n\nWith the help of `Recursive Descent Algorithm`, `parser` is also not that hard to implement.\n\nHowever, it's necessary to prove that the given [BNF](#bnf) satisfy the definition of `LL(1)` before implementing\n`parser` in `Recursive Descent Algorithm`.\n\nProof will be given later.\n\n### Error Handling\n\nI've adopted the welcomed `panic-mode-liked` error handling strategy for this part, to make sure that the `compiler`\ncould find as many errors as possible in one run, instead of being halted by the first error.\n\nTo make sure error could be handled in a `synchronous` way, `FIRST-FOLLOW` table is a must (I've built this manually,\nwhich could be further improved by using auto-tools).\n\n### Codegen\n\n`AST` to `PCode` code-generator is the default strategy for this part.\n\nI'm working on a `AST` to `Lua-Backend-Adapted-Representation` (LBAR) code-generator as well (not implemented yet).\n\n### Virtual Machine (aka. VM / Interpreter)\n\nSense `PCode` is the default execution result of `codegen`, the `Simple-PCode-Interpreter` is the default implementation\nof `Virtual Machine`\n\nStill, I'm trying to implement a `Lua-VM-Liked-VM` for `LBAR`\n\n## Feasibility Analysis\n\n### Proof: [BNF](#bnf) is `LL(1)`\n\nTo satisfy this, 3 conditions should be met:\n\n$$\n\\begin{align*}\n\\text{Condition 1} \u0026~\\dots~ \\text{No \\textit{left recursion pattern} detected in the \\textit{grammar}} \\\\\n\\text{Condition 2} \u0026~\\dots~ \\forall A \\in V_N (A \\rightarrow \\alpha_1 | \\alpha_2 | \\dots | \\alpha_n) \\Rightarrow First(\\alpha_i) \\cap First(\\alpha_j) = \\Phi ~ (i \\ne j) \\\\\n\\text{Condition 3} \u0026~\\dots~ \\forall A \\in V_N (\\epsilon \\in First(A)) \\Rightarrow First(A) \\cap Follow(A) = \\Phi\n\\end{align*}\n$$\n\nNow, let's prove them one by one!\n\n#### Condition 1 ~ No _left recursion pattern_ detected in the _grammar_\n\nAfter having a glance of the given [BNF](#bnf), we could easily prove that:\n\n$$\n\\forall A \\in V_N (A \\rightarrow B ~\\wedge~ B \\in V_N ) \\Rightarrow A \\ne B\n$$\n\nWhich means that, there's no _left recursion pattern_ detected in the _grammar_.\n\n#### Condition 2\n\nThis could be easy, with the reference of [BNF](#bnf) and [first_follow_table](./src/parser/synchronizer/tables.rs)\n\n#### Condition 3\n\nJust the same as `Condition 2`\n\n## Fibonacci Demo\n\nSource code:\n\n```pascal\n\nprogram fibonacci;\n\nconst index := 30;\n\nvar return,i,a;\n\nprocedure fib(a,x);\n\nvar sum;\nbegin\n  sum := 0;\n  if x\u003c2 then\n    return := x\n  else\n    begin\n      call fib(a+1,x-1);\n      sum := sum+return;\n      call fib(a+1,x-2);\n      sum := sum+return;\n      return := sum\n    end\nend\n\nbegin\n  i := 1;\n  a := 2;\n  while i\u003c=index do\n    begin\n      call fib(a+1,i);\n      write(return);\n      i := i+1\n    end\nend\n\n```\n\nResult:\n\n- Console\n\n```txt\n1\n1\n2\n3\n5\n8\n13\n21\n34\n55\n89\n144\n233\n377\n610\n987\n1597\n2584\n4181\n6765\n10946\n17711\n28657\n46368\n75025\n121393\n196418\n317811\n514229\n832040\n```\n\n- PCode\n\n```txt\nPCode List:\n======================================================================\n   0| JMP    0   39\n   1| JMP    0   4\n   2| STA    1   4\n   3| STA    2   3\n   4| INT    0   6\n   5| LIT    0   0\n   6| STO    0   5\n   7| LOD    0   4\n   8| LIT    0   2\n   9| OPR    0   10\n  10| JPC    0   14\n  11| LOD    0   4\n  12| STO    1   3\n  13| JMP    0   38\n  14| LOD    0   3\n  15| LIT    0   1\n  16| OPR    0   2\n  17| LOD    0   4\n  18| LIT    0   1\n  19| OPR    0   3\n  20| CAL    1   2\n  21| LOD    0   5\n  22| LOD    1   3\n  23| OPR    0   2\n  24| STO    0   5\n  25| LOD    0   3\n  26| LIT    0   1\n  27| OPR    0   2\n  28| LOD    0   4\n  29| LIT    0   2\n  30| OPR    0   3\n  31| CAL    1   2\n  32| LOD    0   5\n  33| LOD    1   3\n  34| OPR    0   2\n  35| STO    0   5\n  36| LOD    0   5\n  37| STO    1   3\n  38| OPR    0   0\n  39| INT    0   7\n  40| LIT    0   1\n  41| STO    0   4\n  42| LIT    0   2\n  43| STO    0   5\n  44| LOD    0   4\n  45| LIT    0   30\n  46| OPR    0   13\n  47| JPC    0   61\n  48| LOD    0   5\n  49| LIT    0   1\n  50| OPR    0   2\n  51| LOD    0   4\n  52| CAL    0   2\n  53| LOD    0   3\n  54| OPR    0   14\n  55| OPR    0   15\n  56| LOD    0   4\n  57| LIT    0   1\n  58| OPR    0   2\n  59| STO    0   4\n  60| JMP    0   44\n  61| OPR    0   0\n======================================================================\n```\n\n- Symbol Table\n\n```txt\nSymbol Table:\n======================================================================\n      name | type   | val  | level  | addr | size | scope_list\n======================================================================\n     index | const  | 30   | 0      | 3    | 0    | [\"#\"]\n    return | var    | 0    | 0      | 3    | 0    | [\"#\"]\n         i | var    | 0    | 0      | 4    | 0    | [\"#\"]\n         a | var    | 0    | 0      | 5    | 0    | [\"#\"]\n       fib | proc   | 2    | 0      | 6    | 2    | [\"#\"]\n         a | var    | 0    | 1      | 3    | 0    | [\"#\", \"fib\"]\n         x | var    | 0    | 1      | 4    | 0    | [\"#\", \"fib\"]\n       sum | var    | 0    | 1      | 5    | 0    | [\"#\", \"fib\"]\n======================================================================\n```\n\n## Error Handling Demos\n\nAs is mentioned follow, this implementation of pl/0 compiler has a complete error handling strategy, which means that it\ncould find as many errors as possible in one run, instead of being halted by the first error.\n\nHere are some simple demos:\n\n### Syntax Error (may coexists with `Lexical Error`)\n\n- src\n\n```pascal\nprogram ;\nvar a, b, c;\nbegin\n  a    1;\n  b :=  ;\n  é : 3;\n  if 1 = 1 then\n    write(1\n  else\n    write 0);\n  write a + b + c;\n  wrçte(1)\nend\n```\n\n- console\n\n```txt\nSyntaxError{ Line: 1, Col: 9 }\n  | ~~ Expected \u003cid\u003e field, but not found!\n\nSyntaxError{ Line: 4, Col: 8 }\n  | ~~ Expected `:=`, but got `Integer(1)`\n\nSyntaxError{ Line: 5, Col: 9 }\n  | ~~ Expected `\u003cid\u003e` / `\u003cinteger\u003e` / `(\u003cexp\u003e)` field, but got an unmatchable token `;`\n\nLexicalError{ Line: 6, Col: 3 }\n  | ~~ 'é' is not an ASCII character\n\nLexicalError{ Line: 6, Col: 5 }\n  | ~~ ':' is an undefined sign, did you mean ':='?\n\nSyntaxError{ Line: 6, Col: 7 }\n  | ~~ Expected `:=`, but got `Integer(3)`\n\nSyntaxError{ Line: 6, Col: 7 }\n  | ~~ Expected \u003cstatement\u003e field, but not found!\n\nSyntaxError{ Line: 9, Col: 6 }\n  | ~~ Expected `)`, but got `Else`\n\nSyntaxError{ Line: 10, Col: 11 }\n  | ~~ Expected `(`, but got `Integer(0)`\n\nSyntaxError{ Line: 11, Col: 9 }\n  | ~~ Expected `(`, but got `Identifier(\"a\")`\n\nSyntaxError{ Line: 11, Col: 18 }\n  | ~~ Expected `)`, but got `;`\n\nLexicalError{ Line: 12, Col: 5 }\n  | ~~ 'ç' is not an ASCII character\n\nSyntaxError{ Line: 12, Col: 7 }\n  | ~~ Expected `:=`, but got `Identifier(\"te\")`\n\nSyntaxError{ Line: 12, Col: 7 }\n  | ~~ Expected \u003cstatement\u003e field, but not found!\n\nthread 'main' panicked at src/parser/mod.rs:149:7:\n|\u003e Errors above occurred (during `parsing`), compiling stopped ... \u003c|\n\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n\n```\n\n### Semantic Error\n\n#### Duplicated Definition\n\n- src\n\n```pascal\nprogram MultiDef;\n\nvar a, a, a, a;\n\nprocedure proc();\nbegin\n  write(1)\nend;\n\nprocedure proc();\nbegin\n  write(2)\nend\n\nbegin\n  write(1)\nend\n```\n\n- console\n\n```txt\nSemanticError{ Line: 3, Col: 8 }\n  | ~~ `a` is defined before\n\nSemanticError{ Line: 3, Col: 11 }\n  | ~~ `a` is defined before\n\nSemanticError{ Line: 3, Col: 14 }\n  | ~~ `a` is defined before\n\nSemanticError{ Line: 10, Col: 14 }\n  | ~~ `proc` is defined before\n\nthread 'main' panicked at src/translator/mod.rs:116:7:\nattempt to subtract with overflow\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n```\n\n#### Undefined\n\n- src\n\n```pascal\nprogram undef;\nbegin\n  a := 1;\n  b := 2;\n  write(c)\nend\n```\n\n- console\n\n```txt\nSemanticError{ Line: 3, Col: 3 }\n  | ~~ `a` is undefined\n\nSemanticError{ Line: 4, Col: 3 }\n  | ~~ `b` is undefined\n\nSemanticError{ Line: 5, Col: 9 }\n  | ~~ `c` is undefined\n\nthread 'main' panicked at src/translator/mod.rs:73:7:\n|\u003e Errors above occurred (during `translation/codegen`), compiling stopped ... \u003c|\n\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n```\n\n#### `args_list.length` cannot match with definition(signature)\n\n- src\n\n```pascal\nprogram WrongArgsListLength;\n\nvar a;\n\nprocedure proc();\nbegin\n  write(1)\nend;\n\nprocedure procc(x, t, z);\nbegin\n  write(1)\nend\n\nbegin\n  call proc(1, 1, 1);\n  call procc(3)\nend\n```\n\n- console\n\n```txt\nSemanticError{ Line: 16, Col: 11 }\n  | ~~ `proc` expects 0 args, but received 3\n\nSemanticError{ Line: 17, Col: 12 }\n  | ~~ `procc` expects 3 args, but received 1\n\nthread 'main' panicked at src/translator/mod.rs:73:7:\n|\u003e Errors above occurred (during `translation/codegen`), compiling stopped ... \u003c|\n```\n\n#### Assign to `const` / `procedure`\n\n- src\n\n```pascal\nprogram AssignToConstProc;\nconst i := 1;\n\nprocedure proc();\nbegin\n  write(i)\nend\n\nbegin\n  i := 16;\n  proc := 16\nend\n```\n\n- console\n\n```txt\nSemanticError{ Line: 10, Col: 3 }\n  | ~~ `i` is not a variable\n\nSemanticError{ Line: 11, Col: 6 }\n  | ~~ `proc` is not a variable\n\nthread 'main' panicked at src/translator/mod.rs:73:7:\n|\u003e Errors above occurred (during `translation/codegen`), compiling stopped ... \u003c|\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdreden33773%2Fpl_0","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdreden33773%2Fpl_0","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdreden33773%2Fpl_0/lists"}