From 909a43114b5a246bb1f868d99dbe6eeca9a59694 Mon Sep 17 00:00:00 2001 From: bronku Date: Thu, 20 Nov 2025 16:35:10 +0100 Subject: [PATCH] simpliest c++ example --- README.md | 30 -------------------- makefile | 34 ++++++++-------------- src/ParserCtx.cpp | 18 ------------ src/ParserCtx.hpp | 18 ------------ src/config.cpp | 25 ---------------- src/config.hpp | 12 -------- src/lexer.l | 45 ++++++++++++++++------------- src/main.cpp | 40 ++------------------------ src/parser.y | 72 ++++++++++++++++++++++------------------------- 9 files changed, 73 insertions(+), 221 deletions(-) delete mode 100644 README.md delete mode 100644 src/ParserCtx.cpp delete mode 100644 src/ParserCtx.hpp delete mode 100644 src/config.cpp delete mode 100644 src/config.hpp diff --git a/README.md b/README.md deleted file mode 100644 index 94183a3..0000000 --- a/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Parser -## Explanation of options: -```cpp -// Bison version -%require "3.0.4" - -// skeleton program to be used as parser -%skeleton "lalr1.cc" - -// tells bison to generate a header file -%defines - -// where the parser will live (in what namespace) -%define api.prefix {yy} - -// the name of parser class -%define api.parser.class {Parser} - -/* it will generate a location class which can be used in your lexer */ -%locations - -/* use the constructor for each token, and make_TOKENNAME functions will be generated */ -%define api.token.constructor - -/* use the variant type for $1~$n and $$ those variables */ -%define api.value.type variant - -%define parse.trace -%define parse.error verbose -``` \ No newline at end of file diff --git a/makefile b/makefile index 941e96a..50e3f00 100644 --- a/makefile +++ b/makefile @@ -1,44 +1,32 @@ CXX = g++ -ARGS = -std=c++17 +ARGS = -std=c++17 BISON = /opt/homebrew/opt/bison/bin/bison FLEX = flex TARGET = build/py2rb -$(TARGET): build/lexer.o build/parser.o build/ParserCtx.o build/main.o build/config.o +$(TARGET): build/parser.o build/lexer.o build/main.o $(CXX) $^ -o $@ -build/lexer.hpp build/lexer.cpp: src/lexer.l build/parser.hpp build - $(FLEX) --header-file=build/lexer.hpp -o build/lexer.cpp src/lexer.l +build/parser.cpp build/parser.hpp: src/parser.y | build + $(BISON) -d -o build/parser.cpp src/parser.y -build/parser.hpp build/parser.cpp: src/parser.y build - $(BISON) -d -v -o build/parser.cpp src/parser.y +build/lexer.cpp build/lexer.hpp: src/lexer.l build/parser.hpp | build + $(FLEX) --header-file=build/lexer.hpp -o build/lexer.cpp src/lexer.l -build/lexer.o: build/lexer.cpp build - $(CXX) -c $(ARGS) $< -o $@ +build/%.o: build/%.cpp + $(CXX) -c $(ARGS) $< -o $@ -build/parser.o: build/parser.cpp build - $(CXX) -c $(ARGS) $< -o $@ - -build/ParserCtx.o: src/ParserCtx.cpp build - $(CXX) -c $(ARGS) $< -o $@ - -build/main.o: src/main.cpp build - $(CXX) -c $(ARGS) $< -o $@ - -build/config.o: src/config.cpp build - $(CXX) -c $(ARGS) $< -o $@ +build/main.o: src/main.cpp | build + $(CXX) -c $(ARGS) $< -o $@ build: mkdir -p build -clean: +clean: rm -rf build run: $(TARGET) $(TARGET) -run_lexer: $(TARGET) - $(TARGET) -l - .PHONY: clean run diff --git a/src/ParserCtx.cpp b/src/ParserCtx.cpp deleted file mode 100644 index 35ae95f..0000000 --- a/src/ParserCtx.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "ParserCtx.hpp" -#include "../build/lexer.hpp" -#include "../build/parser.hpp" - -ParserCtx::ParserCtx() -{ - yylex_init(&lexer); - // yyset_in() - loc = new yy::location(); - parser = new yy::Parser(lexer, *loc, *this); -} - -ParserCtx::~ParserCtx() -{ - yylex_destroy(lexer); - delete loc; - delete parser; -} diff --git a/src/ParserCtx.hpp b/src/ParserCtx.hpp deleted file mode 100644 index f4112bb..0000000 --- a/src/ParserCtx.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -namespace yy -{ - class Parser; - class location; -} - -class ParserCtx -{ -public: - ParserCtx(); - ~ParserCtx(); - - void *lexer; - yy::location *loc; - yy::Parser *parser; -}; diff --git a/src/config.cpp b/src/config.cpp deleted file mode 100644 index 429b9ac..0000000 --- a/src/config.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "config.hpp" -#include -#include - -Configuration::Configuration(int argc, char **argv) -{ - int opt; - while ((opt = getopt(argc, argv, "i:o:l")) != -1) - { - switch (opt) - { - case 'i': - input_file = optarg; - break; - case 'o': - output_file = optarg; - break; - case 'l': - debug_lexer = true; - break; - default: - throw std::runtime_error("Invalid arguments"); - } - } -} diff --git a/src/config.hpp b/src/config.hpp deleted file mode 100644 index eedb225..0000000 --- a/src/config.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include - -class Configuration -{ -public: - std::string input_file; - std::string output_file; - bool debug_lexer = false; - - Configuration(int argc, char **argv); -}; diff --git a/src/lexer.l b/src/lexer.l index 56bfb38..c89a0c2 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -1,28 +1,35 @@ -%option reentrant noyywrap nounput +%option c++ +%option noyywrap +%option yyclass="Scanner" %{ -#include #include "../build/parser.hpp" +#include -#define YY_DECL yy::Parser::symbol_type yylex(yyscan_t yyscanner, yy::location& loc) - -#define YY_USER_ACTION loc.columns(yyleng); -#define yyterminate() return yy::Parser::make_END(loc) +class Scanner : public yyFlexLexer { +public: + Scanner(std::istream* in) : yyFlexLexer(in) {} + + int yylex(); + int lex(yy::parser::semantic_type* yylval) { + this->yylval = yylval; + return yylex(); + } + +private: + yy::parser::semantic_type* yylval; +}; +int scanner_lex(yy::parser::semantic_type* yylval) { + static Scanner scanner(&std::cin); + return scanner.lex(yylval); +} %} %% - -%{ - loc.step(); -%} - - -function {return yy::Parser::make_FUNCTION(loc);} - -[ \n\r\t\v] {} - -. {std::cerr << loc << ": " << "token error" << std::endl; exit(1);} - -%% +[0-9]+ { return yy::parser::token::TOK_NUMBER; } +[a-zA-Z]+ { yylval->as() = yytext; return yy::parser::token::TOK_WORD; } +[ \t\r\n]+ { /* skip */ } +. { return yy::parser::token::TOK_CHAR; } +%% \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index b1cd9a6..61e64b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,41 +1,7 @@ #include "../build/parser.hpp" -#include "../build/lexer.hpp" -#include "../src/config.hpp" -#include "../src/ParserCtx.hpp" -#include -#include -yy::Parser::symbol_type yylex(yyscan_t yyscanner, yy::location &loc); - -void dump_tokens(ParserCtx &ctx) +int main() { - while (true) - { - auto tok = yylex(ctx.lexer, *ctx.loc); - - std::cout - << "Token: " - << yy::Parser::symbol_name(tok.kind()) - << ", Location: " << tok.location - << '\n'; - - if (tok.kind() == yy::Parser::symbol_kind::S_YYEOF) - { - break; - } - } -} - -int main(int argc, char *argv[]) -{ - ParserCtx ctx; - Configuration config(argc, argv); - - if (config.debug_lexer) - { - dump_tokens(ctx); - return 0; - } - - return ctx.parser->parse(); + yy::parser p; + return p.parse(); } \ No newline at end of file diff --git a/src/parser.y b/src/parser.y index c73c445..31721a0 100644 --- a/src/parser.y +++ b/src/parser.y @@ -1,51 +1,45 @@ -%require "3.0.4" -%skeleton "lalr1.cc" -%defines -%define api.prefix {yy} -%define api.parser.class {Parser} -%locations -%define api.token.constructor +%skeleton "lalr1.cc" +%require "3.0" +%defines %define api.value.type variant -%define parse.trace -%define parse.error verbose +%define api.parser.class { parser } +%define api.namespace { yy } +%define api.token.prefix {TOK_} - -%code requires -{ -#include -#include -#include -#include "../src/ParserCtx.hpp" +%code requires { + #include } -%code -{ -yy::Parser::symbol_type yylex(void* yyscanner, yy::location& loc); +%code { + #include + + int scanner_lex(yy::parser::semantic_type* yylval); + + #undef yylex + #define yylex scanner_lex } +%token WORD +%token NUMBER +%token CHAR -%token END 0; -%token FUNCTION - - -/* The driver is passed by reference to the parser and to the scanner. This - * provides a simple but effective pure interface, not relying on global - * variables. */ -%lex-param {void *scanner} {yy::location& loc} -%parse-param {void *scanner} {yy::location& loc} { class ParserCtx& ctx } - +%% +input: + /* empty */ + | input element + ; +element: + WORD { std::cout << "WORD: " << $1 << "\n"; } + | NUMBER { std::cout << "NUMBER\n"; } + | CHAR { std::cout << "CHAR\n"; } + ; %% -%start program; - -program: FUNCTION; - - -%% - -void yy::Parser::error(const yy::location& l, const std::string& m) -{ - std::cerr << l << ": " << m << std::endl; +namespace yy { + void parser::error(const std::string& msg) + { + std::cerr << "Error: " << msg << "\n"; + } } \ No newline at end of file