Execute Brain****/J: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
m (Fixed syntax highlighting.)
 
(2 intermediate revisions by one other user not shown)
Line 1: Line 1:
<syntaxhighlight lang="j">require'general/misc/prompt' NB. was require'misc' in j602
<lang J>require'misc'


NB. operations
NB. operations
Line 17: Line 17:


NB. buffered io
NB. buffered io
require'misc'
OBUF=:IBUF=:''
OBUF=:IBUF=:''
flush=:] [ 3 :0
flush=:] [ 3 :0
Line 47: Line 46:
right=: [:/:~/ [:;"1 (#:1 2) <@I.@E."1/ nesting
right=: [:/:~/ [:;"1 (#:1 2) <@I.@E."1/ nesting
next=: (1 + 1 { ])`1:`]}
next=: (1 + 1 { ])`1:`]}
traceNext=: (1 + 1 { ])`1:`]} ([ smoutput) NB. version of next for debugging crashes


NB. interpreter
NB. interpreter
OPCODES=: '><+-.,[]'
OPCODES=: '><+-.,[]'
OPS=: inc`dec`INC`DEC`OUT`ACC`FIXME`FIXME`NOP
OPS=: inc`dec`INC`DEC`OUT`ACC`FIXME`FIXME`NOP
PASS1=: OPS {~ OPCODES i. ([-.-.)&OPCODES
compile=:3 :0
compile=:3 :0
src=. ([-.-.)&OPCODES y
src=. ([-.-.)&OPCODES y
Line 64: Line 61:
step=: [ next ] evoke~ pc { [
step=: [ next ] evoke~ pc { [


trace=: [: flush step^:(pc < #@[)^:a:
run=: [: flush step^:(pc < #@[)^:_
run=: [: flush step^:(pc < #@[)^:_


execute=: 2 }. compile@] run 0 0,0: :[</lang>
execute=: 2 }. compile@] run 0 0,0: :[</syntaxhighlight>


Here is hello world:
Here is hello world:


<lang J>hello=: 0 :0
<syntaxhighlight lang="j">hello=: 0 :0
+++++ +++++ initialize counter (cell #0) to 10
+++++ +++++ initialize counter (cell #0) to 10
[ use loop to set the next four cells to 70/100/30/10
[ use loop to set the next four cells to 70/100/30/10
Line 93: Line 89:
> + . print '!'
> + . print '!'
> . print '\n'
> . print '\n'
)</lang>
)</syntaxhighlight>


Example use:
Example use:
<lang J> execute hello
<syntaxhighlight lang="j"> execute hello
Hello World!
Hello World!


0 87 100 33 10</lang>
0 87 100 33 10</syntaxhighlight>


execute compiles the program, generates an initial state of a blank tape with the data and instruction pointers both being zero, runs the program and then returns as its result the final state of the tape.
execute compiles the program, generates an initial state of a blank tape with the data and instruction pointers both being zero, runs the program and then returns as its result the final state of the tape.
Line 107: Line 103:
Or, here is the addDigit program from wikipedia, with explicit compilation and run as separate operations:
Or, here is the addDigit program from wikipedia, with explicit compilation and run as separate operations:


<lang J> (compile ',>++++++[<-------->-],[<+>-]<.') run 0 0
<syntaxhighlight lang="j"> (compile ',>++++++[<-------->-],[<+>-]<.') run 0 0


23
23
5
5
0 30 53 0</lang>
0 30 53 0</syntaxhighlight>


Here, 2 and 3 were provided as input, and 5 was displayed as the result.
Here, 2 and 3 were provided as input, and 5 was displayed as the result.
Line 119: Line 115:
Note this implementation encodes branch targets into the compiled code, and that <code>compile</code> assumes that <code>[</code> and <code>]</code> are balanced. Unbalanced brackets are not supported (and will cause the first instruction of the program to be replaced with an unspecified branch instruction).
Note this implementation encodes branch targets into the compiled code, and that <code>compile</code> assumes that <code>[</code> and <code>]</code> are balanced. Unbalanced brackets are not supported (and will cause the first instruction of the program to be replaced with an unspecified branch instruction).


You can also use <code>trace</code>, instead of <code>run</code>. <code>trace</code> takes the same arguments as run, but will return every state the tape went through, instead of the final state. This is mostly interested for understanding the behavior of small programs.
You can also use <code>trace=: [: flush step^:(pc < #@[)^:a:</code>, instead of <code>run</code>. <code>trace</code> takes the same arguments as run, but will return every state the tape went through, instead of the final state. This is mostly interested for understanding the behavior of small programs.


When investigating a bug, you can use:
When investigating a bug, you can use:


<syntaxhighlight lang="j">next=: (1 + 1 { ])`1:`]} ([ smoutput)</syntaxhighlight>
<lang J>next=: traceNext</lang>


This means that programs when run (or traced) will display each tape state after it has been generated, which means that when the program crashes you will have displayed a tape state shortly before the crash.
This means that programs when run (or traced) will display each tape state after it has been generated, which means that when the program crashes you will have displayed a tape state shortly before the crash.

Latest revision as of 11:28, 1 September 2022

require'general/misc/prompt' NB. was require'misc' in j602

NB. operations
inc=: 1 0 +/@,: ]
dec=: _1 0 +/@,: ]
INC=: ] +/@,: -@>:@ptr {. 1:
DEC=: ] +/@,: -@>:@ptr {. _1:
OUT=:  ][ ptr output@{ extend
ACC=: ] input`ptr`]} extend
LJ=: JMP^:zero
RJ=: JMP^:(0=zero)

NB. utilities
ptr=: 2 + 0 { ]
pc=: 1 { ]
extend=: ] +/@,: 0,ptr {. 0: NB. tape must be long enough for i/o

NB. buffered io
OBUF=:IBUF=:''
flush=:] [ 3 :0
  1!:2&2 OBUF
  OBUF=:''
)
output=:3 :0@]
  OBUF=:OBUF,7 u:4 u:y
)
input=:3 :0@]
  if.0=#IBUF do.
    IBUF=:prompt flush ''
  end.
  3 u: t [ IBUF=:}.IBUF [ t=.{.IBUF
)   


NB. program sequencing
JMP=: [`1:`]}
fixL=:3 :0"0
  {. y&LJ`''
)
fixR=:3 :0"0
  {. y&RJ`''
)
zero=: (0 = ptr { ]) ::1:
left=: I.@:=&'['
nesting=: [: (~. </ ]) 0,1 _1 0 +/\@:{~ '[]' i. ]
right=: [:/:~/ [:;"1 (#:1 2) <@I.@E."1/ nesting
next=: (1 + 1 { ])`1:`]}

NB. interpreter
OPCODES=: '><+-.,[]'
OPS=: inc`dec`INC`DEC`OUT`ACC`FIXME`FIXME`NOP
compile=:3 :0
  src=. ([-.-.)&OPCODES y
  obj=. OPS {~ OPCODES i. src
  L=. left src
  R=. right src
  (fixR L) R} (fixL R) L} obj
)

evoke=:4 :'x`:6 y'
step=: [ next ] evoke~ pc { [

run=: [: flush step^:(pc < #@[)^:_

execute=: 2 }. compile@] run 0 0,0: :[

Here is hello world:

hello=: 0 :0
+++++ +++++             initialize counter (cell #0) to 10
[                       use loop to set the next four cells to 70/100/30/10
    > +++++ ++              add  7 to cell #1
    > +++++ +++++           add 10 to cell #2 
    > +++                   add  3 to cell #3
    > +                     add  1 to cell #4
    <<<< -                  decrement counter (cell #0)
]                   
> ++ .                  print 'H'
> + .                   print 'e'
+++++ ++ .              print 'l'
.                       print 'l'
+++ .                   print 'o'
>++ .                   print ' '
<< +++++ +++++ +++++ .  print 'W'
> .                     print 'o'
+++ .                   print 'r'
----- - .               print 'l'
----- --- .             print 'd'
> + .                   print '!'
> .                     print '\n'
)

Example use:

   execute hello
Hello World!

0 87 100 33 10

execute compiles the program, generates an initial state of a blank tape with the data and instruction pointers both being zero, runs the program and then returns as its result the final state of the tape.

The first two numbers in that result are the data and code pointers. The following numbers represent the tape itself. In the above example, 0 is the data pointer, and the data cell selected by this data pointer has the value 100.

Or, here is the addDigit program from wikipedia, with explicit compilation and run as separate operations:

   (compile ',>++++++[<-------->-],[<+>-]<.') run 0 0 

23
5
0 30 53 0

Here, 2 and 3 were provided as input, and 5 was displayed as the result.

The buffered I/O implementation here prompts the user for a line of code whenever a character is needed and none has been provided. The prompt will be a blank line. Output is flushed before prompting the user and when completing a program. If a program terminates prematurely (because of an interrupt or a bug), unflushed output will appear in the variable OBUF.

Note this implementation encodes branch targets into the compiled code, and that compile assumes that [ and ] are balanced. Unbalanced brackets are not supported (and will cause the first instruction of the program to be replaced with an unspecified branch instruction).

You can also use trace=: [: flush step^:(pc < #@[)^:a:, instead of run. trace takes the same arguments as run, but will return every state the tape went through, instead of the final state. This is mostly interested for understanding the behavior of small programs.

When investigating a bug, you can use:

next=: (1 + 1 { ])`1:`]} ([ smoutput)

This means that programs when run (or traced) will display each tape state after it has been generated, which means that when the program crashes you will have displayed a tape state shortly before the crash.

All operations and utility functions take tape state as their argument. Operations produce the new tape state as their result. The next(and traceNext) function advances the code pointer to the next instruction. The step function expects compiled code for its left argument and tape state for its right argument and executes one instruction, advancing the code pointer to the next instruction.