{"id":13778732,"url":"https://github.com/bbu/simple-interpreter","last_synced_at":"2025-05-11T12:31:37.181Z","repository":{"id":145282676,"uuid":"40415972","full_name":"bbu/simple-interpreter","owner":"bbu","description":"A hackable and extensible lexer, parser and interpreter for a minimalistic, imperative, C-like language.","archived":false,"fork":false,"pushed_at":"2020-10-01T08:26:52.000Z","size":54,"stargazers_count":142,"open_issues_count":2,"forks_count":16,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-08-03T18:13:17.336Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bbu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-08-08T20:45:07.000Z","updated_at":"2024-07-04T11:14:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"a899483b-c875-4cda-aada-47ef374deb2b","html_url":"https://github.com/bbu/simple-interpreter","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbu%2Fsimple-interpreter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbu%2Fsimple-interpreter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbu%2Fsimple-interpreter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbu%2Fsimple-interpreter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bbu","download_url":"https://codeload.github.com/bbu/simple-interpreter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225048982,"owners_count":17412904,"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":[],"created_at":"2024-08-03T18:00:56.890Z","updated_at":"2024-11-17T14:30:51.215Z","avatar_url":"https://github.com/bbu.png","language":"C","funding_links":[],"categories":["C"],"sub_categories":[],"readme":"## What is this?\n\nThis is a hackable and extensible lexer, parser and interpreter for a minimalistic, imperative, C-like language. It can also be used as an educational tool for understanding lexing and parsing.\n\n## How does it work?\n\nThe lexer produces a list of tokens from the input (a `PROT_READ` memory-mapped file). Each individual token is produced by consuming a character from the input and then asking each of the token functions whether they accept it. Each such token function has an internal state and can return any of `STS_ACCEPT`, `STS_REJECT` or `STS_HUNGRY` on a character consumed. The lexer reads in characters until all of the functions return `STS_REJECT` (at which point they reset their internal state), and then the accepted token is determined by looking back for an `STS_ACCEPT` from the previous iteration.\n\nEssentially, this is a \"maximal munch\" algorithm.\n\nThe parser takes the list of tokens and produces a tree. It does that by continuously shifting tokens off the input to the parse stack and then reducing any matching suffix of the stack to a non-terminal, according to the rules of the grammar. The grammar is defined as a static array of structs, where each struct is a rule. When a rule matches a suffix of the stack, a reduction is made. The reduction essentially creates a single level of child nodes (the symbols that matched the rule) and they get parented by a new non-terminal symbol on the stack (the left-hand side of the matching rule).\n\nIn effect, this is a shift-reduce, bottom-up parser. Because the parser has no state and decision tables, a few additional hacks are implemented in order to support operator precedence and if-elif-else chains.\n\nThe interpreter is really straightforward. It starts from the top of the parse tree and walks down through the child nodes, executing the statements and evaluating the expressions. Any warnings during the execution of the program are written to standard error with a `warn:` prefix.\n\n## The Language\n\n* Control-flow statements (the curly braces are mandatory):\n  * `if (Expr) { N✕Stmt } elif (Expr) { N✕Stmt } else { N✕Stmt }`\n  * `while (Expr) { N✕Stmt }` \n  * `do { N✕Stmt } while (Expr);`\n\n* Variable and array assignment (integers only, `Name` is equivalent to `Name[0]`):\n  * `Name = Expr;`\n  * `Name[Expr] = Expr;`\n\n* Printing to standard output (integers only):\n  * `print \"Placeholder: \" Expr;`\n  * `print Expr;`\n\n* Parenthesised expressions (integers only):\n  * `(Expr)`\n\n* Binary expressions (between two integers):\n  * `Expr OP Expr`, where `OP` is `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `\u003c`, `\u003e`, `\u003c=`, `\u003e=`, `\u0026\u0026` or `||`\n\n* Unary expressions (integers only):\n  * `OP Expr`, where `OP` is `-`, `+` or `!`\n\n* A ternary expression (integers only):\n  * `Expr ? Expr : Expr`\n\n* Line and block comments:\n  * `// line comment`\n  * `/* block comment */`\n\n## Sample Output\nYou start the interpreter by specifying the file containing the code.\n\nOnce the file is opened and mapped into memory, the lexer starts. The tokens will be written to standard output as they appear in the file, in alternating colours (green and yellow), so that you can clearly see where each token starts and ends.\n\nIf the lexing was successful (all the tokens were recognised), the parser starts. On each shift or reduce operation, it outputs a single line with the current contents of the parse stack. Non-terminals are in yellow, terminals are in green. Finally, if the parsing was successful, the parse stack should contain a single non-terminal called \"Unit\".\n\nThe interpreter then starts from the root of the tree (which is always \"Unit\"), and executes the tree produced by the parser.\n```\n$ ./interp tests/fizzbuzz.txt \n*** Lexing ***\nnumber = 1;\n\ndo {\n    if (number % 3 == 0 \u0026\u0026 number % 5 == 0) {\n        print \"FizzBuzz \" number;\n    } elif (number % 5 == 0) {\n        print \"Fizz \" number;\n    } elif (number % 3 == 0) {\n        print \"Buzz \" number;\n    } else {\n        print number;\n    }\n    \n    number = number + 1;\n} while (number \u003c= 100);\n\n*** Parsing ***\nShift: ^ \nShift: ^ number \nShift: ^ number = \nShift: ^ number = 1 \nRed19: ^ number = Atom \nRed20: ^ number = Expr \nShift: ^ number = Expr ; \nRed05: ^ Assn \nRed02: ^ Stmt \nShift: ^ Stmt do \nShift: ^ Stmt do { \nShift: ^ Stmt do { if \nShift: ^ Stmt do { if ( \nShift: ^ Stmt do { if ( number \nRed18: ^ Stmt do { if ( Atom \nRed20: ^ Stmt do { if ( Expr \nShift: ^ Stmt do { if ( Expr % \nShift: ^ Stmt do { if ( Expr % 3 \nRed19: ^ Stmt do { if ( Expr % Atom \nRed20: ^ Stmt do { if ( Expr % Expr \nRed39: ^ Stmt do { if ( Bexp \nRed22: ^ Stmt do { if ( Expr \nShift: ^ Stmt do { if ( Expr == \n...\nRed21: ^ Stmt do { Stmt Stmt } while Expr \nShift: ^ Stmt do { Stmt Stmt } while Expr ; \nRed16: ^ Stmt Dowh \nRed11: ^ Stmt Ctrl \nRed04: ^ Stmt Stmt \nShift: ^ Stmt Stmt $ \nRed01: Unit \nACCEPT Unit \n\n*** Running ***\n1\n2\nBuzz 3\n4\nFizz 5\nBuzz 6\n7\n8\nBuzz 9\nFizz 10\n11\nBuzz 12\n13\n14\nFizzBuzz 15\n...\n94\nFizz 95\nBuzz 96\n97\n98\nBuzz 99\nFizz 100\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbbu%2Fsimple-interpreter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbbu%2Fsimple-interpreter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbbu%2Fsimple-interpreter/lists"}