{"id":25610578,"url":"https://github.com/guntas-13/cs327-compilers","last_synced_at":"2025-07-12T06:36:11.396Z","repository":{"id":277838745,"uuid":"913373330","full_name":"guntas-13/CS327-Compilers","owner":"guntas-13","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-07T12:49:32.000Z","size":43662,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-07T13:39:19.588Z","etag":null,"topics":["abstract-binding-trees","abstract-syntax-tree","compiler","lexer-parser","programming-languages","variable-scoping"],"latest_commit_sha":null,"homepage":"https://guntas-13.github.io/osl.docs/","language":"Python","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/guntas-13.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":"2025-01-07T15:03:25.000Z","updated_at":"2025-04-03T14:53:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"13c763bd-37da-4ee2-8e1e-67babfb8a419","html_url":"https://github.com/guntas-13/CS327-Compilers","commit_stats":null,"previous_names":["guntas-13/cs327-compilers"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/guntas-13/CS327-Compilers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guntas-13%2FCS327-Compilers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guntas-13%2FCS327-Compilers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guntas-13%2FCS327-Compilers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guntas-13%2FCS327-Compilers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guntas-13","download_url":"https://codeload.github.com/guntas-13/CS327-Compilers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guntas-13%2FCS327-Compilers/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264951610,"owners_count":23687974,"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":["abstract-binding-trees","abstract-syntax-tree","compiler","lexer-parser","programming-languages","variable-scoping"],"created_at":"2025-02-21T22:34:37.059Z","updated_at":"2025-07-12T06:36:11.390Z","avatar_url":"https://github.com/guntas-13.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CS327-Compilers\n\n## Overall Grammar\n\n```\nprogram → declaration* EOF;\n\ndeclaration → funDecl | varDecl | statement;\n\nfunDecl → \"fn\" IDENTIFIER \"(\" parameters? \")\" block;\nvarDecl → \"var\" IDENTIFIER (\":=\" expression)? \";\";\nstatement → ifStmt | printStmt | returnStmt | block | expressionStmt;\n\nifStmt → \"if\" expression statement (\"else\" statement)?;\nprintStmt → \"print\" \"(\" expression \")\" \";\";\nreturnStmt → \"return\" (expression)? \";\";\nblock → \"{\" declaration* \"}\";\n\nexpressionStmt → expression \";\";\nexpression → assignment | expB;\nassignment → IDENTIFIER \":=\" expB;\n\nparameters → IDENTIFIER (\",\" IDENTIFIER)*;\n\nexpB → logicOr;\nlogicOr → logicAnd (\"||\" logicAnd)*;\nlogicAnd → comparison (\"\u0026\u0026\" comparison)*;\ncomparison → add ((\"\u003c\" | \"\u003e\" | \"\u003c=\" | \"\u003e=\" | \"=\" | \"!=\") add)*;\nadd → mul ((\"+\" | \"-\") mul)*;\nmul → exp ((\"*\" | \"/\" | \"%\") exp)*;\nexp → unary (\"^\" unary)*;\nunary → (\"-\" | \"√\") unary | atom;\n\natom → NUMBER | IDENTIFIER | funCall | \"(\" expression \")\";\nfunCall → IDENTIFIER \"(\" arguments? \")\";\narguments → expression (\",\" expression)*;\n\nNUMBER → DIGIT+ (\".\" DIGIT*)? | \".\" DIGIT+;\nDIGIT → \"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\";\nIDENTIFIER → LETTER (LETTER | DIGIT | \"_\")*;\nLETTER → \"a\" .. \"z\" | \"A\" .. \"Z\";\n```\n\n## Run Project Euler Analysis\n\n```bash\npython3 eulerProblems.py\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/euler.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Project Euler Problems in `osl.`\n\n```python\neuler_p1 = \"\"\"\nletFunc F(x, s) {\n    if (x = 1000) return s;\n    if (x % 3 = 0 || x % 5 = 0)\n        return F(x + 1, s + x);\n    return F(x + 1, s);\n}\nF(0, 0);\n\"\"\"\n```\n\n```python\neuler_p2 = \"\"\"\nletFunc fib(a, b, s) {\n    if (a \u003e= 4000000) return s;\n    if (a % 2 = 0)\n        return fib(b, a + b, s + a);\n    return fib(b, a + b, s);\n}\nfib(0, 1, 0);\n\"\"\"\n```\n\n```python\neuler_p3 = \"\"\"\nletFunc prime(n, i) {\n    if (i * i \u003e n) return n;\n    if (n % i = 0)\n        return prime(n / i, i);\n    return prime(n, i + 1);\n}\nvar n := 600851475143;\nprime(n, 2);\n\"\"\"\n```\n\n```python\neuler_p4 = \"\"\"\nletFunc isPal(n, rev, org) {\n    if (n = 0) return rev = org;\n    return isPal(n/10, rev*10 + n%10, org);\n}\nletFunc F(i, j, maxPal) {\n    if (i \u003c 100) return maxPal;\n    if (j \u003c 100) return F(i - 1, i - 1, maxPal);\n    var prod := i * j;\n    if ((prod \u003e maxPal) \u0026\u0026 (isPal(prod, 0, prod)))\n        maxPal := prod;\n    return F(i, j - 1, maxPal);\n}\nF(999, 999, 0);\n\"\"\"\n```\n\n## Coverage with Automated Unit Tests\n\n```bash\npython3 -m pytest test_unit_tests.py --cov=calculator_extended_resolved --cov-report=html\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/pytest-cov.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n### `./test_unit_tests.py`\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/test_unit.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n### Coverage Report (`./htmlcov/index.html`)\n\n#### Overall Coverage\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/cov.png\" style=\"width: 100%\"\u003e\n    \u003cimg src = \"./images/cov1.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n#### Function Coverage\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/cov-func.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n#### Class Coverage\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/cov-class.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Make Custom Tests\n\n```bash\npython3 unit_tests.py\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/tests.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Addition of Assignment (22 March 2025)\n\n```python\n@dataclass\nclass Assign(AST):\n    var: AST\n    e1: AST\n\ndef parse(s: str) -\u003e AST:\n    t = peekable(lex(s))\n    i = 0\n\n    def parse_expression():\n        # expression -\u003e expB | assignment\n        # first parse the lhs, if it's a variable and next token is ':=' then it's an assignment\n        # otherwise it's an expB so return it as is.\n        ast = parse_bool()\n        if not isinstance(ast, Variable) and peek() == OperatorToken(\":=\"):\n            raise ParseErr(f\"Expected variable on the left side of assignment := operator at index {i}\")\n        if isinstance(ast, Variable) and peek() == OperatorToken(\":=\"):\n            consume(OperatorToken, \":=\")\n            e1 = parse_bool()\n            return Assign(ast, e1)\n        return ast\n\n\ndef resolve(program: AST, env: Environment = None) -\u003e AST:\n\n    case Assign(Variable(varName, _), e1):\n            re1 = resolve_(e1)\n            return Assign(Variable(varName, env.get(varName)), re1)\n\ndef e(tree: AST, env: Environment = None) -\u003e int | float | bool:\n\n    case Assign(Variable(varName, i), e1):\n            v1 = e_(e1)\n            env.update(f\"{varName}:{i}\", v1)\n            return None\n```\n\n```python\nexp = \"\"\"\nvar x := 0;\nvar n := 121;\n\nx := (x * 10) + (n % 10);\nn := n / 10;\nprint(x);\nprint(n);\n\nx := (x * 10) + (n % 10);\nn := n / 10;\nprint(x);\nprint(n);\n\nx := (x * 10) + (n % 10);\nn := n / 10;\nprint(x);\nn;\n\"\"\"\n```\n\n## Addition of Closures (19 March 2025)\n\n```python\n@dataclass\nclass FunObj:\n    params: List[AST]\n    body: AST\n    env: Environment\n\ndef e(tree: AST, env: Environment = None) -\u003e int | float | bool:\n\n    match tree:\n        case LetFun(Variable(varName, i), params, body):\n            # Closure -\u003e Copy of Environment taken along with the declaration!\n            funObj = FunObj(params, body, None)\n            env.add(f\"{varName}:{i}\", funObj)\n            funObj.env = env.copy()\n            return None\n\n        case CallFun(Variable(varName, i), args):\n            fun = env.get(f\"{varName}:{i}\")\n            rargs = [e_(arg) for arg in args]\n\n            # use the environment that was copied when the function was defined\n            call_env = fun.env.copy()\n            call_env.enter_scope()\n            for param, arg in zip(fun.params, rargs):\n                call_env.add(f\"{param.varName}:{param.id}\", arg)\n\n            rbody = e(fun.body, call_env)\n            return rbody\n```\n\n```python\nexp = \"\"\"\nletFunc f1()\n{\n    var x := 10;\n    letFunc f2()\n    {\n        return x;\n    }\n    return f2;\n}\nvar msg := f1();\nmsg();\n\"\"\"\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/closure.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n```python\nexp = \"\"\"\nvar x := 6;\n\nletFunc F(x)\n{\n    letFunc G()\n    {\n        return x;\n    }\n    return G;\n}\n\nvar y := F(5);\ny() * y();\n\"\"\"\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/closure1.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n```python\nexp = \"\"\"\nletFunc fact(n)\n{\n    if (n = 0)\n        return 1;\n    return n * fact(n - 1);\n}\nfact(5);\n\"\"\"\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/closure3.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Dangling \"else\" matched to latest \"if\" (20 March 2025)\n\n```python\n@dataclass\nclass IfUnM(AST):\n    condition: AST\n    then_body: AST\n```\n\n```python\ndef parse(s: str) -\u003e AST:\n    def parse_if():\n            consume(KeyWordToken, \"if\")\n            condition = parse_expression()\n            then_body = parse_statement()\n            if peek() == KeyWordToken(\"else\"):\n                consume(KeyWordToken, \"else\")\n                else_body = parse_statement()\n                return If(condition, then_body, else_body)\n            return IfUnM(condition, then_body)\n```\n\n```python\nexp = \"\"\"\nvar x := 15;\nif (x \u003e 10)\nif (x \u003c 20)\nprint(x + 1);\nelse print(x - 1);\nx;\n\"\"\"\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/if1.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Functions as First-Class Objects (like variables)\n\n```python\n@dataclass\nclass LetFun(AST):\n    name: AST   # considering functions as first-class just like variables else it'll be str\n    params: List[AST]\n    body: AST\n    expr: AST\n\n@dataclass\nclass CallFun(AST):\n    fn: AST     # considering functions as first-class just like variables else it'll be str\n    args: List[AST]\n```\n\n```python\ndef resolve(program: AST, env: Environment = None) -\u003e AST:\n    if env is None:\n        env = Environment()\n\n    def resolve_(program: AST) -\u003e AST:\n        return resolve(program, env)\n\n    match program:\n        case LetFun(Variable(varName, _), params, body, expr):\n                    env.enter_scope()\n                    env.add(varName, i := fresh())\n                    env.enter_scope()\n                    new_params = []\n                    for param in params:\n                        env.add(param.varName, j := fresh())\n                        new_params.append(Variable(param.varName, j))\n                    new_body = resolve_(body)\n                    env.exit_scope()\n                    new_expr = resolve_(expr)\n                    env.exit_scope()\n                    return LetFun(Variable(varName, i), new_params, new_body, new_expr)\n\n        case CallFun(fn, args):\n            rfn = resolve_(fn)\n            rargs = [resolve_(arg) for arg in args]\n            return CallFun(rfn, rargs)\n```\n\n# Updated - Lexer, Parser Added!\n\nAdd new token for call - the lexer checks if previously yielded token is `KeyWordToken(letFun)` then the next expected token is the function variable in the definition i.e `VariableToken(\u003cfunc-name\u003e)`, otherwise it's a `FunCallToken(\u003cfunc-name\u003e)`.\n\n```python\n@dataclass\nclass FunCallToken(Token):\n    funName: str\n```\n\n# OLDER VERSIONS (From 12 Feb 2025)\n\n## Project Euler Q1\n\n```python\nexp = \"\"\"\nletFunc func(x, s)\n{\n     if x = 1000 then\n         s\n     else if x % 3 = 0 || x % 5 = 0 then\n         func(x + 1, s + x)\n     else\n         func(x + 1, s)\n}\nin\nfunc(0, 0)\nend\n\"\"\"\n```\n\n```python\n## PROJECT EULER 1\nexp = LetFun(Variable(\"func\"),\n             [Variable(\"x\"), Variable(\"s\")],\n             If(BinOp(\"=\", Variable(\"x\"), Number(\"1000\")),\n                Variable(\"s\"),\n                If(BinOp(\"||\", BinOp(\"=\", BinOp(\"%\", Variable(\"x\"), Number(\"3\")), Number(\"0\")), BinOp(\"=\", BinOp(\"%\", Variable(\"x\"), Number(\"5\")), Number(\"0\"))),\n                   CallFun(Variable(\"func\"), [BinOp(\"+\", Variable(\"x\"), Number(\"1\")), BinOp(\"+\", Variable(\"s\"), Variable(\"x\"))]),\n                   CallFun(Variable(\"func\"), [BinOp(\"+\", Variable(\"x\"), Number(\"1\")), Variable(\"s\")])\n                   )\n                ),\n             CallFun(Variable(\"func\"), [Number(\"0\"), Number(\"0\")]))\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/Q1.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Project Euler Q2\n\n```python\nexp = \"\"\"\nletFunc fib(a, b, s)\n{\n    if a \u003e= 4000000 then\n        s\n    else if a % 2 = 0 then\n        fib(b, a + b, s + a)\n    else\n        fib(b, a + b, s)\n}\nin\nfib(0, 1, 0)\nend\n\"\"\"\n```\n\n```python\n## PROJECT EULER 2\nexp = LetFun(Variable(\"fib_sum\"),\n             [Variable(\"a\"), Variable(\"b\"), Variable(\"s\")],\n             If(BinOp(\"\u003e=\", Variable(\"a\"), Number(\"4000000\")),\n                Variable(\"s\"),\n                If(BinOp(\"=\", BinOp(\"%\", Variable(\"a\"), Number(\"2\")), Number(\"0\")),\n                   CallFun(Variable(\"fib_sum\"), [Variable(\"b\"), BinOp(\"+\", Variable(\"a\"), Variable(\"b\")), BinOp(\"+\", Variable(\"s\"), Variable(\"a\"))]),\n                   CallFun(Variable(\"fib_sum\"), [Variable(\"b\"), BinOp(\"+\", Variable(\"a\"), Variable(\"b\")), Variable(\"s\")])\n                   )\n                ),\n             CallFun(Variable(\"fib_sum\"), [Number(\"0\"), Number(\"1\"), Number(\"0\")]))\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/Q2.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Factorial Code\n\n```python\nexp = \"\"\"\nletFunc fact(n)\n{\n    if n = 0 then\n        1\n    else\n        let x := fact(n - 1) in\n        n * x\n        end\n}\nin\nfact(5)\nend\n\"\"\"\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/QFactorial.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Static Scoping\n\n```python\nexp = \"\"\"\nlet x := 5 in\nletFunc f(y) {\n    x\n}\nin\nletFunc g(z) {\n    let x := 6\n    in f(z)\n    end\n}\nin\ng(0)\nend\nend\nend\n\"\"\"\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/Static.png\" style=\"width: 100%\"\u003e\n\u003c/div\u003e\n\n## Unit Tests\n\nColor coded `unit_tests.py` for better readability. (fail shown deliberately by not passing through `resolve()`)\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"./images/unit.png\" style=\"width: 30%\"\u003e\n\u003c/div\u003e\n\n## Even Older Versions\n\n```python\nexp_cond1 = \"\"\"\nif 2 \u003c 3 then\n    0 + 5\nelse\n    1 * 6\nend\n\"\"\"\nast = parse(exp_cond1)\ndot = visualize_ast(ast)\ndot.render(\"ast_cond\", format=\"png\", cleanup=True)\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"https://github.com/guntas-13/CS327-Compilers/blob/master/images/ast_cond.png\" style=\"width: 50%\"\u003e\n\u003c/div\u003e\n\n```python\nexp_cond = \"\"\"\nif 2 \u003c 3 then\n    if 4 \u003e 5 then\n        1\n    else\n        if 6 \u003c= 7 then\n            8\n        else\n            9\n        end\n    end\nelse\n    10\nend\n\"\"\"\nast = parse(exp_cond)\ndot = visualize_ast(ast)\ndot.render(\"ast_nested_cond\", format=\"png\", cleanup=True)\n```\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"https://github.com/guntas-13/CS327-Compilers/blob/master/images/ast_nested_cond.png\" style=\"width: 50%\"\u003e\n\u003c/div\u003e\n\n```python\nexp_sq = \"\\u221a(4 + 12) + \\u221a(9)\"\nast = parse(exp_sq)\ndot = visualize_ast(ast)\ndot.render(\"ast_sqrt\", format=\"png\", cleanup=True)\n```\n\n\u0026radic;(4 + 12) + \u0026radic;(9)\n\n\u003cdiv align = \"center\"\u003e\n    \u003cimg src = \"https://github.com/guntas-13/CS327-Compilers/blob/master/images/ast_sqrt.png\" style=\"width: 20%\"\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguntas-13%2Fcs327-compilers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguntas-13%2Fcs327-compilers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguntas-13%2Fcs327-compilers/lists"}