예제 #1
0
def verify_exception(code: str, error: str):
    """
    Verifies that parsing the code results in the given error.
    """
    with pytest.raises(ParserError) as e:
        parse_file(code, '')
    # Remove line and column information from the error using a regular expression.
    assert re.sub(':[0-9]+:[0-9]+: ', 'file:?:?: ',
                  str(e.value)) == error.strip()
def test_parser_error():
    # Unexpected EOF - missing 'end'.
    with pytest.raises(ParserError, match='Unexpected end-of-input.') as e:
        parse_file(code="""
func f():
const a = 5
""")
    assert str(e.value).endswith("""
const a = 5
          ^""")
예제 #3
0
def test_func_arg_ret_splitting():
    before = """\
func myfunc(a, b, c, foo, bar,
    variable_name_which_is_way_too_long_but_has_to_be_supported, g)
    -> (x, y, z, a_return_arg_which_is_also_waaaaaaay_too_long, w):
  ret
end
"""
    after = """\
func myfunc(
        a, b, c, foo,
        bar,
        variable_name_which_is_way_too_long_but_has_to_be_supported,
        g) -> (
        x, y, z,
        a_return_arg_which_is_also_waaaaaaay_too_long,
        w):
    ret
end
"""
    assert parse_file(before).format(allowed_line_length=25) == after
    before = """\
func myfunc(a, b, c, foo, bar,
    variable_name_which_is_way_too_long_but_has_to_be_supported, g) ->
    (x, y, z):
  ret
end
"""
    after = """\
func myfunc(
        a, b, c, foo,
        bar,
        variable_name_which_is_way_too_long_but_has_to_be_supported,
        g) -> (x, y, z):
    ret
end
"""
    assert parse_file(before).format(allowed_line_length=25) == after
    before = """\
func myfunc(ab, cd, ef) -> (x, y, z, a_return_arg_which_is_also_waaaaaaay_too_long, w):
  ret
end
"""
    after = """\
func myfunc(
        ab, cd, ef) -> (
        x, y, z,
        a_return_arg_which_is_also_waaaaaaay_too_long,
        w):
    ret
end
"""
    assert parse_file(before).format(allowed_line_length=25) == after
예제 #4
0
def main():
    parser = argparse.ArgumentParser(
        description='A tool to automatically format Cairo code.')
    parser.add_argument('-v', '--version', action='version', version=f'%(prog)s {__version__}')
    parser.add_argument('files', metavar='file', type=str, nargs='+', help='File names')
    action = parser.add_mutually_exclusive_group(required=False)
    action.add_argument('-i', dest='inplace', action='store_true', help='Edit files inplace.')
    action.add_argument('-c', dest='check', action='store_true', help='Check files\' formats.')

    args = parser.parse_args()
    return_code = 0

    for path in args.files:
        old_content = open(path).read() if path != '-' else sys.stdin.read()
        try:
            new_content = parse_file(
                old_content, filename='<input>' if path == '-' else path).format()
        except Exception as exc:
            print(exc, file=sys.stderr)
            return 2

        if args.inplace:
            assert path != '-', 'Using "-i" together with "-" is not supported.'
            open(path, 'w').write(new_content)
        elif args.check:
            assert path != '-', 'Using "-c" together with "-" is not supported.'
            if old_content != new_content:
                print(f'File "{path}" is incorrectly formatted.', file=sys.stderr)
                return_code = 1
        else:
            print(new_content, end='')

    return return_code
예제 #5
0
def test_file_format_comment_spaces():
    before = """
#   First line.
#{spaces}
#   Second line.{spaces}
#Fourth line.
#   Third line.
[ap] = [ap] #    inline comment.
#   First line.
#   Second line.

#   First line.
#   Second line.
[ap] = [ap] #{spaces}
""".format(spaces='   ')
    after = """\
# First line.
#
#   Second line.
# Fourth line.
#   Third line.
[ap] = [ap]  # inline comment.
# First line.
#   Second line.

# First line.
#   Second line.
[ap] = [ap]  #
"""
    assert parse_file(before).format() == after
예제 #6
0
def test_with():
    code = """\
with   a , b  as   c,d  :
    [ap] = [ap]
end
"""
    assert parse_file(code).format() == """\
예제 #7
0
    def collect(self, curr_pkg_name: str, location: Optional[Location] = None):
        # Check for circular dependencies.
        if curr_pkg_name in self.curr_ancestors:
            raise UsingCycleError(self.curr_ancestors + [curr_pkg_name])

        if curr_pkg_name in self.collected_data:
            # File already parsed.
            return

        try:
            code, filename = self.read_file(curr_pkg_name)
        except ModuleNotFoundException as e:
            raise ImportLoaderError(str(e), location=location)
        except Exception as e:
            raise ImportLoaderError(
                f"Could not load module '{curr_pkg_name}'.\nError: {e}",
                location=location)

        parsed_file: CairoFile = parse_file(code, filename=filename)

        # Get current file dependencies.
        collector = DirectDependenciesCollector()
        collector.get_using_pkgs_in_block(parsed_file.code_block)

        # Add current package to ancestors list before scanning its dependencies.
        self.curr_ancestors.append(curr_pkg_name)

        # Collect ASTs recursively.
        for pkg_name, location in collector.packages:
            self.collect(pkg_name, location=location)

        # Pop current package from ancestors list after scanning its dependencies.
        self.curr_ancestors.pop()
        self.collected_data[curr_pkg_name] = parsed_file
예제 #8
0
def test_if():
    code = """\
if (a + 1) / b == [fp]:
    [ap] = [ap]
end
"""
    assert parse_file(code).format() == code

    code = """\
if (a + 1) / b != 5:
    [ap] = [ap]
else:
    [ap] = [ap]
end
"""
    assert parse_file(code).format() == code
예제 #9
0
def test_parse_func():
    before = """\
[ap] = 1; ap++

func fib():

      [ap] = 2; ap++
      ap += 3
 ret


 end # Comment.

 call fib
"""
    after = """\
[ap] = 1; ap++

func fib():
    [ap] = 2; ap++
    ap += 3
    ret
end  # Comment.

call fib
"""
    assert parse_file(before).format() == after
예제 #10
0
def test_file_format_hints_indent():
    before = """\
  %{
  hint1
  hint2
%}
[fp] = [fp]
func f():
  %{

    if a:
        b
%}
[fp] = [fp]
end
"""
    after = """\
%{
    hint1
    hint2
%}
[fp] = [fp]
func f():
    %{
        if a:
            b
    %}
    [fp] = [fp]
end
"""
    assert parse_file(before).format() == after
예제 #11
0
def test_directives():
    code = """\
[ap] = [ap]
# Comment.
%builtins ab cd ef  # Comment.

[fp] = [fp]
"""
    assert parse_file(code).format() == code
def _extract_identifiers(code):
    """
    Extracts the identifiers defined in the code block and returns them as strings.
    """
    ast = parse_file(code)
    collector = IdentifierCollector()
    with collector.scoped(ScopedName(), parent=ast):
        collector.visit(ast.code_block)
    return [(str(name), identifier_definition.identifier_type) for name,
            identifier_definition in collector.identifiers.as_dict().items()]
예제 #13
0
def test_file_format():
    before = """

ap+=[ fp ]
[ap + -1] = [fp]  *  3
 const x=y  +  z
 member x:T.S
 let x= ap-y  +  z
 let y:a.b.c= x



  label  :
[ap] = [fp];  ap ++
tempvar x=y*z+w
  alloc_locals
local     z                     :T*=x
assert x*z+x=    y+y
static_assert   ap + (3 +   7 )+ ap   ==fp
return  (1,[fp],
  [ap +3],)
   fibonacci  (a = 3 , b=[fp +1])
[ap - 1] = [fp]#    This is a comment.
  #This is another comment.
label2:

  jmp rel 17 if [ap+3]!=  0
[fp] = [fp] * [fp]"""
    after = """\
ap += [fp]
[ap + (-1)] = [fp] * 3
const x = y + z
member x : T.S
let x = ap - y + z
let y : a.b.c = x

label:
[ap] = [fp]; ap++
tempvar x = y * z + w
alloc_locals
local z : T* = x
assert x * z + x = y + y
static_assert ap + (3 + 7) + ap == fp
return (1, [fp], [ap + 3])
fibonacci(a=3, b=[fp + 1])
[ap - 1] = [fp]  # This is a comment.

# This is another comment.
label2:
jmp rel 17 if [ap + 3] != 0
[fp] = [fp] * [fp]
"""
    assert parse_file(before).format() == after
def test_imports():
    collector = IdentifierCollector()
    collector.identifiers.add_identifier(ScopedName.from_string('foo.bar'),
                                         ConstDefinition(value=0))
    ast = parse_file("""
from foo import bar as bar0
""")
    with collector.scoped(ScopedName(), parent=ast):
        collector.visit(ast.code_block)

    assert collector.identifiers.get_scope(ScopedName()).identifiers == {
        'bar0': AliasDefinition(destination=ScopedName.from_string('foo.bar')),
    }
예제 #15
0
def test_parse_struct():
    before = """\
struct MyStruct:
x = 5
      y=3
 end # Comment.
"""
    after = """\
struct MyStruct:
    x = 5
    y = 3
end  # Comment.
"""
    assert parse_file(before).format() == after
예제 #16
0
def test_parse_namespace():
    before = """\
namespace MyNS:
x = 5
      y=3
 end # Comment.
"""
    after = """\
namespace MyNS:
    x = 5
    y = 3
end  # Comment.
"""
    assert parse_file(before).format() == after
def test_shallow_tree_graph():
    files = {
        'root.file': """
from a import aa
from b import bb
""",
        'a': '[ap] = 1',
        'b': '[ap] = 2'
    }

    expected_res = {name: parse_file(code) for name, code in files.items()}
    assert collect_imports('root.file',
                           read_file_from_dict(files)) == expected_res
    assert set(collect_imports('a',
                               read_file_from_dict(files)).keys()) == {'a'}
예제 #18
0
def test_file_format_comments():
    before = """

# First line.
[ap] = [ap]


# Separator comment.


[ap] = [ap]
[ap] = [ap]


# Comment before label.
  label  :
[ap] = [ap] #    Inline.
  #This is another
  # comment before label.
label2:#Inline (label).

[ap] = [ap]
label3:
[ap] = [ap]
# End of file comment."""
    after = """\
# First line.
[ap] = [ap]

# Separator comment.

[ap] = [ap]
[ap] = [ap]

# Comment before label.
label:
[ap] = [ap]  # Inline.

# This is another
# comment before label.
label2:  # Inline (label).
[ap] = [ap]

label3:
[ap] = [ap]
# End of file comment.
"""
    assert parse_file(before).format() == after
예제 #19
0
def test_return_splitting():
    before = """\
return (a, b, c, foo, bar,
        variable_name_which_is_way_too_long_but_has_to_be_supported, g)
"""
    after = """\
return (
    a,
    b,
    c,
    foo,
    bar,
    variable_name_which_is_way_too_long_but_has_to_be_supported,
    g)
"""
    assert parse_file(before).format(allowed_line_length=20) == after
예제 #20
0
def test_func_arg_splitting():
    before = """\
func myfunc(a, b, c, foo, bar,
    variable_name_which_is_way_too_long_but_has_to_be_supported, g):
  ret
end
"""
    after = """\
func myfunc(
        a, b, c, foo,
        bar,
        variable_name_which_is_way_too_long_but_has_to_be_supported,
        g):
    ret
end
"""
    assert parse_file(before).format(allowed_line_length=25) == after
def _extract_dependency_graph(codes: Dict[str, str]) -> DependencyGraphVisitor:
    """
    Extracts the dependencies from the given codes (given as a map from a file name to its content).
    Returns the DependencyGraphVisitor instance.
    """
    modules = [
        CairoModule(
            cairo_file=parse_file(code),
            module_name=ScopedName.from_string(name),
        ) for name, code in codes.items()]
    identifier_collector = IdentifierCollector()
    for module in modules:
        identifier_collector.visit(module)
    dependency_graph_visitor = DependencyGraphVisitor(identifiers=identifier_collector.identifiers)
    for module in modules:
        dependency_graph_visitor.visit(module)
    return dependency_graph_visitor
예제 #22
0
def _extract_dependency_graph(codes: Dict[str, str]) -> Dict[str, Set[str]]:
    """
    Extracts the dependencies from the given codes (given as a map from a file name to its content).
    Returns the dependencies as a map from scope name to a set of the identifiers it uses.
    """
    modules = [
        CairoModule(
            cairo_file=parse_file(code),
            module_name=ScopedName.from_string(name),
        ) for name, code in codes.items()]
    identifier_collector = IdentifierCollector()
    for module in modules:
        identifier_collector.visit(module)
    dependency_graph_visitor = DependencyGraphVisitor(identifiers=identifier_collector.identifiers)
    for module in modules:
        dependency_graph_visitor.visit(module)
    return {
        str(scope): set(map(str, deps))
        for scope, deps in dependency_graph_visitor.visited_identifiers.items()}
def test_get_imports():
    code = """
from a import b
ap += [fp] + 2; ap++
from b.c.d.e import f as g

my_label:
call that
from vim import ide
# from vs.code import ide
func foo():
  from pytest import cairo_stack_test as ci
end
"""
    ast = parse_file(code)
    collector = DirectDependenciesCollector()
    collector.get_using_pkgs_in_block(ast.code_block)
    assert set([x for x, _ in collector.packages
                ]) == {'a', 'b.c.d.e', 'vim', 'pytest'}
def test_dag():
    files = {
        'root.file': """
from a import aa
from b import bb
""",
        'a': """
from common.first import some1
from common.second import some2
""",
        'b': """
from common.first import some1
from common.second import some2
""",
        'common.first': '[ap] = 1',
        'common.second': '[ap] = 2',
    }

    expected_res = {name: parse_file(code) for name, code in files.items()}
    assert collect_imports('root.file',
                           read_file_from_dict(files)) == expected_res
예제 #25
0
def test_file_format_hint():
    before = """\
label:
 %{
 x = y
 "[ fp ]"#Python comment is not auto-formatted
  %}#Cairo Comment

    %{
    %} # Empty hint.
"""
    after = """\
label:
%{
    x = y
    "[ fp ]"#Python comment is not auto-formatted
%}  # Cairo Comment

%{
%}  # Empty hint.
"""
    assert parse_file(before).format() == after
def _collect_struct_definitions(codes: Dict[str, str]) -> Dict[str, Set[str]]:
    """
    Collects the struct related identifiers from the given codes (given as a map from a file name to
    its content).

    Return the collected identifiers as a dict.
    """
    modules = [
        CairoModule(
            cairo_file=parse_file(code),
            module_name=ScopedName.from_string(name),
        ) for name, code in codes.items()]
    identifier_collector = IdentifierCollector()
    for module in modules:
        identifier_collector.visit(module)
    struct_collector = StructCollector(identifiers=identifier_collector.identifiers)
    for module in modules:
        struct_collector.visit(module)
    return {
        str(name): identifier_definition
        for name, identifier_definition in struct_collector.identifiers.as_dict().items()
        if not isinstance(identifier_definition, FutureIdentifierDefinition)}
def test_long_path_grph():
    files = {f'a{i}': f'from a{i+1} import b' for i in range(10)}
    files['a9'] = '[ap] = 0'

    expected_res = {name: parse_file(code) for name, code in files.items()}
    assert collect_imports('a0', read_file_from_dict(files)) == expected_res