Skip to content

Commit

Permalink
v1.7 (#35)
Browse files Browse the repository at this point in the history
* closes #33

* added Werror

* error symbol is now the default rule

* update LHC

* correct spelling for cacheable

* update LHC

* update Glue

* update tests

* update CPM

* v1.7

* update CMake

* update Glue

* update Glue

* update readme

* update Glue

* upadate Glue
  • Loading branch information
TheLartians committed Apr 12, 2019
1 parent d51c390 commit 1fbd9d1
Show file tree
Hide file tree
Showing 15 changed files with 75 additions and 58 deletions.
17 changes: 11 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ endif()
# ---- Project ----

project(LarsParser
VERSION 1.6
VERSION 1.7
LANGUAGES CXX
)

Expand All @@ -23,19 +23,23 @@ option(BUILD_LARS_PARSER_GLUE_EXTENSION "Build LarsParser Glue extension" OFF)

# ---- Dependencies ----

if(${ENABLE_LARS_PARSER_TESTS})
set(BUILD_LARS_PARSER_GLUE_EXTENSION ON)
endif()

include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/CPM.cmake)

CPMAddPackage(
NAME LHC
GIT_REPOSITORY https://github.com/TheLartians/LHC.git
VERSION 0.4
VERSION 0.7
)

if(BUILD_LARS_PARSER_GLUE_EXTENSION)
if(${BUILD_LARS_PARSER_GLUE_EXTENSION})
CPMAddPackage(
NAME Glue
GIT_REPOSITORY https://github.com/TheLartians/Glue.git
VERSION 0.3
VERSION 0.5
)
endif()

Expand All @@ -62,16 +66,17 @@ SET(sources
"${CMAKE_CURRENT_SOURCE_DIR}/source/peg.cpp"
)

if(BUILD_LARS_PARSER_GLUE_EXTENSION)
if(${BUILD_LARS_PARSER_GLUE_EXTENSION})
LIST(APPEND headers "${CMAKE_CURRENT_SOURCE_DIR}/include/lars/parser/extension.h")
LIST(APPEND sources "${CMAKE_CURRENT_SOURCE_DIR}/source/extension.cpp")
endif()

add_library(LarsParser ${sources} ${headers})
set_target_properties(LarsParser PROPERTIES COMPILE_FLAGS "-Wall -pedantic -Wextra")

target_link_libraries(LarsParser PRIVATE LHC)

if(BUILD_LARS_PARSER_GLUE_EXTENSION)
if(${BUILD_LARS_PARSER_GLUE_EXTENSION})
target_link_libraries(LarsParser PUBLIC Glue)
endif()

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
lars::parser
============

A linear-time C++ parsing expression grammar (PEG) parser generator supporting left-recursion and ambiguous grammars. Written in C++17.
A linear-time C++ parsing expression grammar (PEG) parser generator supporting left-recursion and context-dependent grammars. Written in C++17.

Example
-------
Expand Down Expand Up @@ -50,7 +50,7 @@ With [CPM](https://github.com/TheLartians/CPM), lars::parser can be added to you
```cmake
CPMAddPackage(
NAME LarsParser
VERSION 1.6
VERSION 1.7
GIT_REPOSITORY https://github.com/TheLartians/Parser.git
)
Expand Down
1 change: 1 addition & 0 deletions cmake/CPM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function(CPMAddPackage)

if (${CPM_ARGS_NAME} IN_LIST CPM_PACKAGES)
message(STATUS "CPM: not adding ${CPM_ARGS_NAME}@${CPM_ARGS_GIT_TAG}: already addded package ${CPM_ARGS_NAME}")
return()
endif()

LIST(APPEND CPM_PACKAGES ${CPM_ARGS_NAME})
Expand Down
4 changes: 2 additions & 2 deletions examples/json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ lars::ParserGenerator<JSON> createJSONProgram(){

// Boolean
g["Boolean"] << "True | False";
g["True"] << "'true'" >> [](auto e){ return JSON(true); };
g["False"] << "'false'" >> [](auto e){ return JSON(false); };
g["True"] << "'true'" >> [](auto){ return JSON(true); };
g["False"] << "'false'" >> [](auto){ return JSON(false); };

// Array
g["Array"] << "'[' (JSON (',' JSON)*)? ']'" >> [](auto e){
Expand Down
18 changes: 9 additions & 9 deletions examples/python_indentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ int main() {
blockParser["Indentation"] << "' '*";

/** storage for indentation depths */
std::vector<int> indentations = {-1};
std::vector<unsigned> indentations;

/** initializer is necessarry to reset the state after syntax errors */
blockParser["InitBlocks"] << "''" << [&](auto &) -> bool{
indentations.resize(1); return true;
indentations.resize(0); return true;
};

/**
Expand All @@ -37,35 +37,35 @@ int main() {
blockParser["SameIndentation"] << "Indentation" << [&](auto &s) -> bool{
return s->length() == indentations.back();
};
blockParser["SameIndentation"]->cachable = false;
blockParser["SameIndentation"]->cacheable = false;

/** matches the a deeper block intendation */
blockParser["DeeperIndentation"] << "Indentation" << [&](auto &s) -> bool{
return int(s->length()) > indentations.back();
return s->length() > indentations.back();
};
blockParser["DeeperIndentation"]->cachable = false;
blockParser["DeeperIndentation"]->cacheable = false;

// enters a new block and stores the indentation
blockParser["EnterBlock"] << "Indentation" << [&](auto &s) -> bool{
if (int(s->length()) > indentations.back()){
if (indentations.size() == 0 || s->length() > indentations.back()){
indentations.push_back(s->length());
return true;
} else {
return false;
}
};
blockParser["EnterBlock"]->cachable = false;
blockParser["EnterBlock"]->cacheable = false;

/** matches a line in the current block */
blockParser["Line"] << "SameIndentation (!'\n' .)+ '\n'";
blockParser.getRule("Line")->cachable = false;
blockParser.getRule("Line")->cacheable = false;

/** matches an empty line */
blockParser["EmptyLine"] << "Indentation '\n'";

/** exits a block and pops the current indentation */
blockParser["ExitBlock"] << "''" << [&](auto &) -> bool{ indentations.pop_back(); return true; };
blockParser.getRule("ExitBlock")->cachable = false;
blockParser.getRule("ExitBlock")->cacheable = false;

/** store all successfully parsed blocks */
blockParser["Block"] << "&EnterBlock Line (EmptyLine | Block | Line)* &ExitBlock" >> [](auto e, Blocks &blocks){
Expand Down
2 changes: 1 addition & 1 deletion include/lars/parser/generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace lars {
if (it != rules.end()) {
return it->second;
}
auto rule = peg::makeRule(name, peg::GrammarNode::Empty());
auto rule = peg::makeRule(name, peg::GrammarNode::Error());
rules[name] = rule;
return rule;
}
Expand Down
4 changes: 3 additions & 1 deletion include/lars/parser/grammar.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace lars {
std::string name;
std::shared_ptr<GrammarNode> node;
bool hidden = false;
bool cachable = true;
bool cacheable = true;
Rule(const std::string_view &n, const std::shared_ptr<GrammarNode> &t):name(n), node(t){}
};

Expand All @@ -45,6 +45,7 @@ namespace lars {
ALSO,
NOT,
EMPTY,
ERROR,
RULE,
WEAK_RULE,
END_OF_FILE,
Expand Down Expand Up @@ -81,6 +82,7 @@ namespace lars {
static Shared Also(const Shared &arg){ return Shared(new GrammarNode(Symbol::ALSO, arg)); }
static Shared Not(const Shared &arg){ return Shared(new GrammarNode(Symbol::NOT, arg)); }
static Shared Empty(){ return Shared(new GrammarNode(Symbol::EMPTY)); }
static Shared Error(){ return Shared(new GrammarNode(Symbol::ERROR)); }
static Shared Rule(const std::shared_ptr<peg::Rule> &rule){ return Shared(new GrammarNode(Symbol::RULE, rule)); }
static Shared WeakRule(const std::weak_ptr<peg::Rule> &rule){ return Shared(new GrammarNode(Symbol::WEAK_RULE, rule)); }
static Shared EndOfFile(){ return Shared(new GrammarNode(Symbol::END_OF_FILE)); }
Expand Down
2 changes: 1 addition & 1 deletion include/lars/parser/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace lars {

std::shared_ptr<peg::Rule> grammar;

Parser(const std::shared_ptr<peg::Rule> &grammar = std::make_shared<peg::Rule>("undefined", peg::GrammarNode::Empty()));
Parser(const std::shared_ptr<peg::Rule> &grammar = std::make_shared<peg::Rule>("undefined", peg::GrammarNode::Error()));

static Result parseAndGetError(const std::string_view &str, std::shared_ptr<peg::Rule> grammar);
static std::shared_ptr<SyntaxTree> parse(const std::string_view &str, std::shared_ptr<peg::Rule> grammar);
Expand Down
5 changes: 5 additions & 0 deletions source/grammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ std::ostream & lars::peg::operator<<(std::ostream &stream, const GrammarNode &no
break;
}

case GrammarNode::Symbol::ERROR: {
stream << "<Error>";
break;
}

case GrammarNode::Symbol::RULE: {
auto rule = std::get<std::shared_ptr<Rule>>(node.data);
stream << rule->name;
Expand Down
8 changes: 6 additions & 2 deletions source/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ namespace {
INCREASE_INDENT;
PARSER_TRACE("enter rule " << rule->name);

if (useCache && rule->cachable) {
if (useCache && rule->cacheable) {
auto cached = state.getCached(rule);

if (cached) {
Expand Down Expand Up @@ -316,10 +316,14 @@ namespace {
return !result;
}

case lars::peg::GrammarNode::Symbol::EMPTY: {
case lars::peg::GrammarNode::Symbol::ERROR: {
return false;
}

case lars::peg::GrammarNode::Symbol::EMPTY: {
return true;
}

case lars::peg::GrammarNode::Symbol::RULE: {
const auto &rule = std::get<std::shared_ptr<peg::Rule>>(node->data);
return parseRule(rule, state)->valid;
Expand Down
12 changes: 8 additions & 4 deletions source/peg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,20 @@ peg::GrammarProgram peg::createGrammarProgram(){
return GN::Empty();
}));

auto error = GN::Rule(program.interpreter.makeRule("Error", GN::Word("<Error>"), [](auto,auto&){
return GN::Error();
}));

auto any = GN::Rule(program.interpreter.makeRule("Any", GN::Word("."), [](auto,auto&){
return GN::Any();
}));

auto selectCharacterProgram = createCharacterProgram();
auto selectCharacter = GN::Sequence({GN::Not(GN::Choice({GN::Word("-"),GN::Word("]")})), GN::Rule(selectCharacterProgram.parser.grammar)});
auto range = GN::Rule(program.interpreter.makeRule("Range", GN::Sequence({selectCharacter,GN::Word("-"),selectCharacter}), [interpreter=selectCharacterProgram.interpreter](auto e,auto &g){
auto range = GN::Rule(program.interpreter.makeRule("Range", GN::Sequence({selectCharacter,GN::Word("-"),selectCharacter}), [interpreter=selectCharacterProgram.interpreter](auto e,auto &){
return GN::Range(e[0].evaluateBy(interpreter), e[1].evaluateBy(interpreter));
}));
auto singeCharacter = GN::Rule(program.interpreter.makeRule("Character", selectCharacter, [interpreter=selectCharacterProgram.interpreter](auto e,auto &g){
auto singeCharacter = GN::Rule(program.interpreter.makeRule("Character", selectCharacter, [interpreter=selectCharacterProgram.interpreter](auto e,auto &){
return GN::Word(std::string(1,e[0].evaluateBy(interpreter)));
}));
auto selectSequence = GN::Sequence({GN::Word("["),GN::ZeroOrMore(GN::Choice({range, singeCharacter})),GN::Word("]")});
Expand All @@ -160,7 +164,7 @@ peg::GrammarProgram peg::createGrammarProgram(){
return GN::Choice(args);
}));

auto word = GN::Rule(program.interpreter.makeRule("Word", stringProgram.parser.grammar, [interpreter=stringProgram.interpreter](auto e,auto &g){
auto word = GN::Rule(program.interpreter.makeRule("Word", stringProgram.parser.grammar, [interpreter=stringProgram.interpreter](auto e,auto &){
return GN::Word(e[0].evaluateBy(interpreter));
}));

Expand All @@ -179,7 +183,7 @@ peg::GrammarProgram peg::createGrammarProgram(){
return GN::Not(e[0].evaluate(g));
}));

atomicRule->node = withWhitespace(GN::Choice({andPredicate, notPredicate, word, brackets, endOfFile, any, empty, select, rule}));
atomicRule->node = withWhitespace(GN::Choice({andPredicate, notPredicate, word, brackets, endOfFile, any, empty, error, select, rule}));

auto predicate = GN::Rule(makeRule("Predicate", GN::Choice({GN::Word("+"),GN::Word("*"),GN::Word("?")})));

Expand Down
5 changes: 2 additions & 3 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
find_package(LarsParser REQUIRED)
endif()

include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/get_catch2.cmake)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/catch2)

# ---- Create binary ----

file(GLOB tests_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
add_executable(lars-parser-tests ${tests_sources})
add_dependencies(lars-parser-tests catch2-project)
target_link_libraries(lars-parser-tests catch2)
target_link_libraries(lars-parser-tests Catch2)
target_link_libraries(lars-parser-tests LarsParser)

# ---- Add tests ----
Expand Down
23 changes: 23 additions & 0 deletions tests/catch2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)

include(FetchContent)

FetchContent_Declare(
catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v2.7.0
UPDATE_DISCONNECTED 1
QUIET
)

FetchContent_GetProperties(catch2)
if(NOT catch2_POPULATED)
FetchContent_Populate(catch2)
endif()

add_library(Catch2 INTERFACE)

target_include_directories(Catch2
INTERFACE
$<BUILD_INTERFACE:${catch2_SOURCE_DIR}/single_include>
)
27 changes: 0 additions & 27 deletions tests/cmake/get_catch2.cmake

This file was deleted.

1 change: 1 addition & 0 deletions tests/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ TEST_CASE("PEG Parser") {
REQUIRE(stream_to_string(*parser.run("[abc\\-d]",rc)) == "('a' | 'b' | 'c' | '-' | 'd')");
REQUIRE(stream_to_string(*parser.run("<EOF>",rc)) == "<EOF>");
REQUIRE(stream_to_string(*parser.run("<>",rc)) == "<>");
REQUIRE(stream_to_string(*parser.run("<Error>",rc)) == "<Error>");
REQUIRE(stream_to_string(*parser.run(".",rc)) == ".");
REQUIRE(stream_to_string(*parser.run("a b c",rc)) == "(a b c)");
REQUIRE(stream_to_string(*parser.run("a | b |\tc",rc)) == "(a | b | c)");
Expand Down

0 comments on commit 1fbd9d1

Please sign in to comment.