~kylep/visual-cell-lang

274b09c1e619bafdc0c70a8b47bbbc96827540a4 — Kyle Perik 1 year, 7 months ago 88613ec
Update documentation and add link
3 files changed, 71 insertions(+), 49 deletions(-)

M docs/lang.md
A examples/circle.json
M src/main.js
M docs/lang.md => docs/lang.md +68 -30
@@ 1,37 1,40 @@
# Domain Specific box lang

The code which is defined within boxes runs on a simple stack based language.

Code is defined in the following recursive structure:

```js
{
  codes: ['list', 'of', 'codes'],
  branches: {
    a: { codes, branches },
    b: { codes, branches },
  },
}
```
The code which is defined within boxes runs on a stack based forth-like language.

Also I added a simple parser to make it easier to write. (run with `parse(code)`)
To define a cell, this basic template is used:

```
list of codes
name
a b -> c d

code here

def a:
  code for a procedure
def b:
  codes for b procedure
  def c:
    sub procedure
  input handler code
```

When executed, the uppermost codes are executed from start to finish.
Branches can only be executed from within a direct parent's code, no other control flow exists.
See [Control Flow](#control-flow) for more details.
Line one is the name of the cell.
Line two defines the names of the inputs and outputs, separated by ` -> `.
All text after line two is the code which defines your program.

You can define functions with `def name:`, anything indented 2 spaces
after this definition is run.

Anything not within a definition is run upon right away.

Any functions which match the name of an input is run when an event is received.

Currently the implementation is a very thin layer over basic js operations, meaning
all of the oddities of js come with it. I hope to develop something more standard
to make programs more portable.

Be sure to [check out examples here](https://git.sr.ht/~kylep/visual-cell-lang/tree/master/item/examples/). If you come up with anything interesting, please share with me so I can include in as well.

## Opcodes

Note that these are still unstable and can change.

### I/O

#### out


@@ 50,17 53,29 @@ Pushes the given input value onto the stack

### Logic and Data types

#### equ
#### equ, lth, gth

*( a b -- result )*

Checks for equality and pushes the boolean onto the stack.
Checks for equality, less than or greather than and pushes the boolean onto the stack.

#### int
#### int, flt

*( a -- result )*

Runs `parseInt` on the top of the stack.
Runs `parseInt` or `parseFloat` on the top of the stack.

#### add, sub, div, mul

*( a b -- c )*

Given two numbers, adds, subtracts, multiplies or divides, and pushes the result onto the stack.

#### flr

*( n -- result )*

Runs `Math.floor` on a number and returns the result

#### vec



@@ 72,10 87,22 @@ Pushes a Vec into the stack, given an x and y cooridnate.

#### vecadd

*(a b -- result)*
*( a b -- result )*

Adds two vectors and pushes the result onto the stack.

#### vecmul

*( a n -- result )*

Multiplies a vector by a number and pushes the result onto the stack.

#### vecx, vecy

*( a -- result )*

Gets the x or y value from the vector, pushing it onto the stack.

#### dup

*( a -- a a )*


@@ 103,6 130,12 @@ Stores a given `value` on a given `key`.

Pushes the value for a given `key` onto the stack.

#### arr

*( key -- )*

Given a key, initializes an array for that key.

#### que

*( value key -- )*


@@ 137,10 170,15 @@ Pushes the length of a list onto the stack.

Control flow is pretty limited at the moment.

There is only one opcode which can jump, and that is essentially a conditional
procedure call. Procedures, which are called branches, are portions of code
Procedures, which are called branches, are portions of code
directly under the current code path.

#### jmp

*( branch -- )*

Jumps to a named branch, then returns when finished.

#### jcn

*( condition branch -- )*

A examples/circle.json => examples/circle.json +1 -0
@@ 0,0 1,1 @@
{"boxes":[{"width":5,"height":3,"inputs":{},"outputs":["t"],"clicked":false,"label":"","pos":{"x":8,"y":3},"type":"tick","js":true,"data":{}},{"width":20,"height":20,"inputs":{},"outputs":[],"clicked":false,"label":"","pos":{"x":3,"y":19},"type":"screen","js":true,"data":{},"triggered":false,"failed":false},{"width":5,"height":3,"inputs":{"v":{"codes":["val","#tail","que","#tail","len","#number","get","gth","#pop","jcn","#tail","len","#number","get","gth","#pop","jcn"],"branches":{"pop":{"codes":["#tail","pop","#v","out"],"branches":{}}}},"n":{"codes":["val","#number","kep"],"branches":{}}},"outputs":["v"],"clicked":false,"label":"","pos":{"x":13,"y":14},"type":"delay","data":{"tail":[{"x":0.6180000000000088,"y":19.901999999999997},{"x":1.2360000000000175,"y":19.804},{"x":1.8540000000000265,"y":19.705999999999996},{"x":2.472000000000035,"y":19.607999999999993},{"x":3.090000000000044,"y":19.50999999999999},{"x":3.708000000000053,"y":19.411999999999992},{"x":4.326000000000062,"y":19.31399999999999},{"x":4.94400000000007,"y":19.215999999999987},{"x":5.562000000000079,"y":19.117999999999988},{"x":6.18000000000008,"y":19.01999999999996},{"x":6.7380000000000875,"y":18.735999999999954},{"x":7.296000000000094,"y":18.451999999999952},{"x":7.854000000000103,"y":18.16799999999995},{"x":8.412000000000111,"y":17.883999999999943},{"x":8.97000000000012,"y":17.59999999999994},{"x":9.528000000000127,"y":17.315999999999935},{"x":10.086000000000135,"y":17.031999999999933},{"x":10.644000000000142,"y":16.747999999999927},{"x":11.202000000000151,"y":16.463999999999924}],"number":30},"js":false,"triggered":false,"failed":false,"init":{"codes":["#tail","arr","#30","int","#number","kep"],"branches":{"v":{"codes":["val","#tail","que","#tail","len","#number","get","gth","#pop","jcn","#tail","len","#number","get","gth","#pop","jcn"],"branches":{"pop":{"codes":["#tail","pop","#v","out"],"branches":{}}}},"n":{"codes":["val","#number","kep"],"branches":{}}}},"code":"delay\nv n -> v\n\n#tail arr\n#30 int #number kep\n\ndef v:\n  val #tail que\n  #tail len #number get gth\n  #pop jcn\n  #tail len #number get gth\n  #pop jcn\n  def pop:\n    #tail pop #v out\n  \n\ndef n:\n  val #number kep"},{"width":5,"height":3,"inputs":{},"outputs":["v"],"clicked":false,"label":50,"pos":{"x":16,"y":5},"type":"literal","literalValue":50,"js":true,"data":{"value":50}},{"width":5,"height":3,"inputs":{},"outputs":["v"],"clicked":false,"label":70,"pos":{"x":24,"y":6},"type":"literal","literalValue":70,"js":true,"data":{"value":70}},{"width":5,"height":3,"inputs":{},"outputs":["v"],"clicked":false,"label":10,"pos":{"x":21,"y":3},"type":"literal","literalValue":10,"js":true,"data":{"value":10}},{"width":5,"height":3,"inputs":{"i":{"codes":["#counter","get","#.1","flt","add","#20","int","mod","dup","#counter","kep","dup","#sinat","jmp","swp","#5","int","add","#sinat","jmp","vec","#20","int","vecmul","#p","out"],"branches":{"sinat":{"codes":["dup","dup","#1","int","add","flr","#20","int","mod","#sin","at","swp","#1","int","mod","mul","swp","dup","flr","#20","int","mod","#sin","at","swp","#1","int","mod","#1","int","swp","sub","mul","add"],"branches":{}}}}},"outputs":["p"],"clicked":false,"label":"","pos":{"x":9,"y":9},"type":"walkCircle","init":{"codes":["#0","int","#counter","kep","#sin","arr","#0.000","flt","#sin","que","#0.309","flt","#sin","que","#0.588","flt","#sin","que","#0.809","flt","#sin","que","#0.951","flt","#sin","que","#1.000","flt","#sin","que","#0.951","flt","#sin","que","#0.809","flt","#sin","que","#0.588","flt","#sin","que","#0.309","flt","#sin","que","#0.000","flt","#sin","que","#-0.309","flt","#sin","que","#-0.588","flt","#sin","que","#-0.809","flt","#sin","que","#-0.951","flt","#sin","que","#-1.000","flt","#sin","que","#-0.951","flt","#sin","que","#-0.809","flt","#sin","que","#-0.588","flt","#sin","que","#-0.309","flt","#sin","que"],"branches":{"i":{"codes":["#counter","get","#.1","flt","add","#20","int","mod","dup","#counter","kep","dup","#sinat","jmp","swp","#5","int","add","#sinat","jmp","vec","#20","int","vecmul","#p","out"],"branches":{"sinat":{"codes":["dup","dup","#1","int","add","flr","#20","int","mod","#sin","at","swp","#1","int","mod","mul","swp","dup","flr","#20","int","mod","#sin","at","swp","#1","int","mod","#1","int","swp","sub","mul","add"],"branches":{}}}}}},"code":"walkCircle\ni -> p\n\n#0 int #counter kep\n\n#sin arr #0.000 flt #sin que #0.309 flt #sin que #0.588 flt #sin que #0.809 flt #sin que #0.951 flt #sin que #1.000 flt #sin que #0.951 flt #sin que #0.809 flt #sin que #0.588 flt #sin que #0.309 flt #sin que #0.000 flt #sin que #-0.309 flt #sin que #-0.588 flt #sin que #-0.809 flt #sin que #-0.951 flt #sin que #-1.000 flt #sin que #-0.951 flt #sin que #-0.809 flt #sin que #-0.588 flt #sin que #-0.309 flt #sin que\n\ndef i:\n  #counter get\n  #.1 flt add\n  #20 int mod\n  dup #counter kep\n  dup #sinat jmp\n  swp #5 int add\n  #sinat jmp\n  vec\n  #20 int\n  vecmul\n  #p out\n  \n  def sinat:\n    dup dup\n    #1 int add\n    flr #20 int mod\n    #sin at\n    swp #1 int mod mul\n    \n    swp dup\n    flr #20 int mod\n    #sin at\n    swp #1 int mod\n    #1 int swp sub mul\n    add","js":false,"data":{"counter":2.10000000000003,"sin":[0,0.309,0.588,0.809,0.951,1,0.951,0.809,0.588,0.309,0,-0.309,-0.588,-0.809,-0.951,-1,-0.951,-0.809,-0.588,-0.309]},"triggered":false}],"connections":[{"startOutput":"v","endInput":"c","start":{"x":15.5,"y":17},"end":{"x":16.333333333333332,"y":19},"startBoxIndex":2,"endBoxIndex":1},{"startOutput":"v","endInput":"n","start":{"x":18.5,"y":8},"end":{"x":16.333333333333332,"y":14},"startBoxIndex":3,"endBoxIndex":2},{"startOutput":"v","endInput":"n","start":{"x":26.5,"y":9},"end":{"x":16.333333333333332,"y":14},"startBoxIndex":4,"endBoxIndex":2},{"startOutput":"v","endInput":"n","start":{"x":23.5,"y":6},"end":{"x":16.333333333333332,"y":14},"startBoxIndex":5,"endBoxIndex":2},{"startOutput":"p","endInput":"p","start":{"x":11.5,"y":12},"end":{"x":9.666666666666666,"y":19},"startBoxIndex":6,"endBoxIndex":1},{"startOutput":"p","endInput":"v","start":{"x":11.5,"y":12},"end":{"x":14.666666666666666,"y":14},"startBoxIndex":6,"endBoxIndex":2},{"startOutput":"t","endInput":"i","start":{"x":10.5,"y":6},"end":{"x":11.5,"y":9},"startBoxIndex":0,"endBoxIndex":6}],"customDefinitions":{"test":{"init":{"codes":["#0","int","#counter","kep","#sin","arr","#0.000","flt","#sin","que","#0.309","flt","#sin","que","#0.588","flt","#sin","que","#0.809","flt","#sin","que","#0.951","flt","#sin","que","#1.000","flt","#sin","que","#0.951","flt","#sin","que","#0.809","flt","#sin","que","#0.588","flt","#sin","que","#0.309","flt","#sin","que","#0.000","flt","#sin","que","#-0.309","flt","#sin","que","#-0.588","flt","#sin","que","#-0.809","flt","#sin","que","#-0.951","flt","#sin","que","#-1.000","flt","#sin","que","#-0.951","flt","#sin","que","#-0.809","flt","#sin","que","#-0.588","flt","#sin","que","#-0.309","flt","#sin","que"],"branches":{"i":{"codes":["#counter","get","#.1","flt","add","#20","int","mod","dup","#counter","kep","dup","#sinat","jmp","swp","#5","int","add","#sinat","jmp","vec","#20","int","vecmul","#p","out"],"branches":{"sinat":{"codes":["dup","dup","#1","int","add","flr","#20","int","mod","#sin","at","swp","#1","int","mod","mul","swp","dup","flr","#20","int","mod","#sin","at","swp","#1","int","mod","#1","int","swp","sub","mul","add"],"branches":{}}}}}},"inputs":{"i":{"codes":["#counter","get","#.1","flt","add","#20","int","mod","dup","#counter","kep","dup","#sinat","jmp","swp","#5","int","add","#sinat","jmp","vec","#20","int","vecmul","#p","out"],"branches":{"sinat":{"codes":["dup","dup","#1","int","add","flr","#20","int","mod","#sin","at","swp","#1","int","mod","mul","swp","dup","flr","#20","int","mod","#sin","at","swp","#1","int","mod","#1","int","swp","sub","mul","add"],"branches":{}}}}},"outputs":["p"],"code":"test\ni -> p\n\n#0 int #counter kep\n\n#sin arr #0.000 flt #sin que #0.309 flt #sin que #0.588 flt #sin que #0.809 flt #sin que #0.951 flt #sin que #1.000 flt #sin que #0.951 flt #sin que #0.809 flt #sin que #0.588 flt #sin que #0.309 flt #sin que #0.000 flt #sin que #-0.309 flt #sin que #-0.588 flt #sin que #-0.809 flt #sin que #-0.951 flt #sin que #-1.000 flt #sin que #-0.951 flt #sin que #-0.809 flt #sin que #-0.588 flt #sin que #-0.309 flt #sin que\n\ndef i:\n  #counter get\n  #.1 flt add\n  #20 int mod\n  dup #counter kep\n  dup #sinat jmp\n  swp #5 int add\n  #sinat jmp\n  vec\n  #20 int\n  vecmul\n  #p out\n  \n  def sinat:\n    dup dup\n    #1 int add\n    flr #20 int mod\n    #sin at\n    swp #1 int mod mul\n    \n    swp dup\n    flr #20 int mod\n    #sin at\n    swp #1 int mod\n    #1 int swp sub mul\n    add\n  \n"},"delay":{"init":{"codes":["#tail","arr","#30","int","#number","kep"],"branches":{"v":{"codes":["val","#tail","que","#tail","len","#number","get","gth","#pop","jcn","#tail","len","#number","get","gth","#pop","jcn"],"branches":{"pop":{"codes":["#tail","pop","#v","out"],"branches":{}}}},"n":{"codes":["val","#number","kep"],"branches":{}}}},"inputs":{"v":{"codes":["val","#tail","que","#tail","len","#number","get","gth","#pop","jcn","#tail","len","#number","get","gth","#pop","jcn"],"branches":{"pop":{"codes":["#tail","pop","#v","out"],"branches":{}}}},"n":{"codes":["val","#number","kep"],"branches":{}}},"outputs":["v"],"code":"delay\nv n -> v\n\n#tail arr\n#30 int #number kep\n\ndef v:\n  val #tail que\n  #tail len #number get gth\n  #pop jcn\n  #tail len #number get gth\n  #pop jcn\n  def pop:\n    #tail pop #v out\n  \n\ndef n:\n  val #number kep"},"walkCircle":{"init":{"codes":["#0","int","#counter","kep","#sin","arr","#0.000","flt","#sin","que","#0.309","flt","#sin","que","#0.588","flt","#sin","que","#0.809","flt","#sin","que","#0.951","flt","#sin","que","#1.000","flt","#sin","que","#0.951","flt","#sin","que","#0.809","flt","#sin","que","#0.588","flt","#sin","que","#0.309","flt","#sin","que","#0.000","flt","#sin","que","#-0.309","flt","#sin","que","#-0.588","flt","#sin","que","#-0.809","flt","#sin","que","#-0.951","flt","#sin","que","#-1.000","flt","#sin","que","#-0.951","flt","#sin","que","#-0.809","flt","#sin","que","#-0.588","flt","#sin","que","#-0.309","flt","#sin","que"],"branches":{"i":{"codes":["#counter","get","#.1","flt","add","#20","int","mod","dup","#counter","kep","dup","#sinat","jmp","swp","#5","int","add","#sinat","jmp","vec","#20","int","vecmul","#p","out"],"branches":{"sinat":{"codes":["dup","dup","#1","int","add","flr","#20","int","mod","#sin","at","swp","#1","int","mod","mul","swp","dup","flr","#20","int","mod","#sin","at","swp","#1","int","mod","#1","int","swp","sub","mul","add"],"branches":{}}}}}},"inputs":{"i":{"codes":["#counter","get","#.1","flt","add","#20","int","mod","dup","#counter","kep","dup","#sinat","jmp","swp","#5","int","add","#sinat","jmp","vec","#20","int","vecmul","#p","out"],"branches":{"sinat":{"codes":["dup","dup","#1","int","add","flr","#20","int","mod","#sin","at","swp","#1","int","mod","mul","swp","dup","flr","#20","int","mod","#sin","at","swp","#1","int","mod","#1","int","swp","sub","mul","add"],"branches":{}}}}},"outputs":["p"],"code":"walkCircle\ni -> p\n\n#0 int #counter kep\n\n#sin arr #0.000 flt #sin que #0.309 flt #sin que #0.588 flt #sin que #0.809 flt #sin que #0.951 flt #sin que #1.000 flt #sin que #0.951 flt #sin que #0.809 flt #sin que #0.588 flt #sin que #0.309 flt #sin que #0.000 flt #sin que #-0.309 flt #sin que #-0.588 flt #sin que #-0.809 flt #sin que #-0.951 flt #sin que #-1.000 flt #sin que #-0.951 flt #sin que #-0.809 flt #sin que #-0.588 flt #sin que #-0.309 flt #sin que\n\ndef i:\n  #counter get\n  #.1 flt add\n  #20 int mod\n  dup #counter kep\n  dup #sinat jmp\n  swp #5 int add\n  #sinat jmp\n  vec\n  #20 int\n  vecmul\n  #p out\n  \n  def sinat:\n    dup dup\n    #1 int add\n    flr #20 int mod\n    #sin at\n    swp #1 int mod mul\n    \n    swp dup\n    flr #20 int mod\n    #sin at\n    swp #1 int mod\n    #1 int swp sub mul\n    add"},"snake":{"init":{"codes":["#0","int","#1","int","vec","#direction","kep","#tail","arr","#0","int","#1","int","vec","#tail","que"],"branches":{"d":{"codes":["val","#direction","kep"],"branches":{}},"i":{"codes":["#direction","get","#tail","lst","vecadd","dup","#tail","que","#p","out","#11","int","#tail","len","equ","#pop","jcn"],"branches":{"pop":{"codes":["#tail","pop","#c","out"],"branches":{}}}}}},"inputs":{"d":{"codes":["val","#direction","kep"],"branches":{}},"i":{"codes":["#direction","get","#tail","lst","vecadd","dup","#tail","que","#p","out","#11","int","#tail","len","equ","#pop","jcn"],"branches":{"pop":{"codes":["#tail","pop","#c","out"],"branches":{}}}}},"outputs":["p","c"],"code":"snake\nd i -> p c\n\n#0 int #1 int vec\n#direction kep\n\n#tail arr\n#0 int #1 int vec\n#tail que\n\ndef d:\n  val #direction kep\n\ndef i:\n  #direction get #tail lst vecadd\n  dup #tail que\n  #p out\n  #11 int #tail len equ #pop jcn\n  def pop:\n    #tail pop #c out"}}}

M src/main.js => src/main.js +2 -19
@@ 222,11 222,13 @@ function initUI() {

  const editorTextbox = el({ kind: 'textarea', attributes: { rows: 20 }, style: { width: '15rem' } });
  const editorSaveButton = el({ kind: 'button', text: 'Save' });
  const docsLink = 'https://git.sr.ht/~kylep/visual-cell-lang/tree/master/item/docs/lang.md#domain-specific-box-lang';
  const codeEditorEl = el({
    kind: 'div',
    children: [
      el({ kind: 'label', text: 'editor ' }),
      editorSaveButton,
      el({ kind: 'a', text: 'Documentation', attributes: { href: docsLink } }),
      el({
        kind: 'div',
        children: [


@@ 322,25 324,6 @@ def d:
    }
  });
  loadCodeBox({
    name: 'snake2',
    data: {
      direction: new Vec(1, 0),
      tail: [new Vec(0, 0)],
    },
    outputs: ['p', 'c'],
    inputs: {
      d: parse(`val #direction kep`),
      i: parse(`
#direction get #tail lst vecadd
dup #tail que
#p out
#11 int #tail len equ #pop jcn
def pop:
  #tail pop #c out
`)
    }
  });
  loadCodeBox({
    name: 'pulse',
    data: {
      value: 0,