Skip to content

Commit

Permalink
Merge pull request #1 from sillydan1/feature/basic-initial-impl
Browse files Browse the repository at this point in the history
Feature/basic initial impl
  • Loading branch information
sillydan1 authored Mar 5, 2022
2 parents 8337824 + 775d147 commit 51d6efb
Show file tree
Hide file tree
Showing 25 changed files with 813 additions and 1 deletion.
82 changes: 82 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: CMake Pipeline

on:
pull_request:
branches:
- master
push:
branches:
- master

env:
BUILD_TYPE: Release
GIT_SUBMODULE_STRATEGY: recursive

jobs:
build-linux:
if: ${{ github.event.pull_request.draft == false }}
name: Build For Linux Systems
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Initialize Submodules
uses: snickerbockers/submodules-init@v4
- name: Install dependencies
run: sudo apt-get install -y flex bison make
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
- name: Build
working-directory: ${{github.workspace}}/build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE -j$(nproc)
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: library
path: ${{ github.workspace }}/build/expr
build-windows:
if: ${{ github.event.pull_request.draft == false }}
name: Build For MS Windows Systems
runs-on: windows-latest
env:
CC: x86_64-w64-mingw32-gcc
CXX: x86_64-w64-mingw32-g++
LD: x86_64-w64-mingw32-ld
AR: x86_64-w64-mingw32-ar
AS: x86_64-w64-mingw32-as
NM: x86_64-w64-mingw32-nm
STRIP: x86_64-w64-mingw32-strip
RANLIB: x86_64-w64-mingw32-ranlib
DLLTOOL: x86_64-w64-mingw32-dlltool
OBJDUMP: x86_64-w64-mingw32-objdump
RESCOMP: x86_64-w64-mingw32-windres
steps:
- name: Setup CygWin
uses: egor-tensin/setup-cygwin@v3
with:
packages: flex bison mingw-w64-gcc
- name: Install MinGW
uses: egor-tensin/setup-mingw@v2
with:
cygwin: 1
- name: Checkout Repository
uses: actions/checkout@v2
- name: Initialize Submodules
uses: snickerbockers/submodules-init@v4
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build
- name: Configure CMake
shell: bash
working-directory: ${{github.workspace}}/build
run: cmake $GITHUB_WORKSPACE -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc
- name: Build
working-directory: ${{github.workspace}}/build
shell: bash
# Execute the build. You can specify a specific target with "--target <NAME>"
run: cmake --build . --config $BUILD_TYPE -j 8
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@
*.exe
*.out
*.app

# build folders
cmake-build-*
.idea
build
bin
35 changes: 35 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.0)
project(expr VERSION 1.0.0)
include(cmake/CPM.cmake)
configure_file(src/config.h.in config.h)
set(CMAKE_CXX_STANDARD 20)
set(CXX_STANDARD_REQUIRED ON)

find_package(FLEX REQUIRED)
find_package(BISON REQUIRED)
CPMAddPackage("gh:sillydan1/overload#1.0.0")
BISON_TARGET(expr_parser src/parser/parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp)
FLEX_TARGET(expr_scanner src/parser/scanner.l ${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
ADD_FLEX_BISON_DEPENDENCY(expr_scanner expr_parser)

include_directories(
${CMAKE_CURRENT_BINARY_DIR}/_deps/overload-src/include
${CMAKE_CURRENT_BINARY_DIR}
include
src
)
add_library(${PROJECT_NAME}
${BISON_expr_parser_OUTPUTS}
${FLEX_expr_scanner_OUTPUTS}
src/parser/driver.cpp
src/symbol_table.cpp
src/operations/add.cpp
src/operations/subtract.cpp
src/operations/multiply.cpp
src/operations/divide.cpp
src/operations/boolean.cpp
)
add_executable(${PROJECT_NAME}_demo
src/main.cpp
)
target_link_libraries(${PROJECT_NAME}_demo ${PROJECT_NAME})
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,34 @@
# expr
An expression parser for C++
A variable environment manipulation expression parser written in C++20 with flex and bison.

## Examples
This project comes with an demo example command line interface called `expr_demo` so that you
can test if the project supports your expression syntax. This example cli can
```
./expr_demo -
a := (1 + 2 + 3 + 4);
b := one + two;
^D
a :-> 6 i
b :-> 3 i
```

You can also use the project directly in code like so:
```c++
try { // Errors are handled with exceptions
symbol_map_t env{}; // Initialize an environment
driver drv{env}; // Initialize the expr driver with the environment
if (!drv.parse("a := 32 + 2")) // Parse your expressions
std::cout << drv.result; // Print the result
} catch(const std::exception& e) { // Parsing went wrong. Maybe bad syntax, type error or identifier not in environment
std::cout << e.what(); // Print what went wrong
}
```

## Compile
You should be able to compile with cmake like so:
```
mkdir bin && cd bin
cmake ..
make
```
21 changes: 21 additions & 0 deletions cmake/CPM.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
set(CPM_DOWNLOAD_VERSION 0.35.0)

if(CPM_SOURCE_CACHE)
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_SOURCE_CACHE ${CPM_SOURCE_CACHE} ABSOLUTE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()

if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))
message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}")
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION}
)
endif()

include(${CPM_DOWNLOAD_LOCATION})
8 changes: 8 additions & 0 deletions include/operations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef EXPR_OPERATIONS_H
#define EXPR_OPERATIONS_H
#include "operations/add.h"
#include "operations/subtract.h"
#include "operations/multiply.h"
#include "operations/divide.h"
#include "operations/boolean.h"
#endif //EXPR_OPERATIONS_H
6 changes: 6 additions & 0 deletions include/operations/add.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef EXPR_ADD_H
#define EXPR_ADD_H
#include "symbol_table.h"
symbol_value_t add(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t operator+(const symbol_value_t& a, const symbol_value_t& b);
#endif //EXPR_ADD_H
10 changes: 10 additions & 0 deletions include/operations/boolean.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef EXPR_BOOLEAN_H
#define EXPR_BOOLEAN_H
#include "symbol_table.h"
symbol_value_t and_(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t or_(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t not_(const symbol_value_t& a);
symbol_value_t operator&&(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t operator||(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t operator!(const symbol_value_t& a);
#endif //EXPR_BOOLEAN_H
6 changes: 6 additions & 0 deletions include/operations/divide.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef EXPR_DIVIDE_H
#define EXPR_DIVIDE_H
#include "symbol_table.h"
symbol_value_t divide(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t operator/(const symbol_value_t& a, const symbol_value_t& b);
#endif //EXPR_DIVIDE_H
6 changes: 6 additions & 0 deletions include/operations/multiply.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef EXPR_MULTIPLY_H
#define EXPR_MULTIPLY_H
#include "symbol_table.h"
symbol_value_t multiply(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t operator*(const symbol_value_t& a, const symbol_value_t& b);
#endif //EXPR_MULTIPLY_H
6 changes: 6 additions & 0 deletions include/operations/subtract.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef EXPR_SUBTRACT_H
#define EXPR_SUBTRACT_H
#include "symbol_table.h"
symbol_value_t subtract(const symbol_value_t& a, const symbol_value_t& b);
symbol_value_t operator-(const symbol_value_t& a, const symbol_value_t& b);
#endif //EXPR_SUBTRACT_H
24 changes: 24 additions & 0 deletions include/parser/driver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef EXPR_DRIVER_H
#define EXPR_DRIVER_H
#include <string>
#include "symbol_table.h"
#include "parser.hpp"
#define YY_DECL yy::parser::symbol_type yylex (driver& drv)
YY_DECL;

struct driver {
explicit driver(const symbol_map_t& env);
const symbol_map_t& environment{};
symbol_map_t result{};

int parse(const std::string& f);
std::string file;
bool trace_parsing;

void scan_begin();
void scan_end();
bool trace_scanning;
yy::location location;
};

#endif
23 changes: 23 additions & 0 deletions include/symbol_table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef EXPR_SYMBOL_TABLE_H
#define EXPR_SYMBOL_TABLE_H
#include <variant>
#include <map>
#include <functional>
#include <iostream>

using underlying_symbol_value_t = std::variant<int,float,bool,std::string>;
struct symbol_value_t : public underlying_symbol_value_t {
symbol_value_t() = default;
template<typename T>
symbol_value_t(const T& x) : underlying_symbol_value_t(x) {}
template<typename T>
symbol_value_t& operator=(const T& t) {
this->underlying_symbol_value_t::operator=(t);
return *this;
}
};
using symbol_map_t = std::map<std::string, symbol_value_t>;
std::ostream& operator<<(std::ostream& os, const symbol_value_t& v);
std::ostream& operator<<(std::ostream& os, const symbol_map_t& m);

#endif
8 changes: 8 additions & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef EXPR_CONFIG_H_IN_H
#define EXPR_CONFIG_H_IN_H
#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER "@PROJECT_VERSION@"
#define PROJECT_VER_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VER_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VER_PATCH @PROJECT_VERSION_PATCH@
#endif //EXPR_CONFIG_H_IN_H
45 changes: 45 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <iostream>
#include "parser/driver.h"
#include "config.h"

int main (int argc, char *argv[]) {
symbol_map_t env{};
env["false_b"] = false;
env["one_i"] = 1;
env["two_f"] = 2.0f;
env["hello_s"] = "Hello";
std::cout
<< "=================== Welcome to the " << PROJECT_NAME << " v" << PROJECT_VER << " demo ==================\n"
<< "USAGE: " << argv[0] << " [OPTIONS] EXPR_STR or '-' for using stdin\n"
<< "OPTIONS:\n"
<< " -p\t| enable tracing for the parser\n"
<< " -s\t| enable tracing for the scanner\n"
<< "\n"
<< "For this demo, a simple environment has been provided. (see below)\n"
<< "You can use these variables on the right-hand-side of your expressions\n"
<< "like so: 'a := one + 30'. Variable assignments are done atomically, so\n"
<< "statements like these: 'a := 2 ; b := a + 1' will not compile, because\n"
<< "the variable 'a' is not defined before AFTER all assignments have been\n"
<< "evaluated and performed.\n"
<< "PROVIDED ENVIRONMENT:\n"
<< env
<< "======================================================================\n" << std::endl;
try {
int res = 0;
driver drv{env};
for (int i = 1; i < argc; ++i) {
if (argv[i] == std::string("-p"))
drv.trace_parsing = true;
else if (argv[i] == std::string("-s"))
drv.trace_scanning = true;
else if (!drv.parse(argv[i]))
std::cout << drv.result;
else
res = 1;
}
return res;
} catch(const std::exception& e) {
std::cout << e.what() << std::endl;
return 1;
}
}
33 changes: 33 additions & 0 deletions src/operations/add.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "operations/add.h"
#include "util.h"
#include <sstream>

template<typename T1, typename T2>
auto t_add(const T1&, const T2&) {
throw std::domain_error((std::ostringstream{} << "Unable to add type " << typeid(T1).name() << " and " << typeid(T2).name()).str());
return nullptr; // Must return something
}
template<> auto t_add(const int& x, const int& y) {
return x + y;
}
template<> auto t_add(const float& x, const int& y) {
return x + y;
}
template<> auto t_add(const int& x, const float& y) {
return x + y;
}
template<> auto t_add(const float& x, const float& y) {
return x + y;
}
template<> auto t_add(const std::string& x, const std::string& y) {
return x + y;
}

symbol_value_t add(const symbol_value_t& a, const symbol_value_t& b) {
symbol_value_t res{};
FUNC_IMPL(a, t_add, b, res);
return res;
}
symbol_value_t operator+(const symbol_value_t& a, const symbol_value_t& b) {
return add(a,b);
}
Loading

0 comments on commit 51d6efb

Please sign in to comment.