コード例 #1
0
def test_parser_invalid_line():
    """
    To validate the parser detects the presence of an missing numeral in
    expression
    """
    parser = Parser(Lexer(Scanner("1 + 2 +\n")))

    with pytest.raises(LythSyntaxError) as err:
        next(parser)

    assert err.value.msg is LythError.INCOMPLETE_LINE
    assert err.value.filename == "<stdin>"
    assert err.value.lineno == 0
    assert err.value.offset == 6
    assert err.value.line == "1 + 2 +"

    parser = Parser(Lexer(Scanner("1 + 2 *\n")))

    with pytest.raises(LythSyntaxError) as err:
        next(parser)

    assert err.value.msg is LythError.INCOMPLETE_LINE
    assert err.value.filename == "<stdin>"
    assert err.value.lineno == 0
    assert err.value.offset == 6
    assert err.value.line == "1 + 2 *"
コード例 #2
0
def test_analyzer_wrong_assign(clean_namespace):
    """
    To validate that the analyzer generates exceptions when the assign operator
    is not properly written.
    """
    analyzer = Analyzer(Parser(Lexer(Scanner('7 + 4 -> b + 1\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    with pytest.raises(LythSyntaxError) as err:
        analyzer()

    assert err.value.msg is LythError.GARBAGE_CHARACTERS
    assert err.value.filename == "__test__"
    assert err.value.lineno == 0
    assert err.value.offset == 6
    assert err.value.line == "7 + 4 -> b + 1"

    analyzer = Analyzer(Parser(Lexer(Scanner('7 + 4 <- 1 + 2\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    with pytest.raises(LythSyntaxError) as err:
        analyzer()

    assert err.value.msg is LythError.LEFT_MEMBER_IS_EXPRESSION
    assert err.value.filename == "__test__"
    assert err.value.lineno == 0
    assert err.value.offset == 2
    assert err.value.line == "7 + 4 <- 1 + 2"

    analyzer = Analyzer(Parser(Lexer(Scanner('7 + 4 -> \n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    with pytest.raises(LythSyntaxError) as err:
        analyzer()

    assert err.value.msg is LythError.INCOMPLETE_LINE
    assert err.value.filename == "__test__"
    assert err.value.lineno == 0
    assert err.value.offset == 8
    assert err.value.line == "7 + 4 -> "

    analyzer = Analyzer(Parser(Lexer(Scanner('7 + 4 -> 6\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    with pytest.raises(LythSyntaxError) as err:
        analyzer()

    assert err.value.msg is LythError.NAME_EXPECTED
    assert err.value.filename == "__test__"
    assert err.value.lineno == 0
    assert err.value.offset == 9
    assert err.value.line == "7 + 4 -> 6"
コード例 #3
0
def test_parser_nested_let_assign():
    """
    To validate the parser solves nested let.
    """
    parser = Parser(Lexer(Scanner("let:\n  let:\n    a <- 1 + 2\n  b <- a * 3\n\n")))

    assign = parser()
    assert assign.name == NodeType.Let
    assert str(assign) == "Let(Let(MutableAssign(Name(a), Add(Num(1), Num(2)))), MutableAssign(Name(b), Mul(Name(a), Num(3))))"

    parser = Parser(Lexer(Scanner("let:\n  let:\n    a <- 1 + 2\n\n  b <- a * 3\n\n")))

    assign = parser()
    assert assign.name == NodeType.Let
    assert str(assign) == "Let(Let(MutableAssign(Name(a), Add(Num(1), Num(2)))), MutableAssign(Name(b), Mul(Name(a), Num(3))))"
コード例 #4
0
def test_parser_let_be():
    """
    To validate the parser iterates properly over a variable being assigned the
    result of an expression.
    """
    parser = Parser(Lexer(Scanner("let b:\n")))

    assign = parser()
    assert assign.name == NodeType.Let
    assert str(assign) == "Let(Class(Name(b), None))"

    parser = Parser(Lexer(Scanner("let bit be attribute:\n")))

    assign = parser()
    assert assign.name == NodeType.Let
    assert str(assign) == "Let(Class(Name(bit), Type(Name(attribute))))"
コード例 #5
0
def test_analyzer_immutable_assign(clean_namespace):
    """
    To validate that the analyzer is able to produce a symbol table.

    We test store and load context, that is, a value is stored with the result
    of an expression, and then we use the alias to store another value into
    another variable.
    """

    analyzer = Analyzer(Parser(Lexer(Scanner('7 + 4 -> b\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    analyzer()
    assert analyzer.table[('b', '__test__')].type.value == 11
    assert analyzer.table[('b', '__test__')].type.mutable == Field.IMMUTABLE
    assert analyzer.table[('b', '__test__')].type.type == Field.UNKNOWN

    analyzer.parser.lexer.scanner += '12 -> b\n'
    with pytest.raises(LythSyntaxError) as err:
        analyzer()

    assert err.value.msg is LythError.REASSIGN_IMMUTABLE
    assert err.value.filename == "__test__"
    assert err.value.lineno == 1
    assert err.value.offset == 3
    assert err.value.line == "12 -> b"
コード例 #6
0
def test_parser_let_assign():
    """
    To validate the parser iterates properly over a variable being assigned the
    result of an expression.
    """
    parser = Parser(Lexer(Scanner("let a <- 1 + 2\n")))

    assign = parser()
    assert assign.name == NodeType.Let
    assert str(assign) == "Let(MutableAssign(Name(a), Add(Num(1), Num(2))))"

    parser = Parser(Lexer(Scanner("let b * 2 -> a\n")))

    assign = parser()
    assert assign.name == NodeType.Let
    assert str(assign) == "Let(ImmutableAssign(Name(a), Mul(Name(b), Num(2))))"
コード例 #7
0
def test_parser_docstring():
    """
    To validate that the docstring of a class, or a function is not captured in
    this version of the tool, and considered as multiline comment.
    """
    parser = Parser(Lexer(Scanner('"""\nHello you\n"""\n')))
    test = parser()
    assert test.name == NodeType.Noop
コード例 #8
0
def test_interpreter():
    """
    Basic set of visiting nodes
    """
    interpreter = Interpreter()

    cmd = next(Parser(Lexer(Scanner("1 + 2\n"))))
    assert interpreter.visit(cmd) == 3

    cmd = next(Parser(Lexer(Scanner("1 * 2\n"))))
    assert interpreter.visit(cmd) == 2

    cmd = next(Parser(Lexer(Scanner("1 - 2\n"))))
    assert interpreter.visit(cmd) == -1

    cmd = next(Parser(Lexer(Scanner("\n"))))
    assert interpreter.visit(cmd) is None

    cmd = next(Parser(Lexer(Scanner("a\n"))))
    assert interpreter.visit(cmd) == 'a'
コード例 #9
0
def test_parser_wrong_expression():
    """
    To validate the parser complains under various situations:

    1. Expression has too much trailing characters.
    2. Expression has an unexpected let keyword.
    """
    parser = Parser(Lexer(Scanner("1 + 2 + 3 ")))

    with pytest.raises(LythSyntaxError) as err:
        parser()

    assert err.value.msg is LythError.GARBAGE_CHARACTERS

    parser = Parser(Lexer(Scanner("let 1 + 2 + 3\n")))

    with pytest.raises(LythSyntaxError) as err:
        parser()

    assert err.value.msg is LythError.LET_ON_EXPRESSION
コード例 #10
0
def test_parser_precedence():
    """
    To validate the parser returns the right AST node.
    """
    parser = Parser(Lexer(Scanner("1 + 2 * 3 - 1\n")))

    expr = next(parser)
    assert expr.name == NodeType.Sub
    assert str(expr) == "Sub(Add(Num(1), Mul(Num(2), Num(3))), Num(1))"

    expr = next(parser)
    assert expr.name == NodeType.Noop
    assert str(expr) == "Noop()"
コード例 #11
0
def test_parser_multiplication():
    """
    To validate the parser returns the right AST node.
    """
    parser = Parser(Lexer(Scanner("1 * 2 * 3\n")))

    expr = next(parser)
    assert expr.name == NodeType.Mul
    assert str(expr) == "Mul(Mul(Num(1), Num(2)), Num(3))"

    expr = next(parser)
    assert expr.name == NodeType.Noop
    assert str(expr) == "Noop()"
コード例 #12
0
def test_parser_expression():
    """
    To validate the parser iterates properly over an expression.

    The last node on a properly formatted string should be No Op.
    """
    parser = Parser(Lexer(Scanner("1 + 2 + 3 \n")))

    for i in parser:
        pass

    assert i.name == NodeType.Noop
    assert str(i) == "Noop()"
コード例 #13
0
def test_parser_parenthesis():
    """
    Parentheses offer the highest precedence and change AST node ordering.
    """
    parser = Parser(Lexer(Scanner("1 + (a - 3) * 5\n")))

    expr = next(parser)
    assert expr.name == NodeType.Add
    assert str(expr) == "Add(Num(1), Mul(Sub(Name(a), Num(3)), Num(5)))"

    expr = next(parser)
    assert expr.name == NodeType.Noop
    assert str(expr) == "Noop()"
コード例 #14
0
def test_parser_substraction():
    """
    To validate the parser returns the right AST node.
    """
    parser = Parser(Lexer(Scanner("1 + 2 - 3\n")))

    expr = next(parser)
    assert expr.name == NodeType.Sub
    assert str(expr) == "Sub(Add(Num(1), Num(2)), Num(3))"

    expr = next(parser)
    assert expr.name == NodeType.Noop
    assert str(expr) == "Noop()"
コード例 #15
0
ファイル: cli.py プロジェクト: gmantelet/python-lyth
def main(argv=sys.argv):
    """
    The main entry point of the application.
    """
    settings = fetch(argv[1:])
    error = 0

    # interpreter = Interpreter()

    count = 0

    while count <= settings.cycle:
        try:
            source = input('>>> ')
            if source.rstrip() and source.rstrip()[-1] == ':':
                while True:
                    line = input('... ')
                    source += '\n' + line
                    if not line or len(line) - len(line.lstrip()) == 0:
                        break

            scanner = Scanner(source + "\n")
            parser = Parser(Lexer(scanner))
            analyzer = Analyzer(parser)

            cmd = next(parser)
            # print(interpreter.visit(cmd))

            ret = analyzer.visit(cmd)
            if ret:
                print(ret)

        except LythSyntaxError as e:
            print(e)

        except KeyboardInterrupt:
            print("Keyboard interrupt")
            break

        except Exception:
            exc_type, exc_value, exc_tb = sys.exc_info()
            traceback.print_exception(exc_type, exc_value, exc_tb)
            error = 1
            break

        if settings.cycle:
            count += 1

    print("Goodbye.")
    return error
コード例 #16
0
def test_parser_invalid_expression():
    """
    To validate the parser detects the expression it evaluates is invalid.
    """
    parser = Parser(Lexer(Scanner("1 + 2 + /\n")))

    with pytest.raises(LythSyntaxError) as err:
        next(parser)

    assert err.value.msg is LythError.LITERAL_EXPECTED
    assert err.value.filename == "<stdin>"
    assert err.value.lineno == 0
    assert err.value.offset == 8
    assert err.value.line == "1 + 2 + /"
コード例 #17
0
def test_parser_invalid_let():
    """
    To validate the parser checks let block statement integrity
    """
    parser = Parser(Lexer(Scanner("let: a <- 1 + 2\n    b <- a * 3\n\n")))

    with pytest.raises(LythSyntaxError) as err:
        next(parser)

    assert err.value.msg is LythError.GARBAGE_CHARACTERS
    assert err.value.filename == "<stdin>"
    assert err.value.lineno == 0
    assert err.value.offset == 5
    assert err.value.line == "let: a <- 1 + 2"

    parser = Parser(Lexer(Scanner("let:\na <- 1 + 2\n  b <- a * 3\n\n")))

    with pytest.raises(LythSyntaxError) as err:
        next(parser)

    assert err.value.msg is LythError.INCONSISTENT_INDENT
    assert err.value.filename == "<stdin>"
    assert err.value.lineno == 1
    assert err.value.offset == 0
    assert err.value.line == "a <- 1 + 2"

    parser = Parser(Lexer(Scanner("let:\n  a <- 1 + 2\n    b <- a * 3\n\n")))

    with pytest.raises(LythSyntaxError) as err:
        next(parser)

    assert err.value.msg is LythError.INCONSISTENT_INDENT
    assert err.value.filename == "<stdin>"
    assert err.value.lineno == 2
    assert err.value.offset == 0
    assert err.value.line == "    b <- a * 3"
コード例 #18
0
def test_analyzer_let(clean_namespace):
    """
    To validate that for now the analyzer is not doing anything upon let
    keyword
    """
    analyzer = Analyzer(Parser(Lexer(Scanner('let a <- 1 + 2\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    analyzer()
    assert analyzer.table[('a', '__test__')].type.value == 3
    assert analyzer.table[('a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('a', '__test__')].type.type == Field.UNKNOWN
    assert analyzer.table.left is None
    assert str(analyzer.table.right) == "a, __test__"
コード例 #19
0
def test_analyzer_iterator(clean_namespace):
    """
    To validate the ability to analyze multiple lines on the fly
    """
    analyzer = Analyzer(Parser(Lexer(Scanner('a <- 1 + 2\na * 5 -> b\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"
    assert analyzer.table.left is None
    assert analyzer.table.right is None

    for _ in analyzer:
        print(f"TEST: left node of root is {analyzer.table.left}")
        print(f"TEST: right node of root is {analyzer.table.right}")

    assert analyzer.table[('a', '__test__')].type.value == 3
    assert analyzer.table[('a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('b', '__test__')].type.value == 15
    assert analyzer.table[('b', '__test__')].type.mutable == Field.IMMUTABLE
コード例 #20
0
def test_analyzer_unknown_ast_node(clean_namespace):
    """
    To validate we get a dedicated error message when the AST node is not valid
    """
    parser = Parser(Lexer(Scanner('a <- 1 + 2\na * 5 -> b\n', '__test__')))

    node = next(parser)

    from enum import Enum

    class Dummy(Enum):
        DUMMY = "dummy"

    node.name = Dummy.DUMMY

    analyzer = Analyzer(None, "__dummy__.py")

    with pytest.raises(TypeError):
        analyzer.visit(node)
コード例 #21
0
def test_interpreter_whole_file():
    """
    The interpreter should survive a basic set of lyth commands.

    Next, we'll see if bytecode can be properly produced... ;)
    """
    path = Path(__file__).resolve().parent / 'resources/stm32f4_gpiob.lyth'

    f = open(path)
    try:
        interpreter = Interpreter()
        parser = Parser(Lexer(Scanner(f.read(),
                                      filename="stm32f4_gpiob.lyth")))

        while True:
            cmd = next(parser)
            assert interpreter.visit(cmd) is None

    except Exception:
        raise

    finally:
        f.close()
コード例 #22
0
def test_analyzer_mutable_assign(clean_namespace):
    """
    To validate that the analyzer is able to produce a symbol table.

    We test store and load context, that is, a value is stored with the result
    of an expression, and then we use the alias to store another value into
    another variable.
    """
    analyzer = Analyzer(Parser(Lexer(Scanner('a <- 1 + 2\n', '__test__'))))

    assert str(analyzer.table) == "__test__, root"

    analyzer()
    assert analyzer.table[('a', '__test__')].type.value == 3
    assert analyzer.table[('a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('a', '__test__')].type.type == Field.UNKNOWN
    assert analyzer.table.left is None
    assert str(analyzer.table.right) == "a, __test__"

    analyzer.parser.lexer.scanner += '__a <- a + 2\n'
    assert str(analyzer.table) == "__test__, root"

    analyzer()
    assert analyzer.table[('a', '__test__')].type.value == 3
    assert analyzer.table[('a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('a', '__test__')].type.type == Field.UNKNOWN
    assert analyzer.table[('__a', '__test__')].type.value == 5
    assert analyzer.table[('__a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('__a', '__test__')].type.type == Field.UNKNOWN

    assert str(analyzer.table.left) == "__a, __test__"
    assert str(analyzer.table.right) == "a, __test__"

    analyzer.parser.lexer.scanner += 'a <- 5\n'
    analyzer()
    assert analyzer.table[('a', '__test__')].type.value == 5
    assert analyzer.table[('a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('a', '__test__')].type.type == Field.UNKNOWN
    assert analyzer.table[('__a', '__test__')].type.value == 5
    assert analyzer.table[('__a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('__a', '__test__')].type.type == Field.UNKNOWN

    analyzer.parser.lexer.scanner += 'c <- b + 2\n'
    with pytest.raises(LythSyntaxError) as err:
        analyzer()

    assert err.value.msg is LythError.VARIABLE_REFERENCED_BEFORE_ASSIGNMENT
    assert err.value.filename == "__test__"
    assert err.value.lineno == 3
    assert err.value.offset == 5
    assert err.value.line == "c <- b + 2"

    analyzer = Analyzer(Parser(Lexer(Scanner('a <- 1 - 2\n', '__test__'))))
    analyzer()
    assert analyzer.table[('a', '__test__')].type.value == -1
    assert analyzer.table[('a', '__test__')].type.mutable == Field.MUTABLE
    assert analyzer.table[('a', '__test__')].type.type == Field.UNKNOWN

    analyzer = Analyzer(Parser(Lexer(Scanner('4 / 2 -> d\n', '__test__'))))
    analyzer()
    assert analyzer.table[('d', '__test__')].type.value == 2
    assert analyzer.table[('d', '__test__')].type.mutable == Field.IMMUTABLE
    assert analyzer.table[('d', '__test__')].type.type == Field.UNKNOWN