Skip to content

Commit

Permalink
Add basic Vyper AST layout.
Browse files Browse the repository at this point in the history
  • Loading branch information
jacqueswww committed Apr 3, 2019
1 parent 5319aaf commit a9189eb
Show file tree
Hide file tree
Showing 2 changed files with 351 additions and 0 deletions.
260 changes: 260 additions & 0 deletions vyper/ast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
from itertools import (
chain,
)

from vyper.exceptions import (
CompilerPanic,
)


class VyperNode:
__slots__ = ('node_id', 'source_code', 'col_offset', 'lineno')
ignored_fields = ('ctx', )
only_empty_fields = ()

@classmethod
def get_slots(cls):
return chain.from_iterable(
getattr(klass, '__slots__', [])
for klass in cls.__class__.mro(cls)
)

def __init__(self, **kwargs):

for field_name, value in kwargs.items():
if field_name in self.get_slots():
setattr(self, field_name, value)
# elif value:
# raise CompilerPanic(
# f'Unsupported non-empty value field_name: {field_name}, '
# f' class: {type(self)} value: {value}'
# )


class Module(VyperNode):
__slots__ = ('body', )


class Name(VyperNode):
__slots__ = ('id', )


class Subscript(VyperNode):
__slots__ = ('slice', 'value')


class Index(VyperNode):
__slots__ = ('value', )


class arg(VyperNode):
__slots__ = ('arg', 'annotation')


class Tuple(VyperNode):
__slots__ = ('elts', )


class FunctionDef(VyperNode):
__slots__ = ('args', 'body', 'returns', 'name', 'decorator_list', 'pos')


class arguments(VyperNode):
__slots__ = ('args', 'defaults', 'default')
only_empty_fields = ('vararg', 'kwonlyargs', 'kwarg', 'kw_defaults')


class Import(VyperNode):
pass


class Call(VyperNode):
__slots__ = ('func', 'args', 'keywords', 'keyword')


class keyword(VyperNode):
__slots__ = ('arg', 'value')


class Str(VyperNode):
__slots__ = ('s', )


class Compare(VyperNode):
__slots__ = ('comparators', 'ops', 'left', 'right')


class Num(VyperNode):
__slots__ = ('n', )


class NameConstant(VyperNode):
__slots__ = ('value', )


class Attribute(VyperNode):
__slots__ = ('attr', 'value',)


class Op(VyperNode):
__slots__ = ('op', 'left', 'right')


class BinOp(Op):
pass


class BoolOp(Op):
pass


class UnaryOp(Op):
operand = ('operand', )


class List(VyperNode):
__slots__ = ('elts', )


class Dict(VyperNode):
__slots__ = ('keys', 'values')


class Bytes(VyperNode):
__slots__ = ('s', )


class Add(VyperNode):
pass


class Sub(VyperNode):
pass


class Mult(VyperNode):
pass


class Div(VyperNode):
pass


class Mod(VyperNode):
pass


class Pow(VyperNode):
pass


class In(VyperNode):
pass


class Gt(VyperNode):
pass


class GtE(VyperNode):
pass


class LtE(VyperNode):
pass


class Lt(VyperNode):
pass


class Eq(VyperNode):
pass


class NotEq(VyperNode):
pass


class And(VyperNode):
pass


class Or(VyperNode):
pass


class Not(VyperNode):
pass


class USub(VyperNode):
pass


class Expr(VyperNode):
__slots__ = ('value', )


class Pass(VyperNode):
pass


class AnnAssign(VyperNode):
__slots__ = ('target', 'annotation', 'value', 'simple')


class Assign(VyperNode):
__slots__ = ('targets', 'value')


class If(VyperNode):
__slots__ = ('test', 'body', 'orelse')


class Assert(VyperNode):
__slots__ = ('test', 'msg')


class For(VyperNode):
__slots__ = ('iter', 'target', 'orelse', 'body')


class AugAssign(VyperNode):
__slots__ = ('op', 'target', 'value')


class Break(VyperNode):
pass


class Continue(VyperNode):
pass


class Return(VyperNode):
__slots__ = ('value', )


class Delete(VyperNode):
pass


class stmt(VyperNode):
pass


class ClassDef(VyperNode):
__slots__ = ('class_type', 'name', 'body')


class ImportFrom(VyperNode):
pass


class Raise(VyperNode):
pass


class Slice(VyperNode):
pass
91 changes: 91 additions & 0 deletions vyper/ast_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import ast as python_ast

import vyper.ast as vyper_ast
from vyper.exceptions import (
ParserException,
SyntaxException,
)
from vyper.parser.parser_utils import (
annotate_and_optimize_ast,
)
from vyper.parser.pre_parser import (
pre_parse,
)


def parse_python_ast(source_code, node):
if isinstance(node, list):
o = []
for n in node:
o.append(
parse_python_ast(
source_code=source_code,
node=n,
)
)
return o
elif isinstance(node, python_ast.AST):
class_name = node.__class__.__name__
if hasattr(vyper_ast, class_name):
vyper_class = getattr(vyper_ast, class_name)
init_kwargs = {
'col_offset': getattr(node, 'col_offset', None),
'lineno': getattr(node, 'lineno', None),
'node_id': node.node_id
}
if isinstance(node, python_ast.ClassDef):
init_kwargs['class_type'] = node.class_type
for field_name in node._fields:
val = getattr(node, field_name)
if field_name in vyper_class.ignored_fields:
continue
elif val and field_name in vyper_class.only_empty_fields:
raise SyntaxException(
f'"{field_name}" is an unsupported attribute field '
f'on Python AST "{class_name}" class.', node
)
else:
init_kwargs[field_name] = parse_python_ast(
source_code=source_code,
node=val,
)
return vyper_class(**init_kwargs)
else:
raise SyntaxException(
f'Invalid syntax (unsupported "{class_name}" Python AST node).', node
)
else:
return node


def parse_to_ast(source_code):
class_types, reformatted_code = pre_parse(source_code)
if '\x00' in reformatted_code:
raise ParserException('No null bytes (\\x00) allowed in the source code.')
py_ast = python_ast.parse(reformatted_code)
annotate_and_optimize_ast(py_ast, source_code, class_types)
# Convert to Vyper AST.
vyper_ast = parse_python_ast(
source_code=source_code,
node=py_ast,
)
return vyper_ast.body


def ast_to_dict(node):
skip_list = ('source_code', )
if isinstance(node, vyper_ast.VyperNode):
o = {
f: ast_to_dict(getattr(node, f))
for f in node.get_slots()
if f not in skip_list
}
o.update({'ast_type': node.__class__.__name__})
return o
elif isinstance(node, list):
return [
ast_to_dict(x)
for x in node
]
else:
return node

0 comments on commit a9189eb

Please sign in to comment.