A Python implementation of EO.
- Transcompiler
- Maven plugin that transforms
.eo
programs into.py
programs using XSLT.
- Maven plugin that transforms
- Python runtime environment
- Implementation of atoms and data objects as a pip package.
- Examples of translated programs in
eo-python-runtime/tests/example_programs
aspytest
unit tests. - Sandbox to compile and execute your own EO programs!
Check out README.md
in sandbox
directory.
- Abstraction
- Positional application
- Decoration (nested decoration, free decoratees)
- Dataization
- Inner objects, both closed and abstract
- Varargs
- Arrays
- Metas
- Dataize Once (
!
) memory
atom- Regexes
- Arbitrary partial application, argument labels
@
with free attributes- Anonymous abstract objects (as arguments to copying)
- Maybe some more (whenever you experience a bug, feel free to submit an issue)
At the time of declaration, abstract and closed objects map to classes. In __init__()
method, special (@
, $
, and ^
) attributes are created, and free attributes are initialized to DataizationError()
(more about this). Bound attributes become methods decorated with @property
.
Projections of object and attribute names get prefixes EO
and attr_
correspondingly to avoid name collisions between user-defined and inner entities.
[isbn] > book
"Object Thinking" > title
class EObook(ApplicationMixin, Object):
def __init__(self):
# corresponds to `$`
self.attr__self = self
# corresponds to `^`
self.attr__parent = DataizationError()
# corresponds to `@`
self.attr__phi = DataizationError()
# corresponds to `isbn`
self.attr_isbn = DataizationError()
self.attributes = ["isbn", ]
# corresponds to `title` bound attribute
@property
def attr_title(self):
return (String("Object Thinking"))
Attribute might be tied to an abstract object. Our translation model utilizes classes and partial()
method defined in functools
(Python standard library package) to specify ^
a.k.a. attr__parent
object:
[x y] > point
[to] > distance
length. > @
vector
to.x.sub (^.x)
to.y.sub (^.y)
class EOpoint(ApplicationMixin, Object):
def __init__(self):
# omitted for brevity
self.attributes = ["x", "y", ]
@property
def attr_distance(self):
return partial(EOpointEOdistance, self)
class EOpointEOdistance(ApplicationMixin, Object):
def __init__(self, attr__parent):
# omitted for brevity
self.attributes = ["to", ]
@property
def attr__phi(self):
return (Attribute(
(EOvector()
(Attribute((Attribute((self.attr_to), "x")), "sub")
(Attribute((self.attr__parent), "x")))
(Attribute((Attribute((self.attr_to), "y")), "sub")
(Attribute((self.attr__parent), "y")))),
"length"))
Functionality of decoration is achieved via attr__phi
attribute combined with class-wrapper Attribute
.
Whenever specific attribute is needed, dataize()
in Attribute
searches for the attribute in object itself; upon failure recursively searches for it in object bound to attr__phi
.
[] > a
"nothing else matters" > a_message
[] > b
a > @
[] > c
b.a_message > c_message
class EOa(ApplicationMixin, Object):
def __init__(self):
# special & free attributes handling
@property
def attr_a_message(self):
return (String("nothing else matters"))
class EOb(ApplicationMixin, Object):
def __init__(self):
# special & free attributes handling
@property
def attr__phi(self):
return (EOa())
class EOc(ApplicationMixin, Object):
def __init__(self):
# special & free attributes handling
@property
def attr_c_message(self):
return (Attribute((EOb()), "a_message"))
Being a copy of objects in EO with some arguments, application is class instantiation in Python. Current implementation supports only full positional application (in absence of free attributes in decoratee) as overwritten __call__()
methods. In order to apply some argument to an object you need to __call__()
an object with this argument. __call__()
return an object itself, which means that these 'applications' can be chained as follows:
obj(arg1)(arg2)(arg3)...
The particular implementation of __call__()
is injected into each Object
with the ApplicationMixin
class defined in atoms.py, whom all classes inherit from:
[truth_value] > answer
truth_value.if "yes" "no" > @
class EOanswer(ApplicationMixin, Object):
def __init__(self):
# special attributes
self.attributes = ["truth_value", ]
@property
def attr__phi(self):
return (Attribute((self.attr_truth_value), "if")
(String("yes"))
(String("no")))
As per EO paper, all the varargs are packaged into an Array
atom and can be accessed by index using get
attribute:
[arg1, arg2, varargs...] > obj
stdout > @
sprintf
"%d %d %d"
get.
varargs
3
arg1
arg2
class EOobj(ApplicationMixin, Object):
def __init__(self):
# Free attributes
self.attr__parent = DataizationError()
self.attr__self = self
self.attributes = ["arg1", "arg2", "varargs"]
self.attr_arg1 = DataizationError()
self.attr_arg2 = DataizationError()
self.attr_varargs = Array()
self.varargs = True
@property
def attr__phi(self):
return Stdout()(
Sprintf()
(String("%d %d %d"))
(Attribute(self.attr_varargs, "get")
((Number(3)))
(self.attr_arg1)
(self.attr_arg2)
)
All classes (think, abstract objects), both auto-generated and atomic, implement an Object
interface with only one method - dataize()
. This method is responsible for reducing a complex object to some Atom
object. The Atom
object, in turn, can not be dataized any further and will simply return itself whenever it receives a dataization request.
class Atom(Object):
def dataize(self):
return self
@abstractmethod
def data(self):
raise NotImplementedError()
You can query the actual data associated with the Atom
object by calling its data()
method. This method will return the actual Python data (str
, int
, object
or None
), whereas dataize()
only returns an Atom
object.
The dataization process can be summed up as follows:
- If an auto-generated
Object
is dataized, it returnsself.attr__phi.dataize()
- If an atomic
Object
(e.g.Attrubute
) is dataized, it returns someAtom
based on its internal implementation, potentially with some side effects (Stdout
). - If an
Atom
is dataized, it returns itself.
If for some reason an object does not define attr__phi
attribute, then its dataization would result in an AttributeError
.
All the uninitialized free attributes are initialized as a special DataizationError
object. As its name suggests, an attempt to dataize()
it would yield a runtime exception in Python, resulting in immediate termination of the program. Attributes initialized with DataizationError
are intended to be assigned some value at runtime.
We are yet to come up with examples of:
- Partial application
- Application of abstract object
- Accessing the free attributes of decoratees (O_o)
If you have an example of programs using such constructs of EO, feel free to submit an issue, then we'll see what we can do about it.
We are thinking of a different way to achieve functionality of the partial application other than object instantiation. The reason is that instantiation in Python makes an object out of class, which is structurally different, whilst objects in EO, as a set, 'are closed' under operation of application. So idea is to either work with objects or classes (proof of concept drafts for classes)