Compare commits

...

10 Commits

Author SHA1 Message Date
Thomas Avé d1b51d49c1 Remove TODO 2017-01-31 17:30:24 +01:00
Robin Jadoul 47039e67ab Cleaner error message for lexer errors 2017-01-31 16:59:47 +01:00
Thomas Avé 87fbee24a3 Update TODO 2017-01-31 16:46:41 +01:00
Thomas Avé 285bc87ecb Added test for LR(0) 2017-01-31 16:44:10 +01:00
Robin Jadoul e4647f0ed4 bf rewrite, fixes tests 2017-01-31 16:35:27 +01:00
Robin Jadoul f4bba7dd5e Fix BF segfault 2017-01-31 15:23:42 +01:00
Robin Jadoul 5f723f1774 Fix troubles with clang 2017-01-31 15:03:08 +01:00
Robin Jadoul 27e95a427c Fix bf004.bf 2017-01-31 14:04:33 +01:00
Robin Jadoul 73a16ae922 Document debugging 2017-01-31 13:48:19 +01:00
Thomas Avé f0690690ff Update TODO 2017-01-30 22:29:35 +01:00
15 changed files with 351 additions and 169 deletions

View File

@ -9,7 +9,6 @@ A language agnostic parser generator
* [Building](#building) * [Building](#building)
* [Getting started](#getting-started) * [Getting started](#getting-started)
* [More examples](#more-examples) * [More examples](#more-examples)
* [Tested with](#tested-with)
* [Authors](#authors) * [Authors](#authors)
* [License](#license) * [License](#license)
@ -126,6 +125,14 @@ Comments are from a `#` to the end of the line.
Of course, how you use the generated parser highly depends on which backend you used to generate it. For the default c++ backend however, the easiest way of getting to know the parser is probably having a look at the class definition in the generated header file, usually named *<Parsername>.h*. Of course, how you use the generated parser highly depends on which backend you used to generate it. For the default c++ backend however, the easiest way of getting to know the parser is probably having a look at the class definition in the generated header file, usually named *<Parsername>.h*.
In general, there should be some way to run the parser, along with user defined actions, and get back the generated structure or abstract syntax tree. In general, there should be some way to run the parser, along with user defined actions, and get back the generated structure or abstract syntax tree.
### Debugging the parser
Nobody is perfect, so it is unlikely a grammar will always be completely conflict free from the very first time. It will happen on several occasions that Parsodus will notify you of a problem with your and you're unable to simply see where things go wrong. Or maybe the parser is behaving differently than you would expect. That's why there is a command line option to Parsodus that allows you to inspect the decisions made by the generator.
For the *LR* based parsers, that means that the information about the action and goto table is written, along with information about the configuration sets leading to the actual parser states. Conflicting actions are indicated with a leading exclamation mark, to be able to easily find the exact source of your troubles.
`Parsodus --debug` emits a *debug.log* file to the same output directory as the generated parser.
### Error recovery ### Error recovery
If you provide a rule with `<error>` somewhere inside the body, this can be used to recover from errors. The generated parser will discard states until it encounters somewhere it can use such a rule. Afterwards, it will start discarding tokens until it can proceed in parsing. If it's impossible to recover from the error (cannot find a recovery rule or cannot find a synchronizing token before EOF), there will generally be an error (depending on the backend). Usually, some sort of reporting mechanism should also be in place by the generated parser. If you provide a rule with `<error>` somewhere inside the body, this can be used to recover from errors. The generated parser will discard states until it encounters somewhere it can use such a rule. Afterwards, it will start discarding tokens until it can proceed in parsing. If it's impossible to recover from the error (cannot find a recovery rule or cannot find a synchronizing token before EOF), there will generally be an error (depending on the backend). Usually, some sort of reporting mechanism should also be in place by the generated parser.

40
TODO
View File

@ -1,40 +0,0 @@
-----------------------------------------------------------------------
- TODO -
-----------------------------------------------------------------------
T> Lexesis input file
-> Unit tests
#K 1 LR(1) + LALR(1)
#T 1 LR(0)
-> Table generator (independent of specific tables)???
-> BF tests
-----------------------------------------------------------------------
- MOSTLY DONE -
-----------------------------------------------------------------------
R> README: bash completion + precedence + error recovery + debug
-----------------------------------------------------------------------
- DONE -
-----------------------------------------------------------------------
-> publication / LICENSE
-> presentation
K> Reading terminals from lexesis + checking for unknown symbols
R> simple calculator
K> Parsodus Parsodus parser
-> Precedence + fill precedence in rules
-> rule naming (problem: multiple rules same name -> change in backend)
-> grammar struct change to contain optional name per rule
-> Volgorde belangrijk?
-> logging
-> write configuration sets, table
-> Generator: logging
-> driver/main: debug flag
-> Error reporting
-> bool specialization in backend?
T> Parsodus regex parser in Lexesis
-> Vrijgeven in libraryformaat: mogelijkheid verschillende tokens opvragen
R> Precedence resolution in generator
R> man pages
R> bash completion
R> Generated code error handler
T> Parser selection without enum (Thomas) (flexible matching (case insensitive, ...))

View File

@ -30,54 +30,9 @@ namespace bf {
void Generator::run() { void Generator::run() {
counter = 0; std::size_t data_ptr = 0;
std::memset(array, 0, sizeof(array)); for (auto& i : m_program)
while (!lbrackets.empty()) i->execute(m_data, data_ptr);
lbrackets.pop();
for(int instruct = 0; instruct < program.size(); instruct++) {
switch(program[instruct]) {
case Instruction::EMPTY: break;
case Instruction::PLUS:
array[counter]++;
break;
case Instruction::MINUS:
array[counter]--;
break;
case Instruction::GREATER:
if (++counter == 30000) {
std::cout << "Tried to access unavailable memory" << std::endl;
return;
} break;
case Instruction::LESS:
if (--counter == -1) {
std::cout << "Tried to access unavailable memory" << std::endl;
return;
} break;
case Instruction::POINT:
std::cout << (char)array[counter];
break;
case Instruction::COMMA:
char c;
std::cin >> c;
array[counter] = (int)c;
break;
case Instruction::LBRACKET:
lbrackets.push(instruct);
if (array[counter] == 0)
while (program[++instruct] != Instruction::RBRACKET) {}
break;
case Instruction::RBRACKET:
if (array[counter] != 0)
instruct = lbrackets.top();
else
lbrackets.pop();
break;
default: // Won't happen
break;
}
}
} }

View File

@ -33,17 +33,14 @@ namespace bf {
class Generator { class Generator {
public: public:
Generator(std::deque<Instruction> program) : Generator(std::deque<std::unique_ptr<Instruction>>&& program) :
program(program) { } m_program(std::move(program)) { }
void run(); void run();
private: private:
std::deque<Instruction> program; std::deque<std::unique_ptr<Instruction>> m_program;
int array[30000]; std::array<int, 30000> m_data;
int counter;
std::stack<int> lbrackets;
}; };
} // namespace bf } // namespace bf

View File

@ -25,18 +25,118 @@
#ifndef INSTRUCTION_H #ifndef INSTRUCTION_H
#define INSTRUCTION_H #define INSTRUCTION_H
#include <array>
#include <deque>
#include <iostream>
#include <memory>
namespace bf { namespace bf {
enum class Instruction { using Data = std::array<int, 30000>;
EMPTY,
PLUS, class Instruction {
MINUS, public:
GREATER, virtual void execute(Data& d, std::size_t& data_ptr) const = 0;
LESS, virtual void print() const = 0;
POINT, };
COMMA,
LBRACKET, class InstructionRight : public Instruction {
RBRACKET public:
void execute(Data&, std::size_t& data_ptr) const {
data_ptr++;
if (data_ptr >= 30000) {
std::cerr << "Out of bounds" << std::endl;
std::exit(1);
}
};
void print() const {
std::cout << ">";
}
};
class InstructionLeft : public Instruction {
public:
void execute(Data&, std::size_t& data_ptr) const {
data_ptr--;
if (data_ptr >= 30000) { //wraps around when below 0
std::cerr << "Out of bounds" << std::endl;
std::exit(1);
}
};
void print() const {
std::cout << "<";
}
};
class InstructionInc : public Instruction {
public:
void execute(Data& d, std::size_t& ptr) const {
d[ptr]++;
}
void print() const {
std::cout << "+";
}
};
class InstructionDec : public Instruction {
public:
void execute(Data& d, std::size_t& ptr) const {
d[ptr]--;
}
void print() const {
std::cout << "-";
}
};
class InstructionOut : public Instruction {
public:
void execute(Data& d, std::size_t& ptr) const {
std::cout << static_cast<char>(d[ptr]);
}
void print() const {
std::cout << ".";
}
};
class InstructionIn : public Instruction {
public:
void execute(Data& d, std::size_t& ptr) const {
char c = -1;
if (!std::cin.eof())
std::cin.get(c);
d[ptr] = c;
}
void print() const {
std::cout << ",";
}
};
class InstructionLoop : public Instruction {
public:
InstructionLoop(std::deque<std::unique_ptr<Instruction>>&& body) : m_body(std::move(body)) {};
void execute(Data& d, std::size_t& ptr) const {
while (d[ptr] != 0) {
for (auto& b : m_body)
b->execute(d, ptr);
}
}
void print() const {
std::cout << "[";
for (auto& b : m_body)
b->print();
std::cout << "]";
}
private:
std::deque<std::unique_ptr<Instruction>> m_body;
}; };
} }

View File

@ -31,11 +31,15 @@ using namespace std;
using namespace bf; using namespace bf;
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc != 2) {
std::cerr << "Usage: bf <file.bf>" << std::endl;
std::exit(1);
}
ifstream file(argv[1]); ifstream file(argv[1]);
BfLexer lex(file); BfLexer lex(file);
Parser parser(lex); Parser parser(lex);
Generator gen(*(parser.parse())); Generator gen(std::move(*(parser.parse().release())));
gen.run(); gen.run();
return 0; return 0;

View File

@ -29,85 +29,75 @@
namespace bf { namespace bf {
Parser::Parser(BfLexer lex) : Parser::Parser(BfLexer lex) :
bfParser<std::unique_ptr<std::deque<Instruction>>>(), m_lex(lex) { bfParser<std::unique_ptr<std::deque<std::unique_ptr<Instruction>>>>(), m_lex(lex) {
} }
Parser::Token Parser::lex() { Parser::Token Parser::lex() {
try { try {
BfLexer::Token orig = m_lex.nextToken(); BfLexer::Token orig = m_lex.nextToken();
std::deque<Instruction> pm; auto pm = std::make_unique<std::deque<std::unique_ptr<Instruction>>>();
switch(orig.type) { switch(orig.type) {
case BfLexer::PLUS: { case BfLexer::PLUS: {
pm.push_back(Instruction::PLUS); pm->push_back(std::make_unique<InstructionInc>());
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); //std::cout << "PARSING: found PLUS " << (int)std::unique_ptr<Instruction>::PLUS << std::endl;
//std::cout << "PARSING: found PLUS " << (int)Instruction::PLUS << std::endl; return Token{ bfParser_Symbol::T_PLUS, std::move(pm) };
return Token{ bfParser_Symbol::T_PLUS, std::move(p) };
} case BfLexer::MINUS: { } case BfLexer::MINUS: {
pm.push_back(Instruction::MINUS); pm->push_back(std::make_unique<InstructionDec>());
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); //std::cout << "PARSING: found MINUS" << (int)std::unique_ptr<Instruction>::MINUS << std::endl;
//std::cout << "PARSING: found MINUS" << (int)Instruction::MINUS << std::endl; return Token{ bfParser_Symbol::T_MINUS, std::move(pm) };
return Token{ bfParser_Symbol::T_MINUS, std::move(p) };
} case BfLexer::GREATER: { } case BfLexer::GREATER: {
pm.push_back(Instruction::GREATER); pm->push_back(std::make_unique<InstructionRight>());
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); //std::cout << "PARSING: found GREATER" << (int)std::unique_ptr<Instruction>::GREATER << std::endl;
//std::cout << "PARSING: found GREATER" << (int)Instruction::GREATER << std::endl; return Token{ bfParser_Symbol::T_GREATER, std::move(pm) };
return Token{ bfParser_Symbol::T_GREATER, std::move(p) };
} case BfLexer::LESS: { } case BfLexer::LESS: {
pm.push_back(Instruction::LESS); pm->push_back(std::make_unique<InstructionLeft>());
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); //std::cout << "PARSING: found LESS" << (int)std::unique_ptr<Instruction>::LESS << std::endl;
//std::cout << "PARSING: found LESS" << (int)Instruction::LESS << std::endl; return Token{ bfParser_Symbol::T_LESS, std::move(pm) };
return Token{ bfParser_Symbol::T_LESS, std::move(p) };
} case BfLexer::POINT: { } case BfLexer::POINT: {
pm.push_back(Instruction::POINT); pm->push_back(std::make_unique<InstructionOut>());
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); //std::cout << "PARSING: found POINT" << (int)std::unique_ptr<Instruction>::POINT << std::endl;
//std::cout << "PARSING: found POINT" << (int)Instruction::POINT << std::endl; return Token{ bfParser_Symbol::T_POINT, std::move(pm) };
return Token{ bfParser_Symbol::T_POINT, std::move(p) };
} case BfLexer::COMMA: { } case BfLexer::COMMA: {
pm.push_back(Instruction::COMMA); pm->push_back(std::make_unique<InstructionIn>());
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); //std::cout << "PARSING: found COMMA" << (int)std::unique_ptr<Instruction>::COMMA << std::endl;
//std::cout << "PARSING: found COMMA" << (int)Instruction::COMMA << std::endl; return Token{ bfParser_Symbol::T_COMMA, std::move(pm) };
return Token{ bfParser_Symbol::T_COMMA, std::move(p) };
} case BfLexer::LBRACKET: { } case BfLexer::LBRACKET: {
pm.push_back(Instruction::LBRACKET); //std::cout << "PARSING: found LBRACKET" << (int)std::unique_ptr<Instruction>::LBRACKET << std::endl;
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); return Token{ bfParser_Symbol::T_LBRACKET, nullptr };
//std::cout << "PARSING: found LBRACKET" << (int)Instruction::LBRACKET << std::endl;
return Token{ bfParser_Symbol::T_LBRACKET, std::move(p) };
} case BfLexer::RBRACKET: { } case BfLexer::RBRACKET: {
pm.push_back(Instruction::RBRACKET); //std::cout << "PARSING: found RBRACKET" << (int)std::unique_ptr<Instruction>::RBRACKET << std::endl;
std::unique_ptr<std::deque<Instruction>> p = std::make_unique<std::deque<Instruction>>(pm); return Token{ bfParser_Symbol::T_RBRACKET, nullptr };
//std::cout << "PARSING: found RBRACKET" << (int)Instruction::RBRACKET << std::endl;
return Token{ bfParser_Symbol::T_RBRACKET, std::move(p) };
} }
default:
return Token { bfParser_Symbol::T_EOF, nullptr };
} }
} catch(BfLexer::NoMoreTokens) { } catch(BfLexer::NoMoreTokens) {
return Token{ bfParser_Symbol::T_EOF, nullptr }; return Token{ bfParser_Symbol::T_EOF, nullptr };
} }
} }
std::unique_ptr<std::deque<Instruction>> Parser::reduce_PROGRAM(std::deque<Token> subparts) { std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> Parser::reduce_PROGRAM(std::deque<Token> subparts) {
// <program> ::= <instruction> <program> // <program> ::= <instruction> <program>
if (subparts[1].value == nullptr)
return std::move(subparts[0].value);
for(auto& instr : *(subparts[1].value)) for(auto& instr : *(subparts[1].value))
subparts[0].value->push_back(instr); subparts[0].value->push_back(std::move(instr));
return std::move(subparts[0].value); return std::move(subparts[0].value);
} }
std::unique_ptr<std::deque<Instruction>> Parser::reduce_EMPTY(std::deque<Token> subparts) { std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> Parser::reduce_EMPTY(std::deque<Token>) {
// <program> ::= // <program> ::=
return nullptr; return std::make_unique<std::deque<std::unique_ptr<Instruction>>>();
} }
std::unique_ptr<std::deque<Instruction>> Parser::reduce_OPERATION(std::deque<Token> subparts) { std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> Parser::reduce_OPERATION(std::deque<Token> subparts) {
// <instruction> ::= "PLUS" | "MINUS" | ... | "COMMA" // <instruction> ::= "PLUS" | "MINUS" | ... | "COMMA"
return std::move(subparts[0].value); return std::move(subparts[0].value);
} }
std::unique_ptr<std::deque<Instruction>> Parser::reduce_LOOP(std::deque<Token> subparts) { std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> Parser::reduce_LOOP(std::deque<Token> subparts) {
// <instruction> ::= "LBRACKET" <program> "RBRACKET" // <instruction> ::= "LBRACKET" <program> "RBRACKET"
subparts[1].value->push_front(Instruction::LBRACKET); auto res = std::make_unique<std::deque<std::unique_ptr<Instruction>>>();
subparts[1].value->push_back(Instruction::RBRACKET); res->push_back(std::make_unique<InstructionLoop>(std::move(*subparts[1].value.release())));
return res;
//std::cout << "PARSING: found LOOP" << std::endl; //std::cout << "PARSING: found LOOP" << std::endl;
//for(auto& s : *(subparts[1].value)) std::cout << "LOOP: " << (int)s << std::endl; //for(auto& s : *(subparts[1].value)) std::cout << "LOOP: " << (int)s << std::endl;
return std::move(subparts[1].value);
} }

View File

@ -33,17 +33,17 @@
namespace bf { namespace bf {
class Parser : public bfParser<std::unique_ptr<std::deque<Instruction>>> { class Parser : public bfParser<std::unique_ptr<std::deque<std::unique_ptr<Instruction>>>> {
public: public:
Parser(BfLexer lex); Parser(BfLexer lex);
protected: protected:
Token lex() override; Token lex() override;
std::unique_ptr<std::deque<Instruction>> reduce_PROGRAM(std::deque<Token> subparts) override; std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> reduce_PROGRAM(std::deque<Token> subparts) override;
std::unique_ptr<std::deque<Instruction>> reduce_EMPTY(std::deque<Token> subparts) override; std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> reduce_EMPTY(std::deque<Token> subparts) override;
std::unique_ptr<std::deque<Instruction>> reduce_OPERATION(std::deque<Token> subparts) override; std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> reduce_OPERATION(std::deque<Token> subparts) override;
std::unique_ptr<std::deque<Instruction>> reduce_LOOP(std::deque<Token> subparts) override; std::unique_ptr<std::deque<std::unique_ptr<Instruction>>> reduce_LOOP(std::deque<Token> subparts) override;
private: private:
BfLexer m_lex; BfLexer m_lex;

View File

@ -131,15 +131,15 @@ Parser::Value Parser::reduce_opt_arguments(std::deque<Token> subparts) {
} }
Parser::Value Parser::reduce_arguments(std::deque<Token> subparts) { Parser::Value Parser::reduce_arguments(std::deque<Token> subparts) {
std::unique_ptr<FunctionArguments> args; FunctionArguments* args;
if (subparts.size() > 1) { if (subparts.size() > 1) {
args = std::unique_ptr<FunctionArguments>(dynamic_cast<FunctionArguments*>(subparts[0].value.get())); args = dynamic_cast<FunctionArguments*>(subparts[0].value.get());
subparts[0].value.release(); subparts[0].value.release();
} else { } else {
args = std::make_unique<FunctionArguments>(); args = new FunctionArguments();
} }
args->push_back(std::move(subparts[subparts.size() - 1].value)); args->push_back(std::move(subparts[subparts.size() - 1].value));
return args; return std::unique_ptr<AST>(args);
} }
Parser::Value Parser::reduce_functiondef(std::deque<Token> subparts) { Parser::Value Parser::reduce_functiondef(std::deque<Token> subparts) {
@ -164,15 +164,15 @@ Parser::Value Parser::reduce_opt_idents(std::deque<Token> subparts) {
} }
Parser::Value Parser::reduce_idents(std::deque<Token> subparts) { Parser::Value Parser::reduce_idents(std::deque<Token> subparts) {
std::unique_ptr<FormalParameters> args; FormalParameters* args;
if (subparts.size() > 1) { if (subparts.size() > 1) {
args = std::unique_ptr<FormalParameters>(dynamic_cast<FormalParameters*>(subparts[0].value.get())); args = dynamic_cast<FormalParameters*>(subparts[0].value.get());
subparts[0].value.release(); subparts[0].value.release();
} else { } else {
args = std::make_unique<FormalParameters>(); args = new FormalParameters();
} }
args->push_back(std::move(subparts[subparts.size() - 1].value)); args->push_back(std::move(subparts[subparts.size() - 1].value));
return args; return std::unique_ptr<AST>(args);
} }
Parser::Value Parser::reduce_assign(std::deque<Token> subparts) { Parser::Value Parser::reduce_assign(std::deque<Token> subparts) {

View File

@ -22,6 +22,7 @@
*/ */
#include "Parsodus/parser.h" #include "Parsodus/parser.h"
#include "g3log/g3log.hpp"
#include <deque> #include <deque>
namespace pds { namespace pds {
@ -121,7 +122,10 @@ Parser::Token Parser::lex() {
} catch(ParsodusLexer::NoMoreTokens) { } catch(ParsodusLexer::NoMoreTokens) {
return Token{ parsodusParser_Symbol::T_EOF, nullptr }; return Token{ parsodusParser_Symbol::T_EOF, nullptr };
} } catch (ParsodusLexer::NoMatch) {
LOG(WARNING) << "Unrecognized character: " << m_lex.peek() << std::endl;
throw SyntaxError("Unrecognized character");
}
} }
std::unique_ptr<Config> Parser::reduce_0(std::deque<Token> subparts) { std::unique_ptr<Config> Parser::reduce_0(std::deque<Token> subparts) {

View File

@ -0,0 +1,44 @@
[ This program prints "Hello World!" and a newline to the screen, its
length is 106 active command characters. [It is not the shortest.]
This loop is an "initial comment loop", a simple way of adding a comment
to a BF program such that you don't have to worry about any command
characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
ignored, the "[" and "]" characters just have to be balanced. This
loop and the commands it contains are ignored because the current cell
defaults to a value of 0; the 0 value causes this loop to be skipped.
]
++++++++ Set Cell #0 to 8
[
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
[ as the cell will be cleared by the loop
>++ Add 2 to Cell #2
>+++ Add 3 to Cell #3
>+++ Add 3 to Cell #4
>+ Add 1 to Cell #5
<<<<- Decrement the loop counter in Cell #1
] Loop till Cell #1 is zero; number of iterations is 4
>+ Add 1 to Cell #2
>+ Add 1 to Cell #3
>- Subtract 1 from Cell #4
>>+ Add 1 to Cell #6
[<] Move back to the first zero cell you find; this will
be Cell #1 which was cleared by the previous loop
<- Decrement the loop Counter in Cell #0
] Loop till Cell #0 is zero; number of iterations is 8
The result of this is:
Cell No : 0 1 2 3 4 5 6
Contents: 0 0 72 104 88 32 8
Pointer : ^
>>. Cell #2 has value 72 which is 'H'
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
+++++++..+++. Likewise for 'llo' from Cell #3
>>. Cell #5 is 32 for the space
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
<. Cell #3 was set to 'o' from the end of 'Hello'
+++.------.--------. Cell #3 for 'rl' and 'd'
>>+. Add 1 to Cell #5 gives us an exclamation point
>++. And finally a newline from Cell #6

View File

@ -22,6 +22,7 @@
add_executable(Parsodus-test add_executable(Parsodus-test
lr0_shift-red.cpp lr0_shift-red.cpp
lr1_only.cpp lr1_only.cpp
lr0_only.cpp
) )
target_link_libraries(Parsodus-test target_link_libraries(Parsodus-test
# Parsodus-backends # Parsodus-backends

120
tests/lr0_only.cpp Normal file
View File

@ -0,0 +1,120 @@
/*
* Parsodus - A language agnostic parser generator
* Copyright © 2016-2017 Thomas Avé, Robin Jadoul, Kobe Wullaert
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "Parsodus/lrtables/generator.h"
#include "Parsodus/lrtables/LR0Itemset.h"
#include "Parsodus/util/symbols.h"
#include "gtest/gtest.h"
#include <memory>
TEST(lr0, only) {
using namespace pds;
using namespace pds::lr;
Grammar grammar;
grammar.start = "s";
grammar.variables = {"s","l",};
grammar.terminals = {"X", "LBRACE", "RBRACE", "COMMA"};
for (const std::pair<std::string, std::vector<std::string>>& p : std::vector<std::pair<std::string, std::vector<std::string>>>({
{"s", {"LBRACE", "l", "RBRACE"}},
{"s", {"X"}},
{"l", {"s"}},
{"l", {"l", "COMMA" ,"s"}},
})) {
grammar.rules.emplace_back(std::make_shared<Rule>(p.first, p.second));
}
{
Generator<LR0Itemset> g(grammar);
LRTable table;
ASSERT_NO_THROW(table = g.generate());
std::vector<std::map<std::string, std::pair<Action, std::size_t>>> act = {
{
{"LBRACE", {Action::SHIFT, 1}},
{"X", {Action::SHIFT, 2}}
}, {
{"LBRACE", {Action::SHIFT, 1}},
{"X", {Action::SHIFT, 2}},
}, {
{util::EOF_PLACEHOLDER, {Action::REDUCE, 1}},
{"COMMA", {Action::REDUCE, 1}},
{"LBRACE", {Action::REDUCE, 1}},
{"RBRACE", {Action::REDUCE, 1}},
{"X", {Action::REDUCE, 1}}
}, {
{util::EOF_PLACEHOLDER, {Action::ACCEPT, 0}}
}, {
{"COMMA", {Action::SHIFT, 6}},
{"RBRACE", {Action::SHIFT, 7}}
}, {
{util::EOF_PLACEHOLDER, {Action::REDUCE, 2}},
{"COMMA", {Action::REDUCE, 2}},
{"LBRACE", {Action::REDUCE, 2}},
{"RBRACE", {Action::REDUCE, 2}},
{"X", {Action::REDUCE, 2}},
}, {
{"LBRACE", {Action::SHIFT, 1}},
{"X", {Action::SHIFT, 2}}
}, {
{util::EOF_PLACEHOLDER, {Action::REDUCE, 0}},
{"COMMA", {Action::REDUCE, 0}},
{"LBRACE", {Action::REDUCE, 0}},
{"RBRACE", {Action::REDUCE, 0}},
{"X", {Action::REDUCE, 0}}
}, {
{util::EOF_PLACEHOLDER, {Action::REDUCE, 3}},
{"COMMA", {Action::REDUCE, 3}},
{"LBRACE", {Action::REDUCE, 3}},
{"RBRACE", {Action::REDUCE, 3}},
{"X", {Action::REDUCE, 3}}
}};
ASSERT_EQ(act.size(), table.act.size());
for (std::size_t i = 0; i < act.size(); i++) {
EXPECT_EQ(act[i], table.act[i]);
}
std::vector<std::map<std::string, size_t>> got = {
{ {"s", 3}},
{{"l", 4}, {"s", 5}},
{},
{},
{},
{},
{{"s", 8}},
{},
{}
};
ASSERT_EQ(got.size(), table.goto_.size());
for (std::size_t i = 0; i < got.size(); i++) {
EXPECT_EQ(got[i], table.goto_[i]);
}
}
}

View File

@ -38,7 +38,7 @@ TEST(lr1, only) {
grammar.variables = {"s","a","b"}; grammar.variables = {"s","a","b"};
grammar.terminals = {"A", "B"}; grammar.terminals = {"A", "B"};
for (const std::pair<std::string, std::vector<std::string>>& p : std::initializer_list<std::pair<std::string, std::vector<std::string>>>({ for (const std::pair<std::string, std::vector<std::string>>& p : std::vector<std::pair<std::string, std::vector<std::string>>>({
{"s", {"B", "a", "A"}}, {"s", {"B", "a", "A"}},
{"s", {"b", "A"}}, {"s", {"b", "A"}},
{"s", {"a"}}, {"s", {"a"}},