# \\ <section:top>
## @file /distill.py
## @brief Distilled `metaL` / SICP chapter 4 / -- reference implementation
## <https://www.notion.so/metalang/Distilled-metaL-SICP-chapter-4-237378d385024f899e5a24597da7a19d>

from metaL import *

## metaL core implementation
py = pyFile('metaL')
# / <section:top>

# \\ <section:bot>
py.sync() # flush all changes to disk
# / <section:bot>

SICP Chapter 4 Exercises & Solutions

This chapter draft targets on a short description of the tiniest metaL core which is highly based on the [SICP] chapter 4. In SICP, this chapter is about implementing a Scheme/Lisp metacircular interpreter.

metacircular interpreter

## inherit special Module type for metaLayer-based projects
class meModule(minpyModule):
		## @param[in] V by default `pyModule` uses current `__file__` for module naming
    def __init__(self, V=None):
				# most Python project setup will be done by a parent `pyModule` constructor
        super().__init__(V)

    ## will be called by `super().__init__` to generate `metaL.py`
    def init_py(self):				## module master `.py` `File`
        self['py'] = self.py = py
				# module (project) root `Dir`ectory
        self.diroot // self.py
				# `File` does not reflects on disk until direct `.sync()` call is done
        self.py.sync()

		## patch default `Makefile` from `pyModule` with REPL
    def init_mk(self):
        super().init_mk()
        # leave only one source code file: metaL.py
        self.mk.src.dropall() // ('SRC += %s' % py.file())
				# build Makefile/all section with REPL pattern
        self.mk.all //\\
            '.PHONY: all' //\\
            'all: repl' //\\
            '.PHONY: repl' //\\
            ('repl: $(PY) %s' % py.file()) //\\
            ('\\t$(PY) -i %s' % py.file()) //\\
            '\\t$(MAKE) $@'
        self.mk.sync()

MODULE = meModule('distill')

MODULE['ABOUT'] = ABOUT = "* [`metaL` manifest](<https://github.com/ponyatov/metaL/wiki/metaL-manifest>)"

ponyatov/metaL

Generated Makefile will use recursive rule that restarts the interactive Python section every time when you send exit() into Python's REPL console.

The parent pyModule also generates /.vscode/settings.json with F11 = 'make repl' and F12 = 'exit()' key bindings, and venv ignore rules.

# \\ <section:all>
.PHONY: all
all: repl
.PHONY: repl
repl: $(PY) metaL.py
		$(PYT) test_metaL.py
		$(PY) -i metaL.py
		$(MAKE) $@
# / <section:all>

Class hierarchy for the minimal reference implementation

As metaL is highly oriented on OOP and uses Python as a host language, the principles of a language implementation from Chapter 4 should be adapted to object-oriented concepts. It is clear that every object graph node type must be represented as classes in Python. Also, metaL manifest limits some core class fields. So, the first one and the root of all the universe is the Object class:

py.objs = Section('Object')
py.mid // py.objs

py.obj = Class('Object')
py.objs // py.obj

py.err = Class('Error', [py.obj])
py.objs // py.err
class Object: pass
class Error(Object): pass

Primitive types mostly evaluate to itself but are not so primitive as classical types in other languages. All of them can have arbitrary nested elements and attributes, such as tolerance and units for numbers (useful for CAD and real-life applications). The only exception is the Symbol, which acts as some sort of variable in classic languages -- it does value lookup in the execution context (environment) on its evaluation.

py.prims = Section('Primitive')
py.mid // py.prims

py.prim = Class('Primitive', [py.obj])
py.prims // py.prim

py.str = Class('String', [py.prim])
py.prims // py.str

py.num = Class('Number', [py.prim])
py.prims // py.num

py.int = Class('Integer', [py.prim])
py.prims // py.int

py.symbol = Class('Symbol',[py.prim])
py.prims // py.symbol
class Primitive(Object): pass
class Symbol(Primitive): pass
class String(Primitive): pass
class Number(Primitive): pass
class Integer(Primitive): pass

Data Containers represent storage types that specify the universal Object storage behavior to a more specific one.

py.conts = Section('Container')
py.mid // py.conts

py.cont = Class('Container', [py.obj])
py.conts // py.cont

py.vect = Class('Vector', [py.cont])
py.conts // py.vect

py.stack = Class('Stack', [py.cont])
py.conts // py.stack

py.dict = Class('Dict', [py.cont])
py.conts // py.dict

py.set = Class('Set', [py.cont])
py.conts // py.set

py.queue = Class('Queue', [py.cont])
py.conts // py.queue
class Container(Object): pass
class Vector(Container): pass
class Stack(Container): pass
class Dict(Container): pass
class Set(Container): pass
class Queue(Container): pass