コード例 #1
0
class DocstringCommentTest(unittest.TestCase):
    def test_docstring_success(self):
        self.run_docstring_test('valid')

    @pytest.mark.xfail(strict=True, raises=DocstringCommentException)
    def test_docstring_failure(self):
        self.run_docstring_test('invalid')

    def run_docstring_test(self, case: str):
        assert case in ['valid', 'invalid']
        input_file = FileStream(
            os.path.join(
                os.path.realpath(os.path.join(os.path.dirname(__file__),
                                              case)),
                'DocstringCommentTest.nestml'))
        lexer = PyNestMLLexer(input_file)
        lexer._errHandler = BailErrorStrategy()
        lexer._errHandler.reset(lexer)
        # create a token stream
        stream = CommonTokenStream(lexer)
        stream.fill()
        # parse the file
        parser = PyNestMLParser(stream)
        parser._errHandler = BailErrorStrategy()
        parser._errHandler.reset(parser)
        compilation_unit = parser.nestMLCompilationUnit()
        # now build the meta_model
        ast_builder_visitor = ASTBuilderVisitor(stream.tokens)
        ast = ast_builder_visitor.visit(compilation_unit)
        neuron_body_elements = ast.get_neuron_list()[0].get_body(
        ).get_body_elements()

        # now run the docstring checker visitor
        visitor = CommentCollectorVisitor(stream.tokens, strip_delim=False)
        compilation_unit.accept(visitor)
        # test whether ``"""`` is used correctly
        assert len(
            ast.get_neuron_list()) == 1, "Neuron failed to load correctly"

        class CommentCheckerVisitor(ASTVisitor):
            def visit(self, ast):
                for comment in ast.get_comments():
                    if "\"\"\"" in comment \
                       and not (isinstance(ast, ASTNeuron) or isinstance(ast, ASTNestMLCompilationUnit)):
                        raise DocstringCommentException()
                for comment in ast.get_post_comments():
                    if "\"\"\"" in comment:
                        raise DocstringCommentException()

        visitor = CommentCheckerVisitor()
        ast.accept(visitor)
コード例 #2
0
 def __init__(self, tokens):
     self.__comments = CommentCollectorVisitor(tokens)
     self.data_type_visitor = ASTDataTypeVisitor()
コード例 #3
0
class ASTBuilderVisitor(PyNestMLParserVisitor):
    """
    This class is used to create an internal representation of the model by means of an abstract syntax tree.
    """

    def __init__(self, tokens):
        self.__comments = CommentCollectorVisitor(tokens)
        self.data_type_visitor = ASTDataTypeVisitor()

    # Visit a parse tree produced by PyNESTMLParser#nestmlCompilationUnit.
    def visitNestMLCompilationUnit(self, ctx):
        # now process the actual model
        neurons = list()
        for child in ctx.neuron():
            neurons.append(self.visit(child))
        # extract the name of the artifact from the context
        artifact_name = ntpath.basename(ctx.start.source[1].fileName)
        compilation_unit = ASTNodeFactory.create_ast_nestml_compilation_unit(list_of_neurons=neurons,
                                                                             source_position=create_source_pos(ctx),
                                                                             artifact_name=artifact_name)
        # first ensure certain properties of the neuron
        CoCosManager.check_neuron_names_unique(compilation_unit)
        return compilation_unit

    # Visit a parse tree produced by PyNESTMLParser#datatype.
    def visitDataType(self, ctx):
        is_int = (True if ctx.isInt is not None else False)
        is_real = (True if ctx.isReal is not None else False)
        is_string = (True if ctx.isString is not None else False)
        is_bool = (True if ctx.isBool is not None else False)
        is_void = (True if ctx.isVoid is not None else False)
        unit = self.visit(ctx.unitType()) if ctx.unitType() is not None else None
        ret = ASTNodeFactory.create_ast_data_type(is_integer=is_int, is_boolean=is_bool,
                                                  is_real=is_real, is_string=is_string, is_void=is_void,
                                                  is_unit_type=unit, source_position=create_source_pos(ctx))
        # now update the type
        ret.accept(ASTDataTypeVisitor())
        # self.data_type_visitor.visit_datatype(ret)
        return ret

    # Visit a parse tree produced by PyNESTMLParser#unitType.
    def visitUnitType(self, ctx):
        left_parenthesis = True if ctx.leftParentheses is not None else False
        compound_unit = self.visit(ctx.compoundUnit) if ctx.compoundUnit is not None else None
        is_encapsulated = left_parenthesis and True if ctx.rightParentheses is not None else False
        base = self.visit(ctx.base) if ctx.base is not None else None
        is_pow = True if ctx.powOp is not None else False
        exponent = int(str(ctx.exponent.getText())) if ctx.exponent is not None else None
        if ctx.unitlessLiteral is not None:
            lhs = int(str(ctx.unitlessLiteral.text))
        else:
            lhs = self.visit(ctx.left) if ctx.left is not None else None
        is_times = True if ctx.timesOp is not None else False
        is_div = True if ctx.divOp is not None else False
        rhs = self.visit(ctx.right) if ctx.right is not None else None
        unit = str(ctx.unit.text) if ctx.unit is not None else None
        return ASTNodeFactory.create_ast_unit_type(is_encapsulated=is_encapsulated, compound_unit=compound_unit,
                                                   base=base, is_pow=is_pow,
                                                   exponent=exponent, lhs=lhs, rhs=rhs, is_div=is_div,
                                                   is_times=is_times, unit=unit, source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#rhs.
    def visitExpression(self, ctx):
        # first check if it is a simple rhs
        if ctx.simpleExpression() is not None:
            return self.visitSimpleExpression(ctx.simpleExpression())
        # now it is not directly a simple rhs
        # check if it is an encapsulated rhs
        is_encapsulated = (True if ctx.leftParentheses is not None and ctx.rightParentheses else False)
        # or a term or negated
        unary_operator = (self.visit(ctx.unaryOperator()) if ctx.unaryOperator() is not None else None)
        is_logical_not = (True if ctx.logicalNot is not None else False)
        expression = self.visit(ctx.term) if ctx.term is not None else None
        # otherwise it is a combined one, check first lhs, then the operator and finally rhs
        lhs = (self.visit(ctx.left) if ctx.left is not None else None)
        if ctx.powOp is not None:
            source_pos = ASTSourceLocation.make_ast_source_position(start_line=ctx.powOp.line,
                                                                    start_column=ctx.powOp.column,
                                                                    end_line=ctx.powOp.line,
                                                                    end_column=ctx.powOp.column)
            binary_operator = ASTNodeFactory.create_ast_arithmetic_operator(is_pow_op=True,
                                                                            source_position=source_pos)
        elif ctx.timesOp is not None:
            source_pos = ASTSourceLocation.make_ast_source_position(start_line=ctx.timesOp.line,
                                                                    start_column=ctx.timesOp.column,
                                                                    end_line=ctx.timesOp.line,
                                                                    end_column=ctx.timesOp.column)
            binary_operator = ASTNodeFactory.create_ast_arithmetic_operator(is_times_op=True,
                                                                            source_position=source_pos)
        elif ctx.divOp is not None:
            source_pos = ASTSourceLocation.make_ast_source_position(start_line=ctx.divOp.line,
                                                                    start_column=ctx.divOp.column,
                                                                    end_line=ctx.divOp.line,
                                                                    end_column=ctx.divOp.column)
            binary_operator = ASTNodeFactory.create_ast_arithmetic_operator(is_div_op=True,
                                                                            source_position=source_pos)
        elif ctx.moduloOp is not None:
            source_pos = ASTSourceLocation.make_ast_source_position(start_line=ctx.moduloOp.line,
                                                                    start_column=ctx.moduloOp.column,
                                                                    end_line=ctx.moduloOp.line,
                                                                    end_column=ctx.moduloOp.column)
            binary_operator = ASTNodeFactory.create_ast_arithmetic_operator(is_modulo_op=True,
                                                                            source_position=source_pos)
        elif ctx.plusOp is not None:
            source_pos = ASTSourceLocation.make_ast_source_position(start_line=ctx.plusOp.line,
                                                                    start_column=ctx.plusOp.column,
                                                                    end_line=ctx.plusOp.line,
                                                                    end_column=ctx.plusOp.column)
            binary_operator = ASTNodeFactory.create_ast_arithmetic_operator(is_plus_op=True,
                                                                            source_position=source_pos)
        elif ctx.minusOp is not None:
            source_pos = ASTSourceLocation.make_ast_source_position(start_line=ctx.minusOp.line,
                                                                    start_column=ctx.minusOp.column,
                                                                    end_line=ctx.minusOp.line,
                                                                    end_column=ctx.minusOp.column)
            binary_operator = ASTNodeFactory.create_ast_arithmetic_operator(is_minus_op=True,
                                                                            source_position=source_pos)
        elif ctx.bitOperator() is not None:
            binary_operator = self.visit(ctx.bitOperator())
        elif ctx.comparisonOperator() is not None:
            binary_operator = self.visit(ctx.comparisonOperator())
        elif ctx.logicalOperator() is not None:
            binary_operator = self.visit(ctx.logicalOperator())
        else:
            binary_operator = None
        rhs = (self.visit(ctx.right) if ctx.right is not None else None)
        # not it was not an operator, thus the ternary one ?
        condition = (self.visit(ctx.condition) if ctx.condition is not None else None)
        if_true = (self.visit(ctx.ifTrue) if ctx.ifTrue is not None else None)
        if_not = (self.visit(ctx.ifNot) if ctx.ifNot is not None else None)
        source_pos = create_source_pos(ctx)
        # finally construct the corresponding rhs
        if expression is not None:
            return ASTNodeFactory.create_ast_expression(is_encapsulated=is_encapsulated,
                                                        is_logical_not=is_logical_not,
                                                        unary_operator=unary_operator,
                                                        expression=expression, source_position=source_pos)
        elif (lhs is not None) and (rhs is not None) and (binary_operator is not None):
            return ASTNodeFactory.create_ast_compound_expression(lhs=lhs, binary_operator=binary_operator,
                                                                 rhs=rhs, source_position=source_pos)
        elif (condition is not None) and (if_true is not None) and (if_not is not None):
            return ASTNodeFactory.create_ast_ternary_expression(condition=condition, if_true=if_true,
                                                                if_not=if_not, source_position=source_pos)
        else:
            raise RuntimeError('Type of rhs @%s,%s not recognized!' % (ctx.start.line, ctx.start.column))

    # Visit a parse tree produced by PyNESTMLParser#simpleExpression.
    def visitSimpleExpression(self, ctx):
        function_call = (self.visit(ctx.functionCall()) if ctx.functionCall() is not None else None)
        boolean_literal = ((True if re.match(r'[Tt]rue', str(
            ctx.BOOLEAN_LITERAL())) else False) if ctx.BOOLEAN_LITERAL() is not None else None)
        if ctx.UNSIGNED_INTEGER() is not None:
            numeric_literal = int(str(ctx.UNSIGNED_INTEGER()))
        elif ctx.FLOAT() is not None:
            numeric_literal = float(str(ctx.FLOAT()))
        else:
            numeric_literal = None
        is_inf = (True if ctx.isInf is not None else False)
        variable = (self.visit(ctx.variable()) if ctx.variable() is not None else None)
        string = (str(ctx.string.text) if ctx.string is not None else None)
        return ASTNodeFactory.create_ast_simple_expression(function_call=function_call,
                                                           boolean_literal=boolean_literal,
                                                           numeric_literal=numeric_literal,
                                                           is_inf=is_inf, variable=variable,
                                                           string=string,
                                                           source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#unaryOperator.
    def visitUnaryOperator(self, ctx):
        is_unary_plus = (True if ctx.unaryPlus is not None else False)
        is_unary_minus = (True if ctx.unaryMinus is not None else False)
        is_unary_tilde = (True if ctx.unaryTilde is not None else False)
        return ASTNodeFactory.create_ast_unary_operator(is_unary_plus=is_unary_plus,
                                                        is_unary_minus=is_unary_minus,
                                                        is_unary_tilde=is_unary_tilde,
                                                        source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#bitOperator.
    def visitBitOperator(self, ctx):
        is_bit_and = (True if ctx.bitAnd is not None else False)
        is_bit_xor = (True if ctx.bitXor is not None else False)
        is_bit_or = (True if ctx.bitOr is not None else False)
        is_bit_shift_left = (True if ctx.bitShiftLeft is not None else False)
        is_bit_shift_right = (True if ctx.bitShiftRight is not None else False)
        return ASTNodeFactory.create_ast_bit_operator(is_bit_and=is_bit_and, is_bit_xor=is_bit_xor,
                                                      is_bit_or=is_bit_or,
                                                      is_bit_shift_left=is_bit_shift_left,
                                                      is_bit_shift_right=is_bit_shift_right,
                                                      source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#comparisonOperator.
    def visitComparisonOperator(self, ctx):
        is_lt = (True if ctx.lt is not None else False)
        is_le = (True if ctx.le is not None else False)
        is_eq = (True if ctx.eq is not None else False)
        is_ne = (True if ctx.ne is not None else False)
        is_ne2 = (True if ctx.ne2 is not None else False)
        is_ge = (True if ctx.ge is not None else False)
        is_gt = (True if ctx.gt is not None else False)
        return ASTNodeFactory.create_ast_comparison_operator(is_lt, is_le, is_eq, is_ne, is_ne2, is_ge, is_gt,
                                                             create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#logicalOperator.
    def visitLogicalOperator(self, ctx):
        is_logical_and = (True if ctx.logicalAnd is not None else False)
        is_logical_or = (True if ctx.logicalOr is not None else False)
        return ASTNodeFactory.create_ast_logical_operator(is_logical_and=is_logical_and,
                                                          is_logical_or=is_logical_or,
                                                          source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#variable.
    def visitVariable(self, ctx):
        differential_order = (len(ctx.DIFFERENTIAL_ORDER()) if ctx.DIFFERENTIAL_ORDER() is not None else 0)
        return ASTNodeFactory.create_ast_variable(name=str(ctx.NAME()),
                                                  differential_order=differential_order,
                                                  source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#functionCall.
    def visitFunctionCall(self, ctx):
        name = (str(ctx.calleeName.text))
        args = list()
        if type(ctx.expression()) == list:
            for arg in ctx.expression():
                args.append(self.visit(arg))
        elif ctx.expression() is not None:
            args.append(self.visit(ctx.expression()))
        node = ASTNodeFactory.create_ast_function_call(callee_name=name, args=args,
                                                       source_position=create_source_pos(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#odeFunction.
    def visitOdeFunction(self, ctx):
        is_recordable = (True if ctx.recordable is not None else False)
        variable_name = (str(ctx.variableName.text) if ctx.variableName is not None else None)
        data_type = (self.visit(ctx.dataType()) if ctx.dataType() is not None else None)
        expression = (self.visit(ctx.expression()) if ctx.expression() is not None else None)
        ode_function = ASTNodeFactory.create_ast_ode_function(is_recordable=is_recordable, variable_name=variable_name,
                                                              data_type=data_type, expression=expression,
                                                              source_position=create_source_pos(ctx))
        update_node_comments(ode_function, self.__comments.visit(ctx))
        return ode_function

    # Visit a parse tree produced by PyNESTMLParser#equation.
    def visitOdeEquation(self, ctx):
        lhs = self.visit(ctx.lhs) if ctx.lhs is not None else None
        rhs = self.visit(ctx.rhs) if ctx.rhs is not None else None
        ode_equation = ASTNodeFactory.create_ast_ode_equation(lhs=lhs, rhs=rhs, source_position=create_source_pos(ctx))
        update_node_comments(ode_equation, self.__comments.visit(ctx))
        return ode_equation

    # Visit a parse tree produced by PyNESTMLParser#shape.
    def visitOdeShape(self, ctx):
        lhs = self.visit(ctx.lhs) if ctx.lhs is not None else None
        rhs = self.visit(ctx.rhs) if ctx.rhs is not None else None
        shape = ASTNodeFactory.create_ast_ode_shape(lhs=lhs, rhs=rhs, source_position=create_source_pos(ctx))
        update_node_comments(shape, self.__comments.visit(ctx))
        return shape

    # Visit a parse tree produced by PyNESTMLParser#block.
    def visitBlock(self, ctx):
        stmts = list()
        if ctx.stmt() is not None:
            for stmt in ctx.stmt():
                stmts.append(self.visit(stmt))
        block = ASTNodeFactory.create_ast_block(stmts=stmts, source_position=create_source_pos(ctx))
        return block

    # Visit a parse tree produced by PyNESTMLParser#compound_Stmt.
    def visitCompoundStmt(self, ctx):
        if_stmt = self.visit(ctx.ifStmt()) if ctx.ifStmt() is not None else None
        while_stmt = self.visit(ctx.whileStmt()) if ctx.whileStmt() is not None else None
        for_stmt = self.visit(ctx.forStmt()) if ctx.forStmt() is not None else None
        node = ASTNodeFactory.create_ast_compound_stmt(if_stmt, while_stmt, for_stmt, create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#small_Stmt.
    def visitSmallStmt(self, ctx):
        assignment = self.visit(ctx.assignment()) if ctx.assignment() is not None else None
        function_call = self.visit(ctx.functionCall()) if ctx.functionCall() is not None else None
        declaration = self.visit(ctx.declaration()) if ctx.declaration() is not None else None
        return_stmt = self.visit(ctx.returnStmt()) if ctx.returnStmt() is not None else None
        node = ASTNodeFactory.create_ast_small_stmt(assignment=assignment, function_call=function_call,
                                                    declaration=declaration, return_stmt=return_stmt,
                                                    source_position=create_source_pos(ctx))
        # update_node_comments(node, self.__comments.visit(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#assignment.
    def visitAssignment(self, ctx):
        lhs = self.visit(ctx.lhs_variable) if ctx.lhs_variable is not None else None
        is_direct_assignment = True if ctx.directAssignment is not None else False
        is_compound_sum = True if ctx.compoundSum is not None else False
        is_compound_minus = True if ctx.compoundMinus is not None else False
        is_compound_product = True if ctx.compoundProduct is not None else False
        is_compound_quotient = True if ctx.compoundQuotient is not None else False
        expression = self.visit(ctx.expression()) if ctx.expression() is not None else None
        node = ASTNodeFactory.create_ast_assignment(lhs=lhs, is_direct_assignment=is_direct_assignment,
                                                    is_compound_sum=is_compound_sum,
                                                    is_compound_minus=is_compound_minus,
                                                    is_compound_product=is_compound_product,
                                                    is_compound_quotient=is_compound_quotient,
                                                    expression=expression, source_position=create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#declaration.
    def visitDeclaration(self, ctx):
        is_recordable = (True if ctx.isRecordable is not None else False)
        is_function = (True if ctx.isFunction is not None else False)
        variables = list()
        for var in ctx.variable():
            variables.append(self.visit(var))
        data_type = self.visit(ctx.dataType()) if ctx.dataType() is not None else None
        size_param = str(ctx.sizeParameter.text) if ctx.sizeParameter is not None else None
        expression = self.visit(ctx.rhs) if ctx.rhs is not None else None
        invariant = self.visit(ctx.invariant) if ctx.invariant is not None else None
        declaration = ASTNodeFactory.create_ast_declaration(is_recordable=is_recordable, is_function=is_function,
                                                            variables=variables, data_type=data_type,
                                                            size_parameter=size_param,
                                                            expression=expression,
                                                            invariant=invariant, source_position=create_source_pos(ctx))
        update_node_comments(declaration, self.__comments.visit(ctx))
        return declaration

    # Visit a parse tree produced by PyNESTMLParser#returnStmt.
    def visitReturnStmt(self, ctx):
        ret_expression = self.visit(ctx.expression()) if ctx.expression() is not None else None
        return ASTNodeFactory.create_ast_return_stmt(expression=ret_expression, source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#ifStmt.
    def visitIfStmt(self, ctx):
        if_clause = self.visit(ctx.ifClause()) if ctx.ifClause() is not None else None
        elif_clauses = list()
        if ctx.elifClause() is not None:
            for clause in ctx.elifClause():
                elif_clauses.append(self.visit(clause))
        else_clause = self.visit(ctx.elseClause()) if ctx.elseClause() is not None else None
        return ASTNodeFactory.create_ast_if_stmt(if_clause=if_clause, elif_clauses=elif_clauses,
                                                 else_clause=else_clause, source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#ifClause.
    def visitIfClause(self, ctx):
        condition = self.visit(ctx.expression()) if ctx.expression() is not None else None
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        ret = ASTNodeFactory.create_ast_if_clause(condition=condition, block=block,
                                                  source_position=create_source_pos(ctx))
        update_node_comments(ret, self.__comments.visitStmt(ctx))
        return ret

    # Visit a parse tree produced by PyNESTMLParser#elifClause.
    def visitElifClause(self, ctx):
        condition = self.visit(ctx.expression()) if ctx.expression() is not None else None
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        node = ASTNodeFactory.create_ast_elif_clause(condition=condition, block=block,
                                                     source_position=create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#elseClause.
    def visitElseClause(self, ctx):
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        node = ASTNodeFactory.create_ast_else_clause(block=block, source_position=create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#forStmt.
    def visitForStmt(self, ctx):
        variable = str(ctx.NAME()) if ctx.NAME() is not None else None
        start_from = self.visit(ctx.start_from) if ctx.start_from is not None else None
        end_at = self.visit(ctx.end_at) if ctx.end_at is not None else None
        step_scalar = -1 if ctx.negative is not None else 1
        if ctx.UNSIGNED_INTEGER() is not None:
            value = int(str(ctx.UNSIGNED_INTEGER()))
        else:
            value = float(str(ctx.FLOAT()))

        step = step_scalar * value
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        node = ASTNodeFactory.create_ast_for_stmt(variable=variable, start_from=start_from, end_at=end_at, step=step,
                                                  block=block, source_position=create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#whileStmt.
    def visitWhileStmt(self, ctx):
        cond = self.visit(ctx.expression()) if ctx.expression() is not None else None
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        node = ASTNodeFactory.create_ast_while_stmt(condition=cond, block=block, source_position=create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#neuron.
    def visitNeuron(self, ctx):
        name = str(ctx.NAME()) if ctx.NAME() is not None else None
        body = self.visit(ctx.body()) if ctx.body() is not None else None
        # after we have constructed the meta_model of the neuron,
        # we can ensure some basic properties which should always hold
        # we have to check if each type of block is defined at most once (except for function), and that input,output
        # and update are defined once
        if hasattr(ctx.start.source[1], 'fileName'):
            artifact_name = ntpath.basename(ctx.start.source[1].fileName)
        else:
            artifact_name = 'parsed from string'
        neuron = ASTNodeFactory.create_ast_neuron(name=name, body=body, source_position=create_source_pos(ctx),
                                                  artifact_name=artifact_name)
        # update the comments
        update_node_comments(neuron, self.__comments.visit(ctx))
        # in order to enable the logger to print correct messages set as the source the corresponding neuron
        Logger.set_current_neuron(neuron)
        CoCoEachBlockUniqueAndDefined.check_co_co(node=neuron)
        Logger.set_current_neuron(neuron)
        # now the meta_model seems to be correct, return it
        return neuron

    # Visit a parse tree produced by PyNESTMLParser#body.
    def visitBody(self, ctx):
        """
        Here, in order to ensure that the correct order of elements is kept, we use a method which inspects
        a list of elements and returns the one with the smallest source line.
        """
        body_elements = list()
        # visit all var_block children
        if ctx.blockWithVariables() is not None:
            for child in ctx.blockWithVariables():
                body_elements.append(child)
        if ctx.updateBlock() is not None:
            for child in ctx.updateBlock():
                body_elements.append(child)
        if ctx.equationsBlock() is not None:
            for child in ctx.equationsBlock():
                body_elements.append(child)
        if ctx.inputBlock() is not None:
            for child in ctx.inputBlock():
                body_elements.append(child)
        if ctx.outputBlock() is not None:
            for child in ctx.outputBlock():
                body_elements.append(child)
        if ctx.function() is not None:
            for child in ctx.function():
                body_elements.append(child)
        elements = list()
        while len(body_elements) > 0:
            elem = get_next(body_elements)
            elements.append(self.visit(elem))
            body_elements.remove(elem)
        body = ASTNodeFactory.create_ast_body(elements, create_source_pos(ctx))
        return body

    # Visit a parse tree produced by PyNESTMLParser#blockWithVariables.
    def visitBlockWithVariables(self, ctx):
        declarations = list()
        if ctx.declaration() is not None:
            for child in ctx.declaration():
                declarations.append(self.visit(child))
        block_type = ctx.blockType.text  # the text field stores the exact name of the token, e.g., state
        source_pos = create_source_pos(ctx)
        if block_type == 'state':
            ret = ASTNodeFactory.create_ast_block_with_variables(True, False, False, False, declarations, source_pos)
        elif block_type == 'parameters':
            ret = ASTNodeFactory.create_ast_block_with_variables(False, True, False, False, declarations, source_pos)
        elif block_type == 'internals':
            ret = ASTNodeFactory.create_ast_block_with_variables(False, False, True, False, declarations, source_pos)
        elif block_type == 'initial_values':
            ret = ASTNodeFactory.create_ast_block_with_variables(False, False, False, True, declarations, source_pos)
        else:
            raise RuntimeError('(PyNestML.ASTBuilder) Unspecified type (=%s) of var-block.' % str(ctx.blockType))
        update_node_comments(ret, self.__comments.visit(ctx))
        return ret

    def visitUpdateBlock(self, ctx):
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        ret = ASTNodeFactory.create_ast_update_block(block=block, source_position=create_source_pos(ctx))
        update_node_comments(ret, self.__comments.visit(ctx))
        return ret

    # Visit a parse tree produced by PyNESTMLParser#equations.
    def visitEquationsBlock(self, ctx):
        elements = list()
        if ctx.odeEquation() is not None:
            for eq in ctx.odeEquation():
                elements.append(eq)
        if ctx.odeShape() is not None:
            for shape in ctx.odeShape():
                elements.append(shape)
        if ctx.odeFunction() is not None:
            for fun in ctx.odeFunction():
                elements.append(fun)
        ordered = list()
        while len(elements) > 0:
            elem = get_next(elements)
            ordered.append(self.visit(elem))
            elements.remove(elem)
        ret = ASTNodeFactory.create_ast_equations_block(declarations=ordered,
                                                        source_position=create_source_pos(ctx))
        update_node_comments(ret, self.__comments.visit(ctx))
        return ret

    # Visit a parse tree produced by PyNESTMLParser#inputBuffer.
    def visitInputBlock(self, ctx):
        input_lines = list()
        if ctx.inputLine() is not None:
            for line in ctx.inputLine():
                input_lines.append(self.visit(line))
        ret = ASTNodeFactory.create_ast_input_block(input_definitions=input_lines,
                                                    source_position=create_source_pos(ctx))
        update_node_comments(ret, self.__comments.visit(ctx))
        return ret

    # Visit a parse tree produced by PyNESTMLParser#inputLine.
    def visitInputLine(self, ctx):
        name = str(ctx.name.text) if ctx.name is not None else None
        size_parameter = str(ctx.sizeParameter.text) if ctx.sizeParameter is not None else None
        input_types = list()
        if ctx.inputType() is not None:
            for Type in ctx.inputType():
                input_types.append(self.visit(Type))
        data_type = self.visit(ctx.dataType()) if ctx.dataType() is not None else None
        if ctx.isCurrent:
            signal_type = ASTSignalType.CURRENT
        elif ctx.isSpike:
            signal_type = ASTSignalType.SPIKE
        else:
            signal_type = None
        ret = ASTNodeFactory.create_ast_input_line(name=name, size_parameter=size_parameter, data_type=data_type,
                                                   input_types=input_types, signal_type=signal_type,
                                                   source_position=create_source_pos(ctx))
        update_node_comments(ret, self.__comments.visit(ctx))
        return ret

    # Visit a parse tree produced by PyNESTMLParser#inputType.
    def visitInputType(self, ctx):
        is_inhibitory = True if ctx.isInhibitory is not None else False
        is_excitatory = True if ctx.isExcitatory is not None else False
        return ASTNodeFactory.create_ast_input_type(is_inhibitory=is_inhibitory, is_excitatory=is_excitatory,
                                                    source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#outputBuffer.
    def visitOutputBlock(self, ctx):
        source_pos = create_source_pos(ctx)
        if ctx.isSpike is not None:
            ret = ASTNodeFactory.create_ast_output_block(s_type=ASTSignalType.SPIKE, source_position=source_pos)
            update_node_comments(ret, self.__comments.visit(ctx))
            return ret
        elif ctx.isCurrent is not None:
            ret = ASTNodeFactory.create_ast_output_block(s_type=ASTSignalType.CURRENT, source_position=source_pos)
            update_node_comments(ret, self.__comments.visit(ctx))
            return ret
        else:
            raise RuntimeError('(PyNestML.ASTBuilder) Type of output buffer not recognized.')

    # Visit a parse tree produced by PyNESTMLParser#function.
    def visitFunction(self, ctx):
        name = str(ctx.NAME()) if ctx.NAME() is not None else None
        parameters = list()
        if type(ctx.parameter()) is list:
            for par in ctx.parameter():
                parameters.append(self.visit(par))
        elif ctx.parameters() is not None:
            parameters.append(ctx.parameter())
        block = self.visit(ctx.block()) if ctx.block() is not None else None
        return_type = self.visit(ctx.returnType) if ctx.returnType is not None else None
        node = ASTNodeFactory.create_ast_function(name=name, parameters=parameters, block=block,
                                                  return_type=return_type, source_position=create_source_pos(ctx))
        update_node_comments(node, self.__comments.visit(ctx))
        return node

    # Visit a parse tree produced by PyNESTMLParser#parameter.
    def visitParameter(self, ctx):
        name = str(ctx.NAME()) if ctx.NAME() is not None else None
        data_type = self.visit(ctx.dataType()) if ctx.dataType() is not None else None
        return ASTNodeFactory.create_ast_parameter(name=name, data_type=data_type,
                                                   source_position=create_source_pos(ctx))

    # Visit a parse tree produced by PyNESTMLParser#stmt.
    def visitStmt(self, ctx):
        small = self.visit(ctx.smallStmt()) if ctx.smallStmt() is not None else None
        compound = self.visit(ctx.compoundStmt()) if ctx.compoundStmt() is not None else None
        return ASTNodeFactory.create_ast_stmt(small, compound, create_source_pos(ctx))