Code Monkey home page Code Monkey logo

cparse's Introduction

CParser • Build Status License

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.

Getting Started

If you want to use this library in your project please take a look at our Wiki

Builtin Features

  • 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)

Setup

Download and Compile

cd 'my/project/dir'
git clone https://github.com/cparse/cparse.git
make release -C cparse

Link with your project:

g++ -I cparse -std=c++11 cparse/builtin-features.o cparse/core-shunting-yard.o main.cpp -o main

Running the library tests:

If you want to make sure everything is working in your environment:

make test -C cparse

Customizing your Library

To customize your calculator:

  1. Copy the builtin-features.cpp file and builtin-features/ directory to your project.
  2. Edit the builtin-features/*.inc files as you like.
  3. Then build the project:
    1. Compile the library: make release -C cparse/
    2. Compile your modified features: g++ -I cparse -std=c++11 -c builtin-features.cpp -o my-features.o
    3. Link your project: g++ -I cparse -std=c++11 my-features.o cparse/core-shunting-yard.o main.cpp -o main

For a more detailed guide read our Wiki advanced concepts' section:

Minimal examples

As a simple calculator

#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;
}

As a sub-parser for a programming language

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.

More examples

  • For more examples and a comprehensible guide please read our Wiki

Contributing

  • 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.

cparse's People

Contributors

dimxy avatar ennorehling avatar epsilonq avatar mariadeanton avatar peacalm avatar powof2 avatar veroight avatar vingarcia avatar wille avatar zipochan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cparse's Issues

Token names cannot have UTF8 characters

I have some funky token identifiers in my language. (e.g. the 'ł', '€' and 'ð' characters), but this results in syntax errors. I assume this is because token identifiers only support ASCII characters.

Overriding the = assignement operator for a class

Hi again,

Until now i had to define the different data types and function i was going to use and everything is working great. The data we handle can be pretty big so in our framework we never load anything in memory unless we are using it, so we use files/database which means reads and writes. The reading part is all done, data is transferred and handled by the parser. Now the writing part : i need to be able to write my own assignment operator to add some data handling specific to our codebase and of course to write the results on drive.

So first i thought it was an operator i had to write, but since the operator is expecting a specific signature, with const refs to the operands, i figured it's definitely not the route to go. I checked the keyword page on the wiki and i think that defining the = as a key character is the solution. But i'm not really sure how to go about it and i'm not really sure because i want to make a version specific to my types no to all possible assignments.

i hope you can put me on the correct way to go about it , thank you again for you help !

We should improve the error messages

The error messages were not a priority until now. But it is the moment to fix them.

We have currently 2 major problems:

  1. Some error messages just don't make any sense.
  2. Currently it is not possible to keep track of the line where the error happened.

The calculator should count how many new line characters have been parsed, and keep it as an available variable while compiling and evaluating as well.

operator precedence special case

Hi i did not know how to name the issue but here is an explanation :
My goal is to be able to write this :

W("name1").L("name2")

I have done this the way i wanted to with different classes, next i have to implement different operations possible between my classes. My issue is when using operators , when i write :

W("name1").L("name2") + W("name1").L("name3")

it tries to add the data returned by the expression on the left to the function W (and i'm trying to make add up the two results) , of course when i put the brackets around the second expression it works fine, but i want to force the parser to evaluate the whole expression without putting the brackets.

thanks in advance !

Consider adding a namespace to cparse code

I suggest adding namespace around the cparse code.
(May be useful as I had name conflicts when I was adding cparse to my project, probably other devs might have as well)

Defining new Operations is overly complicated

After writing the Wiki about Defining New Operations I realized that it is overly complicated.

The mask concept makes it hard to explain, and harder to understand the code.

We might still keep the mask concept, but if we do so, the user should not be exposed to it.

For example if instead of making a function that returns a mask, we make a function that returns only the type of the left operand and other for the right operand, and then we create the mask our selfs inside the Operation class constructor for example.

It should not be difficult to implement, but I might not have the time to do it. Also there is the problem of considering all the trade offs, and other possible ways to define it.

Anyway, the way it is it's complicated, we should think about it, and change it in the future.

Warning c4099 is everywhere.

hi great job,
but my output window is filled with all these warnings, can this be fixed, thank you.

1>C:\cparse\containers.h(38,17): warning C4099: 'Iterator': type name first seen using 'class' now seen using 'struct'
1>C:\cparse\containers.h(38): message : see declaration of 'Iterator'
1>C:\cparse\containers.h(64,17): warning C4099: 'TokenMap': type name first seen using 'class' now seen using 'struct'
1>C:\cparse\containers.h(64): message : see declaration of 'TokenMap'
1>C:\cparse\containers.h(136,18): warning C4099: 'TokenList': type name first seen using 'class' now seen using 'struct'
1>C:\cparse\containers.h(136): message : see declaration of 'TokenList'
1>C:\cparse\shunting-yard.h(348,16): warning C4099: 'opMap_t': type name first seen using 'class' now seen using 'struct'

handle_unary() not working?

Hi,

The following unary expressions break:

Expr '3 * -4' -> -4
Expr '3 / -4' -> inf
Expr '3 - -4' -> -1
Expr '3 + -4' -> -1

Code used is very basic:

#include <iostream>
#include "shunting-yard.h"

int main(int argc, char* argv[])
{
    TokenMap vars;
    vars["pi"] = 3.14;

    std::string str;
    for (int idx = 1; idx < argc; ++idx) {
        if (idx > 1)
            str += ' ';
        str += argv[idx];
    }
    std::cout << "Expr: '" << str << "'\n";
    std::cout << calculator::calculate(str.c_str(), &vars) << std::endl;
    return 0;
}

It seems rpnBuilder::handle_unary() is not creating the correct RPN output for the above cases; debugged RPN output of the above show:

compile: '3 * -4'
    RPN: TokenINT(3) TokenINT(0) TokenOP(*) TokenINT(4) TokenOP(-)

compile: '3 / -4'
    RPN: TokenINT(3) TokenINT(0) TokenOP(/) TokenINT(4) TokenOP(-)

compile: '3 + -4'
    RPN: TokenINT(3) TokenINT(0) TokenOP(+) TokenINT(4) TokenOP(-)

compile: '3 - -4'
    RPN: TokenINT(3) TokenINT(0) TokenOP(-) TokenINT(4) TokenOP(-)

The default map should keep insertion order

The current TokenMap class uses the std::map implementation. This map is alright but if you try to iterate over it, e.g.:

TokenMap vars;
calculator::calculate("a = map('c': 1, 'b': 2, 'a': 3)", vars);
std::cout << vars["a"] << std::endl; // { 'a': 3, 'b': 2, 'c': 1 } (alphanumerical order)

It shows the keys in alphanumerical order instead of the insertion order. This is undesired since Javascript or Ruby users would expect it to remember the insertion order.

This feature should be set either hardcoded or as an optional feature (but i think no one will really complain if its hardcoded, so it might be for the best)

The only drawback of the hardcoded approach is that it might be sightly less space efficient, I think.

Should we mimic Python operators and syntax?

Hello @bamos, with the multi-type support I am presenting on my pull request we will have the option to work with:

  • Boolean types
  • String operations

etc.

What if we try to mimic python operations and syntax.

Until now we were following C standarts, so true is the same as 1 and false the same as 0,
there were no boolean type, and no string operations.

Do you think it would be a good idea to borrow syntax from Python? I like it and it is usually easy for the user to understand what is happening.

This changes would imply in:

  1. power operator would be changed from ^ to **
  2. true reserved word would change to True, and the same for false
  3. We would have string concatenation with the operator +

And some other stuff.

What do you think? Is this a good a idea? May I proceed?

Doesn't compile under clang 8.

Most of this is just pedantic grumbling, but the conversion from 'long' to 'const packToken' is ambiguous error looks legit and is preventing it from compiling.

$ git clone https://github.com/cparse/cparse.git
Cloning into 'cparse'...
remote: Enumerating objects: 1134, done.
remote: Total 1134 (delta 0), reused 0 (delta 0), pack-reused 1134
Receiving objects: 100% (1134/1134), 509.58 KiB | 2.01 MiB/s, done.
Resolving deltas: 100% (776/776), done.
$ cd cparse; git rev-parse HEAD
a9f26860283c2653f8a0fcb003cbb07f21b690af
$ make
c++ -std=c++11 -Wall -pedantic -Wmissing-field-initializers -Wuninitialized -g  -c test-shunting-yard.cpp -o test-shunting-yard.o -g 
In file included from test-shunting-yard.cpp:6:
In file included from ././shunting-yard.h:155:
././containers.h:38:1: warning: 'Iterator' defined as a struct here but previously declared as a class
      [-Wmismatched-tags]
struct Iterator : public Iterable {
^
././containers.h:27:1: note: did you mean struct here?
class Iterator;
^~~~~
struct
././containers.h:63:1: warning: 'TokenMap' defined as a struct here but previously declared as a class
      [-Wmismatched-tags]
struct TokenMap : public Container<MapData_t>, public Iterable {
^
././containers.h:50:1: note: did you mean struct here?
class TokenMap;
^~~~~
struct
././shunting-yard.h:147:1: note: did you mean struct here?
class TokenMap;
^~~~~
struct
In file included from test-shunting-yard.cpp:6:
In file included from ././shunting-yard.h:155:
././containers.h:135:1: warning: 'TokenList' defined as a struct here but previously declared as a
      class [-Wmismatched-tags]
struct TokenList : public Container<TokenList_t>, public Iterable {
^
././shunting-yard.h:148:1: note: did you mean struct here?
class TokenList;
^~~~~
struct
././shunting-yard.h:242:37: warning: 'const' qualifier on function type 'rWordParser_t' (aka 'void
      (const char *, const char **, rpnBuilder *)') has no effect [-Wignored-qualifiers]
  void add(const std::string& word, const rWordParser_t* parser) {
                                    ^~~~~~
././shunting-yard.h:247:20: warning: 'const' qualifier on function type 'rWordParser_t' (aka 'void
      (const char *, const char **, rpnBuilder *)') has no effect [-Wignored-qualifiers]
  void add(char c, const rWordParser_t* parser) {
                   ^~~~~~
././shunting-yard.h:347:1: warning: 'opMap_t' defined as a struct here but previously declared as a
      class [-Wmismatched-tags]
struct opMap_t : public std::map<std::string, opList_t> {
^
././shunting-yard.h:214:1: note: did you mean struct here?
class opMap_t;
^~~~~
struct
test-shunting-yard.cpp:925:26: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~10l);
                         ^~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:928:28: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == 2 * ~10l);
                         ~~^~~~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:931:28: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == 2 * ~(10l*3));
                         ~~^~~~~~~~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:964:26: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~10l);
                         ^~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:967:26: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~(2*10l));
                         ^~~~~~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:970:35: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~(2*10l) * 3);
                         ~~~~~~~~~^~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:974:26: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~10l);
                         ^~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:977:28: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == 2 * ~10l);
                         ~~^~~~~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:980:35: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == 2 * ~10l * 3);
                         ~~~~~~~~~^~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:984:35: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == 2 * ~10l * 3);
                         ~~~~~~~~~^~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:987:35: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~(2*10l) * 3);
                         ~~~~~~~~~^~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
test-shunting-yard.cpp:990:35: error: conversion from 'long' to 'const packToken' is ambiguous
    REQUIRE(c1.eval() == ~(2*10l) * 3);
                         ~~~~~~~~~^~~
./catch.hpp:10436:46: note: expanded from macro 'REQUIRE'
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
                                             ^~~~
./catch.hpp:2076:41: note: expanded from macro 'INTERNAL_CATCH_TEST'
    } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime bu...
                                        ^~~~
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
In file included from test-shunting-yard.cpp:4:
./catch.hpp:1336:44: error: conversion from 'long' to 'const packToken' is ambiguous
            return bool( opCast( lhs ) ==  opCast( rhs ) );
                                           ^~~~~~~~~~~~~
./catch.hpp:1381:39: note: in instantiation of member function 'Catch::Internal::Evaluator<packToken,
      long, Catch::Internal::Operator::IsEqualTo>::evaluate' requested here
        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
                                      ^
./catch.hpp:1874:39: note: in instantiation of function template specialization
      'Catch::Internal::compare<Catch::Internal::Operator::IsEqualTo, packToken, long>' requested here
            .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
                                      ^
./catch.hpp:1817:16: note: in instantiation of function template specialization
      'Catch::ExpressionLhs<const packToken &>::captureExpression<Catch::Internal::Operator::IsEqualTo,
      long>' requested here
        return captureExpression<Internal::IsEqualTo>( rhs );
               ^
test-shunting-yard.cpp:925:23: note: in instantiation of function template specialization
      'Catch::ExpressionLhs<const packToken &>::operator==<long>' requested here
    REQUIRE(c1.eval() == ~10l);
                      ^
././packToken.h:25:3: note: candidate constructor
  packToken(int i) : base(new Token<int64_t>(i, INT)) {}
  ^
././packToken.h:26:3: note: candidate constructor
  packToken(int64_t l) : base(new Token<int64_t>(l, INT)) {}
  ^
././packToken.h:27:3: note: candidate constructor
  packToken(bool b) : base(new Token<uint8_t>(b, BOOL)) {}
  ^
././packToken.h:28:3: note: candidate constructor
  packToken(size_t s) : base(new Token<int64_t>(s, INT)) {}
  ^
././packToken.h:29:3: note: candidate constructor
  packToken(float f) : base(new Token<double>(f, REAL)) {}
  ^
././packToken.h:30:3: note: candidate constructor
  packToken(double d) : base(new Token<double>(d, REAL)) {}
  ^
6 warnings and 13 errors generated.
make: *** [test-shunting-yard.o] Error 1

Compiler:

$ clang -v
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Invalid operators, but with ALL operators

All my expressions (on Windows) throw the "Invalid operator" exception and I can't figure out why. Here's the error:

`C:\msys64\mingw64\bin\g++.exe -fdiagnostics-color=always -g C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp -o C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.exe
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/builtin-features.inc:27,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:43:
C:/Users/Jg_747/iCloudDrive/Desktop/Progetti/Pseudo/Current/cparse/builtin-features/functions.inc: In function 'cparse::packToken builtin_functions::default_type(cparse::TokenMap)':
C:/Users/Jg_747/iCloudDrive/Desktop/Progetti/Pseudo/Current/cparse/builtin-features/functions.inc:104:8: error: reference to 'INT' is ambiguous
104 | case INT: return "integer";
| ^~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:38:
C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/shunting-yard.h:38:3: note: candidates are: 'cparse::tokType cparse::INT'
38 | INT = 0x22, // == 0x20 + 0x2 => Integral numbers.
| ^~~
In file included from C:/msys64/mingw64/x86_64-w64-mingw32/include/minwindef.h:163,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/windef.h:9,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/windows.h:69,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/rpc.h:16,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/urlmon.h:7,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:35:
C:/msys64/mingw64/x86_64-w64-mingw32/include/winnt.h:289:15: note: 'typedef int INT'
289 | typedef int INT;
| ^~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/builtin-features.inc:27,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:43:
C:/Users/Jg_747/iCloudDrive/Desktop/Progetti/Pseudo/Current/cparse/builtin-features/functions.inc:105:8: error: reference to 'BOOL' is ambiguous
105 | case BOOL: return "boolean";
| ^~~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:38:
C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/shunting-yard.h:39:3: note: candidates are: 'cparse::tokType cparse::BOOL'
39 | BOOL = 0x23, // == 0x20 + 0x3 => Boolean Type.
| ^~~~
In file included from C:/msys64/mingw64/x86_64-w64-mingw32/include/windef.h:9,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/windows.h:69,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/rpc.h:16,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/urlmon.h:7,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:35:
C:/msys64/mingw64/x86_64-w64-mingw32/include/minwindef.h:131:15: note: 'typedef int BOOL'
131 | typedef int BOOL;
| ^~~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/builtin-features.inc:29,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:43:
C:/Users/Jg_747/iCloudDrive/Desktop/Progetti/Pseudo/Current/cparse/builtin-features/operations.inc: In constructor 'builtin_operations::Startup::Startup()':
C:/Users/Jg_747/iCloudDrive/Desktop/Progetti/Pseudo/Current/cparse/builtin-features/operations.inc:362:28: error: reference to 'BOOL' is ambiguous
362 | opMap.add({UNARY, "!", BOOL}, &UnaryNotOperation);
| ^~~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:38:
C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/shunting-yard.h:39:3: note: candidates are: 'cparse::tokType cparse::BOOL'
39 | BOOL = 0x23, // == 0x20 + 0x3 => Boolean Type.
| ^~~~
In file included from C:/msys64/mingw64/x86_64-w64-mingw32/include/windef.h:9,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/windows.h:69,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/rpc.h:16,
from C:/msys64/mingw64/x86_64-w64-mingw32/include/urlmon.h:7,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:35:
C:/msys64/mingw64/x86_64-w64-mingw32/include/minwindef.h:131:15: note: 'typedef int BOOL'
131 | typedef int BOOL;
| ^~~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/builtin-features.inc:29,
from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:43:
C:/Users/Jg_747/iCloudDrive/Desktop/Progetti/Pseudo/Current/cparse/builtin-features/operations.inc:362:14: error: cannot convert '' to 'cparse::opSignature_t'
362 | opMap.add({UNARY, "!", BOOL}, &UnaryNotOperation);
| ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\Pseudo.cpp:38:
C:\Users\Jg_747\iCloudDrive\Desktop\Progetti\Pseudo\Current\cparse/shunting-yard.h:360:32: note: initializing argument 1 of 'void cparse::opMap_t::add(cparse::opSignature_t, cparse::Operation::opFunc_t)'
360 | void add(const opSignature_t sig, Operation::opFunc_t func) {
| ~~~~~~~~~~~~~~~~~~~~^~~

La compilazione è terminata con errore/i.`

Thread Safety

I was trying to use cparse in multi threaded application as a expression evaluator and varriable manager. I had to use static mutex in packToken class to ensure thread safety, the performance obviously took a hit. Is there a better way to do this?

Thanks

terminating with uncaught exception of type syntax_error: Invalid operator: -

Followed the directions fore getting started; compiles fine but when running I'm getting this error:

terminating with uncaught exception of type syntax_error: Invalid operator: -

File: main.cpp

#include <iostream>
#include "shunting-yard.h"

int main() {
    TokenMap vars;
    vars["pi"] = 3.14;
    std::cout << calculator::calculate("-pi+1", &vars) << std::endl;

    return 0;
}

Garbage Collector List destructor

Hi, so i'm having an issue that i can't really figure out i'm using a list to store a BIG array of doubles so the copying goes fine, but once in my assignment function i write the same data on file then exit the function BUT the program won't go out and keeps calling the List destructor. i've tried letting it do it for hours and still the same issue it never get out of it.

If statement (this is not an issue with cparse)

Hi again @VinGarcia ,

So i'm almost done with my project , really happy to have chosen cparse as the parser !

Since you have already done it for your own language , i'm trying to implement a simple if statement
IF(expression, value if true, value if false) i was thinking about implementing it as a function, but i don't how to go about and somewhat i have an intuition that there is a "correct" way to go about it.

thanks in advance you have been really helpful 👍 !

Garbage collector cycle problem

Currently we use a reference counting system for garbage collecting. I think it is specially efficient with the current architecture because we only need to manage the objects and lists, avoiding the worse part of the draw back of incrementing and decrementing the counter too often.

But there is still a problem to concern, namely the reference counting mechanism can not delete structures if they have cyclic reference among them, because it implies that each of them will always have ate least 1 reference, and thus can never be safely delocated.

We should think about how to fix it. I bet JavaScript uses reference counting since our engine seems to be very similar to theirs, and they have for sure found a solution. Another language I know uses reference counting is Python, and we could probably learn something from them as well.

Compiling on windows using visual Studio

Hi , i want to use this parser in a project we are currently working on.

We are developing on windows and use visual studio, i have always been the IDE guy so i have no idea how makefiles work i tried to create a project adding the files (following my understanding of the makefile - yes i did some reading on makefiles-) but i can't get it to work.

if i understand correctly , there are 2 libraries one is core and the second is the builtin fuctions so i need to create the first .lib file using the files of the core, then compile the built-in-features library that is dependent on the core one.

and of course there is the testing file.

i hope it's not too much to ask, i really like the project and it fits perfectly for my needs specially the fact that i can augment it with my own features.

Thank you in advance.

packToken::str() function breaks on cyclic data types

Currently the packToken::str() function when printing a Map or a List will try to recursively print the entire tree of values. If one these values forms a cycle this recursion will go on until the stack overflows.

We should set a recursion limit for this function.

visual studio 2019 + mfc + cparser

hi.
In MFC defined type BOOL. But cparser redefines identifier BOOL in shunting-yard.h at line 37.
I don't know what i do now. I can't change MFC BOOL. May be change BOOL in shunting-yard.h?
Do you have solution?

calculator ignores variable

I wrote a simple code example:

#include <iostream>
#include "shunting-yard.h"
int main() {
  TokenMap vars;
  calculator c1("v1 v2");
  std::cout << c1.eval(vars) << std::endl; // outputs "v2"
  return 0;
}

This prints "v2" and looks like it ignores v1 completely. I wonder why. I expected this expression would output a syntax error because of the omitted operator between v1 and v2. And this is also not two separate statements (as there is no ';' between them). What does this expression mean to the calculator?

group/vector operation possible? Naive iteration can be expensive when number of elements is huge

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

**vars["b"] = {0.1, 0.2};
std::cout << c1.eval(vars) << std::endl;**

return 0;

}

It doesn't compile in Windows and Linux, am I doing something wrong?

For Ubuntu I followed your Linux setup guide and when I compile the code this is the error given:
https://pastebin.com/nhBt5KPX

Then I tried on VS Code but I can't compile there too, and it give me this error:
https://pastebin.com/SUE7UgbA

The VS Code "task.json" for compiling is the following:
{ "version": "2.0.0", "tasks": [ { "type": "cppbuild", "label": "C/C++: g++.exe compila il file attivo", "command": "C:\\msys64\\mingw64\\bin\\g++.exe", "args": [ "-std=c++11", "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}\\${fileBasenameNoExtension}.exe" ], "options": { "cwd": "${fileDirname}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "compilatore: C:\\msys64\\mingw64\\bin\\g++.exe" } ] }

I don't know if I'm dumb and I'm doing something wrong but I can't figure out how to fix even if I read the issue #55 and the other one about the same problem.

Sorry for the disturb and I appreciate if you can help me

We should add a builtin BOOL type

Currently we represent boolean types as C would, 0 means false, and != 0 means true.

This works and is not bad, but if our users want to have a BOOL type they would be forced to implement one themselfs, and that would be an unnecessary complication.

Since having a BOOL type is quite common in programming languages we should define a builtin one on our system.

strtol() in calculator::toRPN can't support 64bit integers on MinGW, Windows

Hi, cparse is work pretty well on Linux. But when I compile it by MinGW on Windows, the high 32 bits of numbers is allways 0.
It seems long int strtol(...) function return 32-bit integer, rather than 64-bit integer on MinGW/Windows, because of the data model of Windows is LLP.
related code:

      // If the token is a number, add it to the output queue.
      int64_t _int;
      _int = strtol(expr, &nextChar, 10);

Maybe the strtoll() is better?

Memory allocation patterns

My application is pre-compiling symbolic expressions in calculator instances and then repeatedly eval'ing them at runtime. Obviously, the compilation stage allocates lots of TokenXX objects as it builds up the RPN list, but it looks like it then goes ahead and allocates essentially a parallel set on each evaluation:

cparse/shunting-yard.cpp

Lines 494 to 502 in a9f2686

TokenBase* calculator::calculate(const TokenQueue_t& rpn, TokenMap scope,
const Config_t& config) {
evaluationData data(rpn, scope, config.opMap);
// Evaluate the expression in RPN form.
std::stack<TokenBase*> evaluation;
while (!data.rpn.empty()) {
TokenBase* base = data.rpn.front()->clone();
data.rpn.pop();

For an application where memory allocation (or at least syscalls) need to be avoided at runtime, it'd be great to have the expression evaluator able to work without making new allocations (or perhaps with the option of a pre-allocated value stack for the RPN engine).

Numeral operations should return integers when possible

Currently all numeral operations can only return tokens of the REAL type.

It would be expected that if both operands are of type INT that the resulting token would also be of type INT, as it is in most modern languages.

Broken link in README.md

Link in "As a sub-parser for a programming language." section of README.md file to "jSpy programming language" raises 404 error.

Example compilation command in README.md doesn't work for me

If I follow the instructions under Getting Started / Setup, I get an error at the "Link with your project" stage. This happens on macOS and Linux. For main.cpp, I am using the example provided later under "Minimal examples".
The first problem is

$ g++ cparse/builtin-features.o cparse/core-shunting-yard.o main.cpp -o main
main.cpp:2:27: fatal error: shunting-yard.h: No such file or directory

because in the example, compilation happens outside the cparse directory, and cparse is not on the include path. Adding -Icparse to the command fixes this of course.

The next error is

$ g++ cparse/builtin-features.o cparse/core-shunting-yard.o main.cpp -o main -I cparse
In file included from main.cpp:2:0:
cparse/shunting-yard.h:23:9: error: ‘uint8_t’ does not name a type
 typedef uint8_t tokType_t;

This is because uint8_t is a C++11 feature, and this compiler (g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)), does not support C++11 by default. You need to add -std=c++11.

If I use

g++ -std=c++11 cparse/builtin-features.o cparse/core-shunting-yard.o main.cpp -o main -I cparse

it compiles fine. Should the command in the README be updated?

Link operations to operators problem.

I've defined non-strict comparison operator '=%':

opp.add("==", 9); opp.add("!=", 9); opp.add("=%", 9);

But it won't match 'ANY_OP' pattern, so this do not work:

opMap.add({ARR, ANY_OP, ANY_TYPE}, &ArrayOnAnyOperation);

And I have to define it explicitly:

opMap.add({ARR, ANY_OP, ANY_TYPE}, &ArrayOnAnyOperation);
opMap.add({ARR, "=%", ANY_TYPE}, &ArrayOnAnyOperation);

Operator recognition problem.

With opMap:

       OppMap_t& opp = calculator::Default().opPrecedence;
        opp.add("[]", 2); opp.add("()", 2); opp.add(".", 2);
        opp.add("**", 3);
        opp.add("*",  5); opp.add("/", 5); opp.add("%", 5);
        opp.add("+",  6); opp.add("-", 6);
        opp.add("<<", 7); opp.add(">>", 7);
        opp.add("<",  8); opp.add("<=", 8); opp.add(">=", 8); opp.add(">", 8);
        opp.add("==", 9); opp.add("!=", 9); opp.add("=%", 9);
        opp.add("&&", 13);
        opp.add("||", 14);
        opp.add("=", 15); opp.add(":", 15);
        opp.add(",", 16);

Parsing expression 'function() == [1,2,3]' - ok, but 'function()==[1,2,3]' gives 'Invalid operator: ==[' exception.

Custom operations with LValues should be possible

LValues are Left Values in an assignment, e.g.: a = b, i.e. the values that are linked to a named variable.

Currently, there is only one operation that changes the value of a variable and that is the assignment operator =.

An example of another custom operator that changes the value of a variable is the increment and decrement operators of C++, e.g.: a++ and a--. These are not yet possible using CParse.

So, this issue consists of:

  1. Adding this new feature
  2. Moving the assignment operator to a built-in operation.

This would allow us to create the ++ operator and also to change the default assignment operator to something different than =, e.g.: <- or :=.

Syntax errors cause crashes

Hello.
This simple piece of code causes a crash due to an error in the provided expression. The text "Some error" never gets printed.

#include <cparse/shunting-yard.h>

int main()
{
    TokenMap tm;
    tm["x"] = 5;

    try
    {
        calculator calc;
        calc.compile("5&&&x"); // I would expect this to throw an error, or set some flag I could check
        printf("%f", calc.eval(tm).asDouble());
    } 
    catch (...)
    {
        puts("Some error");
    }
}

Changing the expression to e.g. 5*x makes it print the correct result.
This is very unfortunate, since I expect the users of my application to be notified of an error if they input an invalid expression. Is there anything I'm doing wrong or is there any other mechanism for validating the expression?

I'm compiling with gcc on 64-bit MinGW. Everything links successfully.

g++ -std=c++11 main.cpp -I../includes ../libs/cparse/core-shunting-yard.o ../libs/cparse/my-features.o

Also setting the expression to e.g. 5x does not crash the application, but doesn't throw errors either. It evaluates to x, though. However, setting it to sin(5x) does crash it. sin(5*x) works correctly.

The crash kind of looks like a stack overflow to me. Perhaps these expressions cause infinite recursion somewhere in the code?

Now, I have defined a custom function (logarithm) and a custom operator (caret for exponentiation). Those work and I don't think they have anything to do with this problem, but it's worth mentioning anyway.

"ld: unknown option: -O1" when compiling on macOS

When compiling on macOS, I get

$ make release -C cparse
...
ld -r -O1 shunting-yard.o packToken.o functions.o containers.o -o core-shunting-yard.o
ld: unknown option: -O1

because -O1 is not a valid option to the macOS (BSD) linker. I've not come across the -O flag at link time before; is it necessary here? It seems to be gcc-specific. I'd just drop it for simplicity and portability if it's not really necessary.

This could be defined in LDFLAGS in the Makefile, which would allow the user to override LDFLAGS to either add or remove the -O1 option depending on whether you decided to keep it or not.

Passing a pointer to the parser

Hi , it's me again ^^.

I'm currently working on some features (the end goal is to write a little scripting language for a module in our software). I have to pass a pointer of a class to the parser, so i followed your example and stored the pointer as an integer :

parser_data["project"] = reinterpret_cast<int64_t>(_project.data());
std::cout << calculator::calculate("wa = Accessor(project)", parser_data) << "\n";

where Accessor is my constructor for my class and _project is a shared pointer of my project type, but i get an error when trying to read back the integer from the map and the exception token is not a number is thrown :

auto project = reinterpret_cast<datamanager::FPDProject*>(scope["fpdProject"].asInt());

it seemed like a good idea but it doesn't work.

Passing variables by references to the map

Hi folks,

I forked the project and added the support for adding variables by references to a TokenMap instance, in addition to the existing extension by values. Example :

double alpha(0.0);
vars["gain"] = std::reference_wrapper(alpha);

The variables get updated upon assignment in a calculation and there is no need to consider the output stream. Let me know if you think it would be worth a contribution.

Thanks.

Jean-Pierre

Memory leak due to lack of std::forward

Lack of proper forwarding is causing packToken(const packToken&) to be called instead of packToken(packToken&&), given this, the destructor of the local pass in arguments was also not being c
valgrind:memcheck 387,960 bytes in 16,165 blocks are definitely lost in loss record 261 of 267
valgrind:memcheck at 0x4C2A203: operator new(unsigned long) (vg_replace_malloc.c:334)
valgrind:memcheck by 0x1D7FD5A: Token::clone() const (shunting-yard.h:71)
valgrind:memcheck by 0x1D67CB9: RefToken::RefToken(packToken, TokenBase*, packToken) (shunting-yard.h:285)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.