{"id":13667445,"url":"https://github.com/Elnath-123/SimpleCppBison","last_synced_at":"2025-04-26T15:32:53.709Z","repository":{"id":154473579,"uuid":"251535981","full_name":"Elnath-123/SimpleCppBison","owner":"Elnath-123","description":"BJUT Principle of Compiler Experiment Example","archived":false,"fork":false,"pushed_at":"2020-03-31T08:07:57.000Z","size":65,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-11T02:35:50.200Z","etag":null,"topics":["bison","flex-bison","visitor"],"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/Elnath-123.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":"2020-03-31T07:54:15.000Z","updated_at":"2023-12-19T03:02:05.000Z","dependencies_parsed_at":"2023-04-25T17:23:19.030Z","dependency_job_id":null,"html_url":"https://github.com/Elnath-123/SimpleCppBison","commit_stats":null,"previous_names":["elnath-123/simplecppbison"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Elnath-123%2FSimpleCppBison","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Elnath-123%2FSimpleCppBison/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Elnath-123%2FSimpleCppBison/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Elnath-123%2FSimpleCppBison/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Elnath-123","download_url":"https://codeload.github.com/Elnath-123/SimpleCppBison/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251008906,"owners_count":21522196,"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":["bison","flex-bison","visitor"],"created_at":"2024-08-02T07:00:37.175Z","updated_at":"2025-04-26T15:32:48.699Z","avatar_url":"https://github.com/Elnath-123.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"# Cpp Flex and Bison Document\r\n\u0026emsp;本项目使用c++语言编写的bison，与c语言编写的flex构建了一个简单的表达式分析系统。\r\n## 0.1 阅读前备知识\r\n1. flex and bison for C language 的基础知识\r\n2. c++语法的基础知识，包括封装，继承，多态，虚函数，命名空间，以及输入输出流的使用等。\r\n3. c语言的基础知识，包括指针，宏定义等。\r\n请确保你已经有以上的知识后再来阅读。\r\n\r\n## 0.2 主要内容\r\n\u0026emsp;本文档主要介绍c++bison的写法，如何兼容c语言的flex，以及如何使用观察者模式来对抽象语法树（AST）进行分析。\r\n\r\n## 1.1 C++ bison\r\n\u0026emsp;每当你创建c++语法分析器时，bison会创建四个类的头文件：location.hh，position.hh用来定义位置结构，stack.hh定义内部语法分析器堆栈，以及*.tab.hh文件来定义语法分析器自身。与c语言不同，bison将在yy命名空间中创建语法分析器，其类名为parser，其中有一个方法叫做parse，你可以在实例化该类后调用此函数。此外，token也定义在parser中。\r\n\r\n## 1.2 c++兼容的c flex写法\r\n1. 使用%lex-param声明，为lex传入参数。（可选） \r\n2. 由于parser在parse的过程中需要使用到yylex函数，但由于两者并不是同一个语言所编写，因此需要定义与生成的bison文件中yylex所匹配的函数。即定义宏YY_DELC\r\n```c\r\n/* No parameter */\r\n#define YY_DECL int yylex(yy::parser::semantic_type *yylval, yy::parser::location_type *yylloc);\r\n\r\n/* Have parameters */\r\n#define YY_DECL int yylex(yy::parser::semantic_type *yylval, yy::parser::location_type *yylloc, /*your parameter */);\r\n```\r\n3. 处理位置信息，定义宏 YY_USER_ACTION，其会在语义动作执行之前进行调用，具体实现由用户自行定义。\r\n\r\n## 1.3 C++ bison的写法\r\n1. 在bison的Declaration中，首先需要进行声明：`%language \"C++\"`，说明此文件使用c++进行编写。\r\n2. 引入代码，分为无条件形式引入（unqualified form）及条件形式引入(qualified form)\r\n- 对于无条件形式引入，其将会在parser的头文件内容之后。形式为%code{'your code'}\r\n- 有条件引入的条件分为requires, provides, top。\r\nrequire: 在bison头文件中，以及在实现文件生成YYLTYPE， YYSTYPE之后插入代码\r\nprovides: 在bison头文件中，以及在实现文件生成YYLTYPE， YYSTYPE，以及token定义之前插入代码\r\ntop: 在bison实现文件的顶部插入代码\r\n详见bison文档:https://www.gnu.org/software/bison/manual/html_node/_0025code-Summary.html\r\n3. %parse-param声明，用来将parser中的参数传递给driver程序。\r\n4. %location声明，用来把处理位置信息的代码加入语法分析器，这样yylloc才可以使用。\r\n5. 由于生成的parser中，需要使用yylex函数，因此声明：\r\n```c\r\nextern int yylex(yy::parser::semantic_type *yylval, yy::parser::location_type *yylloc);\r\n```\r\n\r\n## 2.1 观察者模式(Visitor Pattern)\r\n\u0026emsp;观察者模式是设计模式中的一种，其本质是利用面向对象中的多态型 (polymorphic)将类间解耦合。在处理抽象语法树(AST)的过程中可以充分的体现其优势。\r\n\u0026emsp;观察者模式可以简单的理解成\"主题-订阅\"模式。其表示了多对多的关系，一个观察者可以订阅多个主题，一个主题也可以被多个观察者订阅。比如AST的节点就是一个主题，我们可以使用许多观察者来订阅AST主题，这些观察者所拥有的方法，就是来对AST节点进行操作。例如表达式求值，以及生成后缀表达式。\r\n每一个主题拥有一个accept函数，用来接受Visitor的访问。每一个观察者都有一个visit方法，用来访问主题。即：\r\n```c++\r\n/* In a class of Subject file */\r\nReturnval SubjectClassName::accept(Visitor v){return v-\u003evisit(this);}\r\n/* In Visitor file */\r\nReturnval VisitorClassName::visit(Subject* node){\r\n    /* Define your own operation for node */\r\n}\r\n\r\n/* In main */\r\n/* Defination of Subject */\r\nSubjectA subject = new SubjectA();\r\nVisitorA va = new VisitorA();\r\nVisitorB vb = new VisitorB();\r\n...\r\n\r\n/* Accept the visitor */\r\nsubjectA-\u003eaccept(va); \r\nsubjectA-\u003eaccept(vb);\r\n...\r\n```\r\n\u0026emsp;我们可以发现，虽然SubjectA中只有一个accept方法，但其利用了多态性，对于不同Visitor(这里为VisitorA和VisitorB)，其完成了不同的功能。因此，如果想要添加对主题的访问功能，使用观察者模式，只需要定义一个新的Visitor，当作参数传给主题，即可完成这个功能，而不需要更改任何的主题内容。这样做，更利益扩展和维护。\r\n\r\n## 2.2 观察者模式在表达式分析中的体现\r\n1. 定义抽象语法树节点。\r\nAstNode为父类，Expression表达式类继承于AstNode，Binop二元运算符继承于Expression，Num浮点型常量继承于Expression。\r\n```c++\r\n/*\r\n* @name ast.h\r\n* @description Hierachy of AST\r\n* @date 03/31/2020\r\n* @author Lrq \r\n*                        |---------|\r\n*                        | AstNode |\r\n*                        |---------|\r\n*                             |\r\n*                             |\r\n*                      |--------------|\r\n*                      |  Expression  |\r\n*                      |--------------|\r\n*                          |      |\r\n*                         |        |\r\n*                        |          |\r\n*                    |-------|    |-------|\r\n*                    | BinOp |    |  Num  |\r\n*                    |-------|    |-------|\r\n*/\r\n#ifndef _AST_H\r\n#define _AST_H\r\n#include \u003ciostream\u003e\r\n#include \"visitor.h\"\r\n\r\nclass AstNode{\r\npublic:\r\n\tAstNode(){ }\r\n\tvirtual double accept (Visitor* v) = 0;\r\n};\r\n\r\nclass Expression : public AstNode{\r\npublic:\r\n\tstd::string type;\r\n\tExpression* left;\r\n\tExpression* right;\r\n\tExpression () { }\r\n\tExpression (std::string type): type(type) { }\r\n\tExpression (std::string type, Expression* left, Expression* right) :\r\n                type (type), left (left), right (right) { }\r\n};\r\n\r\nclass BinOp : public Expression{\r\npublic:\r\n\tdouble accept (Visitor* v) {return v-\u003evisit(this);}\r\n\tBinOp (std::string type, Expression* left, Expression* right ) : \r\n\t\t\t\tExpression (type, left, right) { }\r\n\t\r\n};\r\n\r\nclass Num : public Expression{\r\npublic:\t\t\r\n\tdouble val;\r\n\tdouble accept (Visitor* v) { return v-\u003evisit(this); }\r\n\tNum(std::string type, double val): \r\n        Expression (type), val (val){}\t\r\n};\r\n\r\n#endif\r\n\r\n```\r\n2. 定义观察者类\r\nVisitor为父类接口，EvalVisitor和PostfixVisitor继承于Visitor并实现了父类的纯虚函数。\r\n```c++\r\n#ifndef _VISITOR_H\r\n#define _VISITOR_H\r\nclass AstNode;\r\nclass BinOp;\r\nclass Expression;\r\nclass Num;\r\n\r\nclass Visitor{\r\npublic:\r\n\tvirtual double visit (Expression* n) = 0;\r\n\tvirtual double visit (Num* n) = 0;\r\n};\r\n\r\nclass EvalVisitor : public Visitor{\t\r\npublic:\r\n\tvirtual double visit (Expression* n);\r\n\tvirtual double visit (Num* n);\r\n};\r\n\r\nclass PostfixVisitor : public Visitor{\r\npublic:\r\n\tvirtual double visit (Expression* n);\r\n\tvirtual double visit (Num* n);\r\n};\r\n#endif\r\n```\r\n在Visitor.cpp中，实现了EvalVisitor和PostfixVisitor类中的visit函数。\r\n在主函数中，我们可以使用观察者模式对表达式进行分析：\r\n```c++\r\n/* In driver.cpp */\r\n/* Parse */\r\nExpression *root;\r\nyy::parser parser(\u0026root);\r\nparser.parse();\r\nif(root == NULL){\r\n    std::cerr \u003c\u003c \"error!!\" \u003c\u003c std::endl;\r\n    exit(0);\r\n}\r\n\r\n/* Analyse Expression */\r\nif(eval == 1){\r\n    EvalVisitor* ev = new EvalVisitor();\r\n    double v = root-\u003eaccept(ev);\r\n    std::cout \u003c\u003c \"By Visitor Pattern: Value=\" \u003c\u003c v \u003c\u003c std::endl;\r\n}\r\nif(postfix == 1){\r\n    PostfixVisitor* pv = new PostfixVisitor();\r\n    std::cout \u003c\u003c \"Postfix Expression:\";\r\n    root-\u003eaccept(pv);\r\n    std::cout \u003c\u003c std::endl;\t\r\n}\r\n```\r\n## 3.1 使用方法\r\n- 安装flex和bison\r\n`sudo apt install flex bison`\r\n- 进入目录并编译\r\n`cd $YOUR_PATH/cppbisonsample \u0026\u0026 make`\r\n- 运行程序\r\n**命令行参数：**\r\n-e: 分析表达式并求值\r\n-p: 输出后缀表达式\r\n-h: 输出帮助信息\r\n`./target -[hep]`\r\n\r\n## 3.2 实验环境\r\nlinux ubuntu-18.04（本机使用wsl）, g++-7.5.0, flex-2.6.4, bison-3.0.4","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FElnath-123%2FSimpleCppBison","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FElnath-123%2FSimpleCppBison","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FElnath-123%2FSimpleCppBison/lists"}