lambdaway
::
meta6
5
|
list
|
login
|
load
|
|
{style body { background:#444; } #page_frame, #page_content, .page_menu { background:transparent; color:#fff; border:0; box-shadow:0 0 0 #000; } } {require lib_uncover} {uncover http://epsilonwiki.free.fr/lambdaway/data/mies_pav.jpg 50 500 Less is more, Mies van der Rohe.} {center [[fromroots2canopy]] | [[meta4]] | [[meta5]] | meta6 | ... | [[meta8]]} _h1 (fun calc) _p See also this [[french|http://lambdaway.free.fr/lambdawalks/?view=reflexions_20201219]] version. _p {b (fun calc)} is a minimal fork of '{lambda talk}, working in the wiki, '{lambda tank}, without any {b if} or {b let} special forms, seen as optional syntactic sugars. {b No useless noise!} The goal is to write the cleanest code for {b functions} and to get a chance to introduce {b closures}, to make (on demand) inner functions able to {b automatically} get variables and values defined in their outer local context. _p See the [[console version|data/funcalc/]] of (fun calc) working out this wiki and [[ward's test|http://found.ward.bay.wiki.org/view/fun-calc/view/fun-calc-examples]]. _h2 introduction _p A {b (fun calc)} term is recursively defined as follows {div {@ style="font:bold italic 2.0em times; text-align:center;"} x = w | (fun (:w) x) | (def w x) | (x x) } _p where _ul {b x} is a valid expression _ul {b w} is a word, a group of any character(s) except spaces and braces {b ()} _ul {b (fun (:w) x)} is an abstraction adding to some DICTionary an anonymous function, with {b :w} as argument(s) and {b x} as body, _ul {b (def w x)} is an abstraction binding in the DICTionary a word {b w} to an evaluated expression {b x}, _ul {b (x x)} is an application returning words. _p and the evaluation of a (fun calc) term is done in this order: {b 1) words}, which are evaluated to themselves or to a function's reference if it exists, {b 2) abstractions} and {b 3) applications} which all return words. _p Inside the wiki context {b nested parenthesized prefixed expressions} {code (first rest)} are written in a {code '{eval ...}} '{lambda talk} primitive which triggers the (fun calc) META evaluator. {pre '{eval ... (first rest) -> words ... }} _p The DICTionary comes with a reduced set of {b 6 javascript primitives} {code [equal?,+,*,-,/,sqrt,...]} working on javascript numbers. This set is optional (it could be empty) and extendable on demand and online. _p We have now the required tools to explore nested expressions, functions, booleans, a few data and control structures like pairs, lists and recursion, and more. _h2 1. examples of (fun calc) code {div {@ id="myconsole"}} {pre {eval {b 1. basic nested expressions} '(+ 1 2) -> (+ 1 2) '(+ 1 (* 2 3) 4) // evaluation from inside out '(greedy) -> (+ 1 (* 2 3) 4) '(sqrt (+ (* 3 3) (* 4 4))) -> (sqrt (+ (* 3 3) (* 4 4))) '(* 1 2 3 4 5 6) // +, -, *, / are de facto variadic -> (* 1 2 3 4 5 6) '(foo hello world) -> (foo hello world) '(+ hello world) -> (+ hello world) // Not a Number {b 2. functions and IIFEs} an IIFE is an immediately invoked function expression functions arguments are {b systematically} prefixed by a colon : '(fun (:x) :x) // identity function -> (fun (:x) :x) // evaluated to a system defined reference '((fun (:x) :x) hello) // it's an IIFE -> ((fun (:x) :x) hello) '((fun (:x :y) // replace x and y my name is :y, :x :y. // in this sentence ) james bond) // by james and bond -> ((fun (:x :y) my name is :y, :x :y.) james bond) '((fun (:a :b) (+ :a :b)) 1 2) -> ((fun (:a :b) (+ :a :b)) 1 2) '((fun (:a :b) (+ :a :b)) 3 4) -> ((fun (:a :b) (+ :a :b)) 3 4) {b 3. user defined constants} '(def HI hello world) -> (def HI hello world) '(HI) -> (HI) '(def φ (/ (+ 1 (sqrt 5)) 2)) -> (def φ (/ (+ 1 (sqrt 5)) 2)) '(φ) -> (φ) // the golden ratio {b 4. user defined functions} '(def add (fun (:a :b) (+ :a :b) )) -> (def add (fun (:a :b) (+ :a :b) )) '(add 1 2) -> (add 1 2) '(add 3 4) -> (add 3 4) '(add 5) // a function accepts partial calls -> (add 5) // and returns a function waiting for missing values '((add 5) 6) // functions accept sequential calls '(currying) -> ((add 5) 6) '(add 1 2 3 4 5) // functions accept any number of values '(variadicity) -> (add 1 2 3 4 5) '(def area // functions can be nested (fun (:a :b :c) ((fun (
:a :b :c
:s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) )
:a :b :c
(/ (+ :a :b :c) 2) ))) -> (def area (fun (:a :b :c) ((fun (:a :b :c :s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) :a :b :c (/ (+ :a :b :c) 2) ))) '(area 3 4 5) // tracing shown in the 4th section -> (area 3 4 5) '(area 1 1 (sqrt 2)) -> (area 1 1 (sqrt 2)) {b 5. booleans} '(def true (fun (:a :b) :a)) -> (def true (fun (:a :b) :a)) '(def false (fun (:a :b) :b)) -> (def false (fun (:a :b) :b)) '(def if (fun (:a :b :c) (:a :b :c))) // it's not a special form '(no laziness) -> (def if (fun (:a :b :c) (:a :b :c))) '(if true hello world) -> (if true hello world) '(if false hello world) -> (if false hello world) {b 6. looping with recursion} '(equal? hello hello) -> (equal? hello hello) '(equal? hello world) -> (equal? hello world) '(def fac (fun (:n) ((if (equal? :n 0) (fun (:n) 1) // embedding terms in a function (fun (:n) (* :n (fac (- :n 1))))) :n))) // introduces laziness -> (def fac (fun (:n) ((if (equal? :n 0) (fun (:n) 1) (fun (:n) (* :n (fac (- :n 1))))) :n))) '(fac 6) -> (fac 6) '(fac 100) -> (fac 100) {b 7. pairs & lists} '(def cons (fun (:a :b :c) (:c :a :b))) -> (def cons (fun (:a :b :c) (:c :a :b))) '(def car (fun (:c) (:c (fun (:a :b) :a)))) -> (def car (fun (:c) (:c (fun (:a :b) :a)))) '(def cdr (fun (:c) (:c (fun (:a :b) :b)))) -> (def cdr (fun (:c) (:c (fun (:a :b) :b)))) '(def P (cons hello world)) -> (def P (cons hello world)) '(P) -> (P) '(car (P)) -> (car (P)) '(cdr (P)) -> (cdr (P)) '(def L (cons hello (cons brave (cons new (cons world nil))))) -> (def L (cons hello (cons brave (cons new (cons world nil))))) '(L) -> (L) '(def list.disp (fun (:l) ((if (equal? :l nil) (fun (:l) ) (fun (:l) (car :l) (list.disp (cdr :l)))) :l))) -> (def list.disp (fun (:l) ((if (equal? :l nil) (fun (:l) ) (fun (:l) (car :l) (list.disp (cdr :l)))) :l))) '(list.disp (L)) -> (list.disp (L)) {b 8. serie & map} '(def serie (fun (:a :b :s) ((if (equal? :a :b) (fun (:a :b :s) (cons :a nil)) (fun (:a :b :s) (cons :a (serie (+ :a :s) :b :s)))) :a :b :s))) -> (def serie (fun (:a :b :s) ((if (equal? :a :b) (fun (:a :b :s) (cons :a nil)) (fun (:a :b :s) (cons :a (serie (+ :a :s) :b :s)))) :a :b :s))) '(list.disp (serie 0 10 2)) -> (list.disp (serie 0 10 2)) '(* (list.disp (serie 1 10 1))) -> (* (list.disp (serie 1 10 1))) '(def map (fun (:f :s) ((if (equal? :s nil) (fun (:f :s) nil) (fun (:f :s) (cons (:f (car :s)) (map :f (cdr :s))))) :f :s))) -> (def map (fun (:f :s) ((if (equal? :s nil) (fun (:f :s) nil) (fun (:f :s) (cons (:f (car :s)) (map :f (cdr :s))))) :f :s))) '(list.disp (map (fun (:i) (* :i :i)) (serie 0 20 2))) -> (list.disp (map (fun (:i) (* :i :i)) (serie 0 20 2))) {b 9. the towers of hanoï} '(def hanoi (fun (:n :from :to :via) ((if (equal? :n 0) (fun (:n :from :to :via) ) (fun (:n :from :to :via) (hanoi (- :n 1) :from :via :to) '{br}move disk :n from tower :from to tower :to (hanoi (- :n 1) :via :to :from) )) :n :from :to :via))) -> (def hanoi (fun (:n :from :to :via) ((if (equal? :n 0) (fun (:n :from :to :via) ) (fun (:n :from :to :via) (hanoi (- :n 1) :from :via :to) {br}move disk :n from tower :from to tower :to (hanoi (- :n 1) :via :to :from) )) :n :from :to :via))) '(hanoi 5 A B C) -> (hanoi 5 A B C) {b 10. the Y-combinator} 1. we can apply such a "bridge" function '(def Y (fun (:f) (:f :f))) -> (def Y (fun (:f) (:f :f))) 2. to an "almost recursive" version of fac '(def almost_fac (fun (:f :n) ((if (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) -> (def almost_fac (fun (:f :n) ((if (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) 3. and get the required result '((Y almost_fac) 6) -> ((Y almost_fac) 6) '((Y almost_fac) 100) -> ((Y almost_fac) 100) {b ... as simple as that!} You may compare this approach of the Y-combinator with other papers on the subject like [[Discovering the Y-Combinator by Mistake|https://www.lambdacircle.com/discovering-the-y-combinator-by-mistake-1-2/]]. 4. Well we could stop here but let's go further, let's glue Y and almost_fac into a new one, Yfac '(def Yfac (fun (:n) (((fun (:f) (:f :f)) (fun (:f :n) ((if (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) :n))) -> (def Yfac (fun (:n) (((fun (:f) (:f :f)) (fun (:f :n) ((if (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) :n))) '(Yfac 6) -> (Yfac 6) 5. Now we can forget the name, write an IIFE '((fun (:n) (((fun (:f) (:f :f)) (fun (:f :n) ((if (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) :n)) 6) -> ((fun (:n) (((fun (:f) (:f :f)) (fun (:f :n) ((if (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) :n)) 6) 6. and even forget the function {b if} '((fun (:n) (((fun (:f) (:f :f)) (fun (:f :n) (((fun (:a :b :c) (:a :b :c)) (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) :n)) 6) -> ((fun (:n) (((fun (:f) (:f :f)) (fun (:f :n) (((fun (:a :b :c) (:a :b :c)) (equal? :n 0) (fun (:f :n) 1) (fun (:f :n) (* :n (:f :f (- :n 1))))) :f :n))) :n)) 6) }} _p Finally following Alonso Church, we could implement natural numbers and arithmetical operators {b [=,+,-,*,/]} as functions, and enter in the wonderful - Less is More - world of λ-calculus where everything is reduced to {div {@ style="font:bold italic 2.0em times; text-align:center;"} x = w | (fun (:w) x) | (x x) } _p But it's another story narrated in [[fromroots2canopy]]. {hr} _h2 2. the JS code _p This wiki page contains active code and is computed in about {b 15ms} on my PowerBook Pro. Open the frame editor and test as you want. This is the underlying javascript code. _h3 1. the lambdatalk interface {pre °° //// Lambdatank interface: {eval any META expression} LAMBDATALK.DICT["eval"] = function() { var args = arguments[0].trim(); var bal = META.balance(args); return (bal.left === bal.right) ? META.evaluate( args ) : '('+bal.left+'|'+bal.right+')'; }; °°} _p Outside this wiki, in some HTML file, the {b (fun calc)} evaluator can be called from an HTML texarea element answering to such an event {code onkeyup="evaluate()"} with {pre °° var evaluate = function() { var args = arguments[0].trim(); var bal = META.balance(args); return (bal.left === bal.right) ? META.evaluate( args ) : '('+bal.left+'|'+bal.right+')'; }; °°} _h3 2. the META evaluator {pre °° var META = (function() { var DICT = {}, FUN_num = 0, regexp = /\(([^\s()]*)(?:[\s]*)([^()]*)\)/g; var evaluate = function(s) { s = pre_processing(s); s = eval_specials(s, "'", eval_apo, null); s = eval_specials(s, "fun", eval_fun, null); s = eval_specials(s, "def", eval_def, true); s = eval_forms(s); s = post_processing(s); return s; }; var eval_forms = function(s) { while ( s !== (s = s.replace(regexp, eval_form))) ; return s; }; var eval_specials = function(s, special, eval_special, env) { // sequence of (nested) special forms var exp = special_catch(special, s); // (special exp) -> exp if (exp === "none") { // no more special forms return s } else { var one = "(" + special + " " + exp + ")"; var two = eval_special(exp, env); s = s.replace(one, two); return eval_specials(s, special, eval_special, env) } }; var eval_form = function() { var f = arguments[1] || "", r = arguments[2] || ""; if (DICT.hasOwnProperty(f)) return DICT[f].apply(null, [r]) else return f + " is unknown"; }; var eval_fun = function(s, env) { // eval a (nested) lambda var index = s.indexOf(")"), argStr = supertrim(s.substring(1, index)), args = argStr === "" ? [] : argStr.split(" "), body = supertrim(s.substring(index + 2)), ref = "_FUN_" + FUN_num++; body = eval_specials(body, "fun", eval_fun, env); // recurse inside DICT[ref] = function() { var valStr = supertrim(arguments[0]), vals = valStr === "" ? [] : valStr.split(" "), bod = body; if (vals.length < args.length) { // 1) partial call for (var i = 0; i < vals.length; i++) bod = bod.replace(RegExp(args[i], "g"), vals[i]); var _args_ = args.slice(vals.length).join(" "); bod = eval_fun("(" + _args_ + ") " + bod, env); } else if (vals.length === args.length) { // 2) total call for (var i=0; i < args.length; i++) bod = bod.replace( RegExp(args[i], "g"), vals[i] ); } else { // 3) extra in the last one var _vals_ = vals.slice(0,args.length); _vals_[args.length-1] = vals.slice(args.length-1,vals.length).join(' '); for (var i=0; i < args.length; i++) bod = bod.replace( RegExp(args[i], "g"), _vals_[i] ); } return eval_forms(bod); }; return ref; }; var eval_def = function(s, env) { s = eval_specials(s, "def", eval_def, false); var index = s.search(/\s/); var name = s.substring(0, index).trim(); var body = s.substring(index).trim(); if (body.substring(0, 5) === "_FUN_") { DICT[name] = DICT[body]; // an alias } else { body = eval_forms(body); DICT[name] = function() { return body; }; } return env ? name : ""; }; var special_catch = function(symbol, str) { symbol = "(" + symbol + " "; var start = str.indexOf(symbol); if (start == -1) return "none"; var nb = 1, index = start; while (nb > 0 && index < 100000) { index++; if (str.charAt(index) == "(") nb++; else if (str.charAt(index) == ")") nb--; } return str.substring(start + symbol.length, index); }; var eval_apo = function(s) { return "«" + s.replace( /\(/g, "«" ).replace( /\)/g, "»" ) + "»" }; var pre_processing = function(s) { return s.replace( /'\(/g , "(' " ) }; var post_processing = function(s) { FUN_num = 0; return s.replace( /«/g, "(" ).replace( /»/g, ")" ) }; var supertrim = function(s) { return s.trim().replace(/\s+/g, " ") }; var balance = function(s) { var strt = s.match(/\(/g), stop = s.match(/\)/g); strt = strt ? strt.length : 0; stop = stop ? stop.length : 0; return { left: strt, right: stop }; }; return { // public functions supertrim:supertrim, balance:balance, evaluate:evaluate, DICT:DICT } })(); // end META °°} _h3 3. the DICTionary _p We just start populating the DICTionary, more could be added following the set of lambdatalk primitives in [[meca/JS.js]]. {pre °° META.DICT["equal?"] = function() { var a = META.supertrim(arguments[0]).split(" "); return a[0] === a[1] }; META.DICT["+"] = function() { var a = META.supertrim(arguments[0]).split(" "), r; if (a.length === 0) r = 0; else if (a.length === 1) r = a[0]; else if (a.length === 2) r = Number(a[0]) + Number(a[1]); else for (var r = 0, i = 0; i < a.length; i++) r += Number(a[i]); return r; }; META.DICT["*"] = function() { var a = META.supertrim(arguments[0]).split(" "), r; if (a.length === 0) r = 1; else if (a.length === 1) r = a[0]; else if (a.length === 2) r = a[0] * a[1]; else for (var r = 1, i = 0; i < a.length; i++) r *= a[i]; return r; }; META.DICT["-"] = function() { var a = META.supertrim(arguments[0]).split(" "); var r = a[0]; if (a.length === 1) r = -r; else for (var i = 1; i < a.length; i++) r -= a[i]; return r; }; META.DICT["/"] = function() { var a = META.supertrim(arguments[0]).split(" "); var r = a[0]; if (a.length === 1) r = 1 / r; else for (var i = 1; i < a.length; i++) r /= a[i]; return r; }; META.DICT["sqrt"] = function() { var a = META.supertrim(arguments[0]); return Math.sqrt( a ); }; //// messages displayed in {div {@ id="myconsole"}} var message = "console:< br>" var trace = function() { setTimeout( function() { document.getElementById("myconsole").innerHTML = message }, 1) }; °°} {hr} _h3 4. tracing the evaluation of (area 3 4 5) _p First of all {b (fun calc)} functions don't accept free variables getting automatically their values from the local context. If needed in the inner function's body, outer function's arguments must be
manually
added in the inner function's arguments list. And they get their values thanks to an IIFE. _p It's the case in the {code area} function shown in section 1) with this code {pre (def area (fun (:a :b :c) ((fun ({u {b :a :b :c}} :s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) {u {b :a :b :c}} (/ (+ :a :b :c) 2) ))) } _p writing {pre (def area (fun (:a :b :c) ((fun (:s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) (/ (+ :a :b :c) 2) ))) } _p doesn't work. So let's trace. _p The area of a triangle [a,b,c] is given by {center {pre √{span {@ style="border-top:1px solid"} s*(s-a)*(s-b)*(s-c) } where s = (a+b+c)/2}} _p In order to avoid computing 4 times the intermediate variable {b s}, the function's body is built as an IIFE where an anonymous function adding {b s} to {b a,b,c} is applied to the values given to {b a,b,c} and to {b s} computed once. _p In fact, for short, we will trace the IIFE equivalent to the {b (area 3 4 5)} expression: {pre ((fun (:a :b :c) ((fun (:a :b :c :s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) :a :b :c (/ (+ :a :b :c) 2) )) 3 4 5) } _p Abstractions are first evaluated, here reduced to two nested functions {code (fun (w) x)}, the inner one being part of an IIFE defining the function's body. {pre (fun (:a :b :c) ((fun (:a :b :c :s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) :a :b :c (/ (+ :a :b :c) 2) )) -> args: [:a,:b,:c] -> body: ((fun (:a :b :c :s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) :a :b :c (/ (+ :a :b :c) 2) ) body is evaluated, looking for (fun ...) -> args: [:a,:b,:c,:s] -> body: (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) -> return _FUN_123 -> body: (_FUN_123 :a :b :c (/ (+ :a :b :c) 2) ) -> return _FUN_124 -> (_FUN_124 3 4 5) } _p Now the residual form can be evaluated {pre (_FUN_124 3 4 5) -> ((fun (:a :b :c) (_FUN_123 :a :b :c (/ (+ :a :b :c) 2) ) ) 3 4 5) -> (_FUN_123 3 4 5 (/ (+ 3 4 5) 2) ) -> (_FUN_123 3 4 5 6) -> ((fun (:a :b :c :s) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ) 3 4 5 6) -> (sqrt (* 6 (- 6 3) (- 6 4) (- 6 5))) -> (sqrt (* 6 3 2 1)) -> (sqrt 36) -> 6 // the area of the triangle [3,4,5] } _h2 conclusion _p In the real life a {b let} special form should be added to highlight the binding of local variables and their values: {pre ((fun (:var ...) body) :val ...) could be better written (let ( (:var :val) // local variables ... ) body // local context ) } _p for instance the {b area} function could be rewritten more simply {pre (def area (fun (:a :b :c) (let ( (:a :a) (:b :b) (:c :c) // reassign outer arguments (:s (/ (+ :a :b :c) 2)) // adding the new computed value ) (sqrt (* :s (- :s :a) (- :s :b) (- :s :c))) ))) } _p The {b if} user defined function should be promoted as a special form, with an {i automatic} lazy behaviour, allowing to rewrite the factorial function more simply {pre (def fac (fun (:n) (if (equal? :n 0) // or better (= :n 0) then 1 else (* :n (fac (- :n 1))) ))) } _p Then could be added set of primitives dealing with words, sentences, pairs, lists, arrays, HTML markup, ... and also libraries for complex numbers, big numbers, matrix calculus, 2D and 3D graphics ... _p And {b (fun calc)} would become '{lambda talk}. {uncover data/camouflage-cameleon.jpg 100 700 '{lambda talk} is a kind of cameleon installed on web tools} _p {i Alain Marty, 2020/12/04-08} {script var META = (function() { var DICT = {}, FUN_num = 0, regexp = /\(([^\s()]*)(?:[\s]*)([^()]*)\)/g; var evaluate = function(s) { s = pre_processing(s); s = eval_specials(s, "'", eval_apo, null); s = eval_specials(s, "fun", eval_fun, null); s = eval_specials(s, "def", eval_def, true); s = eval_forms(s); s = post_processing(s); return s; }; var pre_processing = function(s) { return s.replace( /'\(/g , "(' " ) }; var post_processing = function(s) { FUN_num = 0; return s.replace( /«/g, "(" ).replace( /»/g, ")" ) }; var eval_forms = function(s) { while ( s !== (s = s.replace(regexp, eval_form))) ; return s; }; var eval_specials = function(s, special, eval_special, env) { // sequence of (nested) special forms var exp = special_catch(special, s); // (special exp) -> exp if (exp === "none") { // no more special forms return s } else { var one = "(" + special + " " + exp + ")"; var two = eval_special(exp, env); s = s.replace(one, two); return eval_specials(s, special, eval_special, env) } }; var eval_apo = function(s) { return "«" + s.replace( /\(/g, "«" ).replace( /\)/g, "»" ) + "»" }; var eval_form = function() { var f = arguments[1] || "", r = arguments[2] || ""; if (DICT.hasOwnProperty(f)) return DICT[f].apply(null, [r]) else return f + " is unknown"; }; var eval_fun = function(s, env) { // eval a (nested) lambda var index = s.indexOf(")"), argStr = supertrim(s.substring(1, index)), args = argStr === "" ? [] : argStr.split(" "), body = supertrim(s.substring(index + 2)), ref = "_FUN_" + FUN_num++; body = eval_specials(body, "fun", eval_fun, env); // recurse inside DICT[ref] = function() { var valStr = supertrim(arguments[0]), vals = valStr === "" ? [] : valStr.split(" "), bod = body; if (vals.length < args.length) { // 1) partial call for (var i = 0; i < vals.length; i++) bod = bod.replace(RegExp(args[i], "g"), vals[i]); var _args_ = args.slice(vals.length).join(" "); bod = eval_fun("(" + _args_ + ") " + bod, env); } else if (vals.length === args.length) { // 2) total call for (var i=0; i < args.length; i++) bod = bod.replace( RegExp(args[i], "g"), vals[i] ); } else { // 3) extra in the last one var _vals_ = vals.slice(0,args.length); _vals_[args.length-1] = vals.slice(args.length-1,vals.length).join(' '); for (var i=0; i < args.length; i++) bod = bod.replace( RegExp(args[i], "g"), _vals_[i] ); } return eval_forms(bod); }; return ref; }; var eval_def = function(s, env) { s = eval_specials(s, "def", eval_def, false); var index = s.search(/\s/); var name = s.substring(0, index).trim(); var body = s.substring(index).trim(); if (body.substring(0, 5) === "_FUN_") { DICT[name] = DICT[body]; // an alias } else { body = eval_forms(body); DICT[name] = function() { return body; }; } return env ? name : ""; }; var special_catch = function(symbol, str) { // input: (symbol exp) // side effect: none // output: exp or none symbol = "(" + symbol + " "; var start = str.indexOf(symbol); if (start == -1) return "none"; var nb = 1, index = start; while (nb > 0 && index < 100000) { index++; if (str.charAt(index) == "(") nb++; else if (str.charAt(index) == ")") nb--; } return str.substring(start + symbol.length, index); }; var supertrim = function(s) { return s.trim().replace(/\s+/g, " ") // return s.trim() }; var balance = function(s) { // input: a sequence of expressions {first rest} // side effect: none // output: the pair {left,right} var strt = s.match(/\(/g), stop = s.match(/\)/g); strt = strt ? strt.length : 0; stop = stop ? stop.length : 0; return { left: strt, right: stop }; }; return { // public functions balance:balance, supertrim:supertrim, evaluate:evaluate, DICT:DICT } })(); // end META //// Lambdatank interface: {eval any META expression} LAMBDATALK.DICT["eval"] = function() { var args = arguments[0].trim(); var bal = META.balance(args); return (bal.left === bal.right) ? META.evaluate( args ) : '('+bal.left+'|'+bal.right+')'; }; //// starting populting the DICTionary META.DICT["equal?"] = function() { var a = META.supertrim(arguments[0]).split(" "); return a[0] === a[1] }; META.DICT["+"] = function() { var a = META.supertrim(arguments[0]).split(" "), r; if (a.length === 0) r = 0; else if (a.length === 1) r = a[0]; else if (a.length === 2) r = Number(a[0]) + Number(a[1]); else for (var r = 0, i = 0; i < a.length; i++) r += Number(a[i]); return r; }; META.DICT["*"] = function() { var a = META.supertrim(arguments[0]).split(" "), r; if (a.length === 0) r = 1; else if (a.length === 1) r = a[0]; else if (a.length === 2) r = a[0] * a[1]; else for (var r = 1, i = 0; i < a.length; i++) r *= a[i]; return r; }; META.DICT["-"] = function() { var a = META.supertrim(arguments[0]).split(" "); var r = a[0]; if (a.length === 1) r = -r; else for (var i = 1; i < a.length; i++) r -= a[i]; return r; }; META.DICT["/"] = function() { var a = META.supertrim(arguments[0]).split(" "); var r = a[0]; if (a.length === 1) r = 1 / r; else for (var i = 1; i < a.length; i++) r /= a[i]; return r; }; META.DICT["sqrt"] = function() { var a = META.supertrim(arguments[0]); return Math.sqrt( a ); }; //// messages displayed in {div {@ id="myconsole"}} var message = "console:
" var trace = function() { setTimeout( function() { document.getElementById("myconsole").innerHTML = message }, 1) }; }
lambdaway v.20211111