Пример #1
0
    def test_parse_script(self):
        # Test :meth:`parse_script()`.
        from myokit._parsing import parse_script

        # Test simple
        code = (
            '[[script]]',
        )
        script = parse_script(code)
        self.assertIsInstance(script, basestring)
        script = parse_script(''.join(code))
        self.assertIsInstance(script, basestring)

        code = (
            '[[script]]\n',
        )
        script = parse_script(code)
        self.assertIsInstance(script, basestring)

        code = (
            '[[script]]\n',
            'print("hi")',
        )
        script = parse_script(code)
        self.assertIsInstance(script, basestring)

        code = (
            '[[script]]\n',
            'print("hi")\n',
        )
        script = parse_script(code)
        self.assertIsInstance(script, basestring)

        # Not a script
        code = (
            '[[hello]]\n',
            'print("hi")\n',
        )
        self.assertRaisesRegex(
            myokit.ParseError, r'Expecting \[\[script]]', parse_script, code)

        # Not a script, parsing from stream
        from myokit._parsing import Tokenizer, parse_script_from_stream
        raw = iter(code)
        stream = Tokenizer(raw)
        self.assertRaisesRegex(
            myokit.ParseError, r'Expecting \[\[script]]',
            parse_script_from_stream, stream, raw)
Пример #2
0
    def test_parse_protocol(self):
        """ Test :meth:`parse_protocol()`. """
        from myokit._parsing import parse_protocol

        # Test simple
        # Level   Start   Length  Period  Multiplier
        code = ('[[protocol]]\n', )
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)
        protocol = parse_protocol(''.join(code))
        self.assertIsInstance(protocol, myokit.Protocol)

        code = (
            '[[protocol]]\n',
            '0 0 1 0 0\n',
        )
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)

        # Funny numbers
        code = ('[[protocol]]\n', '-1 0 1 0 0\n')
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)
        code = ('[[protocol]]\n', '-1 0 1e3 0 0\n')
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)
        code = ('[[protocol]]\n', '-1 0 1e3 1000 +3\n')
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)
        code = ('[[protocol]]\n', '0.1 0 1 0 0\n')
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)
        code = ('[[protocol]]\n', '0.1e-2 0 1 0 0\n')
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)
        code = ('[[protocol]]\n', '.1e-2 0 1 0 0\n')
        protocol = parse_protocol(code)
        self.assertIsInstance(protocol, myokit.Protocol)

        # Not a protocol
        code = (
            '[[protcle]]\n',
            '0 0 1 0 0\n',
        )
        self.assertRaisesRegex(myokit.ParseError, r'Expecting \[\[protocol]]',
                               parse_protocol, code)

        # Not a protocol (in stream)
        from myokit._parsing import parse_protocol_from_stream, Tokenizer
        stream = Tokenizer(iter(code))
        self.assertRaisesRegex(myokit.ParseError, r'Expecting \[\[protocol]]',
                               parse_protocol_from_stream, stream)

        # Test using 'next'
        code = (
            '[[protocol]]\n',
            '00 0 100 0 0\n',
            '10 next 100 0 0\n',
            '20 next 100 0 0\n',
        )
        p1 = parse_protocol(code)
        code = (
            '[[protocol]]\n',
            '00 0 100 0 0\n',
            '10 100 100 0 0\n',
            '20 200 100 0 0\n',
        )
        p2 = parse_protocol(code)
        self.assertEqual(p1.code(), p2.code())

        # Test invalid use of next (after periodic event)
        code = (
            '[[protocol]]\n',
            '00 0 100 1000 0\n',
            '10 next 100 0 0\n',
        )
        self.assertRaisesRegex(myokit.ProtocolParseError, 'Invalid next',
                               parse_protocol, code)

        # Simultaneous events
        code = (
            '[[protocol]]\n',
            '00 0 100 0 0\n',
            '10 0 100 0 0\n',
        )
        self.assertRaisesRegex(myokit.ProtocolParseError, 'same time',
                               parse_protocol, code)
        code = (
            '[[protocol]]\n',
            '00 0 100 1000 0\n',
            '10 0 1100 0 0\n',
        )
        self.assertRaisesRegex(myokit.ProtocolParseError, 'same time',
                               parse_protocol, code)

        # Wrong order
        code = (
            '[[protocol]]\n',
            '0 10 1 0 0\n',
            '1 0  1 0 0\n',
        )
        self.assertRaisesRegex(myokit.ProtocolParseError, 'chronological',
                               parse_protocol, code)

        # Duration <= 0
        code = ('[[protocol]]\n', '0 10 -1 0 0\n')
        self.assertRaisesRegex(myokit.ProtocolParseError, 'Invalid duration',
                               parse_protocol, code)
        code = ('[[protocol]]\n', '0 10 0 0 0\n')
        self.assertRaisesRegex(myokit.ProtocolParseError, 'Invalid duration',
                               parse_protocol, code)

        # Negative period
        code = ('[[protocol]]\n', '0 10 1 -1 0\n')
        self.assertRaisesRegex(myokit.ProtocolParseError, 'Negative period',
                               parse_protocol, code)

        # Negative multiplier
        # Multiplier for non-periodic event
        code = ('[[protocol]]\n', '0 10 1 100 -1\n')
        self.assertRaisesRegex(myokit.ProtocolParseError,
                               'Negative multiplier', parse_protocol, code)

        # Multiplier for non-periodic event
        code = ('[[protocol]]\n', '0 10 1 0 1\n')
        self.assertRaisesRegex(myokit.ProtocolParseError, 'Invalid multiplier',
                               parse_protocol, code)

        # Found a bug when parsing this
        code = (
            '[[protocol]]\n',
            '-80     next    300     0       0\n',
            '-120    3196.0  50.0    0       0\n',
        )
        parse_protocol(code)
Пример #3
0
 def p(code):
     return parse_model_from_stream(Tokenizer(iter(code)))
Пример #4
0
    def test_tokenizer(self):
        """
        Test basic Tokenizer functionality.
        """
        import myokit._parsing as p
        from myokit._parsing import Tokenizer
        s = Tokenizer('5')
        self.assertEqual(next(s), (p.INTEGER, '5', 1, 0))
        s = Tokenizer('3.0')
        self.assertEqual(next(s), (p.FLOAT, '3.0', 1, 0))
        s = Tokenizer('3.')
        self.assertEqual(next(s), (p.FLOAT, '3.', 1, 0))
        s = Tokenizer('3.E0')
        self.assertEqual(next(s), (p.FLOAT, '3.E0', 1, 0))
        s = Tokenizer('.30')
        self.assertEqual(next(s), (p.FLOAT, '.30', 1, 0))

        # Finished? Then should get a StopIteration
        self.assertEqual(s.peek()[0], p.EOL)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(s.peek()[0], p.EOF)
        self.assertEqual(next(s)[0], p.EOF)
        self.assertRaises(IndexError, s.peek)
        self.assertRaises(StopIteration, next, s)

        # Unknown token
        self.assertRaisesRegex(myokit.ParseError, 'invalid token', Tokenizer,
                               '@')
        s = Tokenizer('x  @')
        self.assertRaisesRegex(myokit.ParseError, 'invalid token', s.next)

        # Block-comment
        s = Tokenizer('"""Hello"""')
        self.assertEqual(next(s)[0], p.EOF)

        # Multi-line string
        s = Tokenizer('x: """Hello\nWo\trld"""')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertEqual(next(s)[0], p.COLON)
        self.assertEqual(s.peek()[0], p.TEXT)
        self.assertEqual(next(s)[1], 'Hello\nWo\trld')
        self.assertEqual(next(s)[0], p.EOL)
        s = Tokenizer('x: """Hello"""World')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertRaisesRegex(myokit.ParseError,
                               'after closing of multi-line string', next, s)
        s = Tokenizer('x: """He\nll\no"""World')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertRaisesRegex(myokit.ParseError,
                               'after closing of multi-line string', next, s)
        s = Tokenizer('x: """Hello\n')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertRaisesRegex(myokit.ParseError, 'Unclosed multi-line',
                               s.next)

        # Indented is removed from multi-line string
        s = Tokenizer('    x: """Hello \n    world"""')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertEqual(next(s)[0], p.COLON)
        self.assertEqual(s.peek()[0], p.TEXT)
        self.assertEqual(s.peek()[1], 'Hello\nworld')
        s = Tokenizer('\tx: """Hello \n\tworld"""')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertEqual(next(s)[0], p.COLON)
        self.assertEqual(s.peek()[0], p.TEXT)
        self.assertEqual(s.peek()[1], 'Hello\nworld')

        # Empty lines
        s = Tokenizer('\n\n\nx: """Hello\nWorld"""\n\n\n')
        next(s)
        next(s)
        self.assertEqual(next(s)[1], 'Hello\nWorld')
        s = Tokenizer('\n\n\nx: """Hello\n\n\nWorld"""\n\n\n')
        next(s)
        next(s)
        self.assertEqual(next(s)[1], 'Hello\n\n\nWorld')

        # Line continuation
        s = Tokenizer('x: this is \\\nthe value of x')
        next(s)
        next(s)
        self.assertEqual(next(s)[1], 'this is the value of x')
        self.assertEqual(next(s)[0], p.EOL)

        s = Tokenizer('x: this is \\\nthe\\\n\n\n value of x')
        next(s)
        next(s)
        self.assertEqual(next(s)[1], 'this is the')
        self.assertEqual(next(s)[0], p.EOL)

        s = Tokenizer('x: this is \\\nthe\\\n\n\n value of x')
        next(s)
        next(s)
        self.assertEqual(next(s)[1], 'this is the')
        self.assertEqual(next(s)[0], p.EOL)

        # Comment doesn't end line continuation
        s = Tokenizer('x: this is \\\n#Hi mike\nthe value of x')
        next(s)
        next(s)
        self.assertEqual(next(s)[1], 'this is the value of x')

        # Line continuation must be last character on line
        s = Tokenizer('1 + \\3')
        next(s)
        self.assertRaisesRegex(myokit.ParseError, ' Backslash must be last',
                               s.next)

        # But backslash is allowed in text (only seen as line cont. if last)
        s = Tokenizer('x: Hello\\michael\nHow are you')
        self.assertEqual(next(s)[0], p.META_NAME)
        self.assertEqual(next(s)[0], p.COLON)
        self.assertEqual(next(s)[1], 'Hello\\michael')

        # Brackets
        s = Tokenizer('x = (\n1 + \n\n2 + (\n3\n\n)\n\n) + 4')
        next(s)
        next(s)
        from myokit._parsing import parse_expression_stream
        e = parse_expression_stream(s)
        self.assertEqual(e.code(), '1 + 2 + 3 + 4')

        # Mismatched brackets
        s = Tokenizer('x = (1 + 2')
        self.assertEqual(next(s)[0], p.NAME)
        self.assertEqual(next(s)[0], p.EQUAL)
        self.assertEqual(next(s)[0], p.PAREN_OPEN)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.PLUS)
        self.assertRaisesRegex(myokit.ParseError, 'Parentheses mismatch',
                               s.next)

        s = Tokenizer('x = 1 + 2)')
        self.assertEqual(next(s)[0], p.NAME)
        self.assertEqual(next(s)[0], p.EQUAL)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.PLUS)
        self.assertRaisesRegex(myokit.ParseError, 'Parentheses mismatch',
                               s.next)

        # Test indenting (Tabs count as 8 spaces)
        s = '\n'.join([
            '1',
            '        2',
            '\t3',
            '        4',
            '5',
        ])
        s = Tokenizer(s)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(next(s)[0], p.INDENT)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(next(s)[0], p.DEDENT)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)

        # Test indenting error
        s = Tokenizer('\n'.join([
            '1',
            '    2',
            '  3',
        ]))
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(next(s)[0], p.INDENT)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertRaisesRegex(myokit.ParseError, 'Unexpected indenting level',
                               s.next)

        # Line feed counts as a newline
        s = Tokenizer('123\f456')
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)
        self.assertEqual(next(s)[0], p.INTEGER)
        self.assertEqual(next(s)[0], p.EOL)