simpliest c++ example

This commit is contained in:
bronku 2025-11-20 16:35:10 +01:00
parent d587646b32
commit 909a43114b
9 changed files with 73 additions and 221 deletions

View file

@ -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
```

View file

@ -5,29 +5,20 @@ 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
build/parser.cpp build/parser.hpp: src/parser.y | build
$(BISON) -d -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/parser.hpp build/parser.cpp: src/parser.y build
$(BISON) -d -v -o build/parser.cpp src/parser.y
build/%.o: build/%.cpp
$(CXX) -c $(ARGS) $< -o $@
build/lexer.o: build/lexer.cpp build
$(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
@ -38,7 +29,4 @@ clean:
run: $(TARGET)
$(TARGET)
run_lexer: $(TARGET)
$(TARGET) -l
.PHONY: clean run

View file

@ -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;
}

View file

@ -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;
};

View file

@ -1,25 +0,0 @@
#include "config.hpp"
#include <unistd.h>
#include <stdexcept>
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");
}
}
}

View file

@ -1,12 +0,0 @@
#pragma once
#include <string>
class Configuration
{
public:
std::string input_file;
std::string output_file;
bool debug_lexer = false;
Configuration(int argc, char **argv);
};

View file

@ -1,28 +1,35 @@
%option reentrant noyywrap nounput
%option c++
%option noyywrap
%option yyclass="Scanner"
%{
#include <iostream>
#include "../build/parser.hpp"
#include <string>
#define YY_DECL yy::Parser::symbol_type yylex(yyscan_t yyscanner, yy::location& loc)
class Scanner : public yyFlexLexer {
public:
Scanner(std::istream* in) : yyFlexLexer(in) {}
#define YY_USER_ACTION loc.columns(yyleng);
#define yyterminate() return yy::Parser::make_END(loc)
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<std::string>() = yytext; return yy::parser::token::TOK_WORD; }
[ \t\r\n]+ { /* skip */ }
. { return yy::parser::token::TOK_CHAR; }
%%

View file

@ -1,41 +1,7 @@
#include "../build/parser.hpp"
#include "../build/lexer.hpp"
#include "../src/config.hpp"
#include "../src/ParserCtx.hpp"
#include <iostream>
#include <fstream>
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();
}

View file

@ -1,51 +1,45 @@
%require "3.0.4"
%skeleton "lalr1.cc"
%require "3.0"
%defines
%define api.prefix {yy}
%define api.parser.class {Parser}
%locations
%define api.token.constructor
%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 <list>
#include <string>
#include <functional>
#include "../src/ParserCtx.hpp"
%code requires {
#include <string>
}
%code
{
yy::Parser::symbol_type yylex(void* yyscanner, yy::location& loc);
%code {
#include <iostream>
int scanner_lex(yy::parser::semantic_type* yylval);
#undef yylex
#define yylex scanner_lex
}
%token <std::string> 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";
}
}