Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added type expr_num_t allowing to replace the expr number type (Fix #2) #3

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7d000bc
added type expr_num_t allowing to replace the expr number type (Fix #2)
silvioprog Apr 27, 2020
2034c85
implemented error handling support (Fix #4)
silvioprog Apr 28, 2020
1b9ada6
added macros to customize functions pow() and fmod()
silvioprog Apr 28, 2020
b78a34d
added file .gitattributes
silvioprog Apr 28, 2020
671819b
added file .editorconfig
silvioprog Apr 28, 2020
ba7abac
added file .clang-format
silvioprog Apr 28, 2020
7640bb6
added macros allowing to customize functions for memory management an…
silvioprog Apr 28, 2020
4917e27
added function expr_calc()
silvioprog Apr 28, 2020
adaaf22
fixed makefile for "make all"
silvioprog Apr 28, 2020
5728919
added support for cmake and three examples
silvioprog Apr 28, 2020
10637db
moved files to their own directories
silvioprog Apr 28, 2020
92aaa47
enabled picky compiler options
silvioprog Apr 28, 2020
04081df
updated README file [ci skip]
silvioprog Apr 28, 2020
8ab5484
added file .cmake-format
silvioprog Apr 28, 2020
38bdda0
added target and instructions for PVS-Studio analysis
silvioprog Apr 28, 2020
9414088
fixed broken build on MSVC
silvioprog Apr 28, 2020
a9c3f59
fixed MSVC alerts
silvioprog Apr 28, 2020
38ce723
fixed MSVC alerts
silvioprog Apr 28, 2020
ac42180
scoping local block to avoid MSVC alerts
silvioprog Apr 28, 2020
1808234
fixed MSVC alerts
silvioprog Apr 28, 2020
a2bd9e6
enabled strict options of the compiler
silvioprog Apr 28, 2020
0dc3374
use memcpy() instead of strncpy() for portability
silvioprog Apr 29, 2020
4420c62
fixed PVS-Studio alerts
silvioprog Apr 29, 2020
e9b86a6
removed unused ifdef from tokenizer test
silvioprog Apr 29, 2020
5413950
muted "unused function" warning and avoided to use strncpy() for more…
silvioprog Apr 29, 2020
6324936
added new function "expr_calc2()"
silvioprog May 1, 2020
a20f68d
added error handling to "expr_calc()"
silvioprog May 2, 2020
ebf05d8
avoid error "unused function"
silvioprog May 2, 2020
f3064c7
improved the function context allowing to send any value as closure
silvioprog May 5, 2020
db26344
called function cleanup even if context is null
silvioprog May 5, 2020
106a62f
Avoided SIGSEGV if evaluate expression without functions.
silvioprog May 9, 2020
083827b
Fixed build on GCC 10.1.1.
silvioprog Jun 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
AlignTrailingComments: false
AllowShortFunctionsOnASingleLine: None
BreakBeforeTernaryOperators: false
ColumnLimit: 80
ContinuationIndentWidth: 2
IndentCaseLabels: true
IndentWrappedFunctionNames: true
ReflowComments: false
SortIncludes: false
SpaceAfterCStyleCast: true
...
10 changes: 10 additions & 0 deletions .cmake-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# How wide to allow formatted cmake files.
line_width = 80
# How many spaces to tab for indent.
tab_size = 2
# What style line endings to use in the output.
line_ending = 'unix'
# Enable comment markup parsing and reflow.
enable_markup = False
# Specify the encoding of the output file.
output_encoding = 'utf-8'
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,18 @@ expr_test
*.gcda
*.gcno
*.gcov
*.profraw
*.profdata

# Debug files
*.dSYM/
*.su

# Makefile local config
config.mk

# Build directory
build/

# VSCode directory
.vscode
61 changes: 61 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
cmake_minimum_required(VERSION 3.0.2)

project(expr C)

set(CMAKE_C_STANDARD 99)

option(EXPR_PICKY_COMPILER "Enable picky compiler options" ON)

if(NOT MSVC)
option(EXPR_PVS_STUDIO "Enable PVS-Studio analysis" OFF)
endif()

#TODO: coverage

set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)

if(EXPR_PICKY_COMPILER)
if(NOT MSVC)
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -Wall -Werror -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline"
)
endif()
endif()

include_directories(include)

if(NOT MSVC)
list(APPEND _libs m)
endif()

add_executable(example1 examples/example1.c)
target_link_libraries(example1 ${_libs})

add_executable(example2 examples/example2.c)
target_link_libraries(example2 ${_libs})

add_executable(example3 examples/example3.c)
target_link_libraries(example3 ${_libs})

add_executable(test expr_test.c)
target_link_libraries(test ${_libs})

if(EXPR_PVS_STUDIO)
include(PVS-Studio)
if(NOT CMAKE_EXPORT_COMPILE_COMMANDS)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif()
pvs_studio_add_target(
TARGET
pvs_studio_analysis
ALL
FORMAT
fullhtml
ANALYZE
example1
example2
example3
test
LOG
pvs_studio_fullhtml)
endif()
11 changes: 6 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Wextra
CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Iinclude
LDFLAGS ?= -lm -O0 -g

INC_DIR := include
TESTBIN := expr_test

all:
@echo make test - run tests
@echo make llvm-cov - report test coverage using LLVM (set LLVM_VER if needed)
@echo make gcov - report test coverage (set GCC_VER if needed)
@echo make test - run tests
@echo make llvm-cov - report test coverage using LLVM "(set LLVM_VER if needed)"
@echo make gcov - report test coverage "(set GCC_VER if needed)"

test: $(TESTBIN)
./$(TESTBIN)

$(TESTBIN): expr_test.o
$(CC) $^ $(LDFLAGS) -o $@

expr_test.o: expr_test.c expr.h expr_debug.h
expr_test.o: expr_test.c $(INC_DIR)/expr.h $(INC_DIR)/expr_debug.h

llvm-cov: CC := clang$(LLVM_VER)
llvm-cov: CFLAGS += -fprofile-instr-generate -fcoverage-mapping
Expand Down
131 changes: 80 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,88 @@ input and returns floating-point number as a result.

## Features

* Supports arithmetic, bitwise and logical operators
* Supports variables
* Can be extended with custom functions
* Simple evaluation takes ~50 nanoseconds on an average PC
* Low memory usage makes it suitable for embedded systems
* Pure C99 with no external dependencies
* Good test coverage
* Easy to understand (~600 LOC in a single header file)
- Supports arithmetic, bitwise and logical operators
- Supports variables
- Supports macros
- Can be extended with custom functions
- Error handling with error kind and position
- Simple evaluation takes ~50 nanoseconds on an average PC
- Low memory usage makes it suitable for embedded systems
- Pure C99 with no external dependencies
- Good test coverage
- Easy to understand (~600 LOC in a single header file)

## Example

```c
#include "expr.h"

// Custom function that returns the sum of its two arguments
static float add(struct expr_func *f, vec_expr_t *args, void *c) {
float a = expr_eval(&vec_nth(args, 0));
float b = expr_eval(&vec_nth(args, 1));
return a + b;
int main(void) {
printf("result: %f\n", expr_calc("2 + 3"));
return 0;
}
```

static struct expr_func user_funcs[] = {
{"add", add, NULL, 0},
{NULL, NULL, NULL, 0},
};

int main() {
const char *s = "x = 5, add(2, x)";
struct expr_var_list vars = {0};
struct expr *e = expr_create(s, strlen(s), &vars, user_funcs);
if (e == NULL) {
printf("Syntax error");
return 1;
}
Output: `result: 5.000000`

float result = expr_eval(e);
printf("result: %f\n", result);
## API

expr_destroy(e, &vars);
return 0;
}
```c
static struct expr *expr_create2(const char *s, size_t len,
struct expr_var_list *vars,
struct expr_func *funcs, int *near,
int *error);

static struct expr *expr_create(const char *s, size_t len,
struct expr_var_list *vars,
struct expr_func *funcs);
```

Output: `result: 7.000000`
Returns compiled expression from the given string. If expression uses
variables - they are bound to `vars`, so you can modify values before evaluation
or check the results after the evaluation. The `near` and `error` arguments are
used for error handling.

## API
```c
static void expr_destroy(struct expr *e, struct expr_var_list *vars);
```

`struct expr *expr_create(const char *s, size_t len, struct expr_var_list
*vars, struct expr_func *funcs)` - returns compiled expression from the given
string. If expression uses variables - they are bound to `vars`, so you can
modify values before evaluation or check the results after the evaluation.
Cleans up. Parameters can be `NULL` (e.g. if you want to clean up expression,
but reuse variables for another expression).

`float expr_eval(struct expr *e)` - evaluates compiled expression.
```c
static expr_num_t expr_eval(struct expr *e);
```

`void expr_destroy(struct expr *e, struct expr_var_list *vars)` - cleans up
memory. Parameters can be NULL (e.g. if you want to clean up expression, but
reuse variables for another expression).
Evaluates compiled expression.

```c
static struct expr_var *expr_var(struct expr_var_list *vars, const char *s,
size_t len);
```

`struct expr_var *expr_var(struct expr_var *vars, const char *s, size_t len)` -
returns/creates variable of the given name in the given list. This can be used
Returns/creates variable of the given name in the given list. This can be used
to get variable references to get/set them manually.

```c
static expr_num_t expr_calc(const char *s);
```

Takes an expression and immediately returns the result of it. If there is a parse error, `expr_calc()` returns `NAN`.

## Supported operators

* Arithmetics: `+`, `-`, `*`, `/`, `%` (remainder), `**` (power)
* Bitwise: `<<`, `>>`, `&`, `|`, `^` (xor or unary bitwise negation)
* Logical: `<`, `>`, `==`, `!=`, `<=`, `>=`, `&&`, `||`, `!` (unary not)
* Other: `=` (assignment, e.g. `x=y=5`), `,` (separates expressions or function parameters)
- Arithmetics: `+`, `-`, `*`, `/`, `%` (remainder), `**` (power)
- Bitwise: `<<`, `>>`, `&`, `|`, `^` (xor or unary bitwise negation)
- Logical: `<`, `>`, `==`, `!=`, `<=`, `>=`, `&&`, `||`, `!` (unary not)
- Other: `=` (assignment, e.g. `x=y=5`), `,` (separates expressions or function parameters)

Only the following functions from libc are used to reduce the footprint and
make it easier to use:

* calloc, realloc and free - memory management
* isnan, isinf, fmodf, powf - math operations
* strlen, strncmp, strncpy, strtof - tokenizing and parsing
- `calloc()`, `realloc()`, `free()` and `memcpy()` - memory management (all replaceable via macro)
- `isnan()`, `isinf()`, `fmod()`, `pow()` - math operations (`fmod()` and `pow()` replaceable via macro)
- `strlen()`, `strncmp()` and `snprintf()` - tokenizing and parsing (all replaceable via macro)

## Running tests

Expand All @@ -94,8 +101,30 @@ depending on whether you use GCC or LLVM/Clang.
Since people may have different compiler versions, one may specify a version
explicitly, e.g. `make llvm-cov LLVM_VER=-3.8` or `make gcov GCC_VER=-5`.

## Building with CMake

To build all the examples, tests and benchmarks using [CMake](https://cmake.org), do:

```bash
mkdir build && cd build/
cmake ..
make
```

## Running PVS Studio analysis

Download and install the PVS's command line tool [`how-to-use-pvs-studio-free`](https://github.com/viva64/how-to-use-pvs-studio-free) according its site instructions. After that, in the library root directory, perform the following commands:

```bash
how-to-use-pvs-studio-free -c 2 -m .
mkdir build && cd build/
cmake -DEXPR_PVS_STUDIO=ON ..
make pvs_studio_analysis
```

The full PVS report will be generated in the `build/pvs_studio_fullhtml` directory.

## License

Code is distributed under MIT license, feel free to use it in your proprietary
projects as well.

Loading