Example #1
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
Example #2
0
    def call(self, args: Sequence[Node]) -> Node:
        """Call a macro with arguments."""
        if len(args) != self.arity:
            raise YovecError('expected {} arguments, but got {}'.format(
                self.arity, len(args)))

        for i, arg in enumerate(args):
            type_ = self.param_types[i]
            if type_ == 'number' and not is_nexpr(arg.kind):  # type: ignore
                raise YovecError(
                    'expected argument to be number expression, but got {}'.
                    format(arg.kind))
            elif type_ == 'vector' and not is_vexpr(arg.kind):  # type: ignore
                raise YovecError(
                    'expected argument to be vector expression, but got {}'.
                    format(arg.kind))
            elif type_ == 'matrix' and not is_mexpr(arg.kind):  # type: ignore
                raise YovecError(
                    'expected argument to be matrix expression, but got {}'.
                    format(arg.kind))

        clone = self.body.clone()
        variables = clone.find(lambda node: node.kind == 'variable')
        for var in variables:
            index = self.param_idents.index(var.value)
            var.parent.replace_child(var, args[index])  # type: ignore
        return clone
Example #3
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)
Example #4
0
 def import_(self, alias: str, target: str) -> 'Env':
     """Import an alias to a target."""
     logger.debug('importing alias with target - {}, {}'.format(alias, target))
     if alias in self.imports:
         raise YovecError('cannot redefine existing import: {}'.format(alias))
     elif target in self.imports.values() or target in self.exports.values():
         raise YovecError('conflicting import target: {}'.format(target))
     clone = deepcopy(self)
     clone._imports[alias] = target
     return clone
Example #5
0
 def define(self, ident: str, macro: Macro) -> 'Env':
     """Define a macro."""
     logger.debug('defining macro - {}'.format(ident))
     if ident in self.macros:
         raise YovecError('cannot redefine existing macro: {}'.format(ident))
     elif ident in self.variables:
         raise YovecError('conflict between macro and variable: {}'.format(ident))
     clone = deepcopy(self)
     clone._macros[ident] = macro
     return clone
Example #6
0
 def var(self, ident: str, expect: Any = None) -> Union[NumVar, VecVar]:
     """Get a variable from the environment."""
     try:
         v = self.variables[ident]
     except KeyError:
         raise YovecError('undefined variable: {}'.format(ident))
     if expect is not None and type(v) != expect:
         raise YovecError(
             'expected variable to have type {}, but got {}'.format(
                 expect, type(v)))
     return v
Example #7
0
 def set_alias(self, alias: str, target: str) -> 'Env':
     """Set an alias."""
     if alias in self.aliases:
         raise YovecError(
             'cannot redefine existing alias: {}'.format(alias))
     if target in self.aliases.values():
         raise YovecError('conflicting alias target: {}'.format(target))
     if set(alias) in set(ascii_uppercase +
                          '_') and alias not in self.variables:
         raise YovecError(
             'cannot export undefined variable: {}'.format(alias))
     clone = deepcopy(self)
     clone.aliases[alias] = target
     return clone
Example #8
0
 def elem(self, row_index: int, col_index: int) -> Number:
     """Get a matrix element by index."""
     try:
         return self.vecs[row_index].nums[col_index]
     except IndexError:
         raise YovecError('element indices {}, {} are out of range'.format(
             row_index, col_index))
Example #9
0
 def target(self, alias: str) -> str:
     """Get the target of an alias."""
     if alias in self.imports:
         return self.imports[alias]
     elif alias in self.exports:
         return self.exports[alias]
     else:
         raise YovecError('undefined alias: {}'.format(alias))
Example #10
0
 def apply(self, op: str, other: 'Matrix') -> 'Matrix':
     """Apply a binary operation to two matrices."""
     if self._rows != other._rows or self._cols != other._cols:
         raise YovecError(
             'cannot apply operation {} to matrices of different sizes'.
             format(op))
     return Matrix(
         [lv.apply(op, rv) for lv, rv in zip(self.vecs, other.vecs)])
Example #11
0
 def set_num(self, ident: str, num_index: int, sn: SimpleNumber) -> 'Env':
     """Set a number variable."""
     if ident in self.variables:
         raise YovecError(
             'cannot redefine existing variable: {}'.format(ident))
     clone = deepcopy(self)
     clone.variables[ident] = NumVar(index=num_index, sn=sn)
     return clone
Example #12
0
 def set_vec(self, ident: str, vec_index: int, sv: SimpleVector) -> 'Env':
     """Set a vector variable."""
     if ident in self.variables:
         raise YovecError(
             'cannot redefine existing variable: {}'.format(ident))
     clone = deepcopy(self)
     clone.variables[ident] = VecVar(index=vec_index, sv=sv)
     return clone
Example #13
0
 def set_mat(self, ident: str, mat_index: int, sm: SimpleMatrix) -> 'Env':
     """Set a matrix variable."""
     if ident in self.variables:
         raise YovecError(
             'cannot redefine existing variable: {}'.format(ident))
     clone = deepcopy(self)
     clone.variables[ident] = MatVar(index=mat_index, sm=sm)
     return clone
Example #14
0
 def matbinary(self, op: str, other: 'Matrix') -> 'Matrix':
     """Apply a binary operation to two matrices."""
     if self._rows != other._rows or self._cols != other._cols:
         raise YovecError(
             'cannot apply operation {} to matrices of different sizes'.
             format(op))
     return Matrix([
         v.vecbinary(op.replace('mat_', 'vec_'), other.vecs[i])
         for i, v in enumerate(self.vecs)
     ])
Example #15
0
 def __init__(self, vecs: List[Vector]):
     assert len(vecs) > 0
     if len(vecs) > 1:
         for v in vecs[1:]:
             if v.length != vecs[0].length:
                 raise YovecError(
                     'all vectors in a matrix must have the same length')
     self.vecs = vecs
     self._rows = len(self.vecs)
     self._cols = self.vecs[0].length
Example #16
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')
Example #17
0
 def __init__(self, svecs: List[SimpleVector]):
     assert len(svecs) > 0
     if len(svecs) > 1:
         n = svecs[0].length
         for sv in svecs[1:]:
             if sv.length != n:
                 raise YovecError(
                     'all vectors in a matrix must have the same length')
     self.svecs = svecs
     self._rows = len(self.svecs)
     self._cols = self.svecs[0].length
Example #18
0
 def let(self, ident: str, value: Value) -> Tuple['Env', List[Node]]:
     """Assign a value to a variable."""
     logger.debug('assigning variable - {}'.format(ident))
     if ident in self.variables:
         raise YovecError('cannot redefine existing variable: {}'.format(ident))
     elif ident in self.macros:
         raise YovecError('conflict between macro and variable: {}'.format(ident))
     if type(value) == Number:
         index = self._num_index
         self._num_index += 1
     elif type(value) == Vector:
         index = self._vec_index
         self._vec_index += 1
     elif type(value) == Matrix:
         index = self._mat_index
         self._mat_index += 1
     else:
         raise AssertionError('unexpected value type: {}'.format(type(value)))
     assignments, value = value.assign(index)
     clone = deepcopy(self)
     clone._variables[ident] = (value, index)
     return clone, assignments
Example #19
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
Example #20
0
 def matmul(self, other: 'Matrix') -> 'Matrix':
     """Multiply two matrices."""
     if self._cols != other._rows:
         raise YovecError('cannot mulitply matrices with mismatching sizes')
     vecs = []
     for i in range(self._rows):
         nums = []
         for j in range(other._cols):
             n = Number(0)
             for k in range(other._rows):
                 n = n.binary(
                     'add',
                     self.vecs[i].nums[k].binary('mul',
                                                 other.vecs[k].nums[j]))
             nums.append(n)
         vecs.append(Vector(nums))
     return Matrix(vecs)
Example #21
0
 def matmul(self, other: 'SimpleMatrix') -> 'SimpleMatrix':
     """Multiply two simple matrices."""
     if self._cols != other._rows:
         raise YovecError('cannot mulitply matrices with mismatching sizes')
     svecs = []
     for i in range(self._rows):
         snums = []
         for j in range(other._cols):
             sn = SimpleNumber(0)
             for k in range(other._rows):
                 sn = sn.binary(
                     'add',
                     self.svecs[i].snums[k].binary('mul',
                                                   other.svecs[k].snums[j]))
             snums.append(sn)
         svecs.append(SimpleVector(snums))
     return SimpleMatrix(svecs)
Example #22
0
 def elem(self, index: int) -> Number:
     """Get a vector element by index."""
     try:
         return self.nums[index]
     except IndexError:
         raise YovecError('element index {} is out of range'.format(index))
Example #23
0
 def alias(self, ident: str) -> str:
     """Get an alias from the environment."""
     try:
         return self.aliases[ident]
     except KeyError:
         raise YovecError('undefined alias: {}'.format(ident))
Example #24
0
 def vecbinary(self, op: str, other: 'SimpleVector') -> 'SimpleVector':
     """Apply a binary operation to two simple vectors."""
     if self.length != other.length:
         raise YovecError('cannot apply operation "{}" to vectors of different lengths'.format(op))
     return SimpleVector([sn.binary(op.strip('vec_'), other.snums[i]) for i, sn in enumerate(self.snums)])
Example #25
0
 def row(self, index: int) -> Vector:
     """Get a matrix row by index."""
     try:
         return self.vecs[index]
     except IndexError:
         raise YovecError('row index {} is out of range'.format(index))
Example #26
0
 def col(self, index: int) -> Vector:
     """Get a matrix column by index."""
     try:
         return Vector([v.nums[index] for v in self.vecs])
     except IndexError:
         raise YovecError('column index {} is out of range'.format(index))
Example #27
0
 def vecbinary(self, op: str, other: 'Vector') -> 'Vector':
     """Apply a binary operation to two vectors."""
     if self.length != other.length:
         raise YovecError('cannot apply operation {} to vectors of different lengths'.format(op))
     return Vector([n.binary(op.strip('vec_'), other.nums[i]) for i, n in enumerate(self.nums)])
Example #28
0
 def macro(self, ident: str) -> Macro:
     """Get a macro."""
     try:
         return self.macros[ident]
     except KeyError:
         raise YovecError('undefined macro: {}'.format(ident))
Example #29
0
 def var(self, ident: str) -> Tuple[Value, int]:
     "Get the value of a variable."
     try:
         return self.variables[ident]
     except KeyError:
         raise YovecError('undefined variable: {}'.format(ident))
Example #30
0
 def apply(self, op: str, other: 'Vector') -> 'Vector':
     """Apply a binary operation to two vectors."""
     if self.length != other.length:
         raise YovecError('cannot apply operation {} to vectors of different lengths'.format(op))
     return Vector([ln.binary(op, rn) for ln, rn in zip(self.nums, other.nums)])