// mcode core for javascript // see "Notes About the Core file" in the primer let‗ r,ver = 'mcode core version 0.08.01.2024' '' ⎕ ver ; ⊙.test = {} // test artifacts namespace r = `// mcode core - generated file mcode.logn('` + ver + ` loaded from cache'); /* jshint asi:true */ ` r += '// Construction functions' r += ⍎.er ` ∇ mcode.create : r // ⍙ create non-literal and non-built-in types, add primitives // ⎕ 'create' ; ⎕.j ⍺ ; ⎕.j ⍵ ; ⎕.j δ // vars: ⍙.v varlist outputs let varlist // classes: [ args ] ⍙.n Class outputs new Class(...[args]) ∇ mtx : i=0,j,q,t r = [] i < ⍺[0] ⌸ j = 0 ; q = [] j < ⍺[1] ⌸ t = δ=='i'?(i==j?1:0):0 // .i for identity matrix else zero matrix q.push(t) ; j++ r.push(q) i++ ⍔ r ∇ vec : i=0 r = [] i < ⍺ ⌸ r = r.concat(0) i++ ⍔ r r = ⍵ ⍵ ⥊ '@' → ⍔ (⍺!=null)?new‗ Date(⍺):new‗ Date() // nb. ternary operator used ? : ⍵ ⥊ '{}' → ⍔ (⍺!=⍬)?new‗ Map(⍺):new‗ Map() // to init Map, ⍺ is [[k v]...] ⍵ ⥊ '/' → ⍔ new‗ RegExp(⍺,δ) // regex, δ are flags ⍵ ⥊ '[[]]' → // create matrix of shape [⍺] .i for identity ( ⍺ instanceof‗ Array ) → ⍔ mtx(⍺,⍵,δ) ⍔ [[]] // empty matrix ⍵ ⥊ '[]' → ⍺ != ⍬ → ⍔ vec(⍺,⍵,δ) ⍔ [] ⍵ ⥊ '#' → ⍔ parseInt(⍺) // convert to integer δ ⥊ 'j' → ⍔ JSON.parse(⍵) // .j for JSON to data // todo: JSON.parse with replacer & try/catch ⍺ != ⍬ → ⍔ mcode.addPrim(⍺,⍵,δ) // if ⍺ given, then define primitive // ⋄ ( ( typeof‗ window[⍵] ) ⥊ 'function' ) ^ ⍺ != ⍬ → // ⋄ ( ⍵ instanceof‗ Function ) ^ ⍺ != ⍬ → // mcode.addPrim(⍺,⍵,δ) // todo: table an object with a field list ⍺, a format string (TBD), data fields ⍔ 0 mcode.addPrim('⍙','mcode.create') // R arg is quoted in transpiler fn 'OPsub' ` r += ⍎.er ` ∇ mcode.format0 // ⍕ convert to string or JSON δ=='j' → ⍔ JSON.stringify(⍵) ⍔ ⍵+'' ` /* not added as primitive since OPsub routes ⍕.mod calls to format0 non-modified format ⍕ is handled by vf, see fmapIn todo: string ops: toString, pad, trim, justify // no ⍺ but δ given .s sprintf style ⍵ is VM (⍺ for each element), table (⍺ for each row) */ ⍎ ` ∇ ⊙.test.create // ⎕ '⊙.test.create' 123 != '123px' ⍙ # → throw‗ 'error: ⍙ # test failed' ⍙.v m,r r = '"1970-01-01T00:00:00.000Z"' r != ⍕.j 0 ⍙ @ → throw‗ 'error: ⍙ @ test failed' m = [['a',0],['b',1]] ⍙ {} 1 != m.get('b') → throw‗ 'error: ⍙ {} test failed' r = 'abc' ⍙.g '/' '/abc/g' != r.toString() → throw‗ "error: ⍙ '\\/' failed" '[[]]' != ⍕.j ⍙ [[]] → throw‗ 'error: ⍙ [[]] failed' '[[0,0,0],[0,0,0]]' != ⍕.j [ 2 3 ] ⍙ [[]] → throw‗ 'error: [] ⍙ [[]] failed' m = [ 3 3 ] ⍙.i [[]] '[[1,0,0],[0,1,0],[0,0,1]]' != ⍕.j m → throw‗ 'error: [] ⍙.i [[]] failed' ⊙.test.create 0 ` r += ⍎.er ` ∇ mcode.typeof : r=1,s // ⍷ ⍷ ⍵ or ⍺ ⍷ literal true if ⍺ is typeof ⍵ [?] V or M // ⎕ 'typeof' ; ⎕ ⍺ ; ⎕ ⍵ ; '⍵.length' ⎕ ⍵.length // debug ⍺!=null → {s=⍵;⍵=⍺;⍺=s} // swap for comparisons below // ( ⍺=='[?]' ) ^ ( typeof‗ ⍵.length ) == 'number' → ⍔ 1 // true if ⍺ is indexed [] string or Array ( ⍺=='[?]' ) ^ ( ⍵ instanceof‗ Array ) → ⍔ 1 // true if ⍺ is any array type ⍵ ⥊ undefined → r = 'U' // undefined usually an error ⋄ ⍵ ⥊ null → r = '⍬' // null means 'nothing' ⋄ ( ⍵ instanceof‗ Array ) → ⍵[0] instanceof‗ Array → r = '[[]]' // matrix ⋄ r = '[]' // vector ⋄ ⍵ instanceof‗ Map → r = '{}' // map (aka dictionary) // todo: table r = '⍑' // below are scalar types: ⋄ ⍵ instanceof‗ Function → r = '()' // function ⋄ ⍵ instanceof‗ Date → r = '@' // date ⋄ ( typeof‗ ⍵ ) ⥊ 'boolean' → r = '~' // boolean ⋄ ( typeof‗ ⍵ ) ⥊ 'number' → r = '#' // number ⋄ ⍵ instanceof‗ RegExp → r = '/' // regex ⋄ ( typeof‗ ⍵ ) ⥊ 'string' → r = '' // string nb. does not show unless as JSON or quoted ⋄ ( typeof‗ ⍵ ) ⥊ 'object' → r = '.' // object // ⋄ isObj ⍵ ⍺!=⍬ → r = 0 + ( ⍺ ⥊ r ) // 1 if typeof ⍺ is symbol ⍵ // ⎕ r // debug ⍔ r // ∇ isObj : p=⍬ // NIU getPrototypeof FAILS on DOM objects // ( typeof‗⍵ ) ⥊ 'object' → p=Object.getPrototypeof(⍵) // ⍔ ⍵ ^ ( ( ( p ⥊ null ) ∨ p ⥊ Object.prototype ) // ∇ isObj : p=Object.getPrototypeof(⍵) // ⍔ ⍵ ^ ( ( typeof‗⍵ ) ⥊ 'object' ) ^ ( ( p ⥊ null ) ∨ p ⥊ Object.prototype ) '⍷' ⍙ mcode.typeof // R arg is quoted for null ⍷ L in transpiler fn 'OPsub' ` r += ⍎.er ` ∇ mcode.system : p,rs // system functions set by modifier δ // many of these operations are specific to JavaScript in the Browser environment and the mcode IDE // ⍠ alone calls browser debugger in emitWord() δ == ⍬ → ⎕ mcode.guide() ⋄ δ=='↑' → throw‗ 'error: ' + ⍵ // ⍠.↑ we're outta here via exception ⋄ δ=='o' → mcode.shellOpts = ⍵ // see 'mcodeOptions.debug' in mcode.js ⋄ δ=='a' → mcode.assertOpts = ⍵ // see ?= mcode.assert ⋄ δ=='m' → mcode.msg(⍵) // 'error' or 'ready' for mcode_ide.js ⋄ δ=='tmo' → setTimeout(⍵) // call function ⍵ after all other processing is done ⋄ δ=='timer' → // make async caller function wait ⎕ 'timer ' + ⍵ + 'ms' ⍔ new‗Promise‗(rs=>setTimeout(()=>rs(0),⍵)) // use await‗ in ∇.a (async) fn ⋄ δ=='busy' → mcode.setBusy() // creates promise for IDE ⋄ δ=='done' → mcode.done(⍵) // resolves promise, ⍵ is return data ⋄ δ=='wait' → // make IDE wait mcode.setBusy() setTimeout(()=>mcode.done('waited '+⍵+'ms'),⍵) '⍠' ⍙ mcode.system ` ⍎ ` ∇ ⊙.test.typeof : V,M,x=0,y='abc',p={} // ⎕ '⊙.test.typeof' ~ x ⍷ # → ⍠.↑ '⍷ test failed on #' ~ y ⍷ '' → ⍠.↑ '⍷ test failed on " var' ~ p ⍷ . → ⍠.↑ '⍷ test failed on object' V = [ 0 1 2 ] '[]' != ⍷ V → ⍠.↑ '⍷ test failed on []' M ← [ 'a', 0 'b', 1 ] '[[]]' != ⍷ M → ⍠.↑ '⍷ test failed on [[]]' 1 != M ⍷ [?] → ⍠.↑ '⍷ test failed on [?]' ⊙.test.typeof 0 ` r += ⍎.er ` ∇ mcode.shape0 // ⍴ simple length ⍵ if ⍵ is [] ⍵ ⍷ [] → ⍔ ⍵.length // nb. full ⍴ is defined later ⍵ ⍷ [[]] → ⍔ [⍵.length,⍵[0].length] ⍔ [ 0 ] '⍴' ⍙ mcode.shape0 ∇ mcode.push0 // ↓ simple push ⍵ on to stack ⍺ if ⍺ is [] ⍺ ⍷ [?] → ⍺.push(⍵) // nb. full ↓ is defined later ⍔ ⍺ '↓' ⍙ mcode.push0 ∇ mcode.concat0 // ⍪ simple concat ⍵ to ⍺ ⍺ ⍷ # → ⍺ = [ ⍺ ] // nb. full ⍪ is defined later ⍺ ⍷ [] → ⍺ = ⍺.concat(⍵) ⍔ ⍺ '⍪' ⍙ mcode.concat0 ∇ mcode.iota0 : r=[],i=0 // ⍳ simple generation of vector 0..n ⍺ ⥊ ⍬ → // nb. full ⍪ is defined later i < ⍵ ⌸ r.push(i++) ; ⍔ r ⍔ r '⍳' ⍙ mcode.iota0 ` r += ⍎.er ` ∇ mcode.select : r=[],b // ⌷ ⍵[⍺] // ⎕ 'select' // ⎕ ⍺ ; ⎕ ⍵ ; ⎕ ⍷ ⍵ // nb. in JS, []==[] is false ⍵.length==0 → ⍔ ⍬ ⍵ ⍷ # → ⍵ = [ ⍵ ] // cvt scalar to vector ⍵ ⍷ [?] → // ⍵ is [] or [[]] ⍺ ⍷ # → ⍔ ⍵[⍺] // if ⍺ is scalar then r is ⍵[⍺] ⍺ ⍷ [] → b ⌻ ⍺ : r ↓ ⍵[b] ; ⍔ r // return vector of ⍵[⍺] ⍵ ⍷ {} → // ⍵ is map ⍺!=⍬ → δ ⥊ ⍬ → ⍔ ⍵.get(⍺) ⍔ ⍵.set(⍺,δ) ⍔ ⍵ ⍵ ⍷ . → ⍺ ⥊ ⍬ → ⍔ Object.keys(⍵) ⍔ ⍵[⍺] ⍔ 0 '⌷' ⍙ mcode.select ` /* nb. see Array.copyWithin() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin nb. selective assignment is written as ⍵[⍺] = or ⍵[i,j] = ⍺ ⌷.v ⍵ or ⍺ ⌷.⍵ v ? ⌷= cb. for selective assignment as in [ 1 3 5 ] ⌷=.v [ 2 4 6 ] see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/with */ ⍎ ` ∇ ⊙.test.select : V,S,P,T // ⎕ '⊙.test.select' V = [ 3 4 5 ] // '[3,4]' != ⍕.j 4 != 1 ⌷ V → ⍠.↑ '⌷ test select 0 failed' '[3,4]' != ⍕.j [ 0 1 ] ⌷ V → ⍠.↑ '⌷ test select 1 failed' S = [['ab','cd'],'ef',['uvw','xyz']] '["ef",["ab","cd"],["uvw","xyz"]]' != ⍕.j [ 1 0 2 ] ⌷ S → ⍠.↑ '⌷ test 2 failed' P = [['a',0],['b',1]] ⍙ {} 'c' ⌷.2 P 2 != 'c' ⌷ P → ⍠.↑ '⌷ test select 2 failed' T={a:1,b:2} 2 != 'b' ⌷ T → ⍠.↑ '⌷ test select 3 failed' ⊙.test.select 0 ` /* nb. operators are called as: mcode.each( ⍺, ⍵, [op_mod,[f0,mod0]] ); eg. 0 ⊢.a ¨.b 1 mcode.each( 0, 1, ['b',[mcode.nyi,'a']] ); 0 ⊢.⊙.x ¨.⊙.y 1 mcode.each( 0, 1, [_cp.y,[mcode.nyi,_cp.x]] ); // nb. ⊙.y contains the modifier value for each ¨ */ r += '\n// Operator functions' r += ⍎.er ` ∇ mcode.each : b,c,f,fm,i=0,n,r=[],t=[] // ⎕ 'each' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ; '⍷ ⍺' ⎕ ⍷ ⍺ ; '⍷ ⍵' ⎕ ⍷ ⍵ [f,fm]=δ[1] // ⎕ f ; ⎕ fm ( ⍵ ⍷ . ) ^ ⍺ ⍷ [] → // each obj b ⌻ ⍺ r ↓ f(b,⍵,fm) ⍔ r ~ ⍺ ⍷ [?] → ⍺ = [ ⍺ ] ~ ⍵ ⍷ [?] → ⍵ = [ ⍵ ] ⍵ ⍷ [] → ⍺ ⍷ [] → // each ⍺ paired with each ⍵ n = 0 ⌷ ⍴ ⍺ b ⌻ ⍵ // ' VV ⍺' ⎕ ⍺[i%n] ; ' ⍵' ⎕ b r ↓ f(⍺[i++%n],b,fm) ⍔ r b ⌻ ⍵ r ↓ f(⍺,b,fm) ⍔ r ⍵ ⍷ [[]] → ⍺ ⍷ [] → // each ⍺ paired with each b of ⍵ rows n = 0 ⌷ ⍴ ⍺ b ⌻ ⍵ i = 0 c ⌻ b : t ↓ f(⍺[i++%n],c,fm) r ↓ t ; t = [] ⍔ r ⍺ ⍷ [[]] → throw‗ 'error: each on matrix ⍺ is NYI' ⍔ 0 '¨' ⍙ mcode.each ` ⍎ ` ∇ ⊙.test.each : cn,M,S,T // ⎕ 'test.each' '[4]' != ⍕.j 2 * ¨ 2 → ⍠.↑ 'each scalar failed' '[4,6]' != ⍕.j 2 * ¨ [ 2 3 ] → ⍠.↑ 'each SV failed' '[0,1]' != ⍕.j + ¨ [ '0' '1' ] → ⍠.↑ 'each + unary failed' '[2,4]' != ⍕.j 2 * ¨ [ 1 2 ] → ⍠.↑ 'each 0 * failed' '[3,5,5]' != ⍕.j [ 1 2 ] { ⍺ + ⍵ } ¨ [ 2 3 4 ] → ⍠.↑ 'each V {} failed' cn ← { (⍺+'')+'x'+(⍵+'') } '["0x1","0x2"]' != ⍕.j 0 cn ¨ [ 1 2 ] → ⍠.↑ 'each foo failed' M ← [ 0, 1 2, 3 ] '[[1,2],[3,4]]' != ⍕.j 1 +. ¨ M → ⍠.↑ 'each matrix failed' S ← [ 'ab', 'cd' 'ef', 'gh' ] '[["xb","cd"],["ef","xh"]]' != ⍕.j 'x' { ⍵.replace(/a|g/g,⍺) } ¨ S → ⍠.↑ 'each string matrix failed' T={a:1,b:2} '[2,1]' != ⍕.j [ 'b' 'a' ] ⌷ ¨ T → ⍠.↑ 'each object failed' ⍔ 0 try‗ ⍎.m \`'each 0 ⊢' ⎕ 0 ¨ [ 1 2 ] // parsing error test (missing fn left of operator)\` catch‗ ⎕ 'error: in test.each' ⊙.test.each 0 ` r += ⍎.er ` ∇ mcode.reduce : b,f,r=null // ⌿ reduce by applying fn δ over ⍵ r=⍺ at start // ⎕ 'reduce' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ [f,fm]=δ[1] // ⎕ f ; ⎕ fm // f == mcode.max → ⍔ Math.max(...⍵) // todo: optimize? // f == mcode.min → ⍔ Math.min(...⍵) ⍵ ⍷ # → ⍵ = [ ⍵ ] ⍵ ⍷ [?] → ⍺ ⥊ ⍬ → b ⌻ ⍵ : r=f(r,b,fm) ⍔ r b ⌻ ⍵ : r ⥊ null → r = b ; ⍐ r=f(r,b,fm) ⍔ r '⌿' ⍙ mcode.reduce ` ⍎ ` ∇ ⊙.test.reduce : cn,r,avg // ⎕ 'test.reduce' // nb. ⍺ is initial value and is used to set dyadic call to + 2 != 0 ⌊ ⌿ [ 2 3 ] → ⍠.↑ 'reduce ⌊ failed' // dyadic min of vector 3 != 0 ⌈ ⌿ [ 2 3 ] → ⍠.↑ 'reduce ⌈ failed' // dyadic max of vector 5 != 0 + ⌿ [ 2 3 ] → ⍠.↑ 'reduce + failed' avg ← { ( 0 + ⌿ ⍵ ) / ⍴ ⍵ } 2.5 != avg [ 2 3 ] → ⍠.↑ 'reduce avg failed' 1 != + ⌿ [ '0' '1' ] → ⍠.↑ 'reduce + failed' 3 != 0 + ⌿ [ 1 2 ] → ⍠.↑ 'reduce 0 + failed' '1x2' != 0 { (⍺+'')+'x'+(⍵+'') } ⌿ [ 1 2 ] → ⍠.↑ 'reduce {} failed' cn ← { (⍺+'')+'x'+(⍵+'') } '1x2' != 0 cn ⌿ [ 1 2 ] → ⍠.↑ 'reduce cn failed' ⊙.test.reduce 0 ` /* r += ⍎.er ` ∇ mcode.scan : b,f,i=0,r=0 ⎕ 'scan' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ⎕ 'scan is not yet implemented' // zvzv NYI ⍔ r '⍀' ⍙ mcode.scan ` */ r += ⍎.er ` ∇ mcode.power : m,f,fm,i=0,r=⍺ // ⎕ 'power' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ // nb. power function ⍺ is r δ is [mod,i] m = δ[0] ; [f,fm]=δ[1] // ⎕ m ; ⎕ ⍷ m ; ⎕ f ; ⎕ fm ( m ⍷ # ) ^ m>0 → i=0) ∨ b.indexOf(⍵)>=0 b = δ==null?'':δ e = opts 'd' // e → // ⎕ 'assert' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ opts 'x' → r = ⍕.j ⍵ ⋄ r = ⍕.j ⍎ ⍵ ; d += ' of ' + ⍵ // add expr tested to d ⍺ != r → s = d+' failed: ' + ⍺ + ' ?= ' + ⍵ + ', got '+r opts 't' → '' ⎕ s ⋄ ⍠.↑ s ⋄ e → ⎕ d+' passed: ' + ⍺ + ' == ' + ⍵ ⍔ r '?=' ⍙ mcode.assert ` ⍎ ` ∇ ⊙.test.format '[3,0]' ?= '⍕ [ π 0.1234 ]' '[3.142,0.123]' ?= '3 ⍕ [ π 0.1234 ]' // nb. JSON result is a string ⊙.test.format 0 ` ⍎ ` ∇ ⊙.test.vf0 '[1,3]' ?= '¦ [ 1.1 2.9 ]' '[2.236]' ?= '3 ⍕ 1 ¦ [ 1 2 ]' '[0.464]' ?= '3 ⍕ atan2θ [ 1 2 ]' '[2,5]' ?= '⌈ [ 1.1 4.8 ]' '[3,4]' ?= '[ 3 2 ] ⌈ [ 1 4 ]' '[3]' ?= '1 +. 2' '[2,3]' ?= '[ 1 2 ] +. 1' '[2,3]' ?= '1 +. [ 1 2 ]' '[2,4]' ?= '[ 0 1 ] +. [ 2 3 ]' '5' ?= '1 +.× [ 2 3 ]' ⍔ 0 ⊙.test.vf0 0 ` ⍎ ` ∇ ⊙.test.vf1 : M,N,V // ⎕ 'test.vf1' // ⍠.a 'd' // nb. M,N,V are locals instead of in ⊙, so we use ?=.x not ?= // (no execute form) // nb. M f.g N is f / g on M cols and N rows M ← [ 1, 2 3, 4 ] N ← [ 5, 6 7, 8 ] V ← [ 9, 10 ] // ⎕ M ; ⎕ N ; ⎕ V '[[2,3],[4,5]]' ?=.x 1 +. M '[[2,3],[4,5]]' ?=.x M +. 1 '[[2,4],[6,8]]' ?=.x M +. M // inner product tests '[[19,22],[43,50]]' ?=.x M +.× N '[[23,34],[31,46]]' ?=.x N +.× M '[29,67]' ?=.x M +.× V '[39,58]' ?=.x V +.× M '[12,18]' ?=.x 3 +.× M '[9,21]' ?=.x M +.× 3 /* APL verification: M ← 2 2 ⍴ ⍳ 4 M 1 2 3 4 N ← M + 4 N 5 6 7 8 V ← 9 10 V 9 10 M +.× N 19 22 43 50 N +.× M 23 34 31 46 M +.× V 29 67 V +.× M 39 58 */ ⍔ 0 ⊙.test.vf1 0 ` ⍎ ` ∇ ⊙.test.vf2 // ⎕ 'vectorized functions' // ⍠.a 'd' '[2,4]' ?= '⌈ [ 1.2 3.4 ]' '[[0,0],[2,3]]' ?= '[[0],[1]] +.× [[2,3]]' // nb. vectors are treated as column vectors when appropriate, without transposition '[0,2]' ?= '[[0],[1]] +.× [2,3]' '3' ?= '[0,1] +.× [2,3]' '[3,0]' ?= '[ 2 3 ] +.× [[0],[1]]' '3' ?= '[ 2 3 ] +.× [ 0 1 ]' '[2,4]' ?= '[ 0 1 ] +. [ 2 3 ]' '[3,4]' ?= '1 +. [ 2 3 ]' '[2]' ?= '1 +. 1' '[2,3]' ?= '2 ⌈ ⌈ [ 0.1 2.7 ]' '[7.07107]' ?= '5 ⍕ 1 ¦ [ 3 4 5 ]' '[0,1]' ?= 'sinθ [ 0 π/2.0 ]' '[1,2]' ?= '1 ⌈ [ 0 2 ]' '[0,2]' ?= '⌈ [ 0 2 ]' '[2,3]' ?= '2 ⌈ ⌈ [ 0.1 2.7 ]' ⊙.test.vf2 0 ` ⍎ ` ∇ ⊙.test.matrix : t,a t = π ÷ 2 '[1]' ?= 'sinθ 0.5 × π' '[1]' ?= 'sinθ π ÷ 2' '[1.571]' ?= '3 ⍕ atan2θ [ 1 0 ]' ⊙.Mz ← [ cosθ t, - sinθ t, 0, // rotate on Z axis sinθ t, cosθ t, 0, 0, 0, 1 ] // 'Mz' ⎕ ⊙.Mz '-1' ?= '⊙.Mz[0,1]' ⍫ ⊙.Mz // delete ⊙.test.matrix 0 ` ⍎ ` ∇ ⊙.test.outer // ⎕ 'test.outer' '[[1]]' ?= '0 ∘.+ 1' '[[4,5],[5,6]]' ?= '[ 1 2 ] ∘.+ [ 3 4 ]' '[[3,4],[6,8]]' ?= '[ 1 2 ] ∘.× [ 3 4 ]' ⍔ 0 ⊙.test.outer 0 ` /* NIU each Map r += ⍎.er ` ∇ mcode.each : k,v,r=[] // ¨ apply ⍺ over ⍵ uses vf ⍵ ⍷ {} → [k,v] ⌻ ⍵ : r.push( k ⍺ v ) // map ⍔ r ⍔ mcode.vf(δ,⍵,[⍺,null]) '¨' ⍙ mcode.each ` ⍎ ` ∇ ⊙.test.eachMap : U,V,M,r=[] V = [ 0 1 2 ] U = [ 2 3 ] // ⎕ V ; ⎕ U '[[0,2],[1,3],[2,2]]' ?=.x ⍕.j V { [⍺,⍵] } ¨ U // .x since U,V are locals M ← [ 'k1', 0, 'k2', 1 ] // ⎕ M Mp = M ⍙ {} // create map // ⎕ Mp { r.push( [ ⍺ , ⍵ ] ) } ¨ Mp // iterate over map r = ⍕.j r '[["k1",0],["k2",1]]' ?=.x r // check ⊙.test.eachMap 0 ` */ /* NIU zvzv r += ⍎.er ` ∇ mcode.notEqual : r=1,i=0 // ≠ hybrid function for arrays strings scalars // ⎕ 'notEqual' ; ⎕ ⍺ instanceof‗ Array ; ⎕ ⍵ instanceof‗ Array ( ⍺ instanceof‗ Array ) ^ ( ⍵ instanceof‗ Array ) ^ ⍺.length==⍵.length → i < ⍺.length ⌸ ⍺[i]!=⍵[i++] → ⍗ r = 0 ⋄ r = Number(⍺!=⍵) // nb. cast result to number ⍔ r '≠' ⍙ mcode.notEqual ` r += ⍎.er ` ∇ mcode.equal // = calls ≠ ⍔ Number( ! ⍺ ≠ ⍵ ) '≈' ⍙ mcode.equal */ ⍎ ` ∇ ⊙.test.equals ⎕ '⊙.test.equals' // equality, matrix, and assert tests // 1 ?=.x 1 // assert test (no mexec) 1 ?= '1' // assert test ⊙.M ← ⍙ '[[]]' // create test use ⊙ so assert can do mexec 1 ?= \`'[[]]' ≈ ⍷ ⊙.M \` 0 ?= \`'[[]]' ≠ ⍷ ⊙.M\` '[[]]' ?= '⍷ ⊙.M' ⊙.M ← [ 1, 0, 0, // also does create and initializes 0, 1, 0, 0, 0, 1, ] ⊙.N ← [ 0, 1, 2 ] '[[]]' ?= '⍷ ⊙.M' '[]' ?= '⍷ ⊙.N' ⍫ ⊙.M ; ⍫ ⊙.N // ⎕ ⊙ // ⊙.test.equals 0 ` /* r += ⍎.er ` ∇ mcode.theta : r // NIU - replaced, performance better as direct calls in OPsub() // was: implements trig fns and also tests switch/case ⍺ ≈ 'atan2' → r = Math.atan2(⍵,δ) // 'atan2' θ.x y ⋄ ⌺.s ⍺ // switch on fn name ⌺ 'sin' : r = Math.sin(⍵) ; ⍗ ⌺ 'cos' : r = Math.cos(⍵) ; ⍗ ⌺ 'tan' : r = Math.tan(⍵) ; ⍗ ⌺ 'sinh' : r = Math.sinh(⍵) ; ⍗ ⌺ 'cosh' : r = Math.cosh(⍵) ; ⍗ ⌺ 'tanh' : r = Math.tanh(⍵) ; ⍗ ⌺ 'asin' : r = Math.asin(⍵) ; ⍗ ⌺ 'acos' : r = Math.acos(⍵) ; ⍗ ⌺ 'atan' : r = Math.atan(⍵) ; ⍗ ⌺ 'asinh' : r = Math.asinh(⍵) ; ⍗ ⌺ 'acosh' : r = Math.acosh(⍵) ; ⍗ ⌺ 'atanh' : r = Math.atanh(⍵) ; ⍗ ⌺.d : r = Math.PI // default is pi, NIU: use π symbol ⍔ r 'θ' ⍙.L 'theta' // Left arg is unquoted ` */ r += ⍎.er ` ∇ mcode.concatenate : r=⍬,i=0,e,t // ⍪ ravel or concatenate // ⎕ 'concatenate' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ⍺ ⥊ ⍬ → // monadic: ravel ⍵ ⍷ [?] → ⍵[0] ⍷ '' → ⍔ ⍵.join('') // join as string, no delimiter 1 == ⍴ ⍵ → ⍔ ⍵[0] // [ x ] to x ⍔ ⍵.flat() // like APL's enlist, make depth 1 δ == ⍬ → // default is concat/join first (row) axis ⍺ ⍷ '' → ⍔ ⍵.join(⍺) // join as string ( ⍺.concat ⍷ () ) ^ ⍵.concat ⍷ () → // concat if both are arrays or matrices ⍔ ⍺.concat(⍵) ⋄ ( δ == 'r' ) → // laminate first (rows) axis ⍺ ⍷ [[]] → // ⍺ is matrix, append ⍵ to last col of ⍺ r = 11 // todo: NYI ⋄ r = [⍺] ; r.push(⍵) ⋄ ( δ == 'c' ) → // laminate second (columns) axis ⍺ ⍷ [[]] → // ⍺ is matrix, append ⍵ to last col of ⍺ r = [] e ⌻ ⍺ : r.push(e.concat(⍵[i++])) ⋄ r = ⍺.concat(⍵) ⍔ r // todo: catenate on strings? (but immutable), table (row or col) '⍪' ⍙ mcode.concatenate ` ⍎ ` ∇ ⊙.test.concatenate // ⎕ 'test.concatenate' // ⍠.a 'd' '"ab"' ?= \` ⍪ [ 'a' 'b' ]\` '"axb"' ?= \` 'x' ⍪ [ 'a' 'b' ]\` ⊙.M ← [ 1, 2, 3, 4 ] ⊙.V ← [ 0, 1, 2 ] // ⎕ ⊙.V ; ⎕ ⊙.M // 'r' ⎕ ⊙.V ⍪.r ⊙.V ⊙.W = [[0,1],2,3] '[0,1,2,3]' ?= '⍪ ⊙.W' // ravel '["ab","cd","ef","gh"]' ?= "⍪ [['ab','cd'],['ef','gh']]" // ravel string matrix '[0,1,2,0,1,2]' ?= '⊙.V ⍪ ⊙.V' // concat/join vectors '[[0,1,2],[0,1,2]]' ?= '⊙.V ⍪.r ⊙.V' // laminate vector to make matrix '[[1,2],[3,4],0,1,2]' ?= '⊙.M ⍪ ⊙.V' // concat matrix to vector '[0,1,2,[1,2],[3,4]]' ?= '⊙.V ⍪ ⊙.M' // concat vector to matrix '[[1,2,0],[3,4,1]]' ?= '⊙.M ⍪.c ⊙.V' // laminate cols of matrix with vector '[0,1,2,[1,2],[3,4]]' ?= '⊙.V ⍪.c ⊙.M' // laminate vector to matrix ⊙.test.concatenate 0 ` r += ⍎.er ` ∇ mcode.iota : r=[],i=0,b,c // ⍳ generate or where // ⎕ 'iota' ; ⎕ ⍺ ; ⎕ ⍵ ⍺ ⥊ ⍬ → // monadic: generate vector of 0..n i < ⍵ ⌸ r.push(i++) ; ⍔ r ⍔ r // ( ⍺ ⍷ [] ) ^ ⍵ ⍷ [] → // indices of ⍺ in ⍵ [ ] r is [ ] // b ⌻ ⍺ : c ⌻ ⍵ : // b == c → r.push(c) ⋄ r.push(null); // ⍔ r ( ⍺ ⍷ [] ) ^ ⍵ ⍷ {} → // values of ⍵ when key ⍺ found in ⍵ r is [ ] b ⌻ ⍺ : c=⍵.get(b) ; r.push(c??null) ⍔ r ⍺ ⍷ '' → ! ⍵ ⍷ '' → ⍔ -1 // ⍺ not in ⍵ ⍔ ⍵.indexOf(⍺) // string ⍺ in string ⍵ // NIU - use ⍺ ⍳ ¨ ⍵ // ( ⍺ ⍷ '' ) ^ ⍵ ⍷ [] → // string ⍺ in each string ⍵ // b ⌻ ⍵ : // b ⍷ '' → // r ↓ b.indexOf(⍺) // ⋄ r ↓ -1 // ⍔ r ⍔ 0 '⍳' ⍙ mcode.iota ` ⍎ ` ∇ ⊙.test.iota // ⎕ 'test.iota' '1' ?= 'x' ⍳ 'axb' ⊙.M ← [ 0, 'a', 1, 'b' ] ⊙.m = ⊙.M ⍙ {} // ⎕ ⊙.m ; 't' ⎕ [ 0 2 ] ⍳ ⊙.m '["a",null]' ?= '[ 0 2 ] ⍳ ⊙.m' // search map '[1,-1]' ?= \`'ab' ⍳ ¨ [ 'xab' 'cde' ]\` // search each string ⊙.test.iota 0 ` r += ⍎.er ` ∇ mcode.shape : r=[],b,c,i=0,j // ⍴ length or size of ⍵ // or reshape vector ⍵ by vector ⍺ // ⎕ 'shape' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ ⍷ ⍵ ⍺ ⥊ ⍬ → ⍵ ⍷ [[]] → r=[⍵.length,⍵[0].length] // assumes matrix not ragged ⋄ ⍵ ⍷ [] → r=[⍵.length] ⋄ ⍵ ⍷ {} → r=⍵.size // nb. non-vector result for maps ⋄ ⍵ ⍷ # → r=0 // shape of scalar and rank 0 ⋄ ⍵ ⍷ '' → ⍔ ⍵.length // string ⋄ r = null // non-array type ⍔ r ⍵ ⍷ # → ⍵ = [ ⍵ ] // convert scalar ⍵ to vector ⍵ ⍷ [[]] → ⍵ = ⍵.flat() // convert array to vector ! ( ⍵ ⍷ [] ) → ⍔ r ⋄ ⍵.length ⥊ 0 → ⍔ r // can't reshape nothing ⋄ ⍺ ⍷ [] → // reshape vector ⍵ into matrix ⍺.length < 2 → ⍺ = [ ⍺ 1 ] i < ⍺[0] ⌸ // i : row j : ⍵ index c : col b = [] ; c=0 ; c < ⍺[1] ⌸ j = (i*⍺[1]+c)%⍵.length b=b.concat(⍵[j]) ; c++ r.push(b) ; i++ ⋄ ⍺ ⍷ # → // reshape vector ⍵ into new vector i < ⍺ ⌸ j=i%(⍵.length) ; r.push(⍵[j]); i++ ⍔ r '⍴' ⍙ mcode.shape ` ⍎ ` ∇ ⊙.test.shape // ⎕ 'test.shape' ⊙.M ← [ 1, 2, 3, 4 ] ⊙.V ← [ 0, 1, 2 ] '3' ?= \`⍴ 'abc'\` ⊙.M = ⊙.V ⍪.r ⊙.V // ⎕ ⊙.V ; ⎕ ⊙.M ; // 'shape V' ⎕ ⍴ ⊙.V '[3]' ?= '⍴ ⊙.V' // 'shape M' ⎕ ⍴ ⊙.M '[2,3]' ?= '⍴ ⊙.M' // 'rank V' ⎕ ⍴ ⍴ ⊙.V '[1]' ?= '⍴ ⍴ ⊙.V' // 'rank M' ⎕ ⍴ ⍴ ⊙.M '[2]' ?= '⍴ ⍴ ⊙.M' '[[0,1,2],[3,4,5]]' ?= '[ 2 3 ] ⍴ ⍳ 6' // 'reshape' ⎕ 4 ⍴ ⍳ 2 '[0,1,0,1]' ?= '4 ⍴ ⍳ 2' // 'reshape 0 0 0 ' ⎕ 3 ⍴ 0 '[0,0,0]' ?= '3 ⍴ 0' // '3x3 identity' ⎕ [ 3 3 ] ⍴ [ 1 0 0 0 ] '[[1,0,0],[0,1,0],[0,0,1]]' ?= '[ 3 3 ] ⍴ [ 1 0 0 0 ]' ⊙.test.shape 0 ` r += ⍎.er ` ∇ mcode.push // ↓ push ⍵ on to stack ⍺ or 'drop' ⍺ elements from ⍵ // ⎕ 'push' ; ⎕ ⍺ ; ⎕ ⍵ ⍺ ⥊ null → ⎕ 'monadic ↓ not impl\\n' ( ⍺ ⍷ [] ) ∨ ⍺ ⍷ [[]] → ⍺.push(⍵) ⋄ ( ⍵ ⍷ [] ) ∨ ( ⍵ ⍷ '' ) → // drop ⍺ is not [] or [[]] ⍺ > 0 → ⍔ ⍵.slice(⍺) // nb. slice does -not- modify strings, but does modify arrays ⋄ ⍔ ⍵.slice(0,⍺) ⍔ ⍺ // ? zvzv // ⋄ ⍺ ⍷ [[]] → // ⍺.splice(⍺.length,0,⍵) ; ⍔ ⍺ // ⋄ ⍺ ⍷ [] → // ⍺.push(⍵) ; ⍔ ⍺ '↓' ⍙ mcode.push ∇ mcode.pop : r // ↑ pop from stack ⍵ or 'take' ⍺ elements from ⍵ ⍺ ⥊ null → r = ⍵.pop() ; ⍔ r ⋄ ( ⍵ ⍷ [] ) ∨ ( ⍵ ⍷ '' ) → // take ⍺ > 0 → ⍔ ⍵.slice(0,⍺) // nb. slice does -not- modify strings, but does modify arrays ⋄ ⍔ ⍵.slice(⍺) ⍔ ⍵ '↑' ⍙ mcode.pop ` ⍎ ` ∇ ⊙.test.push // ⎕ 'test.push' // ⍠.a 'd' ⊙.v = [ 'a', 'b' ] '["a","b","bks","del"]' ?= "( ⊙.v ↓ 'bks' ) ↓ 'del'" // '[0,1,2]' ?= '⍳ 3' ⊙.v = ⍳ 3 '[0,1]' ?= '2 ↑ ⊙.v' '[2]' ?= '2 ↓ ⊙.v' ⊙.s = 'abc' '"ab"' ?= '2 ↑ ⊙.s' '"c"' ?= '-1 ↑ ⊙.s' '"a"' ?= '1 ↑ 2 ↑ ⊙.s' '"c"' ?= '2 ↓ ⊙.s' '"ab"' ?= '-1 ↓ ⊙.s' ⊙.test.push 0 ` r += ⍎.er ` ∇ mcode.insert // » insert (at head or at index) // ⎕ 'insert' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ⍵ ⍷ [] → δ ⥊ ⍬ → ⍵.unshift(⍺) δ ⍷ # → ⍵.splice(δ,0,⍺) ⋄ ⍵ ⍷ '' → δ ⥊ ⍬ → ⍔ ⍺ + ⍵ δ ⍷ # → ⍔ ⍵.slice(0,δ)+⍺+⍵.slice(δ) // nb. return by value, since strings are immutable ⍔ ⍵ // todo: insert at index '»' ⍙ mcode.insert ∇ mcode.remove ⍵ ⍷ [] → ⍵.shift(⍺) // todo: ⋄ ⍵ ⍷ '' → ⍔ ⍺ + ⍵ // « remove (from head or at index) ⍔ ⍵ '«' ⍙ mcode.remove ` ⍎ ` ∇ ⊙.test.insert // ⎕ 'test.insert' ⊙.s = 'abc' ; ⊙.v = ⍳ 3 '[0,"xyz",1,2]' ?= '"xyz" ».1 [ 0 1 2 ]' '"xabc"' ?= '"x" » ⊙.s' '[[8,9],0,1,2]' ?= '[ 8 9 ] » ⊙.v' ⊙.v = ⍳ 3 '["x",0,1,2]' ?= '"x" » ⊙.v' '"abcdef"' ?= '"abc" » "def"' '"dabcef"' ?= '"abc" ».1 "def"' ⊙.test.insert 0 ` r += ⍎.er ` ∇ mcode.match : i // string search δ : e exec ⍺ ⍷ '/' → // ⍺ is a regexp δ==⍬ → ⍔ 0+⍺.test(⍵) δ=='e' → ⍔ ⍺.exec(⍵) // returns exec() array // δ=='m' → ⍔ ⍺.matchAll(⍵) // returns matchAll() array m flag required on regexp ⋄ ⍺ ⍷ '' → i=⍵.indexOf(⍺) ; ⍔ 0+i>=0 // simple string search '≡' ⍙ mcode.match ` r += ⍎.er ` ∇ mcode.replace : b,r='' // string replace ⍺ regexp δ replacement // ⎕ 'replace' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ⍵ ⍷ '' → ⍺ ⍷ '/' → // ⍺ is a regexp ⍺.global → ⍔ ⍵.replaceAll(⍺,δ) ⍔ ⍵.replace(⍺,δ) δ ⥊ 'tmpl' → // wrap any $xyz in brackets for js template strings b ⌻ ⍵.split(' ') ⍂ if (b[0]=='\$') // js replace confused by $ r += ⍺[b.slice(1)] + ' ' ⋄ r += b + ' ' ⍔ r ⍔ ⍵.replaceAll(⍺,δ) ⍔ 0 '≢' ⍙ mcode.replace ` ⍎ ` ∇ ⊙.test.string : e={} // ⎕ 'test.string' ⊙.s = 'abc ok xyz abc' // 1 ?= '/ok/ ≡ ⊙.s' // '"abc nak xyz abc"' ?= '/ok/ ≢.nak ⊙.s' // '"def ok xyz def"' ?= '/abc/g ≢.def ⊙.s' '"abcSokSxyzSabc"' ?=.x /\\s/g ≢.S ⊙.s '"abcSokSxyzSabc"' ?= '\/\\\\s\/g ≢.S ⊙.s' e.sl = 'L' '["L op y","L op2 y"]' ?=.x /x/g ≢.e.sl ¨ [ 'x op y' 'x op2 y' ] '"abc 11 def 22 123 "' ?= \`[ '11' '22' ] ≢.tmpl 'abc $0 def $1 123'\` ⊙.test.string 0 ` r += ⍎.er ` ∇ mcode.split // ⊃ split // ⎕ 'split' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ⍵ ⍷ '' → ⍔ ⍵.split(⍺) ⍔ ⍵ '⊃' ⍙ mcode.split ∇ mcode.join // ⊂ join // ⎕ 'join' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ; 't' ⎕ ⍷ ⍵ ⍵ ⍷ [] → ⍔ ⍵.join(⍺) ⍔ ⍵ '⊂' ⍙ mcode.join ` ⍎ ` ∇ ⊙.test.splitjoin '["abc","def"]' ?= "'x' ⊃ 'abcxdef'" '"abc;def"' ?= '";" ⊂ ["abc","def"]' ⊙.test.splitjoin 0 ` r += ⍎.er ` ∇ mcode.memberof : r=[],b // ∊ is ⍺ member of set ⍵ ⍺ and ⍵ may be vectors ⍵ ⍷ {} → b ⌻ ⍺ : r ↓ ⍵.has(b) ⋄ ⍵ ⍷ [] → ⍺ ⍷ [] → b ⌻ ⍺ : r ↓ 0+⍵.includes(b) ⋄ r=0+⍵.includes(⍺) ⋄ ⍵ ⍷ '' → b ⌻ ⍺ : r ↓ 0+(⍵.indexOf(b)>-1) ⍔ r '∊' ⍙ mcode.memberof ` ⍎ ` ∇ ⊙.test.memberof // ∊ test [ 1 0 ] ?= '[ 2 3 ] ∊ [ 0 1 2 ]' // ⊙.test.memberof 0 ` r += ⍎.er ` ∇ mcode.sort : d,rev=1 // ⍋ sort list ⍵ ⍺ is a sort function // δ flags: r reverse sort direction, v return value array else index array // ⎕ 'sort' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ δ ⍵ ⍷ # → ⍵ = [ ⍵ ] ! ⍵ ⍷ [] → ⍔ 0 // ⍵ must be a list d = ⍵.map((e,i)=>[i,e]) // build data array 0 ≤ 'd' ⍳ δ → rev=-1 // reverse sort direction ∇ comp // ⎕ 'comp' ; ⎕ ⍺ ; ⎕ ⍵ ; ⎕ d ⍺[1] > ⍵[1] → ⍔ rev ⍺[1] < ⍵[1] → ⍔ -1*rev ⍔ 0 ⍺ ⥊ ⍬ → ⍺ ← comp // set comparator fn d.sort(⍺) // sort data array in place // 'd sorted' ⎕ d 0 ≤ 'v' ⍳ δ → // return values, not indices ⍔ d.map(v=>v[1]) ⍔ d.map(v=>v[0]) // return index array '⍋' ⍙ mcode.sort ∇ mcode.sortDown ⍔ mcode.sort(⍺,⍵,'r'+(δ??'')) '⍒' ⍙ mcode.sortDown ` // see // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sorting_with_map ⍎ ` ∇ ⊙.test.sort // ⎕ 'test.sort' ⊙.V = [ 'ab' 'xy' 'cd' ] '[0,2,1]' ?= '⊙.a = ⍋ ⊙.V' '["ab","cd","xy"]' ?= '⊙.a ⌷ ⊙.V' '["ab","cd","xy"]' ?= '⍒.v ⊙.V' ⊙.test.sort 0 ` // r += ⍎.er ` // ∇ mcode.quat // matrix rotations and quaternion operations // ⍔ 0 // NYI // '○' ⍙ 'mcode.quat' // or θ ? // ` // todo: table functions /* tables are objects similar to a database table format ⍕ converts tables to JSON or a readable text format create ⍙ creates new tables */ /* NIU r += ⍎.er ` ∇ mcode.helpFn ⍵=='more' → ⍠ 0 ⍔ '' ∇ mcode.roll δ=='help' → mcode.help() ⋄ ⍵ ⍷ '' → mcode.helpFn ⍵ // roll or deal ⍔ ⍬ '?' ⍙ mcode.roll ` */ r += '\n// File read/write functions' r += ⍎.er ` ∇ mcode.read : p,f // read something or load URL δ is operation ⍺ callback optional or returns promise // ⎕ 'mcode.read' // if ⍺ is null then 1) caller is async fn 2) mexec inserts await left of ⍃ δ ⥊ ⍬ → ⍔ mcode.getURL(⍺,⍵) // read using fetch from server ⋄ δ=='load' → ⍔ mcode.loadURL(⍺,⍵) // uses DOM createElement to load URL ⋄ δ=='ls' → ⍔ mcode.getURL(⍺,'io.php?op=LS') // file list ⋄ δ=='ll' → ⍔ mcode.getURL(⍺,'io.php?op=LL') // file list with details: ls -AgGhFR ⋄ δ=='t' → // make async function wait ⎕ 'read timer ' + ⍵ + 'ms' ⍔ new‗Promise(rs=>setTimeout(()=>rs(0),⍵)) // async caller will wait ⋄ δ=='p' → // must be used in ∇.a async fn p = new‗Promise(rs=>f=rs) // save the resolver function ⍵ instanceof‗ Function → ⍵ f // fn ⍵ calls its ⍵ arg which is rs when done ⍔ p ⋄ δ=='c' → // read console/stdin, must be used in ∇.a async fn // ⎕ 'mcode.getInput' ⎕.nnl ⍵ ⍔ mcode.getInput 0 // returns promise to get console input (see mcode_ide) ⍔ ⍵ '⍃' ⍙ mcode.read // nb. await is inserted by transpiler ` ⍎ ` ∇.a ⊙.test.futures : p // async function ∇.a f // function that will complete in the future ⎕ 'ok' ⍃.t 1000 // await for timer in ms nb. IDE did not wait ⍵('done') // ⍵ is resolver fn p = ⍃.p f // await inserted, f will return result in the future ⎕ p p = ⍃.t 1000 // await for timer in ms nb. IDE did not wait ⎕ p // ⊙.test.futures 0 // nb. test is NIU to not delay startup ` r += ⍎.er ` ∇.a mcode.write : p // write to server restrictions on server side ⍺ callback optional or returns promise // ⎕ 'write' ; ⎕ ⍺ ; ⎕ δ p = await‗fetch('io.php?op=PUT&fn='+δ+'&auth='+mcode.serverAuth,‗ method:"POST",headers:{"Content-Type":"application/octet-stream"},body:⍵‗ // nb. last ‗ suppresses ; ) ⍺ instanceof‗ Function → !p.ok → ⍺(p.status,'') ⋄ p.text().then(⍵=>⍺(p.status,⍵)) // used with callback fn ⋄ p.ok → ⍔ p.text() // used with async/await or .then ⋄ ⍔ p.text() // ).then(rsp=>rsp.text()).then(data=>⍺(0,data)) // .then callback style (for ref) ⍔ null '⍄' ⍙ mcode.write // nb. await is inserted by transpiler ` ⍎ ` ∇.a ⊙.test.io0 : seq=1 ⍠.busy 0 p = await‗ ⍠.timer ⍵ // await for timer in ms here ⎕ 'timer done' seq → // run sequentially ⊙.test.io1 0 ⍠.done 0 ⍔ 0 ` ⍎ ` ∇.a ⊙.test.io1 : f,g1,g2,rs ⍠.busy 0 // { ⎕ ⍵ } ⍄.'test.txt' 'start\\nline 1\\nline 2\\nend' // write test // f = ⍄.'test.txt' 'start\\nline 1\\nline 2\\nend' // async write test // ⎕ f // { ⎕ 'getURL: status = '+⍺+' read:\\n'+⍵ } ⍃ 'out/test.txt' // read test with callback // g = ⍃ 'out/test.txt' // async read test nb. ⍃ does await g1 = ⍃.ls '' // async tests g2 = ⍃.ll '' ⎕ 'io tests' ⎕ 'ls:\\n'+g1 // debug // ⎕ 'll:\\n'+g2 // debug // { ⍎.m 'test' ⎕ ⍵ } ⍃ 'tests.mc' // run more mcode tests ⎕ 'test.io done' // ⍠.done 1 // 1 returned as result to await // ⍠.done ⍃ 'out/test.txt' // or return file as result directly ∇ ⊙.test.io ⎕ '⊙.test.io' ⊙.test.io0 1000 ⊙.test.io1 0 // ⊙.test.io 0 // nb. test is NIU to not delay startup ` r += '\n// Document Object Model (dom) support' r += ⍎.er ` ∇ mcode.dom : w // a few common operations on the HTML DOM ∇.a rd : g // async fn to read html and insert into element g = ⍃ ⍵ ; ⍺.innerHTML = g ; ⍔ ⍺ // document ops, ⍵ is data ⍺ ⥊ ⍬ → δ ⥊ ⍬ → ⍔ document.getElementById(⍵) δ ⥊ 'body' → ⍔ document.body.insertAdjacentHTML('beforeend',⍵) δ ⥊ 'head' → ⍔ document.head.insertAdjacentHTML('beforeend',⍵) δ ⥊ 'css' → ⍔ document.head.insertAdjacentHTML('beforeend','') δ ⥊ 'js' → ⍔ document.head.insertAdjacentHTML('beforeend','') δ ⥊ 'el' → ⍔ document.createElement(⍵) δ ⥊ '+' → ⍔ document.body.append(⍵) δ ⥊ 'qs' → ⍔ document.querySelector(⍵) δ ⥊ 'ael' → ⍔ document.addEventListener(...⍵) // ⍵ = [ type, listener, useCapture ] δ ⥊ 'docwr' → w = window.open('') w.document.write(⍵) w.document.close() ⍔ 0 ⋄ // ⍺ is id or HTML node, ⍵ is data ⍺ ⍷ '' → ⍺ = document.getElementById(⍺) // convert ⍺ id to node !⍺ → ⍔ 0 δ ⥊ ⍬ → ⍺.innerHTML = ⍵ ; ⍔ ⍺ δ ⥊ 'el' → // new el ⍵ with class ⍺, rtns el w = document.createElement(⍵) ; w.classList.add(⍺); ⍔ w δ ⥊ '+' → ⍔ ⍺.appendChild(⍵) // δ ⥊ 'rm' → ⍔ ⍺.removeChild(⍵) δ ⥊ 'rm' → ⍔ ⍺.remove() δ ⥊ 'cl?' → ⍔ ⍺.classList.contains(⍵) δ ⥊ 'cl' → ⍔ ⍺.classList.add(⍵) δ ⥊ 'ael' → ⍔ ⍺.addEventListener(...⍵) // ⍵ = [ type, listener, useCapture ] δ ⥊ 'rd' → ⍔ ⍺ rd ⍵ // read html from file ⍵, append to el ⍺ δ ⥊ 'attr?' → ⍔ ⍺.getAttribute(⍵) δ ⥊ 'attr' → ⍔ ⍺.setAttribute(...⍵) // ⍵ = [ 'attr' value ] δ ⥊ 'dsp' → ⍔ ⍺.style.display = ⍵ δ ⥊ 'clr' → ⍔ ⍺.style.color = ⍵ δ ⥊ 'bg' → ⍔ ⍺.style.backgroundColor = ⍵ δ ⥊ 'brd' → ⍔ ⍺.style.borderColor = ⍵ δ ⥊ 'top' → ⍔ ⍺.style.top = ⍵ δ ⥊ 'left' → ⍔ ⍺.style.left = ⍵ δ ⥊ 'w' → ⍔ ⍺.style.width = ⍵ δ ⥊ 'h' → ⍔ ⍺.style.height = ⍵ ⍔ 0 '⍛' ⍙ 'mcode.dom' ` r += ` // end core ` mcode.cp = {} // clear context mcode.cp.core = r ⍔ ' core loaded, tests passed'