jtye: k in fifty functions repl tests

about

jtye/k is k for javascript.

i write more js these days, mainly for ui and cross platform.
my style evolves to more functional. that's mostly fine but sometimes annoying.

compare: let sum=(f,x)=>x.reduce((a,x)=>f(a,x),0)
why not:                                over(add)

core

k core (k.js) bytes
the core adds 50 primitive functions and 4 helpers which work on numbers, arrays and strings. the functions can be used directly in javascript.

the helper functions rec and atomic can be used to extend the language with more vector functions, e.g.

sin=rec(Math.sin),cos=rec(Math.cos),pow=atomic(Math.pow)

parse

parser (parse.js) bytes
let's add a parser, that transforms k source to js which uses our primitives.

bytecode, virtual machine? we don't need this, we can just use javascript's eval.
see the test's output (in green) what the parser compiles k code to.

ref

+ type   add   ' each prior bin `js`
- neg    sub   / over right join dec
* sqr    mul   \ scan left split enc
% sqrt   div   inv idiv mod         
& flip   min                  atom  
| rev    max                  atomic
< up     less                  curry
> down   more                    rec
= freq   eql                        
~ not    match                      
. value        parse                
! til    dict  token key where      
@ first  at    amend                
? uniq   find  rand                 
^ sort   cut                        
# count  take             if[c;;;;;]
_ floor  drop            while[c;;;]
, list   cat           for[a;b;c;;;]
$ string print       try[]catch[e;;]

adverbs

there are only 3 adverb symbols ' / \ ' is each or prior(for :+-*%&|<>=~), / \ are over and scan. derived verbs from each over and scan are available in variadic form and may also take more than 1 argument. the verb's arity should match the number of arguments: e.g. f'[a;b;c] is triadic each. x/y x\y is is encode/decode for numbers and join/split for strings. x'y is binary search.

ambivalence

primitives are fixed at compile time if possible, e.g. 1-2 is sub, while -x is neg. by themselves, e.g. (-;+) or f:- they default to dyadic, e.g. sub but not neg. this is also true when used in derived +/ which is over(add). the exception is each: -' compiles to ambivalent each((x,y)=>y===undefined?neg(x):sub(x,y)) derived verbs themselves, are monadic by default +/ is sum not add-each-right. the dyadic form is chosen at compile time for x+/y. this is resolved at runtime using variadic forms. compositions however are always dyadic: 1+- is {1+x-y} to simulate a monadic train, append dex(:) e.g. 1+-:

js escape

everything between backticks is compiled verbatim to js and parsed as a noun:
f`x.slice(-3)` compiles to  at(x.slice(3),f)
x$y prints x and y to the js console, usefull for debugging. x maybe a label, y is passed through.

structural

js keywords if for while try catch throw are detected when called with brackets.
the first   1  3   1     0   1     1 arguments are (applied), the rest forms the {block}.

e.g. while[x>1;a;b;c;x-:1]
try is immediately followed by catch: try[a;b;c]catch[e;-a;-b]

there is no else, since we have cond: $[a;b;c;d;e] which compiles to ternary expressions.

web use

<script src="k.js"></script>
<script src="parse.js"></script>
this example shows a website with a dynamic part written in k and executed on page load.

standalone

download
$j [a b.json c.js d.k] -e [expr]  #-e(eval&exit) .json(parse&assign) .js/k(eval) else assign
k)\
j)

caveats

direct call for args!=1. e.g. prevent "illegal invocation" cnv.getContext[0;"2d"]
jtye/k (in //parse out \doc)