Source code for xopgi.ql.lang.expressions.base

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Copyright (c) Merchise Autrement [~º/~] and Contributors
# All rights reserved.
#
# This is free software; you can do what the LICENCE file allows you to.
#
from typing import Any, Mapping
from xoutil.objects import validate_attrs

from ..types.base import Type


class AST:
    pass


[docs]class Identifier(AST): '''A name (variable if you like).''' def __init__(self, name: str) -> None: self.name = name def __repr__(self): return f'Identifier({self.name!r})' def __hash__(self): return hash((Identifier, self.name)) def __eq__(self, other): if isinstance(other, Identifier): return self.name == other.name else: return NotImplemented
# An extension to the algorithm. Literals are allowed, but have a the # most specific type possible.
[docs]class Literal(AST): '''A literal value with is type. The `~xopgi.ql.lang.expressions.parser`:mod: only recognizes strings, chars, and numbers (integers and floats are represented by a single type). .. note:: This is an extension to the algorithm, but you can easily that we may replace literals by identifiers with a predefined type. ''' def __init__(self, value: Any, type_: Type, annotation: Any = None) -> None: self.value = value self.type = type_ self.annotation = annotation def __repr__(self): if self.annotation is not None: return f'Literal({self.value!r}, {self.type!r}, {self.annotation!r})' else: return f'Literal({self.value!r}, {self.type!r})' def __eq__(self, other): if isinstance(other, Literal): return validate_attrs(self, other, ('type', 'value', 'annotation')) else: return NotImplemented
[docs]class Lambda(AST): '''A lambda abstraction over a single parameter. ''' def __init__(self, varname: str, body: AST) -> None: self.varname = varname self.body = body def __repr__(self): return f'Lambda({self.varname!r}, {self.body!r})' def __eq__(self, other): if isinstance(other, Lambda): return self.varname == other.varname and self.body == other.body else: return NotImplemented
[docs]class Application(AST): '''The application of `e1` to its *argument* e2.''' def __init__(self, e1: AST, e2: AST) -> None: self.e1 = e1 self.e2 = e2 def __repr__(self): return f'Application({self.e1!r}, {self.e2!r})' def __eq__(self, other): if isinstance(other, Application): return self.e1 == other.e1 and self.e2 == other.e2 else: return NotImplemented
# We assume (as the Book does) that there are no "translation" errors; i.e # that you haven't put a Let where you needed a Letrec. class _LetExpr(AST): def __init__(self, bindings: Mapping[str, AST], body: AST) -> None: self.bindings = bindings self.body = body def __eq__(self, other): if isinstance(other, type(self)): return self.bindings == other.bindings and self.body == self.body else: return NotImplemented
[docs]class Let(_LetExpr): '''A non-recursive Let expression. The `~xopgi.ql.lang.expressions.parser`:mod: automatically selects between `Let`:class: and `Letrec`:class. If you're creating the program by hand you should choose appropriately. (But the type-checker doesn't really care.) ''' def __repr__(self): return f'Let({self.bindings!r}, {self.body!r})'
[docs]class Letrec(_LetExpr): '''A recursive Let expression. .. sealso:: `Let`:class: ''' def __repr__(self): return f'Letrec({self.bindings!r}, {self.body!r})'