mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 11:55:30 +00:00
commit
78eb383fa1
49
.github/workflows/cmake.yml
vendored
Normal file
49
.github/workflows/cmake.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
name: CMake
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#configuring-a-build-matrix
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Create Build Environment
|
||||
# Some projects don't allow in-source building, so create a separate build directory
|
||||
# We'll use this as our working directory for all subsequent commands
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Configure CMake
|
||||
# Use a bash shell so we can use the same syntax for environment variable
|
||||
# access regardless of the host operating system
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
# Note the current convention is to use the -S and -B options here to specify source
|
||||
# and build directories, but this is only available with CMake 3.13 and higher.
|
||||
# The CMake binaries on the Github Actions machines are (as of this writing) 3.12
|
||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: ctest -C $BUILD_TYPE
|
33
.travis.yml
33
.travis.yml
@ -1,39 +1,20 @@
|
||||
language: cpp
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
- os: linux
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- george-edison55-precise-backports
|
||||
packages:
|
||||
- cmake
|
||||
- cmake-data
|
||||
- gcc-4.9
|
||||
- g++-4.9
|
||||
env: COMPILER=g++-4.9
|
||||
# - compiler: clang
|
||||
# addons:
|
||||
# apt:
|
||||
# sources:
|
||||
# - kubuntu-backports
|
||||
# - ubuntu-toolchain-r-test
|
||||
# - llvm-toolchain-precise-3.7
|
||||
# packages:
|
||||
# - cmake
|
||||
# - clang-3.7
|
||||
# env: COMPILER=clang++-3.7
|
||||
- g++-8
|
||||
env:
|
||||
- MATRIX_EVAL="CC=gcc-8 && CXX=g++-8"
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
before_script:
|
||||
- export CXX=$COMPILER
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- cmake .. && make && ctest -V
|
||||
- cmake .. && make && ./test/test-main
|
||||
|
@ -1,33 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
project("cpp-peglib")
|
||||
|
||||
# Check if a supported compiler is used to setup the C++ standard to use:
|
||||
get_property(known_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
|
||||
list(FIND known_features "cxx_std_17" found)
|
||||
if(NOT ${found} EQUAL -1)
|
||||
# C++17 standard is supported
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
else()
|
||||
# Check for C++11 standard support
|
||||
list(FIND known_features "cxx_std_11" found)
|
||||
if(NOT ${found} EQUAL -1)
|
||||
# C++11 standard is supported
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${found} EQUAL -1)
|
||||
message(FATAL_ERROR "Your compiler is not supported.")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything -Wno-c++98-compat -Wno-padded -Wno-weak-vtables -Wno-exit-time-destructors -Wno-c++2a-compat -Wno-switch-enum -Wno-c++98-compat-pedantic")
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Wextra -Woverloaded-virtual")
|
||||
elseif(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /wd4503 /wd4512 /utf-8")
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus /utf-8")
|
||||
endif()
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
@ -37,10 +15,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(add_link_deps Threads::Threads)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(example)
|
||||
add_subdirectory(lint)
|
||||
|
||||
add_subdirectory(test)
|
||||
enable_testing()
|
||||
|
||||
install(FILES peglib.h DESTINATION include)
|
||||
|
273
README.md
273
README.md
@ -1,10 +1,13 @@
|
||||
cpp-peglib
|
||||
==========
|
||||
|
||||
[![](https://github.com/yhirose/cpp-peglib/workflows/CMake/badge.svg)](https://github.com/yhirose/cpp-peglib/actions)
|
||||
[![Build Status](https://travis-ci.org/yhirose/cpp-peglib.svg?branch=master)](https://travis-ci.org/yhirose/cpp-peglib)
|
||||
[![Bulid Status](https://ci.appveyor.com/api/projects/status/github/yhirose/cpp-peglib?branch=master&svg=true)](https://ci.appveyor.com/project/yhirose/cpp-peglib)
|
||||
|
||||
C++11 header-only [PEG](http://en.wikipedia.org/wiki/Parsing_expression_grammar) (Parsing Expression Grammars) library. You can start using it right away just by including `peglib.h` in your project.
|
||||
C++17 header-only [PEG](http://en.wikipedia.org/wiki/Parsing_expression_grammar) (Parsing Expression Grammars) library. You can start using it right away just by including `peglib.h` in your project.
|
||||
|
||||
Since this library only supports C++17 compilers, please make sure that compiler the option `-std=c++17` is enabled. (`/std:c++17 /Zc:__cplusplus` for MSVC)
|
||||
|
||||
You can also try the online version, PEG Playground at https://yhirose.github.io/cpp-peglib.
|
||||
|
||||
@ -44,8 +47,8 @@ using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
int main(void) {
|
||||
// (2) Make a parser
|
||||
parser parser(R"(
|
||||
// (2) Make a parser
|
||||
parser parser(R"(
|
||||
# Grammar for Calculator...
|
||||
Additive <- Multitive '+' Additive / Multitive
|
||||
Multitive <- Primary '*' Multitive / Primary
|
||||
@ -54,38 +57,38 @@ int main(void) {
|
||||
%whitespace <- [ \t]*
|
||||
)");
|
||||
|
||||
assert((bool)parser == true);
|
||||
assert(static_cast<bool>(parser) == true);
|
||||
|
||||
// (3) Setup actions
|
||||
parser["Additive"] = [](const SemanticValues& sv) {
|
||||
switch (sv.choice()) {
|
||||
case 0: // "Multitive '+' Additive"
|
||||
return any_cast<int>(sv[0]) + any_cast<int>(sv[1]);
|
||||
default: // "Multitive"
|
||||
return any_cast<int>(sv[0]);
|
||||
}
|
||||
};
|
||||
// (3) Setup actions
|
||||
parser["Additive"] = [](const SemanticValues &vs) {
|
||||
switch (vs.choice()) {
|
||||
case 0: // "Multitive '+' Additive"
|
||||
return any_cast<int>(vs[0]) + any_cast<int>(vs[1]);
|
||||
default: // "Multitive"
|
||||
return any_cast<int>(vs[0]);
|
||||
}
|
||||
};
|
||||
|
||||
parser["Multitive"] = [](const SemanticValues& sv) {
|
||||
switch (sv.choice()) {
|
||||
case 0: // "Primary '*' Multitive"
|
||||
return any_cast<int>(sv[0]) * any_cast<int>(sv[1]);
|
||||
default: // "Primary"
|
||||
return any_cast<int>(sv[0]);
|
||||
}
|
||||
};
|
||||
parser["Multitive"] = [](const SemanticValues &vs) {
|
||||
switch (vs.choice()) {
|
||||
case 0: // "Primary '*' Multitive"
|
||||
return any_cast<int>(vs[0]) * any_cast<int>(vs[1]);
|
||||
default: // "Primary"
|
||||
return any_cast<int>(vs[0]);
|
||||
}
|
||||
};
|
||||
|
||||
parser["Number"] = [](const SemanticValues& sv) {
|
||||
return stoi(sv.token(), nullptr, 10);
|
||||
};
|
||||
parser["Number"] = [](const SemanticValues &vs) {
|
||||
return vs.token_to_number<int>();
|
||||
};
|
||||
|
||||
// (4) Parse
|
||||
parser.enable_packrat_parsing(); // Enable packrat parsing.
|
||||
// (4) Parse
|
||||
parser.enable_packrat_parsing(); // Enable packrat parsing.
|
||||
|
||||
int val;
|
||||
parser.parse(" (1 + 2) * 3 ", val);
|
||||
int val;
|
||||
parser.parse(" (1 + 2) * 3 ", val);
|
||||
|
||||
assert(val == 9);
|
||||
assert(val == 9);
|
||||
}
|
||||
```
|
||||
|
||||
@ -104,7 +107,7 @@ auto grammar = R"(
|
||||
parser parser;
|
||||
|
||||
parser.log = [](size_t line, size_t col, const string& msg) {
|
||||
cerr << line << ":" << col << ": " << msg << "\n";
|
||||
cerr << line << ":" << col << ": " << msg << "\n";
|
||||
};
|
||||
|
||||
auto ok = parser.load_grammar(grammar);
|
||||
@ -114,10 +117,10 @@ assert(ok);
|
||||
There are four semantic actions available:
|
||||
|
||||
```cpp
|
||||
[](const SemanticValues& sv, any& dt)
|
||||
[](const SemanticValues& sv)
|
||||
[](SemanticValues& sv, any& dt)
|
||||
[](SemanticValues& sv)
|
||||
[](const SemanticValues& vs, any& dt)
|
||||
[](const SemanticValues& vs)
|
||||
[](SemanticValues& vs, any& dt)
|
||||
[](SemanticValues& vs)
|
||||
```
|
||||
|
||||
`SemanticValues` value contains the following information:
|
||||
@ -129,48 +132,36 @@ There are four semantic actions available:
|
||||
|
||||
`any& dt` is a 'read-write' context data which can be used for whatever purposes. The initial context data is set in `peg::parser::parse` method.
|
||||
|
||||
`peg::any` is a simpler implementatin of std::any. If the compiler in use supports C++17, by default `peg::any` is defined as an alias to `std::any`.
|
||||
|
||||
To force using the simpler `any` implementation that comes with `cpp-peglib`, define `PEGLIB_USE_STD_ANY` as 0 before including `peglib.h`:
|
||||
```cpp
|
||||
#define PEGLIB_USE_STD_ANY 0
|
||||
#include <peglib.h>
|
||||
[...]
|
||||
```
|
||||
|
||||
A semantic action can return a value of arbitrary data type, which will be wrapped by `peg::any`. If a user returns nothing in a semantic action, the first semantic value in the `const SemanticValues& sv` argument will be returned. (Yacc parser has the same behavior.)
|
||||
A semantic action can return a value of arbitrary data type, which will be wrapped by `peg::any`. If a user returns nothing in a semantic action, the first semantic value in the `const SemanticValues& vs` argument will be returned. (Yacc parser has the same behavior.)
|
||||
|
||||
Here shows the `SemanticValues` structure:
|
||||
|
||||
```cpp
|
||||
struct SemanticValues : protected std::vector<any>
|
||||
{
|
||||
// Input text
|
||||
const char* path;
|
||||
const char* ss;
|
||||
// Input text
|
||||
const char* path;
|
||||
const char* ss;
|
||||
|
||||
// Matched string
|
||||
std::string str() const; // Matched string
|
||||
const char* c_str() const; // Matched string start
|
||||
size_t length() const; // Matched string length
|
||||
// Matched string
|
||||
std::string_view sv() const { return sv_; }
|
||||
|
||||
// Line number and column at which the matched string is
|
||||
std::pair<size_t, size_t> line_info() const;
|
||||
// Line number and column at which the matched string is
|
||||
std::pair<size_t, size_t> line_info() const;
|
||||
|
||||
// Tokens
|
||||
std::vector<
|
||||
std::pair<
|
||||
const char*, // Token start
|
||||
size_t>> // Token length
|
||||
tokens;
|
||||
// Tokens
|
||||
std::vector<std::string_view> tokens;
|
||||
std::string_view token(size_t id = 0) const;
|
||||
|
||||
std::string token(size_t id = 0) const;
|
||||
// Token conversion
|
||||
std::string token_to_string(size_t id = 0) const;
|
||||
template <typename T> T token_to_number() const;
|
||||
|
||||
// Choice number (0 based index)
|
||||
size_t choice() const;
|
||||
// Choice number (0 based index)
|
||||
size_t choice() const;
|
||||
|
||||
// Transform the semantic value vector to another vector
|
||||
template <typename T> vector<T> transform(size_t beg = 0, size_t end = -1) const;
|
||||
// Transform the semantic value vector to another vector
|
||||
template <typename T> vector<T> transform(size_t beg = 0, size_t end = -1) const;
|
||||
}
|
||||
```
|
||||
|
||||
@ -178,14 +169,14 @@ The following example uses `<` ... ` >` operator, which is *token boundary* oper
|
||||
|
||||
```cpp
|
||||
peg::parser parser(R"(
|
||||
ROOT <- _ TOKEN (',' _ TOKEN)*
|
||||
TOKEN <- < [a-z0-9]+ > _
|
||||
_ <- [ \t\r\n]*
|
||||
ROOT <- _ TOKEN (',' _ TOKEN)*
|
||||
TOKEN <- < [a-z0-9]+ > _
|
||||
_ <- [ \t\r\n]*
|
||||
)");
|
||||
|
||||
parser["TOKEN"] = [](const SemanticValues& sv) {
|
||||
// 'token' doesn't include trailing whitespaces
|
||||
auto token = sv.token();
|
||||
parser["TOKEN"] = [](const SemanticValues& vs) {
|
||||
// 'token' doesn't include trailing whitespaces
|
||||
auto token = vs.token();
|
||||
};
|
||||
|
||||
auto ret = parser.parse(" token1, token2 ");
|
||||
@ -195,13 +186,13 @@ We can ignore unnecessary semantic values from the list by using `~` operator.
|
||||
|
||||
```cpp
|
||||
peg::parser parser(R"(
|
||||
ROOT <- _ ITEM (',' _ ITEM _)*
|
||||
ITEM <- ([a-z])+
|
||||
~_ <- [ \t]*
|
||||
ROOT <- _ ITEM (',' _ ITEM _)*
|
||||
ITEM <- ([a-z])+
|
||||
~_ <- [ \t]*
|
||||
)");
|
||||
|
||||
parser["ROOT"] = [&](const SemanticValues& sv) {
|
||||
assert(sv.size() == 2); // should be 2 instead of 5.
|
||||
parser["ROOT"] = [&](const SemanticValues& vs) {
|
||||
assert(vs.size() == 2); // should be 2 instead of 5.
|
||||
};
|
||||
|
||||
auto ret = parser.parse(" item1, item2 ");
|
||||
@ -211,9 +202,9 @@ The following grammar is same as the above.
|
||||
|
||||
```cpp
|
||||
peg::parser parser(R"(
|
||||
ROOT <- ~_ ITEM (',' ~_ ITEM ~_)*
|
||||
ITEM <- ([a-z])+
|
||||
_ <- [ \t]*
|
||||
ROOT <- ~_ ITEM (',' ~_ ITEM ~_)*
|
||||
ITEM <- ([a-z])+
|
||||
_ <- [ \t]*
|
||||
)");
|
||||
```
|
||||
|
||||
@ -222,12 +213,12 @@ peg::parser parser(R"(
|
||||
```cpp
|
||||
peg::parser parser("NUMBER <- [0-9]+");
|
||||
|
||||
parser["NUMBER"] = [](const SemanticValues& sv) {
|
||||
auto val = stol(sv.str(), nullptr, 10);
|
||||
if (val != 100) {
|
||||
throw peg::parse_error("value error!!");
|
||||
}
|
||||
return val;
|
||||
parser["NUMBER"] = [](const SemanticValues& vs) {
|
||||
auto val = vs.token_to_number<long>();
|
||||
if (val != 100) {
|
||||
throw peg::parse_error("value error!!");
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
long val;
|
||||
@ -243,15 +234,15 @@ assert(ret == false);
|
||||
|
||||
```cpp
|
||||
parser["RULE"].enter = [](const char* s, size_t n, any& dt) {
|
||||
std::cout << "enter" << std::endl;
|
||||
std::cout << "enter" << std::endl;
|
||||
};
|
||||
|
||||
parser["RULE"] = [](const SemanticValues& sv, any& dt) {
|
||||
std::cout << "action!" << std::endl;
|
||||
parser["RULE"] = [](const SemanticValues& vs, any& dt) {
|
||||
std::cout << "action!" << std::endl;
|
||||
};
|
||||
|
||||
parser["RULE"].leave = [](const char* s, size_t n, size_t matchlen, any& value, any& dt) {
|
||||
std::cout << "leave" << std::endl;
|
||||
std::cout << "leave" << std::endl;
|
||||
};
|
||||
```
|
||||
|
||||
@ -291,9 +282,9 @@ Word expression
|
||||
|
||||
```cpp
|
||||
peg::parser parser(R"(
|
||||
ROOT <- 'hello' 'world'
|
||||
%whitespace <- [ \t\r\n]*
|
||||
%word <- [a-z]+
|
||||
ROOT <- 'hello' 'world'
|
||||
%whitespace <- [ \t\r\n]*
|
||||
%word <- [a-z]+
|
||||
)");
|
||||
|
||||
parser.parse("hello world"); // OK
|
||||
@ -305,14 +296,14 @@ Capture/Backreference
|
||||
|
||||
```cpp
|
||||
peg::parser parser(R"(
|
||||
ROOT <- CONTENT
|
||||
CONTENT <- (ELEMENT / TEXT)*
|
||||
ELEMENT <- $(STAG CONTENT ETAG)
|
||||
STAG <- '<' $tag< TAG_NAME > '>'
|
||||
ETAG <- '</' $tag '>'
|
||||
TAG_NAME <- 'b' / 'u'
|
||||
TEXT <- TEXT_DATA
|
||||
TEXT_DATA <- ![<] .
|
||||
ROOT <- CONTENT
|
||||
CONTENT <- (ELEMENT / TEXT)*
|
||||
ELEMENT <- $(STAG CONTENT ETAG)
|
||||
STAG <- '<' $tag< TAG_NAME > '>'
|
||||
ETAG <- '</' $tag '>'
|
||||
TAG_NAME <- 'b' / 'u'
|
||||
TEXT <- TEXT_DATA
|
||||
TEXT_DATA <- ![<] .
|
||||
)");
|
||||
|
||||
parser.parse("This is <b>a <u>test</u> text</b>."); // OK
|
||||
@ -359,36 +350,36 @@ Regarding the *precedence climbing algorithm*, please see [this article](https:/
|
||||
|
||||
```cpp
|
||||
parser parser(R"(
|
||||
EXPRESSION <- INFIX_EXPRESSION(ATOM, OPERATOR)
|
||||
ATOM <- NUMBER / '(' EXPRESSION ')'
|
||||
OPERATOR <- < [-+/*] >
|
||||
NUMBER <- < '-'? [0-9]+ >
|
||||
%whitespace <- [ \t]*
|
||||
EXPRESSION <- INFIX_EXPRESSION(ATOM, OPERATOR)
|
||||
ATOM <- NUMBER / '(' EXPRESSION ')'
|
||||
OPERATOR <- < [-+/*] >
|
||||
NUMBER <- < '-'? [0-9]+ >
|
||||
%whitespace <- [ \t]*
|
||||
|
||||
# Declare order of precedence
|
||||
INFIX_EXPRESSION(A, O) <- A (O A)* {
|
||||
precedence
|
||||
L + -
|
||||
L * /
|
||||
}
|
||||
# Declare order of precedence
|
||||
INFIX_EXPRESSION(A, O) <- A (O A)* {
|
||||
precedence
|
||||
L + -
|
||||
L * /
|
||||
}
|
||||
)");
|
||||
|
||||
parser["INFIX_EXPRESSION"] = [](const SemanticValues& sv) -> long {
|
||||
auto result = any_cast<long>(sv[0]);
|
||||
if (sv.size() > 1) {
|
||||
auto ope = any_cast<char>(sv[1]);
|
||||
auto num = any_cast<long>(sv[2]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
parser["INFIX_EXPRESSION"] = [](const SemanticValues& vs) -> long {
|
||||
auto result = any_cast<long>(vs[0]);
|
||||
if (vs.size() > 1) {
|
||||
auto ope = any_cast<char>(vs[1]);
|
||||
auto num = any_cast<long>(vs[2]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
parser["OPERATOR"] = [](const SemanticValues& sv) { return *sv.c_str(); };
|
||||
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||
parser["OPERATOR"] = [](const SemanticValues& vs) { return *vs.sv(); };
|
||||
parser["NUMBER"] = [](const SemanticValues& vs) { return vs.token_to_number<long>(); };
|
||||
|
||||
long val;
|
||||
parser.parse(" -1 + (1 + 2) * 3 - -1", val);
|
||||
@ -446,8 +437,8 @@ vector<string> tags;
|
||||
|
||||
Definition ROOT, TAG_NAME, _;
|
||||
ROOT <= seq(_, zom(seq(chr('['), TAG_NAME, chr(']'), _)));
|
||||
TAG_NAME <= oom(seq(npd(chr(']')), dot())), [&](const SemanticValues& sv) {
|
||||
tags.push_back(sv.str());
|
||||
TAG_NAME <= oom(seq(npd(chr(']')), dot())), [&](const SemanticValues& vs) {
|
||||
tags.push_back(vs.str());
|
||||
};
|
||||
_ <= zom(cls(" \t"));
|
||||
|
||||
@ -487,24 +478,24 @@ It's possible to add/override definitions.
|
||||
|
||||
```cpp
|
||||
auto syntax = R"(
|
||||
ROOT <- _ 'Hello' _ NAME '!' _
|
||||
ROOT <- _ 'Hello' _ NAME '!' _
|
||||
)";
|
||||
|
||||
Rules additional_rules = {
|
||||
{
|
||||
"NAME", usr([](const char* s, size_t n, SemanticValues& sv, any& dt) -> size_t {
|
||||
static vector<string> names = { "PEG", "BNF" };
|
||||
for (const auto& name: names) {
|
||||
if (name.size() <= n && !name.compare(0, name.size(), s, name.size())) {
|
||||
return name.size(); // processed length
|
||||
}
|
||||
}
|
||||
return -1; // parse error
|
||||
})
|
||||
},
|
||||
{
|
||||
"~_", zom(cls(" \t\r\n"))
|
||||
}
|
||||
{
|
||||
"NAME", usr([](const char* s, size_t n, SemanticValues& vs, any& dt) -> size_t {
|
||||
static vector<string> names = { "PEG", "BNF" };
|
||||
for (const auto& name: names) {
|
||||
if (name.size() <= n && !name.compare(0, name.size(), s, name.size())) {
|
||||
return name.size(); // processed length
|
||||
}
|
||||
}
|
||||
return -1; // parse error
|
||||
})
|
||||
},
|
||||
{
|
||||
"~_", zom(cls(" \t\r\n"))
|
||||
}
|
||||
};
|
||||
|
||||
auto g = parser(syntax, additional_rules);
|
||||
|
@ -2,9 +2,6 @@ clone_depth: 5
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- JOB: Visual Studio 2015
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: "Visual Studio 14 2015"
|
||||
- JOB: Visual Studio 2017
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: "Visual Studio 15 2017"
|
||||
|
@ -1,2 +1,2 @@
|
||||
source ~/Projects/emsdk/emsdk_env.sh
|
||||
emcc -std=c++11 -O3 --bind -o native.js native.cpp
|
||||
emcc -std=c++17 -O3 --bind -o native.js native.cpp
|
||||
|
File diff suppressed because one or more lines are too long
BIN
docs/native.wasm
BIN
docs/native.wasm
Binary file not shown.
@ -1,13 +1,9 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(example)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(${cxx11_options} /W3)
|
||||
else()
|
||||
add_compile_options(${cxx11_options})
|
||||
endif()
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(calc calc.cc)
|
||||
target_link_libraries(calc ${add_link_deps})
|
||||
|
@ -1,13 +1,13 @@
|
||||
#include <peglib.h>
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <peglib.h>
|
||||
|
||||
using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
int main(void) {
|
||||
// (2) Make a parser
|
||||
parser parser(R"(
|
||||
// (2) Make a parser
|
||||
parser parser(R"(
|
||||
# Grammar for Calculator...
|
||||
Additive <- Multitive '+' Additive / Multitive
|
||||
Multitive <- Primary '*' Multitive / Primary
|
||||
@ -16,36 +16,36 @@ int main(void) {
|
||||
%whitespace <- [ \t]*
|
||||
)");
|
||||
|
||||
assert(static_cast<bool>(parser) == true);
|
||||
assert(static_cast<bool>(parser) == true);
|
||||
|
||||
// (3) Setup actions
|
||||
parser["Additive"] = [](const SemanticValues& sv) {
|
||||
switch (sv.choice()) {
|
||||
case 0: // "Multitive '+' Additive"
|
||||
return any_cast<int>(sv[0]) + any_cast<int>(sv[1]);
|
||||
default: // "Multitive"
|
||||
return any_cast<int>(sv[0]);
|
||||
}
|
||||
};
|
||||
// (3) Setup actions
|
||||
parser["Additive"] = [](const SemanticValues &vs) {
|
||||
switch (vs.choice()) {
|
||||
case 0: // "Multitive '+' Additive"
|
||||
return any_cast<int>(vs[0]) + any_cast<int>(vs[1]);
|
||||
default: // "Multitive"
|
||||
return any_cast<int>(vs[0]);
|
||||
}
|
||||
};
|
||||
|
||||
parser["Multitive"] = [](const SemanticValues& sv) {
|
||||
switch (sv.choice()) {
|
||||
case 0: // "Primary '*' Multitive"
|
||||
return any_cast<int>(sv[0]) * any_cast<int>(sv[1]);
|
||||
default: // "Primary"
|
||||
return any_cast<int>(sv[0]);
|
||||
}
|
||||
};
|
||||
parser["Multitive"] = [](const SemanticValues &vs) {
|
||||
switch (vs.choice()) {
|
||||
case 0: // "Primary '*' Multitive"
|
||||
return any_cast<int>(vs[0]) * any_cast<int>(vs[1]);
|
||||
default: // "Primary"
|
||||
return any_cast<int>(vs[0]);
|
||||
}
|
||||
};
|
||||
|
||||
parser["Number"] = [](const SemanticValues& sv) {
|
||||
return stoi(sv.token(), nullptr, 10);
|
||||
};
|
||||
parser["Number"] = [](const SemanticValues &vs) {
|
||||
return vs.token_to_number<int>();
|
||||
};
|
||||
|
||||
// (4) Parse
|
||||
parser.enable_packrat_parsing(); // Enable packrat parsing.
|
||||
// (4) Parse
|
||||
parser.enable_packrat_parsing(); // Enable packrat parsing.
|
||||
|
||||
int val;
|
||||
parser.parse(" (1 + 2) * 3 ", val);
|
||||
int val;
|
||||
parser.parse(" (1 + 2) * 3 ", val);
|
||||
|
||||
assert(val == 9);
|
||||
assert(val == 9);
|
||||
}
|
||||
|
@ -5,9 +5,9 @@
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <peglib.h>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <peglib.h>
|
||||
|
||||
using namespace peg;
|
||||
|
||||
@ -21,45 +21,47 @@ using namespace peg;
|
||||
// FACTOR_OPERATOR <- [/*]
|
||||
// NUMBER <- [0-9]+
|
||||
//
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc [formula]" << std::endl;
|
||||
return 1;
|
||||
int main(int argc, const char **argv) {
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto reduce = [](const SemanticValues &vs) {
|
||||
auto result = std::any_cast<long>(vs[0]);
|
||||
for (auto i = 1u; i < vs.size(); i += 2) {
|
||||
auto num = std::any_cast<long>(vs[i + 1]);
|
||||
auto ope = std::any_cast<char>(vs[i]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto reduce = [](const SemanticValues& sv) -> long {
|
||||
auto result = any_cast<long>(sv[0]);
|
||||
for (auto i = 1u; i < sv.size(); i += 2) {
|
||||
auto num = any_cast<long>(sv[i + 1]);
|
||||
auto ope = any_cast<char>(sv[i]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
Definition EXPRESSION, TERM, FACTOR, TERM_OPERATOR, FACTOR_OPERATOR, NUMBER;
|
||||
|
||||
Definition EXPRESSION, TERM, FACTOR, TERM_OPERATOR, FACTOR_OPERATOR, NUMBER;
|
||||
EXPRESSION <= seq(TERM, zom(seq(TERM_OPERATOR, TERM))), reduce;
|
||||
TERM <= seq(FACTOR, zom(seq(FACTOR_OPERATOR, FACTOR))), reduce;
|
||||
FACTOR <= cho(NUMBER, seq(chr('('), EXPRESSION, chr(')')));
|
||||
TERM_OPERATOR <= cls("+-"),
|
||||
[](const SemanticValues &vs) { return static_cast<char>(*vs.sv().data()); };
|
||||
FACTOR_OPERATOR <= cls("*/"),
|
||||
[](const SemanticValues &vs) { return static_cast<char>(*vs.sv().data()); };
|
||||
NUMBER <= oom(cls("0-9")),
|
||||
[](const SemanticValues &vs) { return vs.token_to_number<long>(); };
|
||||
|
||||
EXPRESSION <= seq(TERM, zom(seq(TERM_OPERATOR, TERM))), reduce;
|
||||
TERM <= seq(FACTOR, zom(seq(FACTOR_OPERATOR, FACTOR))), reduce;
|
||||
FACTOR <= cho(NUMBER, seq(chr('('), EXPRESSION, chr(')')));
|
||||
TERM_OPERATOR <= cls("+-"), [](const SemanticValues& sv) { return static_cast<char>(*sv.c_str()); };
|
||||
FACTOR_OPERATOR <= cls("*/"), [](const SemanticValues& sv) { return static_cast<char>(*sv.c_str()); };
|
||||
NUMBER <= oom(cls("0-9")), [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||
auto expr = argv[1];
|
||||
long val = 0;
|
||||
if (EXPRESSION.parse_and_get_value(expr, val).ret) {
|
||||
std::cout << expr << " = " << val << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto expr = argv[1];
|
||||
long val = 0;
|
||||
if (EXPRESSION.parse_and_get_value(expr, val).ret) {
|
||||
std::cout << expr << " = " << val << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
||||
|
@ -5,40 +5,39 @@
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <peglib.h>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <peglib.h>
|
||||
|
||||
using namespace peg;
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc3 [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
int main(int argc, const char **argv) {
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc3 [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||
if (ast.name == "NUMBER") {
|
||||
return stol(ast.token);
|
||||
} else {
|
||||
const auto& nodes = ast.nodes;
|
||||
auto result = eval(*nodes[0]);
|
||||
for (auto i = 1u; i < nodes.size(); i += 2) {
|
||||
auto num = eval(*nodes[i + 1]);
|
||||
auto ope = nodes[i]->token[0];
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
std::function<long(const Ast &)> eval = [&](const Ast &ast) {
|
||||
if (ast.name == "NUMBER") {
|
||||
return ast.token_to_number<long>();
|
||||
} else {
|
||||
const auto &nodes = ast.nodes;
|
||||
auto result = eval(*nodes[0]);
|
||||
for (auto i = 1u; i < nodes.size(); i += 2) {
|
||||
auto num = eval(*nodes[i + 1]);
|
||||
auto ope = nodes[i]->token[0];
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
parser parser(R"(
|
||||
parser parser(R"(
|
||||
EXPRESSION <- TERM (TERM_OPERATOR TERM)*
|
||||
TERM <- FACTOR (FACTOR_OPERATOR FACTOR)*
|
||||
FACTOR <- NUMBER / '(' EXPRESSION ')'
|
||||
@ -50,20 +49,20 @@ int main(int argc, const char** argv)
|
||||
%whitespace <- [ \t\r\n]*
|
||||
)");
|
||||
|
||||
parser.enable_ast();
|
||||
parser.enable_ast();
|
||||
|
||||
auto expr = argv[1];
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
auto expr = argv[1];
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "syntax error..." << std::endl;
|
||||
std::cout << "syntax error..." << std::endl;
|
||||
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
||||
|
@ -1,12 +1,12 @@
|
||||
#include <peglib.h>
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <peglib.h>
|
||||
|
||||
using namespace peg;
|
||||
using namespace std;
|
||||
|
||||
int main(void) {
|
||||
parser parser(R"(
|
||||
parser parser(R"(
|
||||
EXPRESSION <- ATOM (OPERATOR ATOM)* {
|
||||
precedence
|
||||
L - +
|
||||
@ -18,25 +18,25 @@ int main(void) {
|
||||
%whitespace <- [ \t\r\n]*
|
||||
)");
|
||||
|
||||
parser["EXPRESSION"] = [](const SemanticValues& sv) -> long {
|
||||
auto result = any_cast<long>(sv[0]);
|
||||
if (sv.size() > 1) {
|
||||
auto ope = any_cast<char>(sv[1]);
|
||||
auto num = any_cast<long>(sv[2]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
parser["OPERATOR"] = [](const SemanticValues& sv) { return *sv.c_str(); };
|
||||
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||
parser["EXPRESSION"] = [](const SemanticValues &vs) {
|
||||
auto result = any_cast<long>(vs[0]);
|
||||
if (vs.size() > 1) {
|
||||
auto ope = any_cast<char>(vs[1]);
|
||||
auto num = any_cast<long>(vs[2]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
parser["OPERATOR"] = [](const SemanticValues &vs) { return *vs.sv().data(); };
|
||||
parser["NUMBER"] = [](const SemanticValues &vs) { return atol(vs.sv().data()); };
|
||||
|
||||
long val;
|
||||
parser.parse(" -1 + (1 + 2) * 3 - -1", val);
|
||||
long val;
|
||||
parser.parse(" -1 + (1 + 2) * 3 - -1", val);
|
||||
|
||||
assert(val == 9);
|
||||
assert(val == 9);
|
||||
}
|
||||
|
@ -5,40 +5,39 @@
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <peglib.h>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <peglib.h>
|
||||
|
||||
using namespace peg;
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc5 [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
int main(int argc, const char **argv) {
|
||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||
std::cout << "usage: calc5 [formula]" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||
if (ast.name == "NUMBER") {
|
||||
return stol(ast.token);
|
||||
} else {
|
||||
const auto& nodes = ast.nodes;
|
||||
auto result = eval(*nodes[0]);
|
||||
if (nodes.size() > 1) {
|
||||
auto ope = nodes[1]->token[0];
|
||||
auto num = eval(*nodes[2]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
std::function<long(const Ast &)> eval = [&](const Ast &ast) {
|
||||
if (ast.name == "NUMBER") {
|
||||
return ast.token_to_number<long>();
|
||||
} else {
|
||||
const auto &nodes = ast.nodes;
|
||||
auto result = eval(*nodes[0]);
|
||||
if (nodes.size() > 1) {
|
||||
auto ope = nodes[1]->token[0];
|
||||
auto num = eval(*nodes[2]);
|
||||
switch (ope) {
|
||||
case '+': result += num; break;
|
||||
case '-': result -= num; break;
|
||||
case '*': result *= num; break;
|
||||
case '/': result /= num; break;
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
parser parser(R"(
|
||||
parser parser(R"(
|
||||
EXPRESSION <- ATOM (OPERATOR ATOM)* {
|
||||
precedence
|
||||
L - +
|
||||
@ -50,20 +49,20 @@ int main(int argc, const char** argv)
|
||||
%whitespace <- [ \t\r\n]*
|
||||
)");
|
||||
|
||||
parser.enable_ast();
|
||||
parser.enable_ast();
|
||||
|
||||
auto expr = argv[1];
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
auto expr = argv[1];
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "syntax error..." << std::endl;
|
||||
std::cout << "syntax error..." << std::endl;
|
||||
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
||||
|
@ -1,9 +1,9 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(peglint)
|
||||
include_directories(..)
|
||||
add_definitions("-std=c++11")
|
||||
add_executable(peglint peglint.cc)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(peglint ${add_link_deps})
|
||||
endif()
|
||||
include_directories(..)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(peglint peglint.cc)
|
||||
target_link_libraries(peglint ${add_link_deps})
|
||||
|
@ -131,7 +131,7 @@ int main(int argc, const char **argv) {
|
||||
parser.enable_trace(
|
||||
[&](const char *name, const char *s, size_t /*n*/,
|
||||
const peg::SemanticValues & /*sv*/, const peg::Context &c,
|
||||
const peg::any & /*dt*/) {
|
||||
const std::any & /*dt*/) {
|
||||
auto pos = static_cast<size_t>(s - c.s);
|
||||
auto backtrack = (pos < prev_pos ? "*" : "");
|
||||
string indent;
|
||||
@ -145,7 +145,7 @@ int main(int argc, const char **argv) {
|
||||
},
|
||||
[&](const char *name, const char *s, size_t /*n*/,
|
||||
const peg::SemanticValues &sv, const peg::Context &c,
|
||||
const peg::any & /*dt*/, size_t len) {
|
||||
const std::any & /*dt*/, size_t len) {
|
||||
auto pos = static_cast<size_t>(s - c.s);
|
||||
if (len != static_cast<size_t>(-1)) { pos += len; }
|
||||
string indent;
|
||||
@ -160,8 +160,9 @@ int main(int argc, const char **argv) {
|
||||
}
|
||||
std::string token;
|
||||
if (!sv.tokens.empty()) {
|
||||
const auto &tok = sv.tokens[0];
|
||||
token += " '" + std::string(tok.first, tok.second) + "'";
|
||||
token += " '";
|
||||
token += sv.tokens[0];
|
||||
token +=+ "'";
|
||||
}
|
||||
std::cout << "L " << pos << "\t" << indent << ret << name << " #"
|
||||
<< c.trace_ids.back() << choice.str() << token << std::endl;
|
||||
|
@ -1,14 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(tcp)
|
||||
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
project(pl0)
|
||||
|
||||
include_directories(..)
|
||||
add_executable(pl0 pl0.cc)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(pl0 pl0.cc)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
set(add_link_deps ${add_link_deps} LLVM)
|
||||
target_include_directories(pl0 PUBLIC ${LLVM_INCLUDE_DIRS})
|
||||
|
56
pl0/pl0.cc
56
pl0/pl0.cc
@ -182,11 +182,11 @@ struct SymbolTable {
|
||||
// _)?
|
||||
const auto& nodes = ast->nodes;
|
||||
for (auto i = 0u; i < nodes.size(); i += 2) {
|
||||
const auto& ident = nodes[i + 0]->token;
|
||||
const auto& ident = nodes[i + 0]->token_to_string();
|
||||
if (scope->has_symbol(ident)) {
|
||||
throw_runtime_error(nodes[i], "'" + ident + "' is already defined...");
|
||||
}
|
||||
auto number = stoi(nodes[i + 1]->token);
|
||||
auto number = nodes[i + 1]->token_to_number<int>();
|
||||
scope->constants.emplace(ident, number);
|
||||
}
|
||||
}
|
||||
@ -196,7 +196,7 @@ struct SymbolTable {
|
||||
// var <- ('VAR' __ ident(',' _ ident)* ';' _) ?
|
||||
const auto& nodes = ast->nodes;
|
||||
for (auto i = 0u; i < nodes.size(); i += 1) {
|
||||
const auto& ident = nodes[i]->token;
|
||||
const auto& ident = nodes[i]->token_to_string();
|
||||
if (scope->has_symbol(ident)) {
|
||||
throw_runtime_error(nodes[i], "'" + ident + "' is already defined...");
|
||||
}
|
||||
@ -209,7 +209,7 @@ struct SymbolTable {
|
||||
// procedure <- ('PROCEDURE' __ ident ';' _ block ';' _)*
|
||||
const auto& nodes = ast->nodes;
|
||||
for (auto i = 0u; i < nodes.size(); i += 2) {
|
||||
const auto& ident = nodes[i + 0]->token;
|
||||
const auto& ident = nodes[i + 0]->token_to_string();
|
||||
auto block = nodes[i + 1];
|
||||
scope->procedures[ident] = block;
|
||||
build_on_ast(block, scope);
|
||||
@ -219,7 +219,7 @@ struct SymbolTable {
|
||||
static void assignment(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<SymbolScope> scope) {
|
||||
// assignment <- ident ':=' _ expression
|
||||
const auto& ident = ast->nodes[0]->token;
|
||||
const auto& ident = ast->nodes[0]->token_to_string();
|
||||
if (scope->has_constant(ident)) {
|
||||
throw_runtime_error(ast->nodes[0],
|
||||
"cannot modify constant value '" + ident + "'...");
|
||||
@ -238,7 +238,7 @@ struct SymbolTable {
|
||||
static void call(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<SymbolScope> scope) {
|
||||
// call <- 'CALL' __ ident
|
||||
const auto& ident = ast->nodes[0]->token;
|
||||
const auto& ident = ast->nodes[0]->token_to_string();
|
||||
if (!scope->has_procedure(ident)) {
|
||||
throw_runtime_error(ast->nodes[0],
|
||||
"undefined procedure '" + ident + "'...");
|
||||
@ -256,7 +256,7 @@ struct SymbolTable {
|
||||
|
||||
static void ident(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<SymbolScope> scope) {
|
||||
const auto& ident = ast->token;
|
||||
const auto& ident = ast->token_to_string();
|
||||
if (!scope->has_symbol(ident)) {
|
||||
throw_runtime_error(ast, "undefined variable '" + ident + "'...");
|
||||
}
|
||||
@ -360,13 +360,13 @@ struct Interpreter {
|
||||
static void exec_assignment(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<Environment> env) {
|
||||
// assignment <- ident ':=' _ expression
|
||||
env->set_variable(ast->nodes[0]->token, eval(ast->nodes[1], env));
|
||||
env->set_variable(ast->nodes[0]->token_to_string(), eval(ast->nodes[1], env));
|
||||
}
|
||||
|
||||
static void exec_call(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<Environment> env) {
|
||||
// call <- 'CALL' __ ident
|
||||
exec_block(env->get_procedure(ast->nodes[0]->token), env);
|
||||
exec_block(env->get_procedure(ast->nodes[0]->token_to_string()), env);
|
||||
}
|
||||
|
||||
static void exec_statements(const shared_ptr<AstPL0> ast,
|
||||
@ -406,7 +406,7 @@ struct Interpreter {
|
||||
// in <- ('in' __ / 'read' __ / '?' _) ident
|
||||
int val;
|
||||
cin >> val;
|
||||
env->set_variable(ast->nodes[0]->token, val);
|
||||
env->set_variable(ast->nodes[0]->token_to_string(), val);
|
||||
}
|
||||
|
||||
static bool eval_condition(const shared_ptr<AstPL0> ast,
|
||||
@ -434,7 +434,7 @@ struct Interpreter {
|
||||
// compare <- expression compare_op expression
|
||||
const auto& nodes = ast->nodes;
|
||||
auto lval = eval_expression(nodes[0], env);
|
||||
auto op = peg::str2tag(nodes[1]->token.c_str());
|
||||
auto op = peg::str2tag(nodes[1]->token_to_string().c_str());
|
||||
auto rval = eval_expression(nodes[2], env);
|
||||
switch (op) {
|
||||
case "="_:
|
||||
@ -473,11 +473,11 @@ struct Interpreter {
|
||||
shared_ptr<Environment> env) {
|
||||
// expression <- sign term (term_op term)*
|
||||
const auto& nodes = ast->nodes;
|
||||
auto sign = nodes[0]->token;
|
||||
auto sign = nodes[0]->token_to_string();
|
||||
auto sign_val = (sign.empty() || sign == "+") ? 1 : -1;
|
||||
auto val = eval(nodes[1], env) * sign_val;
|
||||
for (auto i = 2u; i < nodes.size(); i += 2) {
|
||||
auto ope = nodes[i + 0]->token[0];
|
||||
auto ope = nodes[i + 0]->token_to_string()[0];
|
||||
auto rval = eval(nodes[i + 1], env);
|
||||
switch (ope) {
|
||||
case '+':
|
||||
@ -497,7 +497,7 @@ struct Interpreter {
|
||||
const auto& nodes = ast->nodes;
|
||||
auto val = eval(nodes[0], env);
|
||||
for (auto i = 1u; i < nodes.size(); i += 2) {
|
||||
auto ope = nodes[i + 0]->token[0];
|
||||
auto ope = nodes[i + 0]->token_to_string()[0];
|
||||
auto rval = eval(nodes[i + 1], env);
|
||||
switch (ope) {
|
||||
case '*':
|
||||
@ -516,12 +516,12 @@ struct Interpreter {
|
||||
|
||||
static int eval_ident(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<Environment> env) {
|
||||
return env->get_value(ast, ast->token);
|
||||
return env->get_value(ast, ast->token_to_string());
|
||||
}
|
||||
|
||||
static int eval_number(const shared_ptr<AstPL0> ast,
|
||||
shared_ptr<Environment> env) {
|
||||
return stol(ast->token);
|
||||
return stol(ast->token_to_string());
|
||||
}
|
||||
};
|
||||
|
||||
@ -653,8 +653,8 @@ struct LLVM {
|
||||
|
||||
void compile_const(const shared_ptr<AstPL0> ast) {
|
||||
for (auto i = 0u; i < ast->nodes.size(); i += 2) {
|
||||
auto ident = ast->nodes[i]->token;
|
||||
auto number = stoi(ast->nodes[i + 1]->token);
|
||||
auto ident = ast->nodes[i]->token_to_string();
|
||||
auto number = stoi(ast->nodes[i + 1]->token_to_string());
|
||||
|
||||
auto alloca =
|
||||
builder_.CreateAlloca(builder_.getInt32Ty(), nullptr, ident);
|
||||
@ -664,14 +664,14 @@ struct LLVM {
|
||||
|
||||
void compile_var(const shared_ptr<AstPL0> ast) {
|
||||
for (const auto node : ast->nodes) {
|
||||
auto ident = node->token;
|
||||
auto ident = node->token_to_string();
|
||||
builder_.CreateAlloca(builder_.getInt32Ty(), nullptr, ident);
|
||||
}
|
||||
}
|
||||
|
||||
void compile_procedure(const shared_ptr<AstPL0> ast) {
|
||||
for (auto i = 0u; i < ast->nodes.size(); i += 2) {
|
||||
auto ident = ast->nodes[i]->token;
|
||||
auto ident = ast->nodes[i]->token_to_string();
|
||||
auto block = ast->nodes[i + 1];
|
||||
|
||||
std::vector<Type*> pt(block->scope->free_variables.size(),
|
||||
@ -712,7 +712,7 @@ struct LLVM {
|
||||
}
|
||||
|
||||
void compile_assignment(const shared_ptr<AstPL0> ast) {
|
||||
auto ident = ast->nodes[0]->token;
|
||||
auto ident = ast->nodes[0]->token_to_string();
|
||||
|
||||
auto fn = builder_.GetInsertBlock()->getParent();
|
||||
auto tbl = fn->getValueSymbolTable();
|
||||
@ -726,7 +726,7 @@ struct LLVM {
|
||||
}
|
||||
|
||||
void compile_call(const shared_ptr<AstPL0> ast) {
|
||||
auto ident = ast->nodes[0]->token;
|
||||
auto ident = ast->nodes[0]->token_to_string();
|
||||
|
||||
auto scope = get_closest_scope(ast);
|
||||
auto block = scope->get_procedure(ident);
|
||||
@ -805,7 +805,7 @@ struct LLVM {
|
||||
auto lhs = compile_expression(ast->nodes[0]);
|
||||
auto rhs = compile_expression(ast->nodes[2]);
|
||||
|
||||
const auto& ope = ast->nodes[1]->token;
|
||||
const auto& ope = ast->nodes[1]->token_to_string();
|
||||
switch (ope[0]) {
|
||||
case '=':
|
||||
return builder_.CreateICmpEQ(lhs, rhs, "icmpeq");
|
||||
@ -836,7 +836,7 @@ struct LLVM {
|
||||
Value* compile_expression(const shared_ptr<AstPL0> ast) {
|
||||
const auto& nodes = ast->nodes;
|
||||
|
||||
auto sign = nodes[0]->token;
|
||||
auto sign = nodes[0]->token_to_string();
|
||||
auto negative = !(sign.empty() || sign == "+");
|
||||
|
||||
auto val = compile_term(nodes[1]);
|
||||
@ -845,7 +845,7 @@ struct LLVM {
|
||||
}
|
||||
|
||||
for (auto i = 2u; i < nodes.size(); i += 2) {
|
||||
auto ope = nodes[i + 0]->token[0];
|
||||
auto ope = nodes[i + 0]->token_to_string()[0];
|
||||
auto rval = compile_term(nodes[i + 1]);
|
||||
switch (ope) {
|
||||
case '+':
|
||||
@ -863,7 +863,7 @@ struct LLVM {
|
||||
const auto& nodes = ast->nodes;
|
||||
auto val = compile_factor(nodes[0]);
|
||||
for (auto i = 1u; i < nodes.size(); i += 2) {
|
||||
auto ope = nodes[i + 0]->token[0];
|
||||
auto ope = nodes[i + 0]->token_to_string()[0];
|
||||
auto rval = compile_switch_value(nodes[i + 1]);
|
||||
switch (ope) {
|
||||
case '*':
|
||||
@ -889,7 +889,7 @@ struct LLVM {
|
||||
}
|
||||
|
||||
Value* compile_ident(const shared_ptr<AstPL0> ast) {
|
||||
auto ident = ast->token;
|
||||
auto ident = ast->token_to_string();
|
||||
|
||||
auto fn = builder_.GetInsertBlock()->getParent();
|
||||
auto tbl = fn->getValueSymbolTable();
|
||||
@ -903,7 +903,7 @@ struct LLVM {
|
||||
|
||||
Value* compile_number(const shared_ptr<AstPL0> ast) {
|
||||
return ConstantInt::getIntegerValue(builder_.getInt32Ty(),
|
||||
APInt(32, ast->token, 10));
|
||||
APInt(32, ast->token_to_string(), 10));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(test)
|
||||
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(test-main test-main.cc test1.cc test2.cc test3.cc)
|
||||
target_link_libraries(test-main ${add_link_deps})
|
||||
|
||||
add_test(TestMain test-main)
|
||||
add_test(
|
||||
NAME TestMain
|
||||
COMMAND test-main
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
1071
test/test1.cc
1071
test/test1.cc
File diff suppressed because it is too large
Load Diff
915
test/test2.cc
915
test/test2.cc
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user