{"id":20384331,"url":"https://github.com/gyakobo/bnf-recursive-descent","last_synced_at":"2025-07-12T12:05:04.141Z","repository":{"id":248437263,"uuid":"828674713","full_name":"Gyakobo/BNF-Recursive-Descent","owner":"Gyakobo","description":"This project parses and evaluates arithmetic expressions using recursive descent parsing based on BNF grammar rules.","archived":false,"fork":false,"pushed_at":"2024-07-28T04:21:48.000Z","size":29,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-15T08:06:59.272Z","etag":null,"topics":["backus-naur-form","njit","parse-tree","python","recursion","recursive-descent"],"latest_commit_sha":null,"homepage":"https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form","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/Gyakobo.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":"2024-07-14T21:26:59.000Z","updated_at":"2024-12-03T18:10:13.000Z","dependencies_parsed_at":"2025-01-15T07:30:43.469Z","dependency_job_id":"d3193118-2648-4331-83ed-1c1d8f842e62","html_url":"https://github.com/Gyakobo/BNF-Recursive-Descent","commit_stats":null,"previous_names":["gyakobo/bnf-recursive-descent"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gyakobo%2FBNF-Recursive-Descent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gyakobo%2FBNF-Recursive-Descent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gyakobo%2FBNF-Recursive-Descent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gyakobo%2FBNF-Recursive-Descent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gyakobo","download_url":"https://codeload.github.com/Gyakobo/BNF-Recursive-Descent/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241935269,"owners_count":20044827,"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":["backus-naur-form","njit","parse-tree","python","recursion","recursive-descent"],"created_at":"2024-11-15T02:27:22.938Z","updated_at":"2025-03-04T23:12:59.822Z","avatar_url":"https://github.com/Gyakobo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BNF with Recursive Descent\r\n\r\n![image](https://img.shields.io/badge/Python-FFD43B?style=for-the-badge\u0026logo=python\u0026logoColor=blue)\r\n![image](https://img.shields.io/badge/windows%20terminal-4D4D4D?style=for-the-badge\u0026logo=windows%20terminal\u0026logoColor=white)\r\n\r\nAuthor: [Andrew Gyakobo](https://github.com/Gyakobo)\r\n\r\n\u003e[!NOTE]\r\n\u003ePlease feel free to make pull requests and edit this project to your will. As a matter of fact, oh dear reader, I'd be ecstatic if somebody pulls this branch and adds a byte-code version of this code in pure C.\r\n\r\n## Introduction\r\n\r\n[Backus-Naur Form (BNF)](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) is a notation technique used to describe the grammar of a language in a formal and concise way. It defines the syntax of programming languages, data structures, and protocols. A BNF grammar consists of a set of production rules that define how strings in the language can be generated. Each rule describes how a symbol can be replaced with a sequence of other symbols.\r\n\r\nFor example, the BNF for arithmetic expressions might look like this:\r\n\r\n```html\r\n\u003cexpression\u003e ::= \u003cterm\u003e + \u003cexpression\u003e | \u003cterm\u003e - \u003cexpression\u003e | \u003cterm\u003e\r\n\u003cterm\u003e ::= \u003cfactor\u003e * \u003cterm\u003e | \u003cfactor\u003e / \u003cterm\u003e | \u003cfactor\u003e\r\n\u003cfactor\u003e ::= (\u003cexpression\u003e) | \u003coperand\u003e\r\n\u003coperand\u003e ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\r\n```\r\n\r\nIn this BNF, an `\u003cexpression\u003e` can be a `\u003cterm\u003e` followed by + and another `\u003cexpression\u003e`, or a `\u003cterm\u003e` followed by - and another `\u003cexpression\u003e`, or just a `\u003cterm\u003e`. A `\u003cterm\u003e` can be a `\u003cfactor\u003e` followed by * and another `\u003cterm\u003e`, and so on. This recursive definition allows complex expressions to be built from simpler components.\r\n\r\n[Recursive Descent Parsing](https://en.wikipedia.org/wiki/Recursive_descent_parser) is a top-down parsing technique that uses a set of recursive functions to process the input string and build a parse tree according to the grammar defined by the BNF. Each non-terminal symbol in the BNF corresponds to a function in the parser. These functions call each other recursively to parse the input and build the syntax tree.\r\n\r\nFor example, given the BNF rules above, a recursive descent parser would have functions like `parse_expression`, `parse_term`, `parse_factor`, and `parse_operand`. Each function is responsible for parsing its corresponding non-terminal symbol.\r\n\r\n### Example\r\n\r\nHere is a simple example of how recursive descent parsing works:\r\n\r\n1. *parse_expression*: This function would try to parse a `\u003cterm\u003e`, then check if there is a `+` or `-` operator, and if so, recursively parse another `\u003cexpression\u003e`.\r\n\r\n1. *parse_term*: This function would try to parse a `\u003cfactor\u003e`, then check if there is a `*` or `/` operator, and if so, recursively parse another `\u003cterm\u003e`.\r\n\r\n1. *parse_factor*: This function would check if the input starts a `(`, and if so, recursively parse an `\u003cexpression\u003e` inside the parentheses. If not, it would try to parse an `\u003coperand\u003e`.\r\n\r\n1. *parse_operand*: This function would simply check if the current character is a digit and return it as an operand.\r\n\r\nBy following these steps, a recursive descent parser can build a tree structure that represents the syntactic structure of the input string. This tree can then be used to evaluate the expression, generate code, or perform other tasks.\r\n\r\nRecursive descent parsing is straightforward to implement and understand, making it a good choice for simple grammars. However, it may not be efficient for more complex grammars due to its potential for exponential time complexity in certain cases.\r\n\r\n## Methodology\r\n\r\nThis project aims to parse and evaluate arithmetic expressions using recursive descent parsing based on BNF (Backus-Naur Form) grammar rules. The process involves defining a grammar, implementing a parser, constructing an expression tree, and evaluating the tree.\r\n\r\n1. *Defining the Grammar*:\r\n\r\nThe BNF grammar for arithmetic expressions is defined as follows:\r\n\r\n```html\r\n\u003cexpression\u003e ::= \u003cterm\u003e + \u003cexpression\u003e | \u003cterm\u003e - \u003cexpression\u003e | \u003cterm\u003e\r\n\u003cterm\u003e ::= \u003cfactor\u003e * \u003cterm\u003e | \u003cfactor\u003e / \u003cterm\u003e | \u003cfactor\u003e\r\n\u003cfactor\u003e ::= (\u003cexpression\u003e) | \u003coperand\u003e\r\n\u003coperand\u003e ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\r\n```\r\n\r\nThis grammar describes how complex arithmetic expressions can be built from simpler components.\r\n\r\n2. *Implementing the Parser*:\r\n\r\nThe parser is implemented using recursive descent parsing. Each non-terminal symbol in the grammar corresponds to a function in the parser. \r\n\r\n* Parser Class: Ther `Parser` class handles the parsing of the input string.\r\n\r\n```python\r\nclass Parser:\r\n    def __init__(self, input_string):\r\n        self.input = input_string.replace(\" \", \"\")\r\n        self.position = 0\r\n```\r\n\r\n* Parsing Expressions: The `parse_expression` function parses an `\u003cexpression\u003e`.\r\n\r\n```python\r\ndef parse_expression(self):\r\n    left = self.parse_term()\r\n    while self.current_char() in ('+', '-'):\r\n        operator = self.current_char()\r\n        self.advance()\r\n        right = self.parse_term()\r\n        left = ExpressionNode(left, operator, right)\r\n    return left\r\n```\r\n\r\n* Parsing Terms: The `parse_term` function parses a `\u003cterm\u003e`.\r\n\r\n```python\r\ndef parse_term(self):\r\n    left = self.parse_factor()\r\n    while self.current_char() in ('*', '/'):\r\n        operator = self.current_char()\r\n        self.advance()\r\n        right = self.parse_factor()\r\n        left = TermNode(left, operator, right)\r\n    return left\r\n```\r\n\r\n* Parsing Factors: The `parse_factor` function parses a `\u003cfactor\u003e`\r\n\r\n```python\r\ndef parse_factor(self):\r\n    if self.current_char() == '(':\r\n        self.advance()\r\n        expr = self.parse_expression()\r\n        if self.current_char() == ')':\r\n            self.advance()\r\n        return FactorNode(expr)\r\n    else:\r\n        return self.parse_operand()\r\n```\r\n\r\n* Parsing Operands: The `parse_operand` function parses an `\u003coperand\u003e`.\r\n\r\n```python\r\ndef parse_operand(self):\r\n    start = self.position\r\n    while self.current_char() is not None and self.current_char().isdigit():\r\n        self.advance()\r\n    value = int(self.input[start:self.position])\r\n    return OperandNode(value)\r\n```\r\n\r\n3. *Constructing the Expression Tree*:\r\n\r\nThe parser constructs an expression tree with different types of nodes representing the components of the expression.\r\n\r\n* OperandNode: Represents an operand (integer).\r\n\r\n```python\r\nclass OperandNode:\r\n    def __init__(self, value):\r\n        self.value = value\r\n\r\n    def evaluate(self):\r\n        return self.value\r\n```\r\n\r\n* FactorNode: Represents a factor which could be an expression in parentheses or an operand.\r\n\r\n```python\r\nclass FactorNode:\r\n    def __init__(self, child):\r\n        self.child = child\r\n\r\n    def evaluate(self):\r\n        return self.child.evaluate()\r\n```\r\n\r\n* TermNode: Represents a term which is s product or quotient of factors.\r\n\r\n```python\r\nclass TermNode:\r\n    def __init__(self, left, operator=None, right=None):\r\n        self.left = left\r\n        self.operator = operator\r\n        self.right = right\r\n\r\n    def evaluate(self):\r\n        if not self.operator:\r\n            return self.left.evaluate()\r\n        elif self.operator == '*':\r\n            return self.left.evaluate() * self.right.evaluate()\r\n        elif self.operator == '/':\r\n            return self.left.evaluate() / self.right.evaluate()\r\n```\r\n\r\n* ExpressionNode: Represents an expression which is a sum or difference of terms:\r\n\r\n```python\r\nclass ExpressionNode:\r\n    def __init__(self, left, operator=None, right=None):\r\n        self.left = left\r\n        self.operator = operator\r\n        self.right = right\r\n\r\n    def evaluate(self):\r\n        if not self.operator:\r\n            return self.left.evaluate()\r\n        elif self.operator == '+':\r\n            return self.left.evaluate() + self.right.evaluate()\r\n        elif self.operator == '-':\r\n            return self.left.evaluate() - self.right.evaluate()\r\n```\r\n\r\n4. Evaluating the Expression:\r\n\r\nThe `evaluate_expression` function constructs the expression tree and evaluates it.\r\n\r\n```python\r\ndef evaluate_expression(input_string):\r\n    parser = Parser(input_string)\r\n    tree = parser.parse_expression()\r\n    return tree.evaluate()\r\n```\r\n\r\n5. Command-line Input:\r\n\r\nThe script uses the `argparse` module to handle command-line input.\r\n\r\n```python\r\nimport argparse\r\n\r\ndef main():\r\n    parser = argparse.ArgumentParser(description=\"Evaluate an arithmetic expression.\")\r\n    parser.add_argument(\"expression\", type=str, help=\"Arithmetic expression to evaluate\")\r\n    args = parser.parse_args()\r\n    result = evaluate_expression(args.expression)\r\n    print(f\"Expression: {args.expression}\")\r\n    print(f\"Result: {result}\")\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n```\r\n\r\n## Conclusion\r\n\r\nThis methodology describes the steps to parse and evaluate arithmetic expressions using recursive descent parsing based on BNF grammar. The implementation includes parsing functions for expressions, terms, factors, and operands, constructing and expression tree, and evaluating the tree to obtain the result. The project also handles command-line input for user-friendly operation.\r\n\r\n## License\r\nMIT\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgyakobo%2Fbnf-recursive-descent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgyakobo%2Fbnf-recursive-descent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgyakobo%2Fbnf-recursive-descent/lists"}