This project provides a C library to parse a character sequence as an expression using Dijkstra's Shunting-yard algorithm, which modifies Jesse Brown's original code.
This project was developed by Brandon Amos and Vinícius Garcia.
If you want to use this library in your project please take a look at our Wiki
- Unary operators.
-
- Binary operators.
-
,/
,*
,%
,<<
,>>
,^
,&
,|
,**
- Boolean operators.
<
,>
,<=
,>=
,==
,!=
,&&
,||
- Functions.
sin
,cos
,tan
,abs
,print
- Support for an hierarchy of scopes with local scope, global scope etc.
- Easy to add new operators, operations, functions and even new types
- Easy to implement object-to-object inheritance (with the prototype concept)
- Built-in garbage collector (does not handle cyclic references yet)
cd 'my/project/dir'
git clone https://github.com/cparse/cparse.git
make release -C cparse
g -I cparse -std=c 11 cparse/builtin-features.o cparse/core-shunting-yard.o main.cpp -o main
If you want to make sure everything is working in your environment:
make test -C cparse
To customize your calculator:
- Copy the
builtin-features.cpp
file andbuiltin-features/
directory to your project. - Edit the
builtin-features/*.inc
files as you like. - Then build the project:
- Compile the library:
make release -C cparse/
- Compile your modified features:
g -I cparse -std=c 11 -c builtin-features.cpp -o my-features.o
- Link your project:
g -I cparse -std=c 11 my-features.o cparse/core-shunting-yard.o main.cpp -o main
- Compile the library:
For a more detailed guide read our Wiki advanced concepts' section:
#include <iostream>
#include "shunting-yard.h"
int main() {
TokenMap vars;
vars["pi"] = 3.14;
std::cout << calculator::calculate("-pi 1", &vars) << std::endl;
// Or if you want to evaluate an expression
// several times efficiently:
calculator c1("pi-b");
vars["b"] = 0.14;
std::cout << c1.eval(vars) << std::endl; // 3
vars["b"] = 2.14;
std::cout << c1.eval(vars) << std::endl; // 1
return 0;
}
Here we implement an interpreter for multiple expressions, the delimiter used
will be ;
or \n
just like Javascript or Python and the code must start and end on curly brackets.
A similar architecture can be used for interpreting other common programming language statements like for
loops and if
statements. If you're interested take a look on the jSpy programming language that uses this project as the core parsing system.
#include <iostream>
#include "shunting-yard.h"
#include "shunting-yard-exceptions.h"
struct codeBlock {
static void interpret(const char* start, const char** end, TokenMap vars) {
// Remove white spaces:
while (isspace(*start)) start;
if (*start != '{') {
throw syntax_error("Expected '{'");
} else {
start;
}
while (*start != '}') {
calculator::calculate(start, vars, ";\n}", &start);
// Alternatively you could write above:
// - calculator(start, ";\n}", &start).eval(vars);
// Find the beginning of the next expression:
while(isspace(*start) || *start == ';') start;
}
if (*start == '}') {
*end = start 1;
} else {
throw syntax_error("Expected '}'");
}
}
};
int main() {
GlobalScope vars;
const char* code =
"{"
" a = 10;"
" b = 20\n"
" c = a b }";
codeBlock::interpret(code, &code, vars);
std::cout << vars["c"] << std::endl; // 30
return 0;
}
Please note that a calculator can compile an expression so that it can efficiently be executed several times at a later moment.
- For more examples and a comprehensible guide please read our Wiki
- I would like to keep this library minimal so new features should be very useful to be accepted.
- If proposed change is not a common use case, I will probably not accept it.