예제 #1
0
 def program(self,
             program: Node,
             env: Optional[Env] = None) -> Tuple[Env, Node]:
     """Transpile a program to YOLOL."""
     assert program.kind == 'program'
     logger.debug('transpiling program')
     if env is None:
         env = Env()
     yolol = Node(kind='program')
     statements = [line.children[0] for line in program.children]
     for statement in statements:
         if statement.kind == 'import_group':
             env = self.import_group(env, statement)
         elif statement.kind == 'export':
             env = self.export(env, statement)
         elif statement.kind.startswith('let'):  # type: ignore
             env, line = self.let(env, statement)
             yolol.append_child(line)
         elif statement.kind.startswith('def'):  # type: ignore
             env = self.define(env, statement)
         elif statement.kind == 'using':
             env = self.using(env, statement)
         elif statement.kind == 'comment':
             pass
         else:
             raise AssertionError('unexpected statement kind: {}'.format(
                 statement.kind))
     return env, yolol
예제 #2
0
 def assign(self, index: int) -> Tuple[List[Node], 'Number']:
     """Generate YOLOL assignment statements."""
     ident = '{}{}'.format(Number.PREFIX, index)
     var = Node(kind='variable', value=ident)
     expr = self.evaluate()
     asn = Node(kind='assignment', children=[var, expr])
     return [asn], Number(ident)
예제 #3
0
파일: number.py 프로젝트: Jerald/yovec
 def assign(self, num_index: int) -> Tuple[Node, 'SimpleNumber']:
     """Generate a YOLOL assignment statement."""
     ident = 'n{}'.format(num_index)
     var = Node(kind='variable', value=ident)
     expr = self.evaluate()
     asn = Node(kind='assignment', children=[var, expr])
     return asn, SimpleNumber(ident)
예제 #4
0
 def paste_in(self, sn):
     """ Paste this object in sn """
     Node.paste_in(self, sn)
     sn.set_state(self.get_state())
     if self.get_sps() is not None:
         sn.set_sps(self.get_sps().copy())
     else:
         sn.set_sps(None)
예제 #5
0
 def exp_zero(expr: Node) -> Tuple[Node, bool]:
     """Reduce (0^n) to (0) and (n^0) to (1) for (n!=0)."""
     if not Transform.is_binary_expr(expr):
         return expr, False
     elif expr.kind == 'exp' and expr.children[0].value == 0 and expr.children[1].value != 0:
         return Node(kind='number', value='0'), True
     elif expr.kind == 'exp' and expr.children[0].value != 0 and expr.children[1].value == 0:
         return Node(kind='number', value='1'), True
     else:
         return expr, False
예제 #6
0
 def mul_zero(expr: Node) -> Tuple[Node, bool]:
     """Reduce (0*n) and (n*0) to (0)."""
     if not Transform.is_binary_expr(expr):
         return expr, False
     elif expr.kind == 'mul' and expr.children[0].value == 0:
         return Node(kind='number', value='0'), True
     elif expr.kind == 'mul' and expr.children[1].value == 0:
         return Node(kind='number', value='0'), True
     else:
         return expr, False
예제 #7
0
 def assign(self, index: int) -> Tuple[List[Node], 'Vector']:
     """Generate YOLOL assignment statements."""
     assignments = []
     nums = []
     expressions = [n.evaluate() for n in self.nums]
     for i, expr in enumerate(expressions):
         ident = '{}{}_e{}'.format(Vector.PREFIX, index, i)
         var = Node(kind='variable', value=ident)
         asn = Node(kind='assignment', children=[var, expr])
         assignments.append(asn)
         nums.append(Number(ident))
     return assignments, Vector(nums)
예제 #8
0
 def assign(self, vec_index: int) -> Tuple[List[Node], 'SimpleVector']:
     """Generate YOLOL assignment statements."""
     assignments = []
     snums = []
     expressions = [sn.evaluate() for sn in self.snums]
     for i, expr in enumerate(expressions):
         ident = 'v{}e{}'.format(vec_index, i)
         var = Node(kind='variable', value=ident)
         asn = Node(kind='assignment', children=[var, expr])
         assignments.append(asn)
         snums.append(SimpleNumber(ident))
     return assignments, SimpleVector(snums)
예제 #9
0
 def evaluate(self) -> Node:
     """Generate a YOLOL expression."""
     if type(self.initial) == str:
         node = Node(kind='variable', value=self.initial)
     else:
         node = Node(kind='number', value=self.initial)
     for op, *args in self.queue:
         if len(args) == 0:
             node = Node(kind=op, children=[node])
         elif len(args) == 1:
             node = Node(kind=op, children=[node, args[0].evaluate()])
         else:
             raise AssertionError(
                 'unrecognized item in queue: {}, {}'.format(op, args))
     return node
예제 #10
0
    def __init__(self):
        Node.__init__(self)
        self.__state = 's'  #Represents the actual status of the SpriteNode (it's a letter for it's SpriteScheduler (cf SpriteScheduler)
        self.sps = None  #SpriteScheduler-> if it's None hit boxes will be shown instead of the sprite
        self.animation_speed = 0.05  #Speed of animation (number of frames a single frame stays)
        self.animation_step = 0.0  #Count for the animation

        self.set_mapping(
            "Flat"
        )  #Way to show the image : Flat : extended // Repeatx : Repeted along x

        self.x_offset = 0
        self.y_offset = 0

        self.mult_offset = 1
예제 #11
0
 def assign(self, index: int) -> Tuple[List[Node], 'Matrix']:
     """Generate YOLOL assignment statements."""
     assignments = []
     vecs = []
     for i, v in enumerate(self.vecs):
         nums = []
         for j, n in enumerate(v.nums):
             expr = n.evaluate()
             ident = '{}{}_r{}c{}'.format(Matrix.PREFIX, index, i, j)
             var = Node(kind='variable', value=ident)
             asn = Node(kind='assignment', children=[var, expr])
             assignments.append(asn)
             nums.append(Number(ident))
         vecs.append(Vector(nums))
     return assignments, Matrix(vecs)
예제 #12
0
파일: matrix.py 프로젝트: Jerald/yovec
 def assign(self, mat_index: int) -> Tuple[List[Node], 'SimpleMatrix']:
     """Generate YOLOL assignment statements."""
     assignments = []
     svecs = []
     for i, sv in enumerate(self.svecs):
         snums = []
         for j, sn in enumerate(sv.snums):
             expr = sn.evaluate()
             ident = 'm{}r{}c{}'.format(mat_index, i, j)
             var = Node(kind='variable', value=ident)
             asn = Node(kind='assignment', children=[var, expr])
             assignments.append(asn)
             snums.append(SimpleNumber(ident))
         svecs.append(SimpleVector(snums))
     return assignments, SimpleMatrix(svecs)
예제 #13
0
def use_library(ident: str, parser, root: str) -> Sequence[Node]:
    """Use definitions from a library."""
    logger.debug('using definitions from library - {}'.format(ident))
    matches = list(Path(root).glob('**/{}.lib.yovec'.format(ident))) # type: ignore
    if len(matches) == 0:
        raise YovecError('library not found: {}'.format(ident))
    if len(matches) > 1:
        raise YovecError('multiple files found for library {}: {}'.format(ident, [str(p) for p in matches]))

    try:
        with open(matches[0]) as f:
            text = f.read()
    except IOError as e:
        raise YovecError('unable to load library {}: {}'.format(ident, str(e)))

    try:
        program = Node.from_tree(parser.parse(text))
    except Exception as e:
        raise YovecError('failed to parse library {}: {}'.format(ident, str(e)))

    statements = [line.children[0] for line in program.children]
    definitions = []
    for statement in statements:
        if statement.kind == 'comment':
            continue
        elif statement.kind.startswith('def_'): # type: ignore
            definitions.append(statement)
        else:
            raise YovecError('invalid statement in library {}: {}'.format(ident, statement))
    return definitions
예제 #14
0
def run_yovec(source: str, root: str, no_elim: bool, no_reduce: bool, no_mangle: bool, ast: bool, cylon: bool) -> str:
    """Run Yovec."""
    try:
        parser = Lark(YOVEC_EBNF, start='program') # type: ignore
        yovec = Node.from_tree(parser.parse(source))
    except Exception as e:
        raise YovecError('Parse error: {}'.format(str(e)))

    try:
        transpiler = Transpiler(parser, root) # type: ignore
        env, yolol = transpiler.program(yovec)
        yolol, imported, exported = resolve_aliases(env, yolol)
    except YovecError as e:
        raise YovecError('Transpilation error: {}\n\n{}'.format(str(e), Context.format()))

    try:
        if not no_reduce:
            yolol = reduce_expressions(yolol)
        if not no_elim:
            yolol = eliminate_dead_code(yolol, exported) # type: ignore
        if not no_mangle:
            yolol = mangle_names(yolol, imported, exported) # type: ignore
    except YovecError as e:
        raise YovecError('Optimization error: {}\n\n{}'.format(str(e), Context.format()))

    if ast:
        return yolol.pretty()
    elif cylon:
        return yolol_to_cylon(yolol)
    else:
        return yolol_to_text(yolol)
예제 #15
0
def reduce_expressions(program: Node) -> Node:
    """Reduce expressions in a YOLOL program."""
    assert program.kind == 'program'
    logger.debug('reducing expressions')
    clone = program.clone()
    while _propagate_constants(clone) or _fold_constants(clone):
        pass
    return clone
예제 #16
0
 def exp_one(expr: Node) -> Tuple[Node, bool]:
     """Reduce (1^n) to (1) and (n^1) to (n)."""
     if not Transform.is_binary_expr(expr):
         return expr, False
     elif expr.kind == 'exp' and expr.children[0].value == 1:
         return Node(kind='number', value='1'), True
     elif expr.kind == 'exp' and expr.children[1].value == 1:
         return expr.children[0], True
     else:
         return expr, False
예제 #17
0
파일: mangle.py 프로젝트: averycrespi/yovec
def mangle_names(program: Node, imported: Sequence[str],
                 exported: Sequence[str]) -> Node:
    """Mangle names in a YOLOL program."""
    assert program.kind == 'program'
    logger.debug('mangling names')
    clone = program.clone()
    pool = Pool([*imported, *exported])
    variables = clone.find(lambda node: node.kind == 'variable')
    for var in variables:
        var.value = pool.replace(var.value)  # type: ignore
    return clone
예제 #18
0
def _fold_constants(program: Node) -> bool:
    """Fold constants in a program.

    Returns True if constants were folded.
    """
    assert program.kind == 'program'
    assignments = program.find(lambda node: node.kind == 'assignment')
    for asn in assignments:
        if _fold_assignment(asn):
            return True
    return False
예제 #19
0
    def __init__(self, ident: str, params: Sequence[Node], return_type: str,
                 body: Node):
        self.arity = len(params)
        self.param_types = [p.children[0].value for p in params]
        self.param_idents = [p.children[1].value for p in params]
        self.return_type = return_type
        self.body = body

        if len(set(self.param_idents)) < len(self.param_idents):
            raise YovecError('duplicate parameters')

        variables = body.find(lambda node: node.kind == 'variable')
        for var in variables:
            if var.value not in self.param_idents:
                raise YovecError('undefined variable: {}'.format(var.value))

        calls = body.find(lambda node: node.kind == 'call')
        for call in calls:
            if call.children[0].value == ident:
                raise YovecError('recursion is not allowed')
예제 #20
0
def resolve_aliases(env: Env,
                    program: Node) -> Tuple[Node, Set[str], Set[str]]:
    """Resolve aliases to their targets in a YOLOL program."""
    assert program.kind == 'program'
    logger.debug('resolving aliases')

    clone = program.clone()
    imported = set()
    exported = set()

    for alias, target in env.imports.items():
        variables = clone.find(
            lambda node: node.kind == 'variable' and node.value == alias)
        for var in variables:
            var.value = target
            imported.add(target)

    for alias, target in env.exports.items():
        var, index = env.var(alias)

        if type(var) == Number:
            num_prefix = '{}{}'.format(Number.PREFIX, index)
            num_variables = clone.find(lambda node: node.kind == 'variable' and
                                       node.value.startswith(num_prefix))
            for var in num_variables:
                var.value = var.value.replace(num_prefix,
                                              target)  # type: ignore
                exported.add(var.value)

        elif type(var) == Vector:
            vec_prefix = '{}{}'.format(Vector.PREFIX, index)
            vec_variables = clone.find(lambda node: node.kind == 'variable' and
                                       node.value.startswith(vec_prefix))
            for var in vec_variables:
                var.value = var.value.replace(vec_prefix,
                                              target)  # type: ignore
                exported.add(var.value)

        elif type(var) == Matrix:
            mat_prefix = '{}{}'.format(Matrix.PREFIX, index)
            mat_variables = clone.find(lambda node: node.kind == 'variable' and
                                       node.value.startswith(mat_prefix))
            for var in mat_variables:
                var.value = var.value.replace(mat_prefix,
                                              target)  # type: ignore
                exported.add(var.value)

        else:
            raise AssertionError('unexpected variable type: {}'.format(
                type(var)))

    return clone, imported, exported
예제 #21
0
def _propagate_constants(program: Node) -> bool:
    """Propagate constants in a program.

    Returns True if a constant was propagated.
    """
    assert program.kind == 'program'
    variables = program.find(lambda node: node.kind == 'variable')
    for var in variables:
        replacement, delta = _propagate_var(program, var)
        if delta:
            var.parent.replace_child(var, replacement) # type: ignore
            return True
    return False
예제 #22
0
 def binary_op(expr: Node) -> Tuple[Node, bool]:
     """Reduce a binary operation."""
     if not Transform.is_binary_expr(expr):
         return expr, False
     elif expr.children[0].kind == 'number' and expr.children[1].kind == 'number':
         try:
             left = Decimal(expr.children[0].value)
             right = Decimal(expr.children[1].value)
             result = str(left.binary(expr.kind, right)) # type: ignore
             return Node(kind='number', value=result), True
         except ArithmeticError:
             raise YovecError('failed to fold constants in binary expression')
     else:
         return expr, False
예제 #23
0
def _fold_assignment(assignment: Node) -> bool:
    """Fold constants in an assignment.

    Returns True if constants were folded."""
    assert assignment.kind == 'assignment'
    numbers = assignment.find(lambda node: node.kind == 'number')
    for num in numbers:
        expr = num.parent
        for transform in Transform().functions:
            replacement, delta = transform(expr)
            if delta:
                expr.parent.replace_child(expr, replacement) # type: ignore
                return True
    return False
예제 #24
0
파일: elim.py 프로젝트: averycrespi/yovec
def _graph_deps(program: Node) -> Dict[str, Set[str]]:
    """Graph variable dependencies."""
    assert program.kind == 'program'
    logger.debug('graphing variable dependencies')
    graph = {}
    assignments = program.find(lambda node: node.kind == 'assignment')
    for asn in assignments:
        variable = asn.children[0]
        expr = asn.children[1]
        unique = {
            v.value
            for v in expr.find(lambda node: node.kind == 'variable')
        }
        graph[variable.value] = unique
    return graph
예제 #25
0
파일: elim.py 프로젝트: averycrespi/yovec
def _remove_dead(program: Node, alive: Set[str]) -> Node:
    """Remove dead assignments."""
    assert program.kind == 'program'
    logger.debug('removing dead assignments')
    clone = program.clone()
    assignments = clone.find(lambda node: node.kind == 'assignment')
    for asn in assignments:
        if asn.children[0].value not in alive:
            assert asn.parent is not None
            asn.parent.remove_child(asn)  # type: ignore
    lines = clone.find(lambda node: node.kind == 'line')
    for line in lines:
        if len(line.children) == 0:
            assert line.parent is not None
            line.parent.remove_child(line)  # type: ignore
    return clone
예제 #26
0
 def let(self, env: Env, let: Node) -> Tuple[Env, Node]:
     """Transpile a let statement to YOLOL."""
     assert let.kind.startswith('let')  # type: ignore
     logger.debug('transpiling let statement - {}'.format(let))
     ident = let.children[0].value
     expr = let.children[1]
     if let.kind == 'let_num':
         env, value = self.nexpr(env, expr)
     elif let.kind == 'let_vec':
         env, value = self.vexpr(env, expr)
     elif let.kind == 'let_mat':
         env, value = self.mexpr(env, expr)
     else:
         raise AssertionError('unexpected let kind: {}'.format(let.kind))
     env, assignments = env.let(ident, value)
     line = Node(kind='line', children=assignments)
     return env, line
예제 #27
0
def main(infile, outfile, ast=False):
    with open('grammar/yovec.ebnf') as f:
        grammar = f.read()
    parser = Lark(grammar, start='program')
    with open(infile) as f:
        raw_program = f.read()
    yovec_program = Node.from_tree(parser.parse(raw_program))
    yolol_program = transpile_yovec(yovec_program)
    if ast:
        output = yolol_program.pretty()
    else:
        output = format_yolol(yolol_program)
    if outfile == '':
        print(output)
    else:
        with open(outfile, mode='w') as f:
            f.write(output)
예제 #28
0
def _propagate_var(program: Node, var: Node) -> Tuple[Node, bool]:
    """Propagate constants in a variable.

    Returns True if a constant was propagated.
    """
    if var.parent.kind == 'assignment' and var.parent.children.index(var) == 0: #$ type: ignore
        # Found "A" in "let A = expr"
        return var, False
    assignments = program.find(lambda node: node.kind == 'assignment' and node.children[0].value == var.value)
    if len(assignments) == 0:
        # Found external variable; cannot propagate
        return var, False
    expr = assignments[0].children[1].clone()
    if expr.kind == 'variable':
        # Found assignment of single variable
        return expr, True
    elif len(expr.find(lambda node: node.kind == 'variable')) == 0:
        # Found assignment of constant expression
        return expr, True
    else:
        return var, False
예제 #29
0
파일: text.py 프로젝트: averycrespi/yovec
def yolol_to_text(program: Node) -> str:
    """Format a YOLOL program as text."""
    assert program.kind == 'program'
    assignments = program.find(lambda node: node.kind == 'assignment')
    formatted = [_format_assignment(a) for a in assignments]
    text = ''
    curr = []
    for f in formatted:
        line = ' '.join(curr)
        if len(f) > 70:
            stderr.write('Warning: line exceeds 70 characters\n')
            text += '{}\n{}\n'.format(line, f)
            curr = []
        elif len(line) + len(' ') + len(f) > 70:
            text += '{}\n'.format(line)
            curr = [f]
        else:
            curr.append(f)
    if len(curr) > 0:
        text += '{}\n'.format(' '.join(curr))
    return text.strip('\n')