simpliest c++ example
This commit is contained in:
parent
d587646b32
commit
909a43114b
9 changed files with 73 additions and 221 deletions
30
README.md
30
README.md
|
|
@ -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
|
|
||||||
```
|
|
||||||
26
makefile
26
makefile
|
|
@ -5,28 +5,19 @@ FLEX = flex
|
||||||
|
|
||||||
TARGET = build/py2rb
|
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 $@
|
$(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
|
$(FLEX) --header-file=build/lexer.hpp -o build/lexer.cpp src/lexer.l
|
||||||
|
|
||||||
build/parser.hpp build/parser.cpp: src/parser.y build
|
build/%.o: build/%.cpp
|
||||||
$(BISON) -d -v -o build/parser.cpp src/parser.y
|
|
||||||
|
|
||||||
build/lexer.o: build/lexer.cpp build
|
|
||||||
$(CXX) -c $(ARGS) $< -o $@
|
$(CXX) -c $(ARGS) $< -o $@
|
||||||
|
|
||||||
build/parser.o: build/parser.cpp build
|
build/main.o: src/main.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 $@
|
$(CXX) -c $(ARGS) $< -o $@
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
|
@ -38,7 +29,4 @@ clean:
|
||||||
run: $(TARGET)
|
run: $(TARGET)
|
||||||
$(TARGET)
|
$(TARGET)
|
||||||
|
|
||||||
run_lexer: $(TARGET)
|
|
||||||
$(TARGET) -l
|
|
||||||
|
|
||||||
.PHONY: clean run
|
.PHONY: clean run
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
};
|
|
||||||
41
src/lexer.l
41
src/lexer.l
|
|
@ -1,28 +1,35 @@
|
||||||
%option reentrant noyywrap nounput
|
%option c++
|
||||||
|
%option noyywrap
|
||||||
|
%option yyclass="Scanner"
|
||||||
|
|
||||||
%{
|
%{
|
||||||
#include <iostream>
|
|
||||||
#include "../build/parser.hpp"
|
#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);
|
int yylex();
|
||||||
#define yyterminate() return yy::Parser::make_END(loc)
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
[0-9]+ { return yy::parser::token::TOK_NUMBER; }
|
||||||
%{
|
[a-zA-Z]+ { yylval->as<std::string>() = yytext; return yy::parser::token::TOK_WORD; }
|
||||||
loc.step();
|
[ \t\r\n]+ { /* skip */ }
|
||||||
%}
|
. { return yy::parser::token::TOK_CHAR; }
|
||||||
|
|
||||||
|
|
||||||
function {return yy::Parser::make_FUNCTION(loc);}
|
|
||||||
|
|
||||||
[ \n\r\t\v] {}
|
|
||||||
|
|
||||||
. {std::cerr << loc << ": " << "token error" << std::endl; exit(1);}
|
|
||||||
|
|
||||||
%%
|
%%
|
||||||
40
src/main.cpp
40
src/main.cpp
|
|
@ -1,41 +1,7 @@
|
||||||
#include "../build/parser.hpp"
|
#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);
|
int main()
|
||||||
|
|
||||||
void dump_tokens(ParserCtx &ctx)
|
|
||||||
{
|
{
|
||||||
while (true)
|
yy::parser p;
|
||||||
{
|
return p.parse();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
68
src/parser.y
68
src/parser.y
|
|
@ -1,51 +1,45 @@
|
||||||
%require "3.0.4"
|
|
||||||
%skeleton "lalr1.cc"
|
%skeleton "lalr1.cc"
|
||||||
|
%require "3.0"
|
||||||
%defines
|
%defines
|
||||||
%define api.prefix {yy}
|
|
||||||
%define api.parser.class {Parser}
|
|
||||||
%locations
|
|
||||||
%define api.token.constructor
|
|
||||||
%define api.value.type variant
|
%define api.value.type variant
|
||||||
%define parse.trace
|
%define api.parser.class { parser }
|
||||||
%define parse.error verbose
|
%define api.namespace { yy }
|
||||||
|
%define api.token.prefix {TOK_}
|
||||||
|
|
||||||
|
%code requires {
|
||||||
%code requires
|
#include <string>
|
||||||
{
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include "../src/ParserCtx.hpp"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%code
|
%code {
|
||||||
{
|
#include <iostream>
|
||||||
yy::Parser::symbol_type yylex(void* yyscanner, yy::location& loc);
|
|
||||||
|
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
|
input:
|
||||||
|
/* empty */
|
||||||
|
| input element
|
||||||
/* 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 }
|
|
||||||
|
|
||||||
|
|
||||||
|
element:
|
||||||
|
WORD { std::cout << "WORD: " << $1 << "\n"; }
|
||||||
|
| NUMBER { std::cout << "NUMBER\n"; }
|
||||||
|
| CHAR { std::cout << "CHAR\n"; }
|
||||||
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
%start program;
|
namespace yy {
|
||||||
|
void parser::error(const std::string& msg)
|
||||||
program: FUNCTION;
|
{
|
||||||
|
std::cerr << "Error: " << msg << "\n";
|
||||||
|
}
|
||||||
%%
|
|
||||||
|
|
||||||
void yy::Parser::error(const yy::location& l, const std::string& m)
|
|
||||||
{
|
|
||||||
std::cerr << l << ": " << m << std::endl;
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue