diff --git a/modcc/expression.hpp b/modcc/expression.hpp
index b2eb6e463c254a7d5182fec4e55ad6f9e36c9e8e..a9f752ec382fbac4df6ff83000bdb03532a3eb4b 100644
--- a/modcc/expression.hpp
+++ b/modcc/expression.hpp
@@ -877,7 +877,7 @@ public:
{}
std::string to_string() const override {
- return pprintf("%%", coeff()->to_string(), ident()->to_string());
+ return pprintf("% %", coeff()->to_string(), ident()->to_string());
}
void semantic(std::shared_ptr<scope_type> scp) override;
expression_ptr clone() const override;
diff --git a/modcc/lexer.cpp b/modcc/lexer.cpp
index 74da1a07eed937ef20d94f0df4f26a1a34dc960b..2528f19297c38be9330e5c2b4cbed7d04a6ffe05 100644
--- a/modcc/lexer.cpp
+++ b/modcc/lexer.cpp
@@ -119,6 +119,10 @@ Token Lexer::parse() {
t.type = tok::rbrace;
t.spelling += character();
return t;
+ case '~':
+ t.type = tok::tilde;
+ t.spelling += character();
+ return t;
case '=': {
t.spelling += character();
if(*current_=='=') {
diff --git a/modcc/parser.cpp b/modcc/parser.cpp
index 74692fc91c4f32670e90011ed103d06ac6561070..173da807b6287969c18874230725b0d2f99582f1 100644
--- a/modcc/parser.cpp
+++ b/modcc/parser.cpp
@@ -967,11 +967,18 @@ expression_ptr Parser::parse_stoich_expression() {
auto here = location_;
if(token_.type==tok::integer || token_.type==tok::identifier) {
- terms.push_back(parse_stoich_term());
+ auto term = parse_stoich_term();
+ if (!term) return nullptr;
+
+ terms.push_back(std::move(term));
while(token_.type==tok::plus) {
get_token(); // consume plus
- terms.push_back(parse_stoich_term());
+
+ auto term = parse_stoich_term();
+ if (!term) return nullptr;
+
+ terms.push_back(std::move(term));
}
}
@@ -981,37 +988,48 @@ expression_ptr Parser::parse_stoich_expression() {
expression_ptr Parser::parse_reaction_expression() {
auto here = location_;
- // consume tilde
- get_token();
+ if(token_.type!=tok::tilde) {
+ error(pprintf("expected '%', found '%'", yellow("~"), yellow(token_.spelling)));
+ return nullptr;
+ }
+ get_token(); // consume tilde
expression_ptr lhs = parse_stoich_expression();
+ if (!lhs) return nullptr;
if(token_.type != tok::arrow) {
error(pprintf("expected '%', found '%'", yellow("<->"), yellow(token_.spelling)));
return nullptr;
}
+ get_token(); // consume arrow
expression_ptr rhs = parse_stoich_expression();
+ if (!rhs) return nullptr;
if(token_.type != tok::lparen) {
error(pprintf("expected '%', found '%'", yellow("("), yellow(token_.spelling)));
return nullptr;
}
+ get_token(); // consume lparen
expression_ptr fwd = parse_expression();
+ if (!fwd) return nullptr;
if(token_.type != tok::comma) {
error(pprintf("expected '%', found '%'", yellow(","), yellow(token_.spelling)));
return nullptr;
}
+ get_token(); // consume comma
expression_ptr rev = parse_expression();
+ if (!rev) return nullptr;
if(token_.type != tok::rparen) {
error(pprintf("expected '%', found '%'", yellow(")"), yellow(token_.spelling)));
return nullptr;
}
+ get_token(); // consume rparen
return make_expression<ReactionExpression>(here, std::move(lhs), std::move(rhs),
std::move(fwd), std::move(rev));
}
diff --git a/tests/modcc/driver.cpp b/tests/modcc/driver.cpp
index f32ac59d1471f94c6fb3d7d6333eeaf86d2e1c28..67505a0d20d3406d1ed8142715fb2243e74c6b0b 100644
--- a/tests/modcc/driver.cpp
+++ b/tests/modcc/driver.cpp
@@ -2,10 +2,17 @@
* unit test driver
**************************************************************/
+#include <cstring>
+
#include "test.hpp"
+bool g_verbose_flag = false;
+
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ if (argc>1 && (!std::strcmp(argv[1],"-v") || !std::strcmp(argv[1],"--verbose"))) {
+ g_verbose_flag = true;
+ }
return RUN_ALL_TESTS();
}
diff --git a/tests/modcc/test.hpp b/tests/modcc/test.hpp
index 588b75a36afcb09107adc65b862fd65eceb5a9c6..d3de72506d2ef679227fcc71f0d76aab141b3a9c 100644
--- a/tests/modcc/test.hpp
+++ b/tests/modcc/test.hpp
@@ -5,12 +5,9 @@
#include "parser.hpp"
#include "modccutil.hpp"
-//#define VERBOSE_TEST
-#ifdef VERBOSE_TEST
-#define VERBOSE_PRINT(x) std::cout << (x) << std::endl;
-#else
-#define VERBOSE_PRINT(x)
-#endif
+extern bool g_verbose_flag;
+
+#define VERBOSE_PRINT(x) (g_verbose_flag && std::cout << (x) << "\n")
inline expression_ptr parse_line_expression(std::string const& s) {
return Parser(s).parse_line_expression();
diff --git a/tests/modcc/test_lexer.cpp b/tests/modcc/test_lexer.cpp
index b881f5a0a69c344a8ae0a6e9f8ebd2adc46ea495..b8ba84d92c85ac3bb8efc5533c1115efb031d12d 100644
--- a/tests/modcc/test_lexer.cpp
+++ b/tests/modcc/test_lexer.cpp
@@ -1,11 +1,37 @@
#include <cmath>
#include <iterator>
+#include <utility>
#include "test.hpp"
#include "lexer.hpp"
-//#define PRINT_LEX_STRING std::cout << "________________\n" << string << "\n________________\n";
-#define PRINT_LEX_STRING
+void verbose_print(const char* string) {
+ if (!g_verbose_flag) return;
+ std::cout << "________________\n" << string << "\n________________\n";
+}
+
+void verbose_print(const Token& token) {
+ if (!g_verbose_flag) return;
+ std::cout << "tok: " << token << "\n";
+}
+
+class VerboseLexer: public Lexer {
+public:
+ template <typename... Args>
+ VerboseLexer(Args&&... args): Lexer(std::forward<Args>(args)...) {
+ if (g_verbose_flag) {
+ std::cout << "________________\n" << std::string(begin_, end_) << "\n________________\n";
+ }
+ }
+
+ Token parse() {
+ auto tok = Lexer::parse();
+ if (g_verbose_flag) {
+ std::cout << "token: " << tok << "\n";
+ }
+ return tok;
+ }
+};
/**************************************************************
* lexer tests
@@ -13,8 +39,7 @@
// test identifiers
TEST(Lexer, identifiers) {
char string[] = "_foo:\nbar, buzz f_zz";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ VerboseLexer lexer(string, string+sizeof(string));
auto t1 = lexer.parse();
EXPECT_EQ(t1.type, tok::identifier);
@@ -43,9 +68,8 @@ TEST(Lexer, identifiers) {
// test keywords
TEST(Lexer, keywords) {
- char string[] = "NEURON UNITS SOLVE else TITLE CONDUCTANCE";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ char string[] = "NEURON UNITS SOLVE else TITLE CONDUCTANCE KINETIC";
+ VerboseLexer lexer(string, string+sizeof(string));
// should skip all white space and go straight to eof
auto t1 = lexer.parse();
@@ -69,19 +93,22 @@ TEST(Lexer, keywords) {
EXPECT_NE(t5.type, tok::identifier);
EXPECT_EQ(t5.spelling, "TITLE");
+ auto t6 = lexer.parse();
+ EXPECT_EQ(t6.type, tok::conductance);
+ EXPECT_EQ(t6.spelling, "CONDUCTANCE");
+
auto t7 = lexer.parse();
- EXPECT_EQ(t7.type, tok::conductance);
- EXPECT_EQ(t7.spelling, "CONDUCTANCE");
+ EXPECT_EQ(t7.type, tok::kinetic);
+ EXPECT_EQ(t7.spelling, "KINETIC");
- auto t6 = lexer.parse();
- EXPECT_EQ(t6.type, tok::eof);
+ auto tlast = lexer.parse();
+ EXPECT_EQ(tlast.type, tok::eof);
}
// test white space
TEST(Lexer, whitespace) {
char string[] = " \t\v\f";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ VerboseLexer lexer(string, string+sizeof(string));
// should skip all white space and go straight to eof
auto t1 = lexer.parse();
@@ -91,8 +118,7 @@ TEST(Lexer, whitespace) {
// test new line
TEST(Lexer, newline) {
char string[] = "foo \n bar \n +\r\n-";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ VerboseLexer lexer(string, string+sizeof(string));
// get foo
auto t1 = lexer.parse();
@@ -123,9 +149,8 @@ TEST(Lexer, newline) {
// test operators
TEST(Lexer, symbols) {
- char string[] = "+-/*, t= ^ h'";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ char string[] = "+-/*, t= ^ h'<->~";
+ VerboseLexer lexer(string, string+sizeof(string));
auto t1 = lexer.parse();
EXPECT_EQ(t1.type, tok::plus);
@@ -161,13 +186,18 @@ TEST(Lexer, symbols) {
EXPECT_EQ(t10.type, tok::prime);
auto t11 = lexer.parse();
- EXPECT_EQ(t11.type, tok::eof);
+ EXPECT_EQ(t11.type, tok::arrow);
+
+ auto t12 = lexer.parse();
+ EXPECT_EQ(t12.type, tok::tilde);
+
+ auto tlast = lexer.parse();
+ EXPECT_EQ(tlast.type, tok::eof);
}
TEST(Lexer, comparison_operators) {
char string[] = "< <= > >= == != !";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ VerboseLexer lexer(string, string+sizeof(string));
auto t1 = lexer.parse();
EXPECT_EQ(t1.type, tok::lt);
@@ -191,8 +221,7 @@ TEST(Lexer, comparison_operators) {
// test braces
TEST(Lexer, braces) {
char string[] = "foo}";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ VerboseLexer lexer(string, string+sizeof(string));
auto t1 = lexer.parse();
EXPECT_EQ(t1.type, tok::identifier);
@@ -209,8 +238,7 @@ TEST(Lexer, comments) {
char string[] = "foo:this is one line\n"
"bar : another comment\n"
"foobar ? another comment\n";
- PRINT_LEX_STRING
- Lexer lexer(string, string+sizeof(string));
+ VerboseLexer lexer(string, string+sizeof(string));
auto t1 = lexer.parse();
EXPECT_EQ(t1.type, tok::identifier);
@@ -243,7 +271,7 @@ TEST(Lexer, numbers) {
std::vector<long long> check_ints = {1, 23, 3};
std::vector<long long> ints;
- Lexer lexer(floats_stream.str());
+ VerboseLexer lexer(floats_stream.str());
auto t = lexer.parse();
auto iter = floats.cbegin();
while (t.type != tok::eof && iter != floats.cend()) {
diff --git a/tests/modcc/test_optimization.cpp b/tests/modcc/test_optimization.cpp
index 9b69cee7d2aa5c16af82e2a143cc9632c9b359b5..01f0a69c7e9cc8ec918a307bb77dd2a00d5e961b 100644
--- a/tests/modcc/test_optimization.cpp
+++ b/tests/modcc/test_optimization.cpp
@@ -9,23 +9,23 @@ TEST(Optimizer, constant_folding) {
auto v = make_unique<ConstantFolderVisitor>();
{
auto e = parse_line_expression("x = 2*3");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
EXPECT_EQ(e->is_assignment()->rhs()->is_number()->value(), 6);
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT( "" )
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT( "" );
}
{
auto e = parse_line_expression("x = 1 + 2 + 3");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
EXPECT_EQ(e->is_assignment()->rhs()->is_number()->value(), 6);
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT( "" )
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT( "" );
}
{
auto e = parse_line_expression("x = exp(2)");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
// The tolerance has to be loosend to 1e-15, because the optimizer performs
// all intermediate calculations in 80 bit precision, which disagrees in
@@ -33,24 +33,24 @@ TEST(Optimizer, constant_folding) {
// This is a good thing: by using the constant folder we increase accuracy
// over the unoptimized code!
EXPECT_EQ(std::fabs(e->is_assignment()->rhs()->is_number()->value()-std::exp(2.0))<1e-15, true);
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT( "" )
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT( "" );
}
{
auto e = parse_line_expression("x= 2*2 + 3");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
EXPECT_EQ(e->is_assignment()->rhs()->is_number()->value(), 7);
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT( "" )
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT( "" );
}
{
auto e = parse_line_expression("x= 3 + 2*2");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
EXPECT_EQ(e->is_assignment()->rhs()->is_number()->value(), 7);
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT( "" )
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT( "" );
}
{
// this doesn't work: the (y+2) expression is not a constant, so folding stops.
@@ -58,23 +58,23 @@ TEST(Optimizer, constant_folding) {
// one approach would be try sorting communtative operations so that numbers
// are adjacent to one another in the tree
auto e = parse_line_expression("x= y + 2 + 3");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT( "" )
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT( "" );
}
{
auto e = parse_line_expression("x= 2 + 3 + y");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT("");
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT("");;
}
{
auto e = parse_line_expression("foo(2+3, log(32), 2*3 + x)");
- VERBOSE_PRINT( e->to_string() )
+ VERBOSE_PRINT( e->to_string() );
e->accept(v.get());
- VERBOSE_PRINT( e->to_string() )
- VERBOSE_PRINT("");
+ VERBOSE_PRINT( e->to_string() );
+ VERBOSE_PRINT("");;
}
}
diff --git a/tests/modcc/test_parser.cpp b/tests/modcc/test_parser.cpp
index 1edfe1a9ad61a0e3837dbddba2877037fb30aee2..dd9a3ea90ee04a26c2893a2aedb6b5fc0a5fa6cc 100644
--- a/tests/modcc/test_parser.cpp
+++ b/tests/modcc/test_parser.cpp
@@ -1,12 +1,76 @@
#include <cmath>
+#include <memory>
#include "test.hpp"
#include "module.hpp"
+#include "modccutil.hpp"
#include "parser.hpp"
+template <typename EPtr>
+void verbose_print(const EPtr& e, Parser& p, const char* text) {
+ if (!g_verbose_flag) return;
+
+ if (e) std::cout << e->to_string() << "\n";
+ if (p.status()==lexerStatus::error)
+ std::cout << "in " << red(text) << "\t" << p.error_message() << "\n";
+}
+
+template <typename Derived, typename RetUniqPtr>
+::testing::AssertionResult check_parse(
+ std::unique_ptr<Derived>& derived,
+ RetUniqPtr (Parser::*pmemfn)(),
+ const char* text)
+{
+ Parser p(text);
+ auto e = (p.*pmemfn)();
+ verbose_print(e, p, text);
+
+ if (e==nullptr) {
+ return ::testing::AssertionFailure() << "failed to parse '" << text << "'";
+ }
+
+ if (p.status()!=lexerStatus::happy) {
+ return ::testing::AssertionFailure() << "parser status is not happy";
+ }
+
+ Derived *ptr = e? dynamic_cast<Derived*>(e.get()): nullptr;
+ if (ptr==nullptr) {
+ return ::testing::AssertionFailure() << "failed to cast to derived type";
+ }
+ else {
+ e.release();
+ derived.reset(ptr);
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
+template <typename RetUniqPtr>
+::testing::AssertionResult check_parse(RetUniqPtr (Parser::*pmemfn)(), const char* text) {
+ std::unique_ptr<Expression> e;
+ return check_parse(e, pmemfn, text);
+}
+
+template <typename RetUniqPtr>
+::testing::AssertionResult check_parse_fail(RetUniqPtr (Parser::*pmemfn)(), const char* text) {
+ Parser p(text);
+ auto e = (p.*pmemfn)();
+ verbose_print(e, p, text);
+
+ if (p.status()!=lexerStatus::error) {
+ return ::testing::AssertionFailure() << "parser status is not error";
+ }
+
+ if (e!=nullptr) {
+ return ::testing::AssertionFailure() << "parser returned non-null expression";
+ }
+
+ return ::testing::AssertionSuccess();
+}
+
TEST(Parser, full_file) {
Module m(DATADIR "/test.mod");
- if(m.buffer().size()==0) {
+ if (m.buffer().size()==0) {
std::cout << "skipping Parser.full_file test because unable to open input file" << std::endl;
return;
}
@@ -15,481 +79,339 @@ TEST(Parser, full_file) {
}
TEST(Parser, procedure) {
- std::vector<const char*> calls =
-{
-"PROCEDURE foo(x, y) {"
-" LOCAL a\n"
-" LOCAL b\n"
-" LOCAL c\n"
-" a = 3\n"
-" b = x * y + 2\n"
-" y = x + y * 2\n"
-" y = a + b +c + a + b\n"
-" y = a + b *c + a + b\n"
-"}"
-,
-"PROCEDURE trates(v) {\n"
-" LOCAL qt\n"
-" qt=q10^((celsius-22)/10)\n"
-" minf=1-1/(1+exp((v-vhalfm)/km))\n"
-" hinf=1/(1+exp((v-vhalfh)/kh))\n"
-" mtau = 0.6\n"
-" htau = 1500\n"
-"}"
-};
- for(auto const& str : calls) {
- Parser p(str);
- auto e = p.parse_procedure();
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
- if(p.status()==lexerStatus::error) {
- std::cout << str << std::endl;
- std::cout << red("error ") << p.error_message() << std::endl;
- }
+ std::vector<const char*> calls = {
+ "PROCEDURE foo(x, y) {\n"
+ " LOCAL a\n"
+ " LOCAL b\n"
+ " LOCAL c\n"
+ " a = 3\n"
+ " b = x * y + 2\n"
+ " y = x + y * 2\n"
+ " y = a + b +c + a + b\n"
+ " y = a + b *c + a + b\n"
+ "}"
+ ,
+ "PROCEDURE trates(v) {\n"
+ " LOCAL qt\n"
+ " qt=q10^((celsius-22)/10)\n"
+ " minf=1-1/(1+exp((v-vhalfm)/km))\n"
+ " hinf=1/(1+exp((v-vhalfh)/kh))\n"
+ " mtau = 0.6\n"
+ " htau = 1500\n"
+ "}"
+ };
+
+ for (const auto& str: calls) {
+ EXPECT_TRUE(check_parse(&Parser::parse_procedure, str));
}
}
TEST(Parser, net_receive) {
char str[] =
- "NET_RECEIVE (x, y) { \n"
- " LOCAL a \n"
- " a = 3 \n"
- " x = a+3 \n"
- " y = x+a \n"
- "}";
- Parser p(str);
- auto e = p.parse_procedure();
- #ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- #endif
-
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- auto nr = e->is_symbol()->is_net_receive();
- EXPECT_NE(nr, nullptr);
- if(nr) {
- EXPECT_EQ(nr->args().size(), (unsigned)2);
- }
- if(p.status()==lexerStatus::error) {
- std::cout << str << std::endl;
- std::cout << red("error ") << p.error_message() << std::endl;
+ "NET_RECEIVE (x, y) { \n"
+ " LOCAL a \n"
+ " a = 3 \n"
+ " x = a+3 \n"
+ " y = x+a \n"
+ "}";
+
+ std::unique_ptr<Symbol> sym;
+
+ EXPECT_TRUE(check_parse(sym, &Parser::parse_procedure, str));
+ if (sym) {
+ auto nr = sym->is_net_receive();
+ EXPECT_NE(nullptr, nr);
+ if (nr) {
+ EXPECT_EQ(2u, nr->args().size());
+ }
}
}
TEST(Parser, function) {
- std::vector< const char*> calls =
-{
-"FUNCTION foo(x, y) {"
-" LOCAL a\n"
-" a = 3\n"
-" b = x * y + 2\n"
-" y = x + y * 2\n"
-" foo = a * x + y\n"
-"}"
-};
- for(auto const& str : calls) {
- Parser p(str);
- auto e = p.parse_function();
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
- if(p.status()==lexerStatus::error) {
- std::cout << str << std::endl;
- std::cout << red("error ") << p.error_message() << std::endl;
- }
- }
+ char str[] =
+ "FUNCTION foo(x, y) {"
+ " LOCAL a\n"
+ " a = 3\n"
+ " b = x * y + 2\n"
+ " y = x + y * 2\n"
+ " foo = a * x + y\n"
+ "}";
+
+ std::unique_ptr<Symbol> sym;
+ EXPECT_TRUE(check_parse(sym, &Parser::parse_function, str));
}
TEST(Parser, parse_solve) {
- {
- Parser p("SOLVE states METHOD cnexp");
- auto e = p.parse_solve();
-
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- if(e) {
- SolveExpression* s = dynamic_cast<SolveExpression*>(e.get());
- EXPECT_EQ(s->method(), solverMethod::cnexp);
- EXPECT_EQ(s->name(), "states");
- }
+ std::unique_ptr<SolveExpression> s;
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error) {
- std::cout << red("error") << p.error_message() << std::endl;
- }
+ EXPECT_TRUE(check_parse(s, &Parser::parse_solve, "SOLVE states METHOD cnexp"));
+ if (s) {
+ EXPECT_EQ(s->method(), solverMethod::cnexp);
+ EXPECT_EQ(s->name(), "states");
}
- {
- Parser p("SOLVE states");
- auto e = p.parse_solve();
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- if(e) {
- SolveExpression* s = dynamic_cast<SolveExpression*>(e.get());
- EXPECT_EQ(s->method(), solverMethod::none);
- EXPECT_EQ(s->name(), "states");
- }
-
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error) {
- std::cout << red("error") << p.error_message() << std::endl;
- }
+ EXPECT_TRUE(check_parse(s, &Parser::parse_solve, "SOLVE states"));
+ if (s) {
+ EXPECT_EQ(s->method(), solverMethod::none);
+ EXPECT_EQ(s->name(), "states");
}
}
TEST(Parser, parse_conductance) {
- {
- Parser p("CONDUCTANCE g USEION na");
- auto e = p.parse_conductance();
-
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- if(e) {
- ConductanceExpression* s = dynamic_cast<ConductanceExpression*>(e.get());
- EXPECT_EQ(s->ion_channel(), ionKind::Na);
- EXPECT_EQ(s->name(), "g");
- }
+ std::unique_ptr<ConductanceExpression> s;
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error) {
- std::cout << red("error") << p.error_message() << std::endl;
- }
+ EXPECT_TRUE(check_parse(s, &Parser::parse_conductance, "CONDUCTANCE g USEION na"));
+ if (s) {
+ EXPECT_EQ(s->ion_channel(), ionKind::Na);
+ EXPECT_EQ(s->name(), "g");
}
- {
- Parser p("CONDUCTANCE gnda");
- auto e = p.parse_conductance();
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- if(e) {
- ConductanceExpression* s = dynamic_cast<ConductanceExpression*>(e.get());
- EXPECT_EQ(s->ion_channel(), ionKind::nonspecific);
- EXPECT_EQ(s->name(), "gnda");
- }
-
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error) {
- std::cout << red("error") << p.error_message() << std::endl;
- }
+ EXPECT_TRUE(check_parse(s, &Parser::parse_conductance, "CONDUCTANCE gnda"));
+ if (s) {
+ EXPECT_EQ(s->ion_channel(), ionKind::nonspecific);
+ EXPECT_EQ(s->name(), "gnda");
}
}
TEST(Parser, parse_if) {
- {
- char expression[] =
+ std::unique_ptr<IfExpression> s;
+
+ EXPECT_TRUE(check_parse(s, &Parser::parse_if,
" if(a<b) { \n"
" a = 2+b \n"
" b = 4^b \n"
- " } \n";
- Parser p(expression);
- auto e = p.parse_if();
- EXPECT_NE(e, nullptr);
- if(e) {
- auto ife = e->is_if();
- EXPECT_NE(e->is_if(), nullptr);
- if(ife) {
- EXPECT_NE(ife->condition()->is_binary(), nullptr);
- EXPECT_NE(ife->true_branch()->is_block(), nullptr);
- EXPECT_EQ(ife->false_branch(), nullptr);
- }
- //std::cout << e->to_string() << std::endl;
- }
- else {
- std::cout << p.error_message() << std::endl;
- }
+ " } \n"
+ ));
+ if (s) {
+ EXPECT_NE(s->condition()->is_binary(), nullptr);
+ EXPECT_NE(s->true_branch()->is_block(), nullptr);
+ EXPECT_EQ(s->false_branch(), nullptr);
}
- {
- char expression[] =
+
+ EXPECT_TRUE(check_parse(s, &Parser::parse_if,
" if(a<b) { \n"
" a = 2+b \n"
" } else { \n"
" a = 2+b \n"
- " } ";
- Parser p(expression);
- auto e = p.parse_if();
- EXPECT_NE(e, nullptr);
- if(e) {
- auto ife = e->is_if();
- EXPECT_NE(ife, nullptr);
- if(ife) {
- EXPECT_NE(ife->condition()->is_binary(), nullptr);
- EXPECT_NE(ife->true_branch()->is_block(), nullptr);
- EXPECT_NE(ife->false_branch(), nullptr);
- }
- //std::cout << std::endl << e->to_string() << std::endl;
- }
- else {
- std::cout << p.error_message() << std::endl;
- }
+ " } "
+ ));
+ if (s) {
+ EXPECT_NE(s->condition()->is_binary(), nullptr);
+ EXPECT_NE(s->true_branch()->is_block(), nullptr);
+ EXPECT_NE(s->false_branch(), nullptr);
}
- {
- char expression[] =
+
+ EXPECT_TRUE(check_parse(s, &Parser::parse_if,
" if(a<b) { \n"
" a = 2+b \n"
" } else if(b>a){\n"
" a = 2+b \n"
- " } ";
- Parser p(expression);
- auto e = p.parse_if();
- EXPECT_NE(e, nullptr);
- if(e) {
- auto ife = e->is_if();
- EXPECT_NE(ife, nullptr);
- if(ife) {
- EXPECT_NE(ife->condition()->is_binary(), nullptr);
- EXPECT_NE(ife->true_branch()->is_block(), nullptr);
- EXPECT_NE(ife->false_branch(), nullptr);
- EXPECT_NE(ife->false_branch()->is_if(), nullptr);
- EXPECT_EQ(ife->false_branch()->is_if()->false_branch(), nullptr);
- }
- //std::cout << std::endl << e->to_string() << std::endl;
- }
- else {
- std::cout << p.error_message() << std::endl;
- }
+ " } "
+ ));
+ if (s) {
+ EXPECT_NE(s->condition()->is_binary(), nullptr);
+ EXPECT_NE(s->true_branch()->is_block(), nullptr);
+ ASSERT_NE(s->false_branch(), nullptr);
+ ASSERT_NE(s->false_branch()->is_if(), nullptr);
+ EXPECT_EQ(s->false_branch()->is_if()->false_branch(), nullptr);
}
}
TEST(Parser, parse_local) {
- ////////////////////// test for valid expressions //////////////////////
- {
- Parser p("LOCAL xyz");
- auto e = p.parse_local();
-
- #ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- #endif
- EXPECT_NE(e, nullptr);
- if(e) {
- EXPECT_NE(e->is_local_declaration(), nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
- }
+ std::unique_ptr<LocalDeclaration> s;
+ EXPECT_TRUE(check_parse(s, &Parser::parse_local, "LOCAL xyz"));
+ if (s) {
+ ASSERT_EQ(1u, s->variables().size());
+ }
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error)
- std::cout << red("error") << p.error_message() << std::endl;
- }
-
- {
- Parser p("LOCAL x, y, z");
- auto e = p.parse_local();
-
- #ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- #endif
- EXPECT_NE(e, nullptr);
- if(e) {
- EXPECT_NE(e->is_local_declaration(), nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
- auto vars = e->is_local_declaration()->variables();
- EXPECT_EQ(vars.size(), (unsigned)3);
- EXPECT_NE(vars.find("x"), vars.end());
- EXPECT_NE(vars.find("y"), vars.end());
- EXPECT_NE(vars.find("z"), vars.end());
- }
+ EXPECT_TRUE(check_parse(s, &Parser::parse_local, "LOCAL x, y, z"));
+ if (s) {
+ auto vars = s->variables();
+ ASSERT_EQ(3u, vars.size());
+ ASSERT_TRUE(vars.count("x"));
+ ASSERT_TRUE(vars.count("y"));
+ ASSERT_TRUE(vars.count("z"));
+ }
+
+ EXPECT_TRUE(check_parse_fail(&Parser::parse_local, "LOCAL x,"));
+}
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error)
- std::cout << red("error") << p.error_message() << std::endl;
+TEST(Parser, parse_unary_expression) {
+ const char* good_expr[] = {
+ "+x ",
+ "-x ",
+ "(x + -y) ",
+ "-(x - + -y) ",
+ "exp(x + y) ",
+ "-exp(x + -y) "
+ };
+
+ for (auto& text: good_expr) {
+ EXPECT_TRUE(check_parse(&Parser::parse_unaryop, text));
}
+}
+
+// test parsing of parenthesis expressions
+TEST(Parser, parse_parenthesis_expression) {
+ const char* good_expr[] = {
+ "((celsius-22)/10) ",
+ "((celsius-22)+10) ",
+ "(x+2) ",
+ "((x)) ",
+ "(((x))) ",
+ "(x + (x * (y*(2)) + 4))",
+ };
- ////////////////////// test for invalid expressions //////////////////////
- {
- Parser p("LOCAL 2");
- auto e = p.parse_local();
+ for (auto& text: good_expr) {
+ EXPECT_TRUE(check_parse(&Parser::parse_parenthesis_expression, text));
+ }
- EXPECT_EQ(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::error);
+ const char* bad_expr[] = {
+ "(x ",
+ "((x+3) ",
+ "(x+ +) ",
+ "(x=3) ", // assignment inside parenthesis isn't allowed
+ "(a + (b*2^(x)) ", // missing closing parenthesis
+ };
- #ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- if(p.status()==lexerStatus::error)
- std::cout << "in " << cyan(bad_expression) << "\t" << p.error_message() << std::endl;
- #endif
+ for (auto& text: bad_expr) {
+ EXPECT_TRUE(check_parse_fail(&Parser::parse_parenthesis_expression, text));
}
+}
+
+// test parsing of line expressions
+TEST(Parser, parse_line_expression) {
+ const char* good_expr[] = {
+ "qt=q10^((celsius-22)/10)"
+ "x=2 ",
+ "x=2 ",
+ "x = -y\n "
+ "x=2*y ",
+ "x=y + 2 * z",
+ "x=(y + 2) * z ",
+ "x=(y + 2) * z ^ 3 ",
+ "x=(y + 2 * z ^ 3) ",
+ "foo(x+3, y, bar(21.4))",
+ "y=exp(x+3) + log(exp(x/y))",
+ "a=x^y^z",
+ "a=x/y/z"
+ };
- {
- Parser p("LOCAL x, ");
- auto e = p.parse_local();
+ for (auto& text: good_expr) {
+ EXPECT_TRUE(check_parse(&Parser::parse_line_expression, text));
+ }
- EXPECT_EQ(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::error);
+ const char* bad_expr[] = {
+ "x=2+ ", // incomplete binary expression on rhs
+ "x= ", // missing rhs of assignment
+ "x=)y + 2 * z",
+ "x=(y + 2 ",
+ "x=(y ++ z ",
+ "x/=3 ", // compound binary expressions not supported
+ "foo+8 ", // missing assignment
+ "foo()=8 ", // lhs of assingment must be an lvalue
+ };
- #ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- if(p.status()==lexerStatus::error)
- std::cout << "in " << cyan(bad_expression) << "\t" << p.error_message() << std::endl;
- #endif
+ for (auto& text: bad_expr) {
+ EXPECT_TRUE(check_parse_fail(&Parser::parse_line_expression, text));
}
}
-TEST(Parser, parse_unary_expression) {
- std::vector<const char*> good_expressions =
- {
-"+x ",
-"-x ",
-"(x + -y) ",
-"-(x - + -y) ",
-"exp(x + y) ",
-"-exp(x + -y) ",
+TEST(Parser, parse_stoich_term) {
+ const char* good_expr[] = {
+ "B", "B3", "3B3", "0A", "12A"
};
- for(auto const& expression : good_expressions) {
- Parser p(expression);
- auto e = p.parse_unaryop();
+ for (auto& text: good_expr) {
+ std::unique_ptr<StoichTermExpression> s;
+ EXPECT_TRUE(check_parse(s, &Parser::parse_stoich_term, text));
+ }
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
+ const char* bad_expr[] = {
+ "-A", "-3A", "0.2A", "5"
+ };
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error)
- std::cout << red("error") << p.error_message() << std::endl;
+ for (auto& text: bad_expr) {
+ EXPECT_TRUE(check_parse_fail(&Parser::parse_stoich_term, text));
}
}
-// test parsing of parenthesis expressions
-TEST(Parser, parse_parenthesis_expression) {
- std::vector<const char*> good_expressions =
- {
-"((celsius-22)/10) ",
-"((celsius-22)+10) ",
-"(x+2) ",
-"((x)) ",
-"(((x))) ",
-"(x + (x * (y*(2)) + 4))",
+TEST(Parser, parse_stoich_expression) {
+ const char* single_expr[] = {
+ "B", "B3", "3xy"
};
- for(auto const& expression : good_expressions) {
- Parser p(expression);
- auto e = p.parse_parenthesis_expression();
-
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error)
- std::cout << cyan(expression) << "\t"
- << red("error") << p.error_message() << std::endl;
- }
-
- std::vector<const char*> bad_expressions =
- {
-"(x ",
-"((x+3) ",
-"(x+ +) ",
-"(x=3) ", // assignment inside parenthesis isn't allowed
-"(a + (b*2^(x)) ", // missing closing parenthesis
+ for (auto& text: single_expr) {
+ std::unique_ptr<StoichExpression> s;
+ EXPECT_TRUE(check_parse(s, &Parser::parse_stoich_expression, text));
+ EXPECT_EQ(1, s->terms().size());
+ }
+
+ const char* double_expr[] = {
+ "B+A", "a1 + 2bn", "4c+d"
};
- for(auto const& expression : bad_expressions) {
- Parser p(expression);
- auto e = p.parse_parenthesis_expression();
+ for (auto& text: double_expr) {
+ std::unique_ptr<StoichExpression> s;
+ EXPECT_TRUE(check_parse(s, &Parser::parse_stoich_expression, text));
+ EXPECT_EQ(2, s->terms().size());
+ }
- EXPECT_EQ(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::error);
+ const char* other_good_expr[] = {
+ "", "a+b+c", "1a+2b+3c+4d"
+ };
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- if(p.status()==lexerStatus::error)
- std::cout << "in " << cyan(expression) << "\t" << p.error_message() << std::endl;
-#endif
+ for (auto& text: other_good_expr) {
+ std::unique_ptr<StoichExpression> s;
+ EXPECT_TRUE(check_parse(s, &Parser::parse_stoich_expression, text));
}
-}
-// test parsing of line expressions
-TEST(Parser, parse_line_expression) {
- std::vector<const char*> good_expressions =
- {
-"qt=q10^((celsius-22)/10)"
-"x=2 ",
-"x=2 ",
-"x = -y\n "
-"x=2*y ",
-"x=y + 2 * z",
-"x=(y + 2) * z ",
-"x=(y + 2) * z ^ 3 ",
-"x=(y + 2 * z ^ 3) ",
-"foo(x+3, y, bar(21.4))",
-"y=exp(x+3) + log(exp(x/y))",
-"a=x^y^z",
-"a=x/y/z"
+ const char* bad_expr[] = {
+ "A+B+", "A+5+B"
};
- for(auto const& expression : good_expressions) {
- Parser p(expression);
- auto e = p.parse_line_expression();
-
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
-
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error)
- std::cout << red("error") << p.error_message() << std::endl;
- }
-
- std::vector<const char*> bad_expressions =
- {
-"x=2+ ", // incomplete binary expression on rhs
-"x= ", // missing rhs of assignment
-"x=)y + 2 * z",
-"x=(y + 2 ",
-"x=(y ++ z ",
-"x/=3 ", // compound binary expressions not supported
-"foo+8 ", // missing assignment
-"foo()=8 ", // lhs of assingment must be an lvalue
+ for (auto& text: bad_expr) {
+ EXPECT_TRUE(check_parse_fail(&Parser::parse_stoich_expression, text));
+ }
+}
+
+// test parsing of stoich and reaction expressions
+TEST(Parser, parse_reaction_expression) {
+ const char* good_expr[] = {
+ "~ A + B <-> C + D (k1, k2)",
+ "~ 2B <-> C + D + E (k1(3,v), k2)",
+ "~ <-> C + D + 7 E (k1, f(a,b)-2)",
+ "~ <-> (f,g)",
+ "~ A + 3B + C<-> (f,g)"
};
- for(auto const& expression : bad_expressions) {
- Parser p(expression);
- auto e = p.parse_line_expression();
+ for (auto& text: good_expr) {
+ std::unique_ptr<ReactionExpression> s;
+ EXPECT_TRUE(check_parse(s, &Parser::parse_reaction_expression, text));
+ }
- EXPECT_EQ(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::error);
+ const char* bad_expr[] = {
+ "~ A + B <-> C + D (k1, k2, k3)",
+ "~ A + B <-> C + (k1, k2)",
+ "~ 2.3B <-> C + D + E (k1(3,v), k2)",
+ "~ <-> C + D + 7E",
+ "~ <-> (,g)",
+ "~ A - 3B + C<-> (f,g)",
+ " A <-> B (k1, k2)",
+ "~ A <- B (k1)",
+ "~ A -> B (k2)",
+ };
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
- if(p.status()==lexerStatus::error)
- std::cout << "in " << cyan(expression) << "\t" << p.error_message() << std::endl;
-#endif
+ for (auto& text: bad_expr) {
+ EXPECT_TRUE(check_parse_fail(&Parser::parse_reaction_expression, text));
}
}
long double eval(Expression *e) {
- if(auto n = e->is_number()) {
+ if (auto n = e->is_number()) {
return n->value();
}
- if(auto b = e->is_binary()) {
+ if (auto b = e->is_binary()) {
auto lhs = eval(b->lhs());
auto rhs = eval(b->rhs());
switch(b->op()) {
@@ -501,7 +423,7 @@ long double eval(Expression *e) {
default:;
}
}
- if(auto u = e->is_unary()) {
+ if (auto u = e->is_unary()) {
auto val = eval(u->expression());
switch(u->op()) {
case tok::plus : return val;
@@ -515,8 +437,7 @@ long double eval(Expression *e) {
// test parsing of expressions for correctness
// by parsing rvalue expressions with numeric atoms, which can be evalutated using eval
TEST(Parser, parse_binop) {
- std::vector<std::pair<const char*, double>> tests =
- {
+ std::pair<const char*, double> tests[] = {
// simple
{"2+3", 2.+3.},
{"2-3", 2.-3.},
@@ -540,15 +461,9 @@ TEST(Parser, parse_binop) {
{"3^2*5.", std::pow(3.,2.)*5.},
};
- for(auto const& test_case : tests) {
- Parser p(test_case.first);
- auto e = p.parse_expression();
-
-#ifdef VERBOSE_TEST
- if(e) std::cout << e->to_string() << std::endl;
-#endif
- EXPECT_NE(e, nullptr);
- EXPECT_EQ(p.status(), lexerStatus::happy);
+ for (const auto& test_case: tests) {
+ std::unique_ptr<Expression> e;
+ EXPECT_TRUE(check_parse(e, &Parser::parse_expression, test_case.first));
// A loose tolerance of 1d-10 is required here because the eval()
// function uses long double for intermediate results (like constant
@@ -556,15 +471,11 @@ TEST(Parser, parse_binop) {
// operations this can see relatively large divergence between the
// double and long double results.
EXPECT_NEAR(eval(e.get()), test_case.second, 1e-10);
-
- // always print the compiler errors, because they are unexpected
- if(p.status()==lexerStatus::error)
- std::cout << red("error") << p.error_message() << std::endl;
}
}
TEST(Parser, parse_state_block) {
- std::vector<const char*> state_blocks = {
+ const char* state_blocks[] = {
"STATE {\n"
" h\n"
" m r\n"
@@ -587,14 +498,12 @@ TEST(Parser, parse_state_block) {
"}"
};
- for (auto const& str: state_blocks) {
- Module m(str, sizeof(str));
+ expression_ptr null;
+ for (auto& text: state_blocks) {
+ Module m(text, sizeof(text));
Parser p(m, false);
p.parse_state_block();
EXPECT_EQ(lexerStatus::happy, p.status());
- if (p.status() == lexerStatus::error) {
- std::cout << str << "\n"
- << red("error") << p.error_message() << "\n";
- }
+ verbose_print(null, p, text);
}
}