The first cut of a talk on the R&D process in software development, including taking an invention to patent.
Includes two sets of code examples. One is Forth implemented in a 1980s dialect of Basic.
The other introduces evolutionary prototyping using a hybrid ruby/bash methodology.
State of the Smart Building Startup Landscape 2024!
[2023] Putting the R! in R&D.pdf
1. 1
Eleanor McHugh
co-founder @ InIdSol
https://inidsol.uk
https://shayk.online
x://@feyeleanor
leanpub://GoNotebook
slideshare://feyeleanor
linkedin://in/eleanormchugh
putting the R! in
R&D
Rough Cut
2. 2
solving puzzles the hard way
Elric sent his mind into twisting tunnels of logic, across
endless plains of ideas, through mountains of symbolism and
endless universes of alternate truths; he sent his mind out
further and further and as it went he sent with it the words
[...] words that few of his contemporaries would
understand...
- Elric of Melniboné, Michael Moorcock
3. 3
solving puzzles the hard way:
the ratio of squares
inscribe a circle inside a square ao
the circle touches the sides at their midpoint
inscribe a square ai inside the circle
its vertices touch the circumference
ai
ao
4. 4
2r
2πr
2r
ai
ao
solving puzzles the hard way:
the ratio of squares
we know a lot about circles
their diameter is twice their radius r
and their circumference is 2πr
5. 5
y
πd
d
x
ai = (2y)2
ao = d2
ai
ao
solving puzzles the hard way:
the ratio of squares
and we know a lot about squares
such as their area being the square of the
length of one side
this allows us to express the areas of the
two squares in terms of d and y
7. 7
y
πd
d
x
ai = 4y2
ao = (2x + 2y)2
ai
ao
solving puzzles the hard way:
the ratio of squares
8. 8
y
πd
d
x
ai = 4y2
ao = 4x2 + 8xy + 4y2
ai
ao
solving puzzles the hard way:
the ratio of squares
9. 9
y
πd
d
x
ai = 4y2
ao = 4x2 + 8xy + 4y2
ai
ao
solving puzzles the hard way:
the ratio of squares
note that the calculation of ao includes ai
which is interesting but not directly helpful in
calculating the ratio of ao to ai
however this still leaves us to figure out both
x and y which isn't particularly useful
10. 10
y
πd
d
x
ai
ao
solving puzzles the hard way:
the ratio of squares
however we can reframe this problem
if we bisect ai along the line formed by
opposing vertices we find that this has
shares the length d - the circle diameter
d
11. 11
y
πd
d
x
h2 = o2 + a2
d2 = (2y)2 + (2y)2
ai
ao
solving puzzles the hard way:
the ratio of squares
and by applying the pythagorean theorem
we can calculate d in terms of y
d
12. 12
y
πd
d
x
h2 = o2 + a2
d2 = 4y2 + 4y2
ai
ao
solving puzzles the hard way:
the ratio of squares
d
13. 13
y
πd
d
x
h2 = o2 + a2
d2 = 8y2
ai
ao
solving puzzles the hard way:
the ratio of squares
d
18. 18
*blink*:
the ratio of squares
algebra is a rational tool that can solve many
problems by brute force
however when you study a problem domain
in depth you start to develop intuitions -
ways of identifying shortcuts to the answer
you're after
ai
ao
d
19. 19
*blink*:
the ratio of squares
algebra is a rational tool that can solve many
problems by brute force
however when you study a problem domain
in depth you start to develop intuitions -
ways of identifying shortcuts to the answer
you're after
ai
ao
d
21. 21
*blink*:
the ratio of squares
we can see that the area of ao must be twice
the area of ai
ao
ai
22. 22
*blink*:
the ratio of squares
a
y
y
d 2 2
Y 6 842
I s d from
d 2 24
g
a
e
12 4 2
84 442
v
using
8112 4 2
84 442
Sino
I 242 42 2 4 42
x 2 4 42 0
Cos
ay
Sino sin450
sin450
tano
Ia
seco
ha I E ai y
coseco
to ay de di 442
14472 242
12 8,2
do de
90 8 2
Sin
II
do
Cos
450
I g 2
tan450 1
sin900 1
Cos
900 0
tango
undefined
25. 25
● for a particle with few
degrees of freedom, its
position x and its
momentum p cannot
both be determined at
the same time
● Schrödinger's cat sums
up the weirdness of this
Uncertain Quanta
Heisenberg
Schrödinger
26. 26
● at the smallest scale
things behave as if
distance doesn't exist
● at the largest scale
nothing can move faster
then the speed of light
● which is more true?
● wormholes!
● time travel!
● (and conspiracies!!!)
Locality & The Real
Bell
Einstein, Rosen, Podolsky
29. 29
● Turing-Church
● robot reads infinite tape
● applies rules that can
overwrite the tape then
moves the tape one
step left or right
● can compute anything
which can be computed
● physical analogue of
the Lambda Calculus
Universal Machines
The input value to be bound
to the argument x
Substitute the value into the body
The body becomes the return value
λ x . x
( ) 2
λ 2 . x
( )
λ 2 . 2
( )
2
Turing
Church
30. 30
● the study of causal
processes and
feedback loops
● these loops regulate or
control the operation of
a system
● feedback can be
positive or negative
● used to steer a system
towards desired goals
Cybernetic Noise
31. 31
● entropy describes the
information contained in
a system
● the higher the entropy,
the more information
that can be contained
● applications in
cryptography and
telecommunications
Information Overload
32. 32
Loose Lips Save Ships
● Bletchley Park GC&CS
● traffic analysis
● Enigma and the Turing
electro-mechanical
bombe
● Lorenz stream cipher
and Colossus, the
world's first electronic
programmable
computer
Flowers
33. 33
● the Memex, an electro-
mechanical platform for
managing deeply
interlinked knowledge
● Project Xanadu a
platform for publishing
hypertext & hypermedia
● WWW adds typesetting
and programmability to
hypertext
Webs of Meaning
Ted Nelson
Berners-Lee
34. 34
● algorithms are tractable
to rigorous analysis
● Big O notation - how
long an algorithm runs
● or how much memory
an algorithm uses
● O(logx n)
● O(nx)
● O(n!)
Beyond the Machine
in programming, simplicity and
clarity ... are not a dispensable luxury,
but a crucial matter that decides
between success and failure
Dijkstra
Knuth
35. 35
● falsifiability as the test
of empirical study
● constructivism - the
pursuit of knowledge is
shaped by social
factors
● the methods of one
paradigm do not
necessarily translate
into another paradigm
All Life is Problem Solving
In so far as a scientific statement speaks about
reality, it must be falsifiable; and in so far as it is
not falsifiable it does not speak about reality
Kuhn
Popper
36. 36
that first deep-dive into insanity
Love is the ghost haunting your head
Love is the killer you thought was your friend
Love is the creature who lives in the dark
Sneaks up, will stick you
And painfully pick you apart
- The Beast, Concrete Blonde
41. 41
● stack-based
● programs compose
word definitions from a
threaded dictionary
● limited type system
● ASM versions are great
for embedded systems
● Open Firmware
● gforth
● Factor (functional)
Forth
42. 5 2 3 56 76 23 65 .s <7> 5 2 3 56 76 23 65 ok
5 4 + . 9 ok
13 2 / . 6 ok
13 2 mod . 1 ok
3 dup - . 0 ok
2 5 swap / . 2 ok
6 4 5 rot .s <3> 4 5 6 ok
4 0 drop 2 / . 2 ok
1 2 3 nip .s <2> 1 3 ok
: square ( n -- n ) dup * ; ok
5 square . 25 ok
see square : square dup * ; ok
42 42 = . -1 ok
12 53 = . 0 ok
: ?>64 dup 64 > if ." More than" else ." Less than" then ." 64!" ; ok
100 ?>64 More than 64! ok
20 ?>64 Less than 64! ok
: squares ( n -- ) 1 do i square loop ; ok
5 squares .s <4> 1 4 9 16 ok
: threes ( n n -- ) ?do i . 3 +loop ; ok
15 0 threes 0 3 6 9 12 ok
0 0 threes ok
variable age ok
21 age ! ok
age @ . 21 ok
age ? 21 ok
100 constant WATER-BOILING-POINT ok
WATER-BOILING-POINT . 100 ok
variable num-array 2 cells allot ok
num-array 3 cells erase ok
num-array 3 cells 0 fill ok
create num-array 64 , 9001 , 1337 , ok
0 cells num-array + ? 64 ok
1 cells num-array + ? 9001 ok
: of-arr ( n n -- n ) cells + ; ok
num-array 2 of-arr ? 1337 ok
20 num-array 1 of-arr ! ok
num-array 1 of-arr ? 20 ok
: i-from-rstack ( -- ) 4 0 do r@ . loop ; ok
i-from-rstack 0 1 2 3 ok
5 6 4 >r swap r> .s 6 5 4 ok
5 6 4 clearstack ok
bye
42
see https://learnxinyminutes.com/docs/forth/
43. 5 2 3 56 76 23 65 .s
<7> 5 2 3 56 76 23 65 ok
5 4 + .
9 ok
13 2 / .
6 ok
13 2 mod .
1 ok
3 dup .s cr - .
<2> 3 3
0 ok
43
44. 2 5 swap .s
<2> 5 2 ok
6 4 5 rot .
<3> 4 5 6 ok
4 0 drop 2 / .
2 ok
1 2 3 nip .s
<2> 1 3 ok
4 5 6 clearstack .s
<0> ok
44
45. : square ( n -- n ) dup * ;
ok
5 square .
25 ok
see square
: square dup * ; ok
45
46. : print-result . cr .s ;
12 42 42 42 = print-result cr = print-result
-1
<2> 12 42
0
<0> ok
: ?>64
dup 64 >
if ." More than"
else ." Less than"
then ." 64!" ;
100 ?>64
More than 64! ok
20 ?>64
Less than 64! ok
46
47. : squares ( n -- ) 1 do i square loop ;
5 .s .cr squares .s
<1> 5
<4> 1 4 9 16 ok
: threes ( n n -- ) ?do i 3 +loop ;
: reverse-stack ( n*x -- n*x') depth 1 do i roll loop ;
: print-stack ( n -- ) depth 0 do . cr loop ;
15 0 threes cr .s cr reverse-stack .s cr print-stack
<5> 0 3 6 9 12 ok
<5> 12 9 6 3 0 ok
0
3
6
9
12
ok
0 0 threes
ok
47
54. 54
● tokenised interpreter
● running on Zilog Z80
● unstructured code with
GOTO and GOSUB
● no UDTs
● simple line editor
● painfully slow execution
● if you can code in this
you can probably code
in anything
Locomotive BASIC
55. 1 REM ******************
2 REM * Amstrad Forth *
3 REM * by *
4 REM * Stephen Devine *
5 REM ******************
6 REM
10 REM (c) Computing with the Amstrad
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
250 REM Input Commands
260 w$="":er=0:LINE INPUT ln$:IF ln$="" THEN 690 ELSE IF LEN(ln$)>240 THEN PRINT"Line too
long":GOTO 260
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":G$
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
55
https://www.cpcwiki.eu/index.php/CWTA_Issue_17_-_May_1986_-_Type-Ins
Used under Fair Use for purpose of review and critique
56. 56
1 REM ******************
2 REM * Amstrad Forth *
3 REM * by *
4 REM * Stephen Devine *
5 REM ******************
6 REM
10 REM (c) Computing with the Amstrad
100 REM Initialisation
250 REM Input Commands
680 REM Error Routine
710 REM Compile New Word
750 REM Execute Commands
1080 REM Command List
1690 REM Process Editing Commands
2540 REM Data for Core Words
Outer Interpreter (tokeniser & parser)
Word Compiler (adds entry to dictionary)
Inner Interpreter (invoke entries from dictionary)
Implemented Primitives
Implementation extension words
Primitive tokens and stack behaviour
57. 57
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
58. 58
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
59. 59
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
60. 60
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
61. 61
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
62. 62
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
63. 63
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
64. 64
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
65. 65
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
66. 66
100 REM Initialisation
110 MODE 2:BORDER 13:INK 1,0:INK 0,13
120 forth$=CHR$(12)+"Fred Forth V1.1"
130 OPENOUT "dummy":MEMORY HIMEM-1:CLOSEOUT
140 temp=0:DEFINT a-z
150 DIM w$(130),p(130),beg(40),ff(40),df(40),loop(40),ll(40),li(40)
160 er$(0)="OK":er$(1)="Stack Underflow":er$(2)="Empty Stack"
170 er$(3)=" already defined":er$(4)=" - Illegal variable name":er$(5)=" - Bad Word":er$(6)="Stack
Full":er$(7)="Return Stack Full"
180 spmax=100:DIM s(spmax):sp=-1
190 cvn=58:DIM cvoc$(cvn),dsp(cvn,1)
200 FOR i=0 TO cvn:READ cvoc$(i),dsp(i,0),dsp(i,1):NEXT
210 cvoc$(5)="."+CHR$(34)
220 umax=100:vmax=100:DIM uvoc$(umax),uvex$(umax),var$(vmax),var(vmax)
230 uvn=-1:vrn=-1:PRINT forth$:PRINT:PRINT er$(0)
240 ON ERROR GOTO 2530
2530 er=6:IF sp>100 THEN sp=100:RESUME NEXT ELSE w$="":RESUME 690
2540 REM Data for Core Words
2550 DATA "!",2,-2,"*",2,-1,"+",2,-1,"-",2,-1,".",0,-1,"?",0,0,"/",2,-1,"/
MOD",2,0,"0<",1,0,"0=",1,0,"<",2,-1
2560 DATA
"=",2,-1,">",2,-1,"?",1,-1,"@",1,0,"ABS",1,0,"AND",2,-1,"C@",1,0,"CR",0,0,"DROP",1,-1,"DUP",1,1
2570 DATA "EMIT",1,-1,"KEY",0,1,"MAX",2,-1,"MIN",2,-1,"MINUS",1,0,"MOD",2,-1,"OR",2,-1,"OVER",2,1
2580 DATA "SPACE",0,0,"SPACES",1,-1,"SWAP",2,0,"VARIABLE",1,-1,"XOR",2,-1,"BEGIN",0,0,"UNTIL",1,-1
2590 DATA
"WHILE",1,-1,"REPEAT",0,0,"IF",1,-1,"THEN",0,0,"ELSE",0,0,"FORTH",0,0,"CLEAR",0,0,"ROT",3,0,"DO",2,
-2,"LOOP",0,0,"I",0,+1
2600 REM Data for Amstrad Words
2610 DATA "CLG",1,-1,"DRAW",2,-2,"DRAWR",2,-2,"FRE",0,1,"MOVE",2,-2,"MOVER",2,-2
2620 DATA "PLOT",2,-2,"PLOTR",2,-2,"RND",0,1,"TEST",2,-1,"TESTR",2,-1,"GRAPEN",1,-1
68. 68
250 REM Input Commands
260 w$="":er=0:LINE INPUT ln$:IF ln$="" THEN 690 ELSE IF LEN(ln$)>240 THEN PRINT"Line too
long":GOTO 260
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
680 REM Error Routine
690 IF POS(#0)>1 THEN PRINT CHR$(32);
700 PRINT w$;er$(er):GOTO 260
69. 69
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
380 te=INSTR(q,ln$,CHR$(34)+CHR$(32)):IF te=0 THEN PRINT".";CHR$(34);" without ";CHR$(34):GOTO 260
390 x$=x$+MID$(ln$,q+1,te-q-1)+CHR$(4):q=te+1:GOTO 640
400 FOR i=uvn TO 0 STEP-1:IF uvoc$(i)<>w$ THEN NEXT:GOTO 440
410 IF comp THEN IF wn=0 THEN er=3:GOTO 690
420 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
430 x$=x$+CHR$(1)+CHR$(i+14):GOTO 640
440 FOR i=vrn TO 0 STEP-1:IF var$(i)<>w$ THEN NEXT:GOTO 480
450 IF comp THEN IF wn=0 THEN er=3:GOTO 690
460 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=3:GOTO 690
470 x$=x$+CHR$(2)+CHR$(i+14):GOTO 640
480 FOR i=1 TO LEN(w$)
490 IF i=1 AND (ASC(w$)=ASC("+") OR ASC(w$)=ASC("-")) AND LEN(w$)>1 THEN 510
500 IF MID$(w$,i,1)<"0" OR MID$(w$,i,1)>"9" THEN 560
510 NEXT i
520 IF comp AND wn=0 THEN er=5:GOTO 690
530 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=5:GOTO 690
540 IF VAL(w$)>32767 OR VAL(w$)<-32767 THEN PRINT"Number ";w$;" too large":GOTO 260
550 x$=x$+CHR$(3)+w$+CHR$(4):GOTO 640
560 IF wn<0 THEN 590 ELSE IF w$(wn)<>"VARIABLE" THEN 590
570 IF comp AND w$=w$(1) THEN er=3:GOTO 690
580 x$=x$+w$+CHR$(4):GOTO 640
590 IF w$<>";" THEN 610
600 IF comp=0 OR q<>LEN(ln$) THEN PRINT"Illegal semi-colon":GOTO 260 ELSE 640
610 IF comp THEN IF wn=0 THEN 640
620 IF comp THEN IF w$<>w$(1) THEN er=5:GOTO 690 ELSE x$=x$+CHR$(1)+CHR$(uvn+15):GOTO 640
630 er=5:GOTO 690
640 wn=wn+1:w$(wn)=w$
650 WHILE MID$(ln$,q,1)=" ":q=q+1:WEND
660 WEND
670 x$=x$+CHR$(13):IF comp THEN 720 ELSE 760
produce token stream
70. 70
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
380 te=INSTR(q,ln$,CHR$(34)+CHR$(32)):IF te=0 THEN PRINT".";CHR$(34);" without ";CHR$(34):GOTO 260
390 x$=x$+MID$(ln$,q+1,te-q-1)+CHR$(4):q=te+1:GOTO 640
400 FOR i=uvn TO 0 STEP-1:IF uvoc$(i)<>w$ THEN NEXT:GOTO 440
410 IF comp THEN IF wn=0 THEN er=3:GOTO 690
420 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
430 x$=x$+CHR$(1)+CHR$(i+14):GOTO 640
440 FOR i=vrn TO 0 STEP-1:IF var$(i)<>w$ THEN NEXT:GOTO 480
450 IF comp THEN IF wn=0 THEN er=3:GOTO 690
460 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=3:GOTO 690
470 x$=x$+CHR$(2)+CHR$(i+14):GOTO 640
480 FOR i=1 TO LEN(w$)
490 IF i=1 AND (ASC(w$)=ASC("+") OR ASC(w$)=ASC("-")) AND LEN(w$)>1 THEN 510
500 IF MID$(w$,i,1)<"0" OR MID$(w$,i,1)>"9" THEN 560
510 NEXT i
520 IF comp AND wn=0 THEN er=5:GOTO 690
530 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=5:GOTO 690
540 IF VAL(w$)>32767 OR VAL(w$)<-32767 THEN PRINT"Number ";w$;" too large":GOTO 260
550 x$=x$+CHR$(3)+w$+CHR$(4):GOTO 640
560 IF wn<0 THEN 590 ELSE IF w$(wn)<>"VARIABLE" THEN 590
570 IF comp AND w$=w$(1) THEN er=3:GOTO 690
580 x$=x$+w$+CHR$(4):GOTO 640
590 IF w$<>";" THEN 610
600 IF comp=0 OR q<>LEN(ln$) THEN PRINT"Illegal semi-colon":GOTO 260 ELSE 640
610 IF comp THEN IF wn=0 THEN 640
620 IF comp THEN IF w$<>w$(1) THEN er=5:GOTO 690 ELSE x$=x$+CHR$(1)+CHR$(uvn+15):GOTO 640
630 er=5:GOTO 690
640 wn=wn+1:w$(wn)=w$
650 WHILE MID$(ln$,q,1)=" ":q=q+1:WEND
660 WEND
670 x$=x$+CHR$(13):IF comp THEN 720 ELSE 760
look for extension words
71. 71
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
380 te=INSTR(q,ln$,CHR$(34)+CHR$(32)):IF te=0 THEN PRINT".";CHR$(34);" without ";CHR$(34):GOTO 260
390 x$=x$+MID$(ln$,q+1,te-q-1)+CHR$(4):q=te+1:GOTO 640
400 FOR i=uvn TO 0 STEP-1:IF uvoc$(i)<>w$ THEN NEXT:GOTO 440
410 IF comp THEN IF wn=0 THEN er=3:GOTO 690
420 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
430 x$=x$+CHR$(1)+CHR$(i+14):GOTO 640
440 FOR i=vrn TO 0 STEP-1:IF var$(i)<>w$ THEN NEXT:GOTO 480
450 IF comp THEN IF wn=0 THEN er=3:GOTO 690
460 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=3:GOTO 690
470 x$=x$+CHR$(2)+CHR$(i+14):GOTO 640
480 FOR i=1 TO LEN(w$)
490 IF i=1 AND (ASC(w$)=ASC("+") OR ASC(w$)=ASC("-")) AND LEN(w$)>1 THEN 510
500 IF MID$(w$,i,1)<"0" OR MID$(w$,i,1)>"9" THEN 560
510 NEXT i
520 IF comp AND wn=0 THEN er=5:GOTO 690
530 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=5:GOTO 690
540 IF VAL(w$)>32767 OR VAL(w$)<-32767 THEN PRINT"Number ";w$;" too large":GOTO 260
550 x$=x$+CHR$(3)+w$+CHR$(4):GOTO 640
560 IF wn<0 THEN 590 ELSE IF w$(wn)<>"VARIABLE" THEN 590
570 IF comp AND w$=w$(1) THEN er=3:GOTO 690
580 x$=x$+w$+CHR$(4):GOTO 640
590 IF w$<>";" THEN 610
600 IF comp=0 OR q<>LEN(ln$) THEN PRINT"Illegal semi-colon":GOTO 260 ELSE 640
610 IF comp THEN IF wn=0 THEN 640
620 IF comp THEN IF w$<>w$(1) THEN er=5:GOTO 690 ELSE x$=x$+CHR$(1)+CHR$(uvn+15):GOTO 640
630 er=5:GOTO 690
640 wn=wn+1:w$(wn)=w$
650 WHILE MID$(ln$,q,1)=" ":q=q+1:WEND
660 WEND
670 x$=x$+CHR$(13):IF comp THEN 720 ELSE 760
outer interpreter loop
72. 72
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
380 te=INSTR(q,ln$,CHR$(34)+CHR$(32)):IF te=0 THEN PRINT".";CHR$(34);" without ";CHR$(34):GOTO 260
390 x$=x$+MID$(ln$,q+1,te-q-1)+CHR$(4):q=te+1:GOTO 640
400 FOR i=uvn TO 0 STEP-1:IF uvoc$(i)<>w$ THEN NEXT:GOTO 440
410 IF comp THEN IF wn=0 THEN er=3:GOTO 690
420 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
430 x$=x$+CHR$(1)+CHR$(i+14):GOTO 640
440 FOR i=vrn TO 0 STEP-1:IF var$(i)<>w$ THEN NEXT:GOTO 480
450 IF comp THEN IF wn=0 THEN er=3:GOTO 690
460 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=3:GOTO 690
470 x$=x$+CHR$(2)+CHR$(i+14):GOTO 640
480 FOR i=1 TO LEN(w$)
490 IF i=1 AND (ASC(w$)=ASC("+") OR ASC(w$)=ASC("-")) AND LEN(w$)>1 THEN 510
500 IF MID$(w$,i,1)<"0" OR MID$(w$,i,1)>"9" THEN 560
510 NEXT i
520 IF comp AND wn=0 THEN er=5:GOTO 690
530 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=5:GOTO 690
540 IF VAL(w$)>32767 OR VAL(w$)<-32767 THEN PRINT"Number ";w$;" too large":GOTO 260
550 x$=x$+CHR$(3)+w$+CHR$(4):GOTO 640
560 IF wn<0 THEN 590 ELSE IF w$(wn)<>"VARIABLE" THEN 590
570 IF comp AND w$=w$(1) THEN er=3:GOTO 690
580 x$=x$+w$+CHR$(4):GOTO 640
590 IF w$<>";" THEN 610
600 IF comp=0 OR q<>LEN(ln$) THEN PRINT"Illegal semi-colon":GOTO 260 ELSE 640
610 IF comp THEN IF wn=0 THEN 640
620 IF comp THEN IF w$<>w$(1) THEN er=5:GOTO 690 ELSE x$=x$+CHR$(1)+CHR$(uvn+15):GOTO 640
630 er=5:GOTO 690
640 wn=wn+1:w$(wn)=w$
650 WHILE MID$(ln$,q,1)=" ":q=q+1:WEND
660 WEND
670 x$=x$+CHR$(13):IF comp THEN 720 ELSE 760
word definition to compile
73. 73
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
380 te=INSTR(q,ln$,CHR$(34)+CHR$(32)):IF te=0 THEN PRINT".";CHR$(34);" without ";CHR$(34):GOTO 260
390 x$=x$+MID$(ln$,q+1,te-q-1)+CHR$(4):q=te+1:GOTO 640
400 FOR i=uvn TO 0 STEP-1:IF uvoc$(i)<>w$ THEN NEXT:GOTO 440
410 IF comp THEN IF wn=0 THEN er=3:GOTO 690
420 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
430 x$=x$+CHR$(1)+CHR$(i+14):GOTO 640
440 FOR i=vrn TO 0 STEP-1:IF var$(i)<>w$ THEN NEXT:GOTO 480
450 IF comp THEN IF wn=0 THEN er=3:GOTO 690
460 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=3:GOTO 690
470 x$=x$+CHR$(2)+CHR$(i+14):GOTO 640
480 FOR i=1 TO LEN(w$)
490 IF i=1 AND (ASC(w$)=ASC("+") OR ASC(w$)=ASC("-")) AND LEN(w$)>1 THEN 510
500 IF MID$(w$,i,1)<"0" OR MID$(w$,i,1)>"9" THEN 560
510 NEXT i
520 IF comp AND wn=0 THEN er=5:GOTO 690
530 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=5:GOTO 690
540 IF VAL(w$)>32767 OR VAL(w$)<-32767 THEN PRINT"Number ";w$;" too large":GOTO 260
550 x$=x$+CHR$(3)+w$+CHR$(4):GOTO 640
560 IF wn<0 THEN 590 ELSE IF w$(wn)<>"VARIABLE" THEN 590
570 IF comp AND w$=w$(1) THEN er=3:GOTO 690
580 x$=x$+w$+CHR$(4):GOTO 640
590 IF w$<>";" THEN 610
600 IF comp=0 OR q<>LEN(ln$) THEN PRINT"Illegal semi-colon":GOTO 260 ELSE 640
610 IF comp THEN IF wn=0 THEN 640
620 IF comp THEN IF w$<>w$(1) THEN er=5:GOTO 690 ELSE x$=x$+CHR$(1)+CHR$(uvn+15):GOTO 640
630 er=5:GOTO 690
640 wn=wn+1:w$(wn)=w$
650 WHILE MID$(ln$,q,1)=" ":q=q+1:WEND
660 WEND
670 x$=x$+CHR$(13):IF comp THEN 720 ELSE 760
execute "byte code" or add to dictionary
check for user defined word
check for user defined variable
must be a number
"byte code" generated for token
74. 74
270 WHILE ASC(ln$)=32:IF LEN(ln$)>1 THEN ln$=RIGHT$(ln$,LEN(ln$)-1):WEND ELSE 690
280 WHILE RIGHT$(ln$,1)=CHR$(32):ln$=LEFT$(ln$,LEN(ln$)-1):WEND
290 ln$=UPPER$(ln$):IF ASC(ln$)=ASC("*") THEN IF LEN(ln$)>1 AND LEFT$(ln$,2)<>"* " THEN GOSUB
1700:IF er THEN 260 ELSE w$="":GOTO 690
300 ln$=ln$+CHR$(32):x$="":q=1:wn=-1:comp=0
310 WHILE q<LEN(ln$)
320 p=q:WHILE MID$(ln$,q,1)<>" ":q=q+1:WEND
330 w$=MID$(ln$,p,q-p):IF w$=":" THEN IF wn=-1 AND RIGHT$(ln$,2)="; " THEN comp=-1:GOTO 640 ELSE
PRINT"Bad definition":GOTO 260
340 FOR i=cvn TO 0 STEP-1:IF cvoc$(i)<>w$ THEN NEXT:GOTO 400
350 IF comp THEN IF wn=0 THEN er=3:GOTO 690
360 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
370 x$=x$+CHR$(0)+CHR$(i+14):IF w$<>"."+CHR$(34) THEN 640
380 te=INSTR(q,ln$,CHR$(34)+CHR$(32)):IF te=0 THEN PRINT".";CHR$(34);" without ";CHR$(34):GOTO 260
390 x$=x$+MID$(ln$,q+1,te-q-1)+CHR$(4):q=te+1:GOTO 640
400 FOR i=uvn TO 0 STEP-1:IF uvoc$(i)<>w$ THEN NEXT:GOTO 440
410 IF comp THEN IF wn=0 THEN er=3:GOTO 690
420 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=4:GOTO 690
430 x$=x$+CHR$(1)+CHR$(i+14):GOTO 640
440 FOR i=vrn TO 0 STEP-1:IF var$(i)<>w$ THEN NEXT:GOTO 480
450 IF comp THEN IF wn=0 THEN er=3:GOTO 690
460 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=3:GOTO 690
470 x$=x$+CHR$(2)+CHR$(i+14):GOTO 640
480 FOR i=1 TO LEN(w$)
490 IF i=1 AND (ASC(w$)=ASC("+") OR ASC(w$)=ASC("-")) AND LEN(w$)>1 THEN 510
500 IF MID$(w$,i,1)<"0" OR MID$(w$,i,1)>"9" THEN 560
510 NEXT i
520 IF comp AND wn=0 THEN er=5:GOTO 690
530 IF wn>=0 THEN IF w$(wn)="VARIABLE" THEN er=5:GOTO 690
540 IF VAL(w$)>32767 OR VAL(w$)<-32767 THEN PRINT"Number ";w$;" too large":GOTO 260
550 x$=x$+CHR$(3)+w$+CHR$(4):GOTO 640
560 IF wn<0 THEN 590 ELSE IF w$(wn)<>"VARIABLE" THEN 590
570 IF comp AND w$=w$(1) THEN er=3:GOTO 690
580 x$=x$+w$+CHR$(4):GOTO 640
590 IF w$<>";" THEN 610
600 IF comp=0 OR q<>LEN(ln$) THEN PRINT"Illegal semi-colon":GOTO 260 ELSE 640
610 IF comp THEN IF wn=0 THEN 640
620 IF comp THEN IF w$<>w$(1) THEN er=5:GOTO 690 ELSE x$=x$+CHR$(1)+CHR$(uvn+15):GOTO 640
630 er=5:GOTO 690
640 wn=wn+1:w$(wn)=w$
650 WHILE MID$(ln$,q,1)=" ":q=q+1:WEND
660 WEND
670 x$=x$+CHR$(13):IF comp THEN 720 ELSE 760
add "byte code" for primitive
add "byte code" for user defined word
add "byte code" for user defined variable
add "byte code" for number
compiler mode sanity checks
76. 76
710 REM Compile New Word
720 IF wn<3 THEN PRINT"Insufficient definition":GOTO 260
730 uvn=uvn+1:uvoc$(uvn)=w$(1):uvex$(uvn)=x$
740 w$="":GOTO 690
77. 77
710 REM Compile New Word
720 IF wn<3 THEN PRINT"Insufficient definition":GOTO 260
730 uvn=uvn+1:uvoc$(uvn)=w$(1):uvex$(uvn)=x$
740 w$="":GOTO 690
78. 78
710 REM Compile New Word
720 IF wn<3 THEN PRINT"Insufficient definition":GOTO 260
730 uvn=uvn+1:uvoc$(uvn)=w$(1):uvex$(uvn)=x$
740 w$="":GOTO 690
80. 80
750 REM Execute Commands
760 ln=0:w$(ln)=x$:er=0
770 GOSUB 780:w$="":GOTO 690
780 p(ln)=1:ff(ln)=0:df(ln)=0
790 WHILE MID$(w$(ln),p(ln),1)<>CHR$(13)
800 class=ASC(MID$(w$(ln),p(ln),1)):p(ln)=p(ln)+1
810 IF class<>0 THEN 920
820 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
830 IF ff(ln)=0 OR word=37 OR word=39 OR word=40 THEN 860
840 IF word=5 OR word=32 THEN WHILE ASC(MID$(w$(ln),p(ln),1))<>4:p(ln)=p(ln)+1:WEND:p(ln)=p(ln)+1
850 GOTO 1060
860 IF sp+dsp(word,1)<-1 OR sp+dsp(word,1)>spmax THEN er=1:GOTO 1070
870 IF sp-dsp(word,0)<-1 THEN er=2:GOTO 1070
880 sp=sp+dsp(word,1)
890 IF word <43 THEN ON word+1 GOSUB
1090,1110,1120,1130,1140,1150,1160,1170,1180,1190,1200,1210,1220,1230,1240,1250,1260,1270,1280,1290
,1300,1310,1320,1330,1340,1350,1360,1370,1380,1390,1400,1410,1420,1430,1440,1450,1460,1470,1480,149
0,1500,1510,1520
900 IF word>42 THEN ON word-42 GOSUB
1530,1540,1550,1560,1570,1580,1590,1600,1610,1620,1630,1640,1650,1660,1670,1680
910 IF er=0 THEN 1060 ELSE 1070
920 IF class<>1 THEN 970
930 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
940 IF ff(ln) THEN 1060
950 IF ln<34 THEN ln=ln+1:w$(ln)=uvex$(word) ELSE er=7:RETURN
960 GOSUB 780:IF ln=0 OR er=0 THEN 1060 ELSE RETURN
970 IF class<>2 THEN 1010
980 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
990 IF ff(ln) THEN 1060
1000 sp=sp+1:s(sp)=@var(word):GOTO 1060
1010 IF class<>3 THEN er=1:GOTO 1070
1020 p=p(ln):WHILE ASC(MID$(w$(ln),p,1))<>4:p=p+1:WEND
1030 v=VAL(MID$(w$(ln),p(ln),p-p(ln)+1)):p(ln)=p+1
1040 IF ff(ln) THEN 1060
1050 sp=sp+1:s(sp)=v
1060 WEND
1070 ln=ln-1:RETURN
81. 81
750 REM Execute Commands
760 ln=0:w$(ln)=x$:er=0
770 GOSUB 780:w$="":GOTO 690
780 p(ln)=1:ff(ln)=0:df(ln)=0
790 WHILE MID$(w$(ln),p(ln),1)<>CHR$(13)
800 class=ASC(MID$(w$(ln),p(ln),1)):p(ln)=p(ln)+1
810 IF class<>0 THEN 920
820 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
830 IF ff(ln)=0 OR word=37 OR word=39 OR word=40 THEN 860
840 IF word=5 OR word=32 THEN WHILE ASC(MID$(w$(ln),p(ln),1))<>4:p(ln)=p(ln)+1:WEND:p(ln)=p(ln)+1
850 GOTO 1060
860 IF sp+dsp(word,1)<-1 OR sp+dsp(word,1)>spmax THEN er=1:GOTO 1070
870 IF sp-dsp(word,0)<-1 THEN er=2:GOTO 1070
880 sp=sp+dsp(word,1)
890 IF word <43 THEN ON word+1 GOSUB
1090,1110,1120,1130,1140,1150,1160,1170,1180,1190,1200,1210,1220,1230,1240,1250,1260,1270,1280,1290
,1300,1310,1320,1330,1340,1350,1360,1370,1380,1390,1400,1410,1420,1430,1440,1450,1460,1470,1480,149
0,1500,1510,1520
900 IF word>42 THEN ON word-42 GOSUB
1530,1540,1550,1560,1570,1580,1590,1600,1610,1620,1630,1640,1650,1660,1670,1680
910 IF er=0 THEN 1060 ELSE 1070
920 IF class<>1 THEN 970
930 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
940 IF ff(ln) THEN 1060
950 IF ln<34 THEN ln=ln+1:w$(ln)=uvex$(word) ELSE er=7:RETURN
960 GOSUB 780:IF ln=0 OR er=0 THEN 1060 ELSE RETURN
970 IF class<>2 THEN 1010
980 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
990 IF ff(ln) THEN 1060
1000 sp=sp+1:s(sp)=@var(word):GOTO 1060
1010 IF class<>3 THEN er=1:GOTO 1070
1020 p=p(ln):WHILE ASC(MID$(w$(ln),p,1))<>4:p=p+1:WEND
1030 v=VAL(MID$(w$(ln),p(ln),p-p(ln)+1)):p(ln)=p+1
1040 IF ff(ln) THEN 1060
1050 sp=sp+1:s(sp)=v
1060 WEND
1070 ln=ln-1:RETURN
these are our execution classes
0 = primitives
1 = user defined word
2 = user defined variable
3 = numbers
82. 82
750 REM Execute Commands
760 ln=0:w$(ln)=x$:er=0
770 GOSUB 780:w$="":GOTO 690
780 p(ln)=1:ff(ln)=0:df(ln)=0
790 WHILE MID$(w$(ln),p(ln),1)<>CHR$(13)
800 class=ASC(MID$(w$(ln),p(ln),1)):p(ln)=p(ln)+1
810 IF class<>0 THEN 920
820 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
830 IF ff(ln)=0 OR word=37 OR word=39 OR word=40 THEN 860
840 IF word=5 OR word=32 THEN WHILE ASC(MID$(w$(ln),p(ln),1))<>4:p(ln)=p(ln)+1:WEND:p(ln)=p(ln)+1
850 GOTO 1060
860 IF sp+dsp(word,1)<-1 OR sp+dsp(word,1)>spmax THEN er=1:GOTO 1070
870 IF sp-dsp(word,0)<-1 THEN er=2:GOTO 1070
880 sp=sp+dsp(word,1)
890 IF word <43 THEN ON word+1 GOSUB
1090,1110,1120,1130,1140,1150,1160,1170,1180,1190,1200,1210,1220,1230,1240,1250,1260,1270,1280,1290
,1300,1310,1320,1330,1340,1350,1360,1370,1380,1390,1400,1410,1420,1430,1440,1450,1460,1470,1480,149
0,1500,1510,1520
900 IF word>42 THEN ON word-42 GOSUB
1530,1540,1550,1560,1570,1580,1590,1600,1610,1620,1630,1640,1650,1660,1670,1680
910 IF er=0 THEN 1060 ELSE 1070
920 IF class<>1 THEN 970
930 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
940 IF ff(ln) THEN 1060
950 IF ln<34 THEN ln=ln+1:w$(ln)=uvex$(word) ELSE er=7:RETURN
960 GOSUB 780:IF ln=0 OR er=0 THEN 1060 ELSE RETURN
970 IF class<>2 THEN 1010
980 word=ASC(MID$(w$(ln),p(ln),1))-14:p(ln)=p(ln)+1
990 IF ff(ln) THEN 1060
1000 sp=sp+1:s(sp)=@var(word):GOTO 1060
1010 IF class<>3 THEN er=1:GOTO 1070
1020 p=p(ln):WHILE ASC(MID$(w$(ln),p,1))<>4:p=p+1:WEND
1030 v=VAL(MID$(w$(ln),p(ln),p-p(ln)+1)):p(ln)=p+1
1040 IF ff(ln) THEN 1060
1050 sp=sp+1:s(sp)=v
1060 WEND
1070 ln=ln-1:RETURN
these are computed GOSUBs
implementing a despatch table
to BASIC for each primitive
83. 83
1080 REM Command List
1090 temp!=s(sp+1):IF temp!<0 THEN temp!=temp!+65536
1100 POKE s(sp+2),temp!-256*INT(temp!/256):POKE s(sp+2)+1,INT(temp!/256):RETURN
1110 s(sp)=s(sp)*s(sp+1):RETURN
1120 s(sp)=s(sp)+s(sp+1):RETURN
1130 s(sp)=s(sp)-s(sp+1):RETURN
1140 PRINT s(sp+1);CHR$(8);:RETURN
1150 WHILE ASC(MID$(w$(ln),p(ln),1))<>4:PRINT MID$(w$(ln),p(ln),1);:p(ln)=p(ln)+1:WEND:p(ln)=p(ln)
+1:RETURN
1160 s(sp)=INT(s(sp)/s(sp+1)):RETURN
1170 temp=s(sp):s(sp)=INT(s(sp-1)/s(sp)):s(sp-1)=s(sp-1)-s(sp)*temp:RETURN
1180 s(sp)=(s(sp)<0):RETURN
1190 s(sp)=(s(sp)=0):RETURN
1200 s(sp)=(s(sp)<s(sp+1)):RETURN
1210 s(sp)=(s(sp)=s(sp+1)):RETURN
1220 s(sp)=(s(sp)>s(sp+1)):RETURN
1230 temp!=PEEK(s(sp+1))+256*PEEK(s(sp+1)+1):IF temp!>32767 THEN temp!
=temp!-65536:PRINTtemp!;CHR$(8);:RETURN ELSE PRINTtemp!;CHR$(8);:RETURN
1240 temp!=PEEK(s(sp))+256*PEEK(s(sp)+1):IF temp!>32767 THEN s(sp)=temp!-65536:RETURN ELSE
s(sp)=temp!:RETURN
1250 s(sp)=ABS(s(sp)):RETURN
1260 s(sp)=s(sp) AND s(sp+1):RETURN
1270 s(sp)=PEEK(s(sp)):RETURN
1280 PRINT:RETURN
1290 RETURN
1300 s(sp)=s(sp-1):RETURN
1310 PRINTCHR$(s(sp+1));:RETURN
1320 in$=INKEY$:IF in$="" THEN 1320 ELSE s(sp)=ASC(in$):RETURN
1330 s(sp)=MAX(s(sp),s(sp+1)):RETURN
1340 s(sp)=MIN(s(sp),s(sp+1)):RETURN
1350 s(sp)=-s(sp):RETURN
1360 s(sp)=s(sp) MOD s(sp+1):RETURN
1370 s(sp)=s(sp) OR s(sp+1):RETURN
1380 s(sp)=s(sp-2):RETURN
1390 PRINT" ";:RETURN
1400 PRINT USING "&";SPACE$(s(sp+1)-256*INT(s(sp+1)/256));:RETURN
1410 temp=s(sp):s(sp)=s(sp-1):s(sp-1)=temp:RETURN
1420 vrn=vrn+1:var(vrn)=s(sp+1):WHILE ASC(MID$(w$(ln),p(ln),1))<>4:var$(vrn)=var$(vrn)+MID$(w$
(ln),p(ln),1):p(ln)=p(ln)+1:WEND:p(ln)=p(ln)+1:RETURN
1430 s(sp)=s(sp) XOR s(sp+1):RETURN
1440 beg(ln)=p(ln):RETURN
1450 IF s(sp+1)=0 THEN p(ln)=beg(ln):RETURN ELSE RETURN
1460 IF s(sp+1)<>0 THEN RETURN ELSE ff(ln)=-1:RETURN
this is why switch statements exist
and why fallthrough is superior to break!
84. 84
that's probably more than
you ever wanted to know
about Forth or BASIC
or how I spent the 80s...
89. Kent Beck
89
eXtreme Programming
the birth of agile
Planning/feedback loops
Release plan
Code
Iteration plan
Acceptance test
Stand-up meeting
Pair negotiation
Unit test
Pair programming
Months
Weeks
Days
One day
Hours
Minutes
Seconds
By DonWells - Own work based on: XP-feedback.gif, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=27448045
92. 92
software considerations in airborne systems and equipment certification
RTCA DO-178B
Level Failure condition Objectives[5] Failure Rate
A Catastrophic 66 10−9/h
B Hazardous 65 10−7/h
C Major 57 10−5/h
D Minor 28 10−3/h
E No Effect 0 n/a
93. 93
evolutionary prototyping
the boehm spiral
Plan next Phase
Develop, verify
next-level product
Determine objectives,
alternatives, constraints
Evaluate alternatives
identify, resolve risks
Cummulative
cost
Review
Commitment
partition
Risk
analysis
Risk
analysis
Risk
analysis
Prototype 1 Prototype 2 Prototype 3
Operational
Prototype
Risk
analy-
sis
Requirements plan
life-cycle plan
Develop-
ment plan
Integration
and test
plan
Concept of
operation
Simulations, models, benchmarks
Software
requirements
Requirements
validation
Software
product
design
Design validation
and verification
Detailed
design
Code
Unit
test
Integration
and test
Acceptance
test
Implementation
97. 97
● digital identity
● messaging
● privacy
● trust
what we do
IDENTITY & TRUST IN MONITORED SPACES
THE VIRTUES OF PRIVACY BY DESIGN
Eleanor McHugh
Romek Szczesniak
99. 99
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
this is part of a proof of concept
for a digital identity system
it's part of a series of prototypes
evolved from a series of pure
BaSH scripts
all the techniques demonstrated
are applicable to any language
with either quick compilation
speed or fast interpreter launch
like golang or ruby
100. 100
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
Fair Use Exemptions Apply
101. 101
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
bangs allow scripts to be executable
102. 102
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
Environment Variables
for configuration
103. 103
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
Environment Variables
for configuration
104. 104
#!/bin/bash
# (c) 2017 Innovative Identity Solutions Limited
# all rights reserved
# This script defines addresses for a service infrastructure baselined at $BASEPORT."
#
# Usage:"
# export BASE_SERVICE=9000"
# source infrastructure.sh"
#
PORT=$BASE_SERVICE_PORT
ADDRESS=${SERVER_ADDRESS:=localhost}
export DOMAIN_SERVICE=$PORT
export DOMAIN_ADDRESS="$ADDRESS:$DOMAIN_SERVICE"
let PORT++
export LOGGING_SERVICE=$PORT
export LOGGING_ADDRESS="$ADDRESS:$LOGGING_SERVICE"
let PORT++
export SECURE_STORE_SERVICE=$PORT
export SECURE_STORE_ADDRESS="$ADDRESS:$SECURE_STORE_SERVICE"
let PORT++
export REGISTRY_SERVICE=$PORT
export REGISTRY_ADDRESS="$ADDRESS:$REGISTRY_SERVICE"
let PORT++
export ORACLE_SERVICE=$PORT
export ORACLE_ADDRESS="$ADDRESS:$ORACLE_SERVICE"
let PORT++
export BEAR_SERVICE=$PORT
export BEAR_ADDRESS="$ADDRESS:$BEAR_SERVICE"
let PORT++
export VAL_SERVICE=$PORT
export VAL_ADDRESS="$ADDRESS:$VAL_SERVICE"
BASE_SERVICE_PORT=$DOMAIN_SERVICE
Environment Variables
for static configuration
105. 105
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
command-line switches
for instance variables
106. 106
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
check for missing parameters
107. 107
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
now for some Ruby magic
108. 108
RUBY_DIR="$ROOT_DIR/ruby"
source scripts/basics.sh
# http_requests will print headers and response body along with stack dumps
# if the env variable DEBUG_HTTP_REQUESTS exists
ruby_web_client () {
local program=$1
ruby -e "
require 'json'
require '$CLIENTS_DIR/web'
$program
"
}
http_request () {
local version=$1
local method=$2
local address=$3
local endpoint=$4
local request_body=$5
ruby_web_client "
if ENV['DEBUG_HTTP_REQUESTS']
response = WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
puts 'REQUEST:'
puts $request_body
$request_body
end
puts 'RESPONSE:'
response.each_header do |k, v|
puts "#{k}: #{v}"
end
puts "body: #{response.body}"
puts
else
WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
$request_body
end rescue nil
end
"
}
where we keep our ruby scripts
some handy Bash functions
110. 110
DATA_DIR="$ROOT_DIR/data"
COMMANDS_DIR="$ROOT_DIR/commands"
MODELS_DIR="$ROOT_DIR/models"
detect_wsl () {
set -e
grep -qE "(Microsoft|WSL)" /proc/version &> /dev/null
}
UNIX_PROCESS_STARTUP_DELAY=1
WSL_PROCESS_STARTUP_DELAY=5
if detect_wsl ; then
wait_for_process_launch () {
sleep WSL_PROCESS_STARTUP_DELAY
}
else
wait_for_process_launch () {
sleep UNIX_PROCESS_STARTUP_DELAY
}
fi
heading () {
ruby -e "
require '$COMMANDS_DIR/console'
Console.heading '$1'
"
}
subheading () {
ruby -e "
require '$COMMANDS_DIR/console'
Console.subheading '$1'
"
}
print_cic_statement() {
ruby "$COMMANDS_DIR/cic_banner.rb"
}
finished () {
heading "DONE"
}
WSL can take a long time
to launch a Linux process
so we check for it and add
a longer startup delay for
our system to boot
111. 111
DATA_DIR="$ROOT_DIR/data"
COMMANDS_DIR="$ROOT_DIR/commands"
MODELS_DIR="$ROOT_DIR/models"
detect_wsl () {
set -e
grep -qE "(Microsoft|WSL)" /proc/version &> /dev/null
}
UNIX_PROCESS_STARTUP_DELAY=1
WSL_PROCESS_STARTUP_DELAY=5
if detect_wsl ; then
wait_for_process_launch () {
sleep WSL_PROCESS_STARTUP_DELAY
}
else
wait_for_process_launch () {
sleep UNIX_PROCESS_STARTUP_DELAY
}
fi
heading () {
ruby -e "
require '$COMMANDS_DIR/console'
Console.heading '$1'
"
}
subheading () {
ruby -e "
require '$COMMANDS_DIR/console'
Console.subheading '$1'
"
}
print_cic_statement() {
ruby "$COMMANDS_DIR/cic_banner.rb"
}
finished () {
heading "DONE"
}
each of these is a string
containing Ruby code to
be executed by the
interpreter
112. 112
RUBY_DIR="$ROOT_DIR/ruby"
source scripts/basics.sh
# http_requests will print headers and response body along with stack dumps
# if the env variable DEBUG_HTTP_REQUESTS exists
ruby_web_client () {
local program=$1
ruby -e "
require 'json'
require '$CLIENTS_DIR/web'
$program
"
}
http_request () {
local version=$1
local method=$2
local address=$3
local endpoint=$4
local request_body=$5
ruby_web_client "
if ENV['DEBUG_HTTP_REQUESTS']
response = WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
puts 'REQUEST:'
puts $request_body
$request_body
end
puts 'RESPONSE:'
response.each_header do |k, v|
puts "#{k}: #{v}"
end
puts "body: #{response.body}"
puts
else
WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
$request_body
end rescue nil
end
"
}
use local to give shell function
parameters meaningful names
113. 113
RUBY_DIR="$ROOT_DIR/ruby"
source scripts/basics.sh
# http_requests will print headers and response body along with stack dumps
# if the env variable DEBUG_HTTP_REQUESTS exists
ruby_web_client () {
local program=$1
ruby -e "
require 'json'
require '$CLIENTS_DIR/web'
$program
"
}
http_request () {
local version=$1
local method=$2
local address=$3
local endpoint=$4
local request_body=$5
ruby_web_client "
if ENV['DEBUG_HTTP_REQUESTS']
response = WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
puts 'REQUEST:'
puts $request_body
$request_body
end
puts 'RESPONSE:'
response.each_header do |k, v|
puts "#{k}: #{v}"
end
puts "body: #{response.body}"
puts
else
WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
$request_body
end rescue nil
end
"
}
shell variables are strings
this allows us to pass in a
ruby program as a string
which will then be injected
into this string as a text
replacement
114. 114
RUBY_DIR="$ROOT_DIR/ruby"
source scripts/basics.sh
# http_requests will print headers and response body along with stack dumps
# if the env variable DEBUG_HTTP_REQUESTS exists
ruby_web_client () {
local program=$1
ruby -e "
require 'json'
require '$CLIENTS_DIR/web'
$program
"
}
http_request () {
local version=$1
local method=$2
local address=$3
local endpoint=$4
local request_body=$5
ruby_web_client "
if ENV['DEBUG_HTTP_REQUESTS']
response = WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
puts 'REQUEST:'
puts $request_body
$request_body
end
puts 'RESPONSE:'
response.each_header do |k, v|
puts "#{k}: #{v}"
end
puts "body: #{response.body}"
puts
else
WebClient.$method($version, '$address', endpoint = $endpoint) do |req|
$request_body
end rescue nil
end
"
}
here we use it to make an
HTTP call using our Ruby
WebClient object
115. 115
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
launch our service network
117. 117
ROOT_DIR=`pwd`
REGISTRY_DIR="$ROOT_DIR/registry"
COMMANDS_DIR="$ROOT_DIR/commands"
MODELS_DIR="$ROOT_DIR/models"
create_registry () {
subheading "create registry"
rm -Rf "$REGISTRY_DIR"
mkdir -p "$REGISTRY_DIR"
pushd "$REGISTRY_DIR"
ruby -e "
require '$MODELS_DIR/oracle'
require '$COMMANDS_DIR/encryption'
Console.section 'Create $REGISTRY_DIR/Oracle.db' do
oracle = Models::Oracle.new
Console.section 'Create User Zero' do
key = Encryption::create_rsa_key filename: 'keyzero.pem', save_to_disk: true
digest = OpenSSL::Digest::SHA256.new(Base64.encode64 key.to_pem)
oracle.keys.insert ID: 0,
Label: '::1',
Key: key.public_key.to_pem,
Checksum: ENV['USE_AMALGALITE'] ? digest : digest.to_s
Console.display_records oracle, :Keys
end
end
"
popd
}
create_user_database () {
local database_dir="$REGISTRY_DIR/$1"
local selfie="$ROOT_DIR/$2"
heading "create user database: $database_dir"
mkdir -p "$database_dir"
pushd "$database_dir"
ln -s "$REGISTRY_DIR/oracle.db" "$database_dir"
ln -s "$selfie" "$database_dir"
popd "$database_dir"
}
we can directly use Ruby's
OpenSSL wrapper as well
as our own API over that
118. 118
ROOT_DIR=`pwd`
REGISTRY_DIR="$ROOT_DIR/registry"
COMMANDS_DIR="$ROOT_DIR/commands"
MODELS_DIR="$ROOT_DIR/models"
create_registry () {
subheading "create registry"
rm -Rf "$REGISTRY_DIR"
mkdir -p "$REGISTRY_DIR"
pushd "$REGISTRY_DIR"
ruby -e "
require '$MODELS_DIR/oracle'
require '$COMMANDS_DIR/encryption'
Console.section 'Create $REGISTRY_DIR/Oracle.db' do
oracle = Models::Oracle.new
Console.section 'Create User Zero' do
key = Encryption::create_rsa_key filename: 'keyzero.pem', save_to_disk: true
digest = OpenSSL::Digest::SHA256.new(Base64.encode64 key.to_pem)
oracle.keys.insert ID: 0,
Label: '::1',
Key: key.public_key.to_pem,
Checksum: ENV['USE_AMALGALITE'] ? digest : digest.to_s
Console.display_records oracle, :Keys
end
end
"
popd
}
create_user_database () {
local database_dir="$REGISTRY_DIR/$1"
local selfie="$ROOT_DIR/$2"
heading "create user database: $database_dir"
mkdir -p "$database_dir"
pushd "$database_dir"
ln -s "$REGISTRY_DIR/oracle.db" "$database_dir"
ln -s "$selfie" "$database_dir"
popd "$database_dir"
}
we're also able to use
SQLite via Amalgalite
which compiles it as an
in-process extension
119. 119
#!/bin/bash
# (c) 2018 Innovative Identity Solutions Limited
# all rights reserved
source scripts/ruby_web_api.sh
print_cic_statement
BASE_SERVICE_PORT=9000
source ./infrastructure.sh
while getopts b:v:sh option
do
case "${option}" in
h) echo
echo "This script launches the infrastructure for user registration and interaction."
echo "For each user and the oracle it creates a running web service."
echo "It initialises the database when it runs."
echo
echo "Usage:"
echo " ./demo.sh -b bearer-name -v validator-name"
echo " ./demo.sh -h"
echo " ./demo.sh -s"
exit 0
;;
b) BEARER=${OPTARG};;
v) VALIDATOR=${OPTARG};;
s) shutdown $DOMAIN_ADDRESS;;
esac
done
if [ -z ${BEARER+x} ]; then
echo "specify bearer name with -b";
exit 1
fi
if [ -z ${VALIDATOR+x} ]; then
echo "specify validator name with -v";
exit 2
fi
scripts/startup.sh $BASE_SERVICE_PORT
heading "Launching bearer devices"
launch_bearer $BEAR_SERVICE $BEARER "bear.jpg"
launch_bearer $VAL_SERVICE $VALIDATOR "val.jpg"
to create Ruby web services
120. 120
source "$SCRIPTS_DIR/ruby_web_client.sh"
launch_ruby_service () {
local service=$1
local address=$2
local directory="$REGISTRY_DIR$3"
local command_key=$4
pushd "$directory"
ADDRESS="$address" COMMAND_KEY="$command_key" ruby "$SERVICES_DIR/$service""_service.rb" &
wait_for_process_launch
popd
}
launch_service () {
local service=$1
local address=$2
local command_key=$3
local service_name="$service""_service"
http_request :v1 post $DOMAIN_ADDRESS :A "
req.set_form_data name: '$service_name', address: '$address'"
launch_ruby_service "$service" "$address" "" "$command_key"
}
halt_service () {
local version=$1
local address=$2
local command_key=$3
http_request :v1 shutdown $address nil "req.body = '$command_key'"
}
launch_bearer () {
local port="$1"
local name="$2"
local selfie="$3"
local command_key="$4"
create_user_database "$name" "$selfie"
pushd "$REGISTRY_DIR/$name"
PORT="$port" COMMAND_KEY="$command_key" ruby "$SERVICES_DIR/bearer_service.rb" &
sleep 1
popd
}
we need this to make web requests
121. 121
source "$SCRIPTS_DIR/ruby_web_client.sh"
launch_ruby_service () {
local service=$1
local address=$2
local directory="$REGISTRY_DIR$3"
local command_key=$4
pushd "$directory"
ADDRESS="$address" COMMAND_KEY="$command_key" ruby "$SERVICES_DIR/$service""_service.rb" &
wait_for_process_launch
popd
}
launch_service () {
local service=$1
local address=$2
local command_key=$3
local service_name="$service""_service"
http_request :v1 post $DOMAIN_ADDRESS :A "
req.set_form_data name: '$service_name', address: '$address'"
launch_ruby_service "$service" "$address" "" "$command_key"
}
halt_service () {
local version=$1
local address=$2
local command_key=$3
http_request :v1 shutdown $address nil "req.body = '$command_key'"
}
launch_bearer () {
local port="$1"
local name="$2"
local selfie="$3"
local command_key="$4"
create_user_database "$name" "$selfie"
pushd "$REGISTRY_DIR/$name"
PORT="$port" COMMAND_KEY="$command_key" ruby "$SERVICES_DIR/bearer_service.rb" &
sleep 1
popd
}
we can also start Ruby programs
with dynamically set Environment
Variables to configure them
122. 122
source "$SCRIPTS_DIR/ruby_web_client.sh"
launch_ruby_service () {
local service=$1
local address=$2
local directory="$REGISTRY_DIR$3"
local command_key=$4
pushd "$directory"
ADDRESS="$address" COMMAND_KEY="$command_key" ruby "$SERVICES_DIR/$service""_service.rb" &
wait_for_process_launch
popd
}
launch_service () {
local service=$1
local address=$2
local command_key=$3
local service_name="$service""_service"
http_request :v1 post $DOMAIN_ADDRESS :A "
req.set_form_data name: '$service_name', address: '$address'"
launch_ruby_service "$service" "$address" "" "$command_key"
}
halt_service () {
local version=$1
local address=$2
local command_key=$3
http_request :v1 shutdown $address nil "req.body = '$command_key'"
}
launch_bearer () {
local port="$1"
local name="$2"
local selfie="$3"
local command_key="$4"
create_user_database "$name" "$selfie"
pushd "$REGISTRY_DIR/$name"
PORT="$port" COMMAND_KEY="$command_key" ruby "$SERVICES_DIR/bearer_service.rb" &
sleep 1
popd
}
for each credential bearer in our
system we can launch a custom
ruby process in the background
to handle their behaviour
123. 123
known by their works
Love is a poet, love sings the songs
Pointing his
fi
nger you follow along
Voices are calling,
The monster wants out of you
Paws you and claws you, you try not to fall
- The Beast, Concrete Blonde
124. 124
● I have mixed feelings
on patents
● I filed some, and selling
the IP paid for several
years of research
● it also means people
can see what I do
● but the process can be
complicated
● and many patents suck
The Agony & The Ecstasy
125. 125
● original inventions
● not preexisting in
nature - except biotech
● no maths - unless it's
software and you're in
the US
● must have a concrete
implementation
● cannot have been
published before
What Can We Patent?
Dell? Mac? WTF?!?!
126. 126
● inventors have to publish
how their invention works
- except Marconi?
● the patent provides a
fixed term monopoly
● but the description of the
invention is in the public
domain
● only granted claims are
enforceable
Why Should We Patent?
Nikola Tesla!