diff --git a/data/test.mod b/data/test.mod index d7403c791f390dacf899fe6a32429ad4b5f4096d..bbb8ce41cf9ba422e9513a4387819ef957d1ba4c 100644 --- a/data/test.mod +++ b/data/test.mod @@ -10,7 +10,7 @@ NEURON { } STATE { - h + h (nA) m r } diff --git a/modcc/blocks.hpp b/modcc/blocks.hpp index b9e41bfa3d2fb1963691abae539f4ad94d45de2c..3ea276085f0f05c9f8b36f3e13d7424ddc7c15f7 100644 --- a/modcc/blocks.hpp +++ b/modcc/blocks.hpp @@ -26,6 +26,28 @@ enum class moduleKind { density }; +typedef std::vector<Token> unit_tokens; +struct Id { + Token token; + std::string value; // store the value as a string, not a number : empty + // string == no value + unit_tokens units; + + Id(Token const& t, std::string const& v, unit_tokens const& u) + : token(t), value(v), units(u) + {} + + Id() {} + + bool has_value() const { + return value.size()>0; + } + + std::string const& name() const { + return token.spelling; + } +}; + // information stored in a NEURON {} block in mod file. struct NeuronBlock { bool threadsafe = false; @@ -42,7 +64,7 @@ struct NeuronBlock { // information stored in a NEURON {} block in mod file struct StateBlock { - std::vector<std::string> state_variables; + std::vector<Id> state_variables; auto begin() -> decltype(state_variables.begin()) { return state_variables.begin(); } @@ -52,32 +74,11 @@ struct StateBlock { }; // information stored in a NEURON {} block in mod file -typedef std::vector<Token> unit_tokens; struct UnitsBlock { typedef std::pair<unit_tokens, unit_tokens> units_pair; std::vector<units_pair> unit_aliases; }; -struct Id { - Token token; - std::string value; // store the value as a string, not a number : empty string == no value - unit_tokens units; - - Id(Token const& t, std::string const& v, unit_tokens const& u) - : token(t), value(v), units(u) - {} - - Id() {} - - bool has_value() const { - return value.size()>0; - } - - std::string const& name() const { - return token.spelling; - } -}; - // information stored in a NEURON {} block in mod file struct ParameterBlock { std::vector<Id> parameters; @@ -164,4 +165,3 @@ inline std::ostream& operator<< (std::ostream& os, AssignedBlock const& A) { return os; } - diff --git a/modcc/module.cpp b/modcc/module.cpp index 6001e1b458f8cf16ffa488ca10ceb4e46268787e..3e8fe3d77da7c2e2be24493cc04c00393f111f03 100644 --- a/modcc/module.cpp +++ b/modcc/module.cpp @@ -31,15 +31,21 @@ Module::Module(std::string const& fname) buffer_[size] = 0; // append \0 to terminate string } -Module::Module(std::vector<char> const& buffer) -{ +Module::Module(std::vector<char> const& buffer) { buffer_ = buffer; // add \0 to end of buffer if not already present - if(buffer_[buffer_.size()-1] != 0) + if (buffer_[buffer_.size()-1] != 0) buffer_.push_back(0); } +Module::Module(const char* buffer, size_t count) { + auto size = std::distance(buffer, std::find(buffer, buffer+count, '\0')); + buffer_.reserve(size+1); + buffer_.insert(buffer_.end(), buffer, buffer+size); + buffer_.push_back(0); +} + std::vector<Module::symbol_ptr>& Module::procedures() { return procedures_; @@ -612,7 +618,7 @@ void Module::add_variables_to_symbols() { // add state variables for(auto const &var : state_block()) { - VariableExpression *id = new VariableExpression(Location(), var); + VariableExpression *id = new VariableExpression(Location(), var.name()); id->state(true); // set state to true // state variables are private @@ -623,7 +629,7 @@ void Module::add_variables_to_symbols() { id->range(rangeKind::range); // always a range id->access(accessKind::readwrite); - symbols_[var] = symbol_ptr{id}; + symbols_[var.name()] = symbol_ptr{id}; } // add the parameters diff --git a/modcc/module.hpp b/modcc/module.hpp index f427d870ebc1cb770e8e424349d4d140892b3f45..1cd1cfe89b4e31d1bb18e3e3cedb6e173d049ee2 100644 --- a/modcc/module.hpp +++ b/modcc/module.hpp @@ -15,6 +15,7 @@ public : Module(std::string const& fname); Module(std::vector<char> const& buffer); + Module(const char* buffer, size_t count); std::vector<char> const& buffer() const { return buffer_; diff --git a/modcc/parser.cpp b/modcc/parser.cpp index 0b4b81068ed22111b6c28ed04313d245ce613f4a..ffa498c08f50e4b0b1c2d7a6a3b8370c4e8c38de 100644 --- a/modcc/parser.cpp +++ b/modcc/parser.cpp @@ -368,17 +368,35 @@ void Parser::parse_state_block() { return; } - // there are no use cases for curly brace in a STATE block, so we don't have to count them - // we have to get the next token before entering the loop to handle the case of - // an empty block {} + // there are no use cases for curly brace in a STATE block, so we don't have + // to count them we have to get the next token before entering the loop to + // handle the case of an empty block {} get_token(); - while(token_.type!=tok::rbrace) { + while(token_.type!=tok::rbrace && token_.type != tok::eof) { + int line = location_.line; + Id parm; + if(token_.type != tok::identifier) { - error(pprintf("'%' is not a valid name for a state variable", token_.spelling)); + error(pprintf("'%' is not a valid name for a state variable", + token_.spelling)); return; } - state_block.state_variables.push_back(token_.spelling); + + parm.token = token_; + //state_block.state_variables.push_back(token_.spelling); get_token(); + + // get unit parameters + if (line == location_.line && token_.type == tok::lparen) { + parm.units = unit_description(); + if (status_ == lexerStatus::error) { + error(pprintf("STATUS block unexpected symbol '%s'", + token_.spelling)); + return; + } + } + + state_block.state_variables.push_back(parm); } // add this state block information to the module @@ -505,7 +523,7 @@ void Parser::parse_parameter_block() { parm_exit: // only write error message if one hasn't already been logged by the lexer if(!success && status_==lexerStatus::happy) { - error(pprintf("PARAMETER block unexpected symbol '%'", token_.spelling)); + error(pprintf("PARAMETER block unexpected symbol '%s'", token_.spelling)); } return; } diff --git a/modcc/parser.hpp b/modcc/parser.hpp index 6768749df46e51181ade05d0d6acb1d4dddbe7c6..b55bede17e698aa9d790211ddbee5a54f90ba56a 100644 --- a/modcc/parser.hpp +++ b/modcc/parser.hpp @@ -39,9 +39,6 @@ public: return error_string_; } -private: - Module *module_; - // functions for parsing descriptive blocks // these are called in the first pass, and do not // construct any AST information @@ -52,6 +49,9 @@ private: void parse_assigned_block(); void parse_title(); +private: + Module *module_; + std::vector<Token> comma_separated_identifiers(); std::vector<Token> unit_description(); @@ -69,4 +69,3 @@ private: bool expect(tok, const char *str=""); bool expect(tok, std::string const& str); }; - diff --git a/tests/modcc/test_parser.cpp b/tests/modcc/test_parser.cpp index 1e590d8315a5d95b3e93555d5ddbd2dbd9ad00be..1edfe1a9ad61a0e3837dbddba2877037fb30aee2 100644 --- a/tests/modcc/test_parser.cpp +++ b/tests/modcc/test_parser.cpp @@ -15,7 +15,7 @@ TEST(Parser, full_file) { } TEST(Parser, procedure) { - std::vector< const char*> calls = + std::vector<const char*> calls = { "PROCEDURE foo(x, y) {" " LOCAL a\n" @@ -550,10 +550,11 @@ TEST(Parser, parse_binop) { EXPECT_NE(e, nullptr); EXPECT_EQ(p.status(), lexerStatus::happy); - // A loose tolerance of 1d-10 is required here because the eval() function uses long double - // for intermediate results (like constant folding in modparser). - // For expressions with transcendental operations this can see relatively large divergence between - // the double and long double results. + // A loose tolerance of 1d-10 is required here because the eval() + // function uses long double for intermediate results (like constant + // folding in modparser). For expressions with transcendental + // 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 @@ -561,3 +562,39 @@ TEST(Parser, parse_binop) { std::cout << red("error") << p.error_message() << std::endl; } } + +TEST(Parser, parse_state_block) { + std::vector<const char*> state_blocks = { + "STATE {\n" + " h\n" + " m r\n" + "}", + "STATE {\n" + " h (nA)\n" + " m r\n" + "}", + "STATE {\n" + " h (nA)\n" + " m (nA) r\n" + "}", + "STATE {\n" + " h (nA)\n" + " m r (uA)\n" + "}", + "STATE {\n" + " h (nA)\n" + " m (nA) r (uA)\n" + "}" + }; + + for (auto const& str: state_blocks) { + Module m(str, sizeof(str)); + 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"; + } + } +}