1051 lines
24 KiB
Plaintext
1051 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from notebook_preamble import D, J, V, define"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Compiling Joy\n",
|
|
"\n",
|
|
"Given a Joy program like:\n",
|
|
"\n",
|
|
" sqr == dup mul"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" • 23 sqr\n",
|
|
" 23 • sqr\n",
|
|
" 23 • dup mul\n",
|
|
"23 23 • mul\n",
|
|
" 529 • \n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"V('23 sqr')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"How would we go about compiling this code (to Python for now)?\n",
|
|
"\n",
|
|
"## Naive Call Chaining\n",
|
|
"The simplest thing would be to compose the functions from the library:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"dup, mul = D['dup'], D['mul']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def sqr(stack, expression, dictionary):\n",
|
|
" return mul(*dup(stack, expression, dictionary))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"old_sqr = D['sqr']\n",
|
|
"D['sqr'] = sqr"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" • 23 sqr\n",
|
|
" 23 • sqr\n",
|
|
"529 • \n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"V('23 sqr')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"It's simple to write a function to emit this kind of crude \"compiled\" code."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def compile_joy(name, expression):\n",
|
|
" term, expression = expression\n",
|
|
" code = term +'(stack, expression, dictionary)'\n",
|
|
" format_ = '%s(*%s)'\n",
|
|
" while expression:\n",
|
|
" term, expression = expression\n",
|
|
" code = format_ % (term, code)\n",
|
|
" return '''\\\n",
|
|
"def %s(stack, expression, dictionary):\n",
|
|
" return %s\n",
|
|
"''' % (name, code)\n",
|
|
"\n",
|
|
"\n",
|
|
"def compile_joy_definition(defi):\n",
|
|
" return compile_joy(defi.name, defi.body)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"def sqr(stack, expression, dictionary):\n",
|
|
" return mul(*dup(stack, expression, dictionary))\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print(compile_joy_definition(old_sqr))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"But what about literals?\n",
|
|
"\n",
|
|
" quoted == [unit] dip"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"unit, dip = D['unit'], D['dip']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# print compile_joy_definition(D['quoted'])\n",
|
|
"# raises\n",
|
|
"# TypeError: can only concatenate tuple (not \"str\") to tuple"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"For a program like `foo == bar baz 23 99 baq lerp barp` we would want something like:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def foo(stack, expression, dictionary):\n",
|
|
" stack, expression, dictionary = baz(*bar(stack, expression, dictionary))\n",
|
|
" return barp(*lerp(*baq((99, (23, stack)), expression, dictionary)))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"You have to have a little discontinuity when going from a symbol to a literal, because you have to pick out the stack from the arguments to push the literal(s) onto it before you continue chaining function calls."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Compiling Yin Functions\n",
|
|
"Call-chaining results in code that does too much work. For functions that operate on stacks and only rearrange values, what I like to call \"Yin Functions\", we can do better.\n",
|
|
"\n",
|
|
"We can infer the stack effects of these functions (or \"expressions\" or \"programs\") automatically, and the stack effects completely define the semantics of the functions, so we can directly write out a two-line Python function for them. This is already implemented in the `joy.utils.types.compile_()` function."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"ename": "ModuleNotFoundError",
|
|
"evalue": "No module named 'joy.utils.types'",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
|
|
"\u001b[0;32m<ipython-input-14-d5ef3c7560be>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mjoy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtypes\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mcompile_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdoc_from_stack_effect\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minfer_string\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mjoy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlibrary\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mSimpleFunctionWrapper\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
|
"\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'joy.utils.types'"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from joy.utils.types import compile_, doc_from_stack_effect, infer_string\n",
|
|
"from joy.library import SimpleFunctionWrapper"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effects = infer_string('tuck over dup')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Yin functions have only a single stack effect, they do not branch or loop."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"for fi, fo in stack_effects:\n",
|
|
" print doc_from_stack_effect(fi, fo)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"source = compile_('foo', stack_effects[0])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"All Yin functions can be described in Python as a tuple-unpacking (or \"-destructuring\") of the stack datastructure followed by building up the new stack structure."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print source"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"ename": "SyntaxError",
|
|
"evalue": "invalid syntax (<ipython-input-9-1a7e90bf2d7b>, line 1)",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;36m File \u001b[0;32m\"<ipython-input-9-1a7e90bf2d7b>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m exec compile(source, '__main__', 'single')\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"exec compile(source, '__main__', 'single')\n",
|
|
"\n",
|
|
"D['foo'] = SimpleFunctionWrapper(foo)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"V('23 18 foo')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Compiling from Stack Effects\n",
|
|
"\n",
|
|
"There are times when you're deriving a Joy program when you have a stack effect for a Yin function and you need to define it. For example, in the Ordered Binary Trees notebook there is a point where we must derive a function `Ee`:\n",
|
|
"\n",
|
|
" [key old_value left right] new_value key [Tree-add] Ee\n",
|
|
" ------------------------------------------------------------\n",
|
|
" [key new_value left right]\n",
|
|
"\n",
|
|
"While it is not hard to come up with this function manually, there is no necessity. This function can be defined (in Python) directly from its stack effect:\n",
|
|
"\n",
|
|
" [a b c d] e a [f] Ee\n",
|
|
" --------------------------\n",
|
|
" [a e c d]\n",
|
|
"\n",
|
|
"(I haven't yet implemented a simple interface for this yet. What follow is an exploration of how to do it.)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from joy.parser import text_to_expression"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"Ein = '[a b c d] e a [f]' # The terms should be reversed here but I don't realize that until later.\n",
|
|
"Eout = '[a e c d]'\n",
|
|
"E = '[%s] [%s]' % (Ein, Eout)\n",
|
|
"\n",
|
|
"print E"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"(fi, (fo, _)) = text_to_expression(E)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"fi, fo"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"Ein = '[a1 a2 a3 a4] a5 a6 a7'\n",
|
|
"Eout = '[a1 a5 a3 a4]'\n",
|
|
"E = '[%s] [%s]' % (Ein, Eout)\n",
|
|
"\n",
|
|
"print E"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"(fi, (fo, _)) = text_to_expression(E)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"fi, fo"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def type_vars():\n",
|
|
" from joy.library import a1, a2, a3, a4, a5, a6, a7, s0, s1\n",
|
|
" return locals()\n",
|
|
"\n",
|
|
"tv = type_vars()\n",
|
|
"tv"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from joy.utils.types import reify"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effect = reify(tv, (fi, fo))\n",
|
|
"print doc_from_stack_effect(*stack_effect)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print stack_effect"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Almost, but what we really want is something like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effect = eval('(((a1, (a2, (a3, (a4, s1)))), (a5, (a6, (a7, s0)))), ((a1, (a5, (a3, (a4, s1)))), s0))', tv)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Note the change of `()` to `JoyStackType` type variables."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print doc_from_stack_effect(*stack_effect)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we can omit `a3` and `a4` if we like:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effect = eval('(((a1, (a2, s1)), (a5, (a6, (a7, s0)))), ((a1, (a5, s1)), s0))', tv)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The `right` and `left` parts of the ordered binary tree node are subsumed in the tail of the node's stack/list."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print doc_from_stack_effect(*stack_effect)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"source = compile_('Ee', stack_effect)\n",
|
|
"print source"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Oops! The input stack is backwards..."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effect = eval('((a7, (a6, (a5, ((a1, (a2, s1)), s0)))), ((a1, (a5, s1)), s0))', tv)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print doc_from_stack_effect(*stack_effect)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"source = compile_('Ee', stack_effect)\n",
|
|
"print source"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Compare:\n",
|
|
"\n",
|
|
" [key old_value left right] new_value key [Tree-add] Ee\n",
|
|
" ------------------------------------------------------------\n",
|
|
" [key new_value left right]\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"eval(compile(source, '__main__', 'single'))\n",
|
|
"D['Ee'] = SimpleFunctionWrapper(Ee)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"V('[a b c d] 1 2 [f] Ee')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Working with Yang Functions\n",
|
|
"\n",
|
|
"Consider the compiled code of `dup`:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"\n",
|
|
"def dup(stack):\n",
|
|
" (a1, s23) = stack\n",
|
|
" return (a1, (a1, s23))\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"To compile `sqr == dup mul` we can compute the stack effect:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effects = infer_string('dup mul')\n",
|
|
"for fi, fo in stack_effects:\n",
|
|
" print doc_from_stack_effect(fi, fo)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Then we would want something like this:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"\n",
|
|
"def sqr(stack):\n",
|
|
" (n1, s23) = stack\n",
|
|
" n2 = mul(n1, n1)\n",
|
|
" return (n2, s23)\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"How about..."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effects = infer_string('mul mul sub')\n",
|
|
"for fi, fo in stack_effects:\n",
|
|
" print doc_from_stack_effect(fi, fo)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"\n",
|
|
"def foo(stack):\n",
|
|
" (n1, (n2, (n3, (n4, s23)))) = stack\n",
|
|
" n5 = mul(n1, n2)\n",
|
|
" n6 = mul(n5, n3)\n",
|
|
" n7 = sub(n6, n4)\n",
|
|
" return (n7, s23)\n",
|
|
"\n",
|
|
"\n",
|
|
"# or\n",
|
|
"\n",
|
|
"def foo(stack):\n",
|
|
" (n1, (n2, (n3, (n4, s23)))) = stack\n",
|
|
" n5 = sub(mul(mul(n1, n2), n3), n4)\n",
|
|
" return (n5, s23)\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"stack_effects = infer_string('tuck')\n",
|
|
"for fi, fo in stack_effects:\n",
|
|
" print doc_from_stack_effect(fi, fo)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Compiling Yin~Yang Functions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"First, we need a source of Python identifiers. I'm going to reuse `Symbol` class for this."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from joy.parser import Symbol"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def _names():\n",
|
|
" n = 0\n",
|
|
" while True:\n",
|
|
" yield Symbol('a' + str(n))\n",
|
|
" n += 1\n",
|
|
"\n",
|
|
"names = _names().next"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we need an object that represents a Yang function that accepts two args and return one result (we'll implement other kinds a little later.)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Foo(object):\n",
|
|
"\n",
|
|
" def __init__(self, name):\n",
|
|
" self.name = name\n",
|
|
"\n",
|
|
" def __call__(self, stack, expression, code):\n",
|
|
" in1, (in0, stack) = stack\n",
|
|
" out = names()\n",
|
|
" code.append(('call', out, self.name, (in0, in1)))\n",
|
|
" return (out, stack), expression, code"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"A crude \"interpreter\" that translates expressions of args and Yin and Yang functions into a kind of simple dataflow graph."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def I(stack, expression, code):\n",
|
|
" while expression:\n",
|
|
" term, expression = expression\n",
|
|
" if callable(term):\n",
|
|
" stack, expression, _ = term(stack, expression, code)\n",
|
|
" else:\n",
|
|
" stack = term, stack\n",
|
|
" code.append(('pop', term))\n",
|
|
"\n",
|
|
" s = []\n",
|
|
" while stack:\n",
|
|
" term, stack = stack\n",
|
|
" s.insert(0, term)\n",
|
|
" if s:\n",
|
|
" code.append(('push',) + tuple(s))\n",
|
|
" return code"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Something to convert the graph into Python code."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"strtup = lambda a, b: '(%s, %s)' % (b, a)\n",
|
|
"strstk = lambda rest: reduce(strtup, rest, 'stack')\n",
|
|
"\n",
|
|
"\n",
|
|
"def code_gen(code):\n",
|
|
" coalesce_pops(code)\n",
|
|
" lines = []\n",
|
|
" for t in code:\n",
|
|
" tag, rest = t[0], t[1:]\n",
|
|
"\n",
|
|
" if tag == 'pop':\n",
|
|
" lines.append(strstk(rest) + ' = stack')\n",
|
|
"\n",
|
|
" elif tag == 'push':\n",
|
|
" lines.append('stack = ' + strstk(rest))\n",
|
|
"\n",
|
|
" elif tag == 'call':\n",
|
|
" #out, name, in_ = rest\n",
|
|
" lines.append('%s = %s%s' % rest)\n",
|
|
"\n",
|
|
" else:\n",
|
|
" raise ValueError(tag)\n",
|
|
"\n",
|
|
" return '\\n'.join(' ' + line for line in lines)\n",
|
|
"\n",
|
|
"\n",
|
|
"def coalesce_pops(code):\n",
|
|
" index = [i for i, t in enumerate(code) if t[0] == 'pop']\n",
|
|
" for start, end in yield_groups(index):\n",
|
|
" code[start:end] = \\\n",
|
|
" [tuple(['pop'] + [t for _, t in code[start:end][::-1]])]\n",
|
|
"\n",
|
|
"\n",
|
|
"def yield_groups(index):\n",
|
|
" '''\n",
|
|
" Yield slice indices for each group of contiguous ints in the\n",
|
|
" index list.\n",
|
|
" '''\n",
|
|
" k = 0\n",
|
|
" for i, (a, b) in enumerate(zip(index, index[1:])):\n",
|
|
" if b - a > 1:\n",
|
|
" if k != i:\n",
|
|
" yield index[k], index[i] + 1\n",
|
|
" k = i + 1\n",
|
|
" if k < len(index):\n",
|
|
" yield index[k], index[-1] + 1\n",
|
|
"\n",
|
|
"\n",
|
|
"def compile_yinyang(name, expression):\n",
|
|
" return '''\\\n",
|
|
"def %s(stack):\n",
|
|
"%s\n",
|
|
" return stack\n",
|
|
"''' % (name, code_gen(I((), expression, [])))\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"A few functions to try it with..."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"mul = Foo('mul')\n",
|
|
"sub = Foo('sub')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def import_yin():\n",
|
|
" from joy.utils.generated_library import *\n",
|
|
" return locals()\n",
|
|
"\n",
|
|
"yin_dict = {name: SimpleFunctionWrapper(func) for name, func in import_yin().iteritems()}\n",
|
|
"\n",
|
|
"yin_dict\n",
|
|
"\n",
|
|
"dup = yin_dict['dup']\n",
|
|
"\n",
|
|
"#def dup(stack, expression, code):\n",
|
|
"# n, stack = stack\n",
|
|
"# return (n, (n, stack)), expression"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"... and there we are."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print compile_yinyang('mul_', (names(), (names(), (mul, ()))))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"e = (names(), (dup, (mul, ())))\n",
|
|
"print compile_yinyang('sqr', e)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"e = (names(), (dup, (names(), (sub, (mul, ())))))\n",
|
|
"print compile_yinyang('foo', e)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"e = (names(), (names(), (mul, (dup, (sub, (dup, ()))))))\n",
|
|
"print compile_yinyang('bar', e)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"e = (names(), (dup, (dup, (mul, (dup, (mul, (mul, ())))))))\n",
|
|
"print compile_yinyang('to_the_fifth_power', e)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 2",
|
|
"language": "python",
|
|
"name": "python2"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.8.3"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|