diff --git a/modcc/expression.cpp b/modcc/expression.cpp
index 06a70e0c99bb7a9d9c38d472f2b2382e0f44371d..05fdb33dd23c5516384057e5cc2946b5c8c6fab4 100644
--- a/modcc/expression.cpp
+++ b/modcc/expression.cpp
@@ -110,16 +110,12 @@ expression_ptr IdentifierExpression::clone() const {
     return make_expression<IdentifierExpression>(location_, spelling_);
 }
 
-bool IdentifierExpression::is_lvalue() {
-    // check for global variable that is writeable
-    auto var = symbol_->is_variable();
-    if(var) return var->is_writeable();
-
-    // else look for local symbol
-    if( symbol_->kind() == symbolKind::local_variable ) {
-        return true;
-    }
+bool IdentifierExpression::is_lvalue() const {
+    return is_global_lvalue() || symbol_->kind() == symbolKind::local_variable;
+}
 
+bool IdentifierExpression::is_global_lvalue() const {
+    if(auto var = symbol_->is_variable()) return var->is_writeable();
     return false;
 }
 
@@ -131,6 +127,14 @@ expression_ptr NumberExpression::clone() const {
     return make_expression<NumberExpression>(location_, value_);
 }
 
+/*******************************************************************************
+  IntegerExpression
+********************************************************************************/
+
+expression_ptr IntegerExpression::clone() const {
+    return make_expression<IntegerExpression>(location_, integer_);
+}
+
 /*******************************************************************************
   LocalDeclaration
 *******************************************************************************/
@@ -250,6 +254,75 @@ std::string IndexedVariable::to_string() const {
         + ", ion" + (ion_channel()==ionKind::none ? red(ch) : green(ch)) + ") ";
 }
 
+/*******************************************************************************
+  ReactionExpression
+*******************************************************************************/
+
+std::string ReactionExpression::to_string() const {
+    return blue("reaction") +
+           pprintf(" % <-> % (%, %)",
+               lhs()->to_string(), rhs()->to_string(),
+                fwd_rate()->to_string(), rev_rate()->to_string());
+}
+
+expression_ptr ReactionExpression::clone() const {
+    return make_expression<ReactionExpression>(
+        location_, lhs()->clone(), rhs()->clone(), fwd_rate()->clone(), rev_rate()->clone());
+}
+
+void ReactionExpression::semantic(std::shared_ptr<scope_type> scp) {
+    scope_ = scp;
+    lhs()->semantic(scp);
+    rhs()->semantic(scp);
+    fwd_rate()->semantic(scp);
+    rev_rate()->semantic(scp);
+}
+
+/*******************************************************************************
+  StoichTermExpression
+*******************************************************************************/
+
+expression_ptr StoichTermExpression::clone() const {
+    return make_expression<StoichTermExpression>(
+        location_, coeff()->clone(), ident()->clone());
+}
+
+void StoichTermExpression::semantic(std::shared_ptr<scope_type> scp) {
+    scope_ = scp;
+    ident()->semantic(scp);
+}
+
+/*******************************************************************************
+  StoichExpression
+*******************************************************************************/
+
+expression_ptr StoichExpression::clone() const {
+    std::vector<expression_ptr> cloned_terms;
+    for(auto& e: terms()) {
+        cloned_terms.emplace_back(e->clone());
+    }
+
+    return make_expression<StoichExpression>(location_, std::move(cloned_terms));
+}
+
+std::string StoichExpression::to_string() const {
+    std::string s;
+    bool first = true;
+    for(auto& e: terms()) {
+        if (!first) s += "+";
+        s += e->to_string();
+        first = false;
+    }
+    return s;
+}
+
+void StoichExpression::semantic(std::shared_ptr<scope_type> scp) {
+    scope_ = scp;
+    for(auto& e: terms()) {
+        e->semantic(scp);
+    }
+}
+
 /*******************************************************************************
   CallExpression
 *******************************************************************************/
@@ -709,7 +782,6 @@ expression_ptr IfExpression::clone() const {
 void Expression::accept(Visitor *v) {
     v->visit(this);
 }
-
 void Symbol::accept(Visitor *v) {
     v->visit(this);
 }
@@ -746,6 +818,9 @@ void IndexedVariable::accept(Visitor *v) {
 void NumberExpression::accept(Visitor *v) {
     v->visit(this);
 }
+void IntegerExpression::accept(Visitor *v) {
+    v->visit(this);
+}
 void LocalDeclaration::accept(Visitor *v) {
     v->visit(this);
 }
@@ -794,6 +869,15 @@ void BinaryExpression::accept(Visitor *v) {
 void AssignmentExpression::accept(Visitor *v) {
     v->visit(this);
 }
+void ReactionExpression::accept(Visitor *v) {
+    v->visit(this);
+}
+void StoichExpression::accept(Visitor *v) {
+    v->visit(this);
+}
+void StoichTermExpression::accept(Visitor *v) {
+    v->visit(this);
+}
 void AddBinaryExpression::accept(Visitor *v) {
     v->visit(this);
 }
diff --git a/modcc/expression.hpp b/modcc/expression.hpp
index b1076b5b4a59e50f12aa936207ac0f3ddf9097bb..b2eb6e463c254a7d5182fec4e55ad6f9e36c9e8e 100644
--- a/modcc/expression.hpp
+++ b/modcc/expression.hpp
@@ -23,6 +23,7 @@ class IfExpression;
 class VariableExpression;
 class IndexedVariable;
 class NumberExpression;
+class IntegerExpression;
 class LocalDeclaration;
 class ArgumentExpression;
 class DerivativeExpression;
@@ -40,6 +41,9 @@ class CosUnaryExpression;
 class SinUnaryExpression;
 class BinaryExpression;
 class AssignmentExpression;
+class ReactionExpression;
+class StoichExpression;
+class StoichTermExpression;
 class AddBinaryExpression;
 class SubBinaryExpression;
 class MulBinaryExpression;
@@ -77,6 +81,7 @@ enum class procedureKind {
     initial,     ///< INITIAL
     net_receive, ///< NET_RECEIVE
     breakpoint,  ///< BREAKPOINT
+    kinetic,     ///< KINETIC
     derivative   ///< DERIVATIVE
 };
 std::string to_string(procedureKind k);
@@ -157,16 +162,19 @@ public:
     virtual PrototypeExpression*   is_prototype()         {return nullptr;}
     virtual IdentifierExpression*  is_identifier()        {return nullptr;}
     virtual NumberExpression*      is_number()            {return nullptr;}
+    virtual IntegerExpression*     is_integer()           {return nullptr;}
     virtual BinaryExpression*      is_binary()            {return nullptr;}
     virtual UnaryExpression*       is_unary()             {return nullptr;}
     virtual AssignmentExpression*  is_assignment()        {return nullptr;}
+    virtual ReactionExpression*    is_reaction()          {return nullptr;}
     virtual ConditionalExpression* is_conditional()       {return nullptr;}
     virtual InitialBlock*          is_initial_block()     {return nullptr;}
     virtual SolveExpression*       is_solve_statement()   {return nullptr;}
     virtual Symbol*                is_symbol()            {return nullptr;}
     virtual ConductanceExpression* is_conductance_statement() {return nullptr;}
 
-    virtual bool is_lvalue() {return false;}
+    virtual bool is_lvalue() const {return false;}
+    virtual bool is_global_lvalue() const {return false;}
 
     // force all derived classes to implement visitor
     // this might be a bad idea
@@ -259,7 +267,8 @@ public:
 
     IdentifierExpression* is_identifier() override {return this;}
 
-    bool is_lvalue() override;
+    bool is_lvalue() const override;
+    bool is_global_lvalue() const override;
 
     ~IdentifierExpression() {}
 
@@ -298,14 +307,14 @@ public:
 class NumberExpression : public Expression {
 public:
     NumberExpression(Location loc, std::string const& value)
-        : Expression(loc), value_(std::stod(value))
+        : Expression(loc), value_(std::stold(value))
     {}
 
     NumberExpression(Location loc, long double value)
         : Expression(loc), value_(value)
     {}
 
-    long double value() const {return value_;};
+    virtual long double value() const {return value_;};
 
     std::string to_string() const override {
         return purple(pprintf("%", value_));
@@ -324,6 +333,38 @@ private:
     long double value_;
 };
 
+// an integral number
+class IntegerExpression : public NumberExpression {
+public:
+    IntegerExpression(Location loc, std::string const& value)
+        : NumberExpression(loc, value), integer_(std::stoll(value))
+    {}
+
+    IntegerExpression(Location loc, long long integer)
+        : NumberExpression(loc, static_cast<long double>(integer)), integer_(integer)
+    {}
+
+    long long integer_value() const {return integer_;}
+
+    std::string to_string() const override {
+        return purple(pprintf("%", integer_));
+    }
+
+    // do nothing for number semantic analysis
+    void semantic(std::shared_ptr<scope_type> scp) override {};
+    expression_ptr clone() const override;
+
+    IntegerExpression* is_integer() override {return this;}
+
+    ~IntegerExpression() {}
+
+    void accept(Visitor *v) override;
+private:
+    long long integer_;
+};
+
+
+
 // declaration of a LOCAL variable
 class LocalDeclaration : public Expression {
 public:
@@ -788,6 +829,93 @@ private:
     std::vector<expression_ptr> args_;
 };
 
+class ReactionExpression : public Expression {
+public:
+    ReactionExpression(Location loc,
+                       expression_ptr&& lhs_terms,
+                       expression_ptr&& rhs_terms,
+                       expression_ptr&& fwd_rate_expr,
+                       expression_ptr&& rev_rate_expr)
+    : Expression(loc),
+      lhs_(std::move(lhs_terms)), rhs_(std::move(rhs_terms)),
+      fwd_rate_(std::move(fwd_rate_expr)), rev_rate_(std::move(rev_rate_expr))
+    {}
+
+    ReactionExpression* is_reaction() override {return this;}
+
+    std::string to_string() const override;
+    void semantic(std::shared_ptr<scope_type> scp) override;
+    expression_ptr clone() const override;
+    void accept(Visitor *v) override;
+
+    expression_ptr& lhs() { return lhs_; }
+    const expression_ptr& lhs() const { return lhs_; }
+
+    expression_ptr& rhs() { return rhs_; }
+    const expression_ptr& rhs() const { return rhs_; }
+
+    expression_ptr& fwd_rate() { return fwd_rate_; }
+    const expression_ptr& fwd_rate() const { return fwd_rate_; }
+
+    expression_ptr& rev_rate() { return rev_rate_; }
+    const expression_ptr& rev_rate() const { return rev_rate_; }
+
+private:
+    expression_ptr lhs_;
+    expression_ptr rhs_;
+    expression_ptr fwd_rate_;
+    expression_ptr rev_rate_;
+};
+
+class StoichTermExpression : public Expression {
+public:
+    StoichTermExpression(Location loc,
+                         expression_ptr&& coeff,
+                         expression_ptr&& ident)
+    : Expression(loc),
+      coeff_(std::move(coeff)), ident_(std::move(ident))
+    {}
+
+    std::string to_string() const override {
+        return pprintf("%%", coeff()->to_string(), ident()->to_string());
+    }
+    void semantic(std::shared_ptr<scope_type> scp) override;
+    expression_ptr clone() const override;
+    void accept(Visitor *v) override;
+
+    expression_ptr& coeff() { return coeff_; }
+    const expression_ptr& coeff() const { return coeff_; }
+
+    expression_ptr& ident() { return ident_; }
+    const expression_ptr& ident() const { return ident_; }
+
+private:
+    expression_ptr coeff_;
+    expression_ptr ident_;
+};
+
+class StoichExpression : public Expression {
+public:
+    StoichExpression(Location loc, std::vector<expression_ptr>&& terms)
+    : Expression(loc), terms_(std::move(terms))
+    {}
+
+    StoichExpression(Location loc)
+    : Expression(loc)
+    {}
+
+    std::string to_string() const override;
+    void semantic(std::shared_ptr<scope_type> scp) override;
+    expression_ptr clone() const override;
+    void accept(Visitor *v) override;
+
+    std::vector<expression_ptr>& terms() { return terms_; }
+    const std::vector<expression_ptr>& terms() const { return terms_; }
+
+private:
+    std::vector<expression_ptr> terms_;
+};
+
 // marks a call site in the AST
 // is used to mark both function and procedure calls
 class CallExpression : public Expression {
diff --git a/modcc/lexer.cpp b/modcc/lexer.cpp
index 0a85de24500d1f846c1f0156f863908453cca961..74da1a07eed937ef20d94f0df4f26a1a34dc960b 100644
--- a/modcc/lexer.cpp
+++ b/modcc/lexer.cpp
@@ -90,11 +90,7 @@ Token Lexer::parse() {
             // number
             case '0' ... '9':
             case '.':
-                t.spelling = number();
-
-                // test for error when reading number
-                t.type = (status_==lexerStatus::error) ? tok::reserved : tok::number;
-                return t;
+                return number();
 
             // identifier or keyword
             case 'a' ... 'z':
@@ -172,6 +168,11 @@ Token Lexer::parse() {
                     t.spelling += character();
                     t.type = tok::lte;
                 }
+                else if(*current_=='-' && current_[1]=='>') {
+                    t.spelling += character();
+                    t.spelling += character();
+                    t.type = tok::arrow;
+                }
                 else {
                     t.type = tok::lt;
                 }
@@ -228,7 +229,7 @@ Token Lexer::peek() {
 }
 
 // scan floating point number from stream
-std::string Lexer::number() {
+Token Lexer::number() {
     std::string str;
     char c = *current_;
 
@@ -284,7 +285,18 @@ std::string Lexer::number() {
         status_ = lexerStatus::error;
     }
 
-    return str;
+    tok type;
+    if(status_==lexerStatus::error) {
+        type = tok::reserved;
+    }
+    else if(num_point<1 && !uses_scientific_notation) {
+        type = tok::integer;
+    }
+    else {
+        type = tok::real;
+    }
+
+    return Token(type, str, location_);
 }
 
 // scan identifier from stream
diff --git a/modcc/lexer.hpp b/modcc/lexer.hpp
index 52917e94477a09173d7e580ea067f9e7b691f2ed..142a5d3b1f7483bd548eb0a377e70ced5e9f2a19 100644
--- a/modcc/lexer.hpp
+++ b/modcc/lexer.hpp
@@ -73,7 +73,7 @@ public:
     Token peek();
 
     // scan a number from the stream
-    std::string number();
+    Token number();
 
     // scan an identifier string from the stream
     std::string identifier();
diff --git a/modcc/parser.cpp b/modcc/parser.cpp
index ffa498c08f50e4b0b1c2d7a6a3b8370c4e8c38de..74692fc91c4f32670e90011ed103d06ac6561070 100644
--- a/modcc/parser.cpp
+++ b/modcc/parser.cpp
@@ -104,11 +104,12 @@ bool Parser::parse() {
             case tok::assigned :
                 parse_assigned_block();
                 break;
-            // INITIAL, DERIVATIVE, PROCEDURE, NET_RECEIVE and BREAKPOINT blocks
+            // INITIAL, KINETIC, DERIVATIVE, PROCEDURE, NET_RECEIVE and BREAKPOINT blocks
             // are all lowered to ProcedureExpression
             case tok::net_receive:
             case tok::breakpoint :
             case tok::initial    :
+            case tok::kinetic    :
             case tok::derivative :
             case tok::procedure  :
                 {
@@ -169,7 +170,7 @@ std::vector<Token> Parser::comma_separated_identifiers() {
             error(pprintf("found keyword '%', expected a variable name", token_.spelling));
             return tokens;
         }
-        else if(token_.type == tok::number) {
+        else if(token_.type == tok::real || token_.type == tok::integer) {
             error(pprintf("found number '%', expected a variable name", token_.spelling));
             return tokens;
         }
@@ -490,7 +491,7 @@ void Parser::parse_parameter_block() {
                 parm.value = "-";
                 get_token();
             }
-            if(token_.type != tok::number) {
+            if(token_.type != tok::integer && token_.type != tok::real) {
                 success = 0;
                 goto parm_exit;
             }
@@ -595,7 +596,7 @@ ass_exit:
 }
 
 std::vector<Token> Parser::unit_description() {
-    static const tok legal_tokens[] = {tok::identifier, tok::divide, tok::number};
+    static const tok legal_tokens[] = {tok::identifier, tok::divide, tok::real, tok::integer};
     int startline = location_.line;
     std::vector<Token> tokens;
 
@@ -726,6 +727,12 @@ symbol_ptr Parser::parse_procedure() {
             if( !expect( tok::identifier ) ) return nullptr;
             p = parse_prototype();
             break;
+        case tok::kinetic:
+            kind = procedureKind::kinetic;
+            get_token(); // consume keyword token
+            if( !expect( tok::identifier ) ) return nullptr;
+            p = parse_prototype();
+            break;
         case tok::procedure:
             kind = procedureKind::normal;
             get_token(); // consume keyword token
@@ -810,6 +817,8 @@ expression_ptr Parser::parse_statement() {
             return parse_local();
         case tok::identifier :
             return parse_line_expression();
+        case tok::tilde :
+            return parse_reaction_expression();
         case tok::initial :
             // only used for INITIAL block in NET_RECEIVE
             return parse_initial();
@@ -938,6 +947,75 @@ expression_ptr Parser::parse_line_expression() {
     return lhs;
 }
 
+expression_ptr Parser::parse_stoich_term() {
+    expression_ptr coeff = make_expression<IntegerExpression>(location_, 1);
+    auto here = location_;
+
+    if(token_.type==tok::integer) {
+        coeff = parse_integer();
+    }
+
+    if(token_.type!=tok::identifier) {
+        error(pprintf("expected an identifier, found '%'", yellow(token_.spelling)));
+        return nullptr;
+    }
+    return make_expression<StoichTermExpression>(here, std::move(coeff), parse_identifier());
+}
+
+expression_ptr Parser::parse_stoich_expression() {
+    std::vector<expression_ptr> terms;
+    auto here = location_;
+
+    if(token_.type==tok::integer || token_.type==tok::identifier) {
+        terms.push_back(parse_stoich_term());
+
+        while(token_.type==tok::plus) {
+            get_token(); // consume plus
+            terms.push_back(parse_stoich_term());
+        }
+    }
+
+    return make_expression<StoichExpression>(here, std::move(terms));
+}
+
+expression_ptr Parser::parse_reaction_expression() {
+    auto here = location_;
+
+    // consume tilde
+    get_token();
+
+    expression_ptr lhs = parse_stoich_expression();
+
+    if(token_.type != tok::arrow) {
+        error(pprintf("expected '%', found '%'", yellow("<->"), yellow(token_.spelling)));
+        return nullptr;
+    }
+
+    expression_ptr rhs = parse_stoich_expression();
+
+    if(token_.type != tok::lparen) {
+        error(pprintf("expected '%', found '%'", yellow("("), yellow(token_.spelling)));
+        return nullptr;
+    }
+
+    expression_ptr fwd = parse_expression();
+
+    if(token_.type != tok::comma) {
+        error(pprintf("expected '%', found '%'", yellow(","), yellow(token_.spelling)));
+        return nullptr;
+    }
+
+    expression_ptr rev = parse_expression();
+
+    if(token_.type != tok::rparen) {
+        error(pprintf("expected '%', found '%'", yellow(")"), yellow(token_.spelling)));
+        return nullptr;
+    }
+
+    return make_expression<ReactionExpression>(here, std::move(lhs), std::move(rhs),
+        std::move(fwd), std::move(rev));
+}
+
 expression_ptr Parser::parse_expression() {
     auto lhs = parse_unaryop();
 
@@ -1005,8 +1083,10 @@ expression_ptr Parser::parse_unaryop() {
 ///  ::  parenthesis expression (parsed recursively)
 expression_ptr Parser::parse_primary() {
     switch(token_.type) {
-        case tok::number:
-            return parse_number();
+        case tok::real:
+            return parse_real();
+        case tok::integer:
+            return parse_integer();
         case tok::identifier:
             if( peek().type == tok::lparen ) {
                 return parse_call();
@@ -1043,11 +1123,15 @@ expression_ptr Parser::parse_parenthesis_expression() {
     return e;
 }
 
-expression_ptr Parser::parse_number() {
+expression_ptr Parser::parse_real() {
     auto e = make_expression<NumberExpression>(token_.location, token_.spelling);
-
     get_token(); // consume the number
+    return e;
+}
 
+expression_ptr Parser::parse_integer() {
+    auto e = make_expression<IntegerExpression>(token_.location, token_.spelling);
+    get_token(); // consume the number
     return e;
 }
 
@@ -1280,6 +1364,10 @@ expression_ptr Parser::parse_block(bool is_nested) {
                 error("LOCAL variable declarations are not allowed inside a nested scope");
                 return nullptr;
             }
+            if(e->is_reaction()) {
+                error("reaction expressions are not allowed inside a nested scope");
+                return nullptr;
+            }
         }
 
         body.emplace_back(std::move(e));
diff --git a/modcc/parser.hpp b/modcc/parser.hpp
index b55bede17e698aa9d790211ddbee5a54f90ba56a..ed7b3317b5bea5a6ff26c213e7bbbcea645298c5 100644
--- a/modcc/parser.hpp
+++ b/modcc/parser.hpp
@@ -17,12 +17,16 @@ public:
     expression_ptr parse_prototype(std::string);
     expression_ptr parse_statement();
     expression_ptr parse_identifier();
-    expression_ptr parse_number();
+    expression_ptr parse_integer();
+    expression_ptr parse_real();
     expression_ptr parse_call();
     expression_ptr parse_expression();
     expression_ptr parse_primary();
     expression_ptr parse_parenthesis_expression();
     expression_ptr parse_line_expression();
+    expression_ptr parse_stoich_expression();
+    expression_ptr parse_stoich_term();
+    expression_ptr parse_reaction_expression();
     expression_ptr parse_binop(expression_ptr&&, Token);
     expression_ptr parse_unaryop();
     expression_ptr parse_local();
diff --git a/modcc/token.cpp b/modcc/token.cpp
index 1216c91287a7be1df4a0311c65c8436bdb4fb1cb..58de39c9377c695a450e342a12bbadd205cab120 100644
--- a/modcc/token.cpp
+++ b/modcc/token.cpp
@@ -29,6 +29,7 @@ static Keyword keywords[] = {
     {"STATE",       tok::state},
     {"BREAKPOINT",  tok::breakpoint},
     {"DERIVATIVE",  tok::derivative},
+    {"KINETIC",     tok::kinetic},
     {"PROCEDURE",   tok::procedure},
     {"FUNCTION",    tok::function},
     {"INITIAL",     tok::initial},
@@ -72,6 +73,8 @@ static TokenString token_strings[] = {
     {">=",          tok::gte},
     {"==",          tok::equality},
     {"!=",          tok::ne},
+    {"<->",         tok::arrow},
+    {"~",           tok::tilde},
     {",",           tok::comma},
     {"'",           tok::prime},
     {"{",           tok::lbrace},
@@ -79,7 +82,8 @@ static TokenString token_strings[] = {
     {"(",           tok::lparen},
     {")",           tok::rparen},
     {"identifier",  tok::identifier},
-    {"number",      tok::number},
+    {"real",        tok::real},
+    {"integer",     tok::integer},
     {"TITLE",       tok::title},
     {"NEURON",      tok::neuron},
     {"UNITS",       tok::units},
@@ -88,6 +92,7 @@ static TokenString token_strings[] = {
     {"STATE",       tok::state},
     {"BREAKPOINT",  tok::breakpoint},
     {"DERIVATIVE",  tok::derivative},
+    {"KINETIC",     tok::kinetic},
     {"PROCEDURE",   tok::procedure},
     {"FUNCTION",    tok::function},
     {"INITIAL",     tok::initial},
diff --git a/modcc/token.hpp b/modcc/token.hpp
index 8e97e47f9fa8a46bed23913a4762a90324bf60e2..2f0d23caadeb3bb8a84704618aa47e368c21f3fd 100644
--- a/modcc/token.hpp
+++ b/modcc/token.hpp
@@ -23,6 +23,12 @@ enum class tok {
     equality,// ==
     ne,      // !=
 
+    // <->
+    arrow,
+
+    // ~
+    tilde,
+
     // , '
     comma, prime,
 
@@ -35,7 +41,7 @@ enum class tok {
     identifier,
 
     // numbers
-    number,
+    real, integer,
 
     /////////////////////////////
     // keywords
@@ -44,7 +50,7 @@ enum class tok {
     title,
     neuron, units, parameter,
     assigned, state, breakpoint,
-    derivative, procedure, initial, function,
+    derivative, kinetic, procedure, initial, function,
     net_receive,
 
     // keywoards inside blocks
@@ -76,7 +82,7 @@ enum class tok {
 struct Token {
     // the spelling string contains the text of the token as it was written
     // in the input file
-    //   type = tok::number     : spelling = "3.1415"  (e.g.)
+    //   type = tok::real       : spelling = "3.1415"  (e.g.)
     //   type = tok::identifier : spelling = "foo_bar" (e.g.)
     //   type = tok::plus       : spelling = "+"       (always)
     //   type = tok::if         : spelling = "if"      (always)
diff --git a/modcc/visitor.hpp b/modcc/visitor.hpp
index faab5cca2500e7a144263a3402364284860bc227..ae5c42dcd29f7760a0b8dcc533fdb80e9561ed0f 100644
--- a/modcc/visitor.hpp
+++ b/modcc/visitor.hpp
@@ -20,6 +20,7 @@ public:
     virtual void visit(LocalVariable *e)        { visit((Expression*) e); }
     virtual void visit(IdentifierExpression *e) { visit((Expression*) e); }
     virtual void visit(NumberExpression *e)     { visit((Expression*) e); }
+    virtual void visit(IntegerExpression *e)    { visit((NumberExpression*) e); }
     virtual void visit(LocalDeclaration *e)     { visit((Expression*) e); }
     virtual void visit(ArgumentExpression *e)   { visit((Expression*) e); }
     virtual void visit(PrototypeExpression *e)  { visit((Expression*) e); }
diff --git a/tests/modcc/test_lexer.cpp b/tests/modcc/test_lexer.cpp
index b7d94515798b966b2e6d39f26ab68f1787f46e4b..b881f5a0a69c344a8ae0a6e9f8ebd2adc46ea495 100644
--- a/tests/modcc/test_lexer.cpp
+++ b/tests/modcc/test_lexer.cpp
@@ -231,6 +231,7 @@ TEST(Lexer, comments) {
 
 // test numbers
 TEST(Lexer, numbers) {
+    auto numeric = [](tok t) { return t==tok::real || t==tok::integer; };
     std::istringstream floats_stream("1 23 .3 87.99 12. 1.e3 1.2e+2 23e-3 -3");
 
     std::vector<double> floats;
@@ -238,6 +239,10 @@ TEST(Lexer, numbers) {
               std::istream_iterator<double>(),
               std::back_inserter(floats));
 
+    // hand-parse these ...
+    std::vector<long long> check_ints = {1, 23, 3};
+    std::vector<long long> ints;
+
     Lexer lexer(floats_stream.str());
     auto t = lexer.parse();
     auto iter = floats.cbegin();
@@ -249,11 +254,13 @@ TEST(Lexer, numbers) {
             // decide if the minus is a binary or unary expression
             EXPECT_EQ(tok::minus, t.type);
             t = lexer.parse();
-            EXPECT_EQ(tok::number, t.type);
+            EXPECT_TRUE(numeric(t.type));
+            if (t.type==tok::integer) ints.push_back(std::stoll(t.spelling));
             EXPECT_EQ(-(*iter), std::stod(t.spelling));
         }
         else {
-            EXPECT_EQ(t.type, tok::number);
+            EXPECT_TRUE(numeric(t.type));
+            if (t.type==tok::integer) ints.push_back(std::stoll(t.spelling));
             EXPECT_EQ(*iter, std::stod(t.spelling));
         }
 
@@ -263,4 +270,5 @@ TEST(Lexer, numbers) {
 
     EXPECT_EQ(floats.cend(), iter);
     EXPECT_EQ(tok::eof, t.type);
+    EXPECT_EQ(check_ints, ints);
 }