├── forth-notebooks-architecture.png ├── setup.sh ├── forth-client.py ├── LICENSE ├── gforth-server.fs ├── hello-forth.ipynb ├── README.md └── stack-reverse.ipynb /forth-notebooks-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uho/forth-notebooks/HEAD/forth-notebooks-architecture.png -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -p forth-in ] 4 | then 5 | mkfifo forth-in 6 | fi 7 | 8 | if [ ! -p forth-out ] 9 | then 10 | mkfifo forth-out 11 | fi 12 | 13 | 14 | -------------------------------------------------------------------------------- /forth-client.py: -------------------------------------------------------------------------------- 1 | # forth-client.py 2 | # author: uho@xlerb.de 3 | # 4 | # This is part of the gforth integration with IPython notebook 5 | # 6 | # This script implements a client program that communicates with a running 7 | # gforth instance via pipes "forth-in" and "forth-out" and via 8 | # stdin and stdout with the IPython kernel. 9 | # 10 | # This script is intended to be called by IPython cell magic 11 | # such as %%gforth 12 | 13 | # start the gforth instance by issuing the command 14 | # > gforth gforth-server.fs -e "start" 15 | 16 | import sys 17 | 18 | # read in rest of IPython cell form stdin and push it to the forth-in pipe 19 | forth_in=open("forth-in","wb") 20 | for l in sys.stdin.readlines(): 21 | forth_in.write(l+"\n") 22 | forth_in.flush() 23 | forth_in.close() 24 | 25 | # read the forth-out pipe and push it to line by line to stdout 26 | forth_out=open("forth-out","rb") 27 | for l in forth_out.read().split("\n"): 28 | print l 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Ulrich Hoffmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /gforth-server.fs: -------------------------------------------------------------------------------- 1 | \ gforth-serverclient.fs 2 | \ author: uho@xlerb.de 3 | \ 4 | \ This is part of the gforth integration with IPython notebook 5 | \ 6 | \ This script implements a gforth server program that communicates with 7 | \ a client via pipes "forth-in" and "forth-out". 8 | 9 | \ start this gforth server by issuing the command 10 | \ 11 | \ gforth gforth-server.fs -e "start" 12 | 13 | \ patch all output to go via outfile 14 | 15 | : becomes ( new-xt -- ) \ makes behave as new-xt 16 | here >r 17 | >r ' >body dp ! 18 | postpone ahead r> >body dp ! postpone THEN 19 | r> dp ! 20 | ; 21 | 22 | :noname ; becomes >stderr 23 | 24 | : forth-in ( -- addr len ) s" forth-in" ; \ named pipe for client->server communication: mkfifo forth-in 25 | : forth-out ( -- addr len ) s" forth-out" ; \ named pipe for server->client communication: mkfifo forth-out 26 | 27 | : server ( -- ) 28 | ." gforth server ready" cr 29 | BEGIN 30 | forth-in r/o open-file 31 | ?dup 0= 32 | WHILE ( ) 33 | forth-out w/o open-file throw to outfile-id 34 | ['] include-file catch ?dup IF <# DoError clearstacks ELSE ." ok" cr outfile-id flush-file throw THEN 35 | outfile-id stdout to outfile-id close-file throw 36 | REPEAT 37 | ." gforth server died with " . bye ; 38 | 39 | : start ( -- ) server ; 40 | -------------------------------------------------------------------------------- /hello-forth.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "" 4 | }, 5 | "nbformat": 3, 6 | "nbformat_minor": 0, 7 | "worksheets": [ 8 | { 9 | "cells": [ 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "# Hello Forth" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Using the %%gforth cell magic, it's possible to \n", 22 | "do Forth Literate Programming with IPython.\n", 23 | "\n", 24 | "You can do definitions:" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "collapsed": false, 30 | "input": [ 31 | "%%gforth\n", 32 | "\n", 33 | ": hello ( -- ) .\" Hello, IPython notebook world with Forth.\" ;\n", 34 | " \n", 35 | ": hellos ( n -- ) 0 DO cr I . hello LOOP ;" 36 | ], 37 | "language": "python", 38 | "metadata": {}, 39 | "outputs": [ 40 | { 41 | "output_type": "stream", 42 | "stream": "stdout", 43 | "text": [ 44 | " ok\n", 45 | "\n" 46 | ] 47 | } 48 | ], 49 | "prompt_number": 1 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "Then run your definitions:" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "collapsed": false, 61 | "input": [ 62 | "%%gforth\n", 63 | "\n", 64 | "4 hellos" 65 | ], 66 | "language": "python", 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "output_type": "stream", 71 | "stream": "stdout", 72 | "text": [ 73 | "\n", 74 | "0 Hello, IPython notebook world with Forth.\n", 75 | "1 Hello, IPython notebook world with Forth.\n", 76 | "2 Hello, IPython notebook world with Forth.\n", 77 | "3 Hello, IPython notebook world with Forth. ok\n", 78 | "\n" 79 | ] 80 | } 81 | ], 82 | "prompt_number": 2 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Looks interesting to me." 89 | ] 90 | } 91 | ], 92 | "metadata": {} 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Forth Notebooks # 2 | 3 | ## Introduction ## 4 | 5 | This is an integration of Forth (currently the [gforth](https://www.gnu.org/software/gforth/) system) 6 | with [IPython notebook](http://ipython.org/notebook.html). 7 | 8 | IPython notebook allows external programs to be called by a mechanism called 9 | [cell magic](http://ipython.org/ipython-doc/stable/interactive/reference.html#magic-command-system) 10 | which passes the cell content of an IPython notebook to stdin of the external program and its stdout 11 | will become the cell result. 12 | 13 | Forth notebooks follows this architecture: 14 | 15 | ![forth notebooks architecture](forth-notebooks-architecture.png "architecture") 16 | 17 | The %%gforth cell magic will call the gforth client which communicates via 18 | sockets with the gforth server system. This way the gforth server can maintain 19 | system state as only the client is repeatedly invoked. 20 | 21 | IPython's client server message protocol is documented in a 22 | [public specification](http://ipython.org/ipython-doc/stable/development/messaging.html#messaging). 23 | 24 | ## Installation ## 25 | Forth Notebooks has been used with Linux and Mac OS X installations of IPython notebook. 26 | 27 | ### Configure the cell magic %%gforth 28 | 29 | In your ~/.ipython/profile_default/ipython_config.py file 30 | add gforth magic, so the file looks like: 31 | 32 | # If you want to add script magics that aren't on your path, specify them in 33 | # script_paths 34 | c.ScriptMagics.script_magics=['sh', 'bash', 'perl', 'ruby', 'gforth'] 35 | 36 | # Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby' 37 | # 38 | # Only necessary for items in script_magics where the default path will not find 39 | # the right interpreter. 40 | c.ScriptMagics.script_paths = { 41 | 'gforth' : 'python $HOME/forth-notebooks/forth-client.py' 42 | } 43 | 44 | ### Create forth-in and forth-out pipes 45 | 46 | Run the command setup.sh 47 | 48 | $ source setup.sh 49 | 50 | or inspect the file and run its commands manually if you don't want to run arbitrary 51 | shell files with your access rights. 52 | 53 | ## Running Forth notebooks 54 | 55 | 1. In order to run Forth notebooks you first start the forth server like: 56 | 57 | $ gforth gforth-server.fs -e 'start' 58 | 59 | 2. Start IPython notebook: 60 | 61 | $ ipython notebook --matplotlib=inline 62 | 63 | 3. Once your browser starts with IPython, open one of the sample notebooks such as 64 | [hello-forth.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/uho/forth-notebooks/master/hello-forth.ipynb) 65 | or [stack-reverse.ipynb](http://nbviewer.ipython.org/urls/raw.github.com/uho/forth-notebooks/master/stack-reverse.ipynb). 66 | 67 | 68 | -------------------------------------------------------------------------------- /stack-reverse.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "" 4 | }, 5 | "nbformat": 3, 6 | "nbformat_minor": 0, 7 | "worksheets": [ 8 | { 9 | "cells": [ 10 | { 11 | "cell_type": "heading", 12 | "level": 1, 13 | "metadata": {}, 14 | "source": [ 15 | "How to reverse the stack" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "At euroForth 2013 we raised the question how to reverse (parts of) the stack.\n", 23 | "\n", 24 | "Here is a definition of the word __nreverse__ that reverses the order of the \n", 25 | "topmost n items." 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "collapsed": false, 31 | "input": [ 32 | "%%gforth\n", 33 | "\n", 34 | ": nreverse ( n*x n -- n*x' )\n", 35 | " 1 DO I roll LOOP ;" 36 | ], 37 | "language": "python", 38 | "metadata": {}, 39 | "outputs": [ 40 | { 41 | "output_type": "stream", 42 | "stream": "stdout", 43 | "text": [ 44 | " ok\n", 45 | "\n" 46 | ] 47 | } 48 | ], 49 | "prompt_number": 24 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "What? How does it work? It moves the topmost item to the top, then the second and so on until it moves\n", 56 | "the nth item to the top, alas the topmost n items are reversed." 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "So let's try it:" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "collapsed": false, 69 | "input": [ 70 | "%%gforth\n", 71 | "\n", 72 | "clearstack\n", 73 | "\n", 74 | "1 2 3 10 20 30 40 50 5 nreverse .s" 75 | ], 76 | "language": "python", 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "output_type": "stream", 81 | "stream": "stdout", 82 | "text": [ 83 | "<8> 1 2 3 50 40 30 20 10 ok\n", 84 | "\n" 85 | ] 86 | } 87 | ], 88 | "prompt_number": 25 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "Fine - works as expected." 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "But - it uses __roll__ that is rather inefficient. Can't we find another implementation of __nreverse__?\n", 102 | "\n", 103 | "Sure - we could copy the stack items to main memory and then read them back to the stack in required order:" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "collapsed": false, 109 | "input": [ 110 | "%%gforth\n", 111 | "\n", 112 | ": nreverse ( n*x n -- n*x' )\n", 113 | " here >r dup >r here >r\n", 114 | " 0 DO , LOOP\n", 115 | " r> r> cells bounds DO I @ 1 cells +LOOP\n", 116 | " r> dp ! ;\n", 117 | " " 118 | ], 119 | "language": "python", 120 | "metadata": {}, 121 | "outputs": [ 122 | { 123 | "output_type": "stream", 124 | "stream": "stdout", 125 | "text": [ 126 | "redefined nreverse ok\n", 127 | "\n" 128 | ] 129 | } 130 | ], 131 | "prompt_number": 26 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "Temporary space in the dictionary is allocated by ,-ing the items from the stack.\n", 138 | "After reading the items back to the stack, the dictionary pointer is restored. " 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "So, what is this __bounds__ word? __bounds__ is defined like this:" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "collapsed": false, 151 | "input": [ 152 | "%%gforth\n", 153 | "\n", 154 | "see bounds" 155 | ], 156 | "language": "python", 157 | "metadata": {}, 158 | "outputs": [ 159 | { 160 | "output_type": "stream", 161 | "stream": "stdout", 162 | "text": [ 163 | "\n", 164 | ": bounds \n", 165 | " over + swap ; ok\n", 166 | "\n" 167 | ] 168 | } 169 | ], 170 | "prompt_number": 27 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "Essentially it converts __addr__ __count__ to __end-addr__ __start-addr__ so that the __DO__ loop can iterate over addresses." 177 | ] 178 | }, 179 | { 180 | "cell_type": "markdown", 181 | "metadata": {}, 182 | "source": [ 183 | "OK, let's test this new implementation." 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "collapsed": false, 189 | "input": [ 190 | "%%gforth\n", 191 | "\n", 192 | "clearstack here .\n", 193 | "\n", 194 | "1 2 3 10 20 30 40 50 5 nreverse .s\n", 195 | "\n", 196 | "cr here ." 197 | ], 198 | "language": "python", 199 | "metadata": {}, 200 | "outputs": [ 201 | { 202 | "output_type": "stream", 203 | "stream": "stdout", 204 | "text": [ 205 | "4312103552 <8> 1 2 3 50 40 30 20 10 \n", 206 | "4312103552 ok\n", 207 | "\n" 208 | ] 209 | } 210 | ], 211 | "prompt_number": 28 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "Right, the dictionary pointer stays the same and the stack is reversed. Fine." 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "---" 225 | ] 226 | } 227 | ], 228 | "metadata": {} 229 | } 230 | ] 231 | } --------------------------------------------------------------------------------