From fbc68b9393f59f45b0ad980f104574784b17ead6 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 10 Jun 2018 16:18:39 +0300 Subject: [PATCH] moved Slang to a separate repo... Signed-off-by: Alex A. Naanou --- Slang/README.md | 4 - Slang/manifest.json | 7 - Slang/slang.appcache | 11 - Slang/slang.html | 279 ----------- Slang/slang.js | 1134 ------------------------------------------ 5 files changed, 1435 deletions(-) delete mode 100644 Slang/README.md delete mode 100755 Slang/manifest.json delete mode 100755 Slang/slang.appcache delete mode 100755 Slang/slang.html delete mode 100755 Slang/slang.js diff --git a/Slang/README.md b/Slang/README.md deleted file mode 100644 index 7a441e1..0000000 --- a/Slang/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Slang - -For and interactive version of Slang interpreter go here: -http://flynx.github.io/Course-JavaScript/Slang/slang.html diff --git a/Slang/manifest.json b/Slang/manifest.json deleted file mode 100755 index 4276016..0000000 --- a/Slang/manifest.json +++ /dev/null @@ -1,7 +0,0 @@ - -{ - "short_name": "Slang", - "name": "Slang", - "display": "standalone", - "Theme_color": "white" -} diff --git a/Slang/slang.appcache b/Slang/slang.appcache deleted file mode 100755 index 9f31719..0000000 --- a/Slang/slang.appcache +++ /dev/null @@ -1,11 +0,0 @@ -CACHE MANIFEST -# Timestamp: 20170929020625 - -CACHE: -slang.html -slang.js -manifest.json - -NETWORK: -* - diff --git a/Slang/slang.html b/Slang/slang.html deleted file mode 100755 index 4896af6..0000000 --- a/Slang/slang.html +++ /dev/null @@ -1,279 +0,0 @@ - - - -Slang - - - - - - - - - - - - - -

Slang

- Toggle bootstrap code view... -
-

Available words

-

- This section includes constants (red) and constant-like - words (words that allways yield the same value), - built-in words (blue), and 2'nd gen words (black): -

-

-

Slang Console

-
-
-
-
- - - diff --git a/Slang/slang.js b/Slang/slang.js deleted file mode 100755 index 17809a8..0000000 --- a/Slang/slang.js +++ /dev/null @@ -1,1134 +0,0 @@ -/********************************************************************** -* -* -* -**********************************************************************/ - -/* XXX for some odd reason this breaks the interpreter... -Array.prototype.toString = function(){ - return '[ ' + this.join(', ') + ' ]' -} -*/ - - -/*********************************************************************/ - -function run(context){ - - context.stack = context.stack == null ? [] : context.stack - - while(context.code.length > 0){ - - var cur = context.code.splice(0, 1)[0] - - // exit... - if(typeof(cur) == typeof('abc') && cur == '_exit'){ - return context - - // word - } else if(typeof(cur) == typeof('abc') && cur in context.ns){ - var word = context.ns[cur] - // low-level word... - if(typeof(word) == typeof(function(){})){ - var res = context.ns[cur](context) - - // hi-level word... - } else if(typeof(word) == typeof([]) && word && word.constructor.name == 'Array'){ - // XXX add isolation with a continuation... - context.code.splice.apply(context.code, [0, 0].concat(word)) - var res = undefined - - // variable... - } else { - var res = word - } - - if(res !== undefined){ - context.stack.push(res) - } - - // everything else... - } else { - context.stack.push(cur) - } - } - - return context -} - -// XXX make this add '\n' / EOL words to the stream... -//var SPLITTER = /\s*\([^\)]*\)\s*|\s*--.*[\n$]|\s*"([^"]*)"\s*|\s*'([^']*)'\s*|\s+/m -var SPLITTER = RegExp([ - /* XXX there are two ways to deal with comments: - // 1) lexer-based -- this section commented, next uncommented... - // 2) macro-based -- this section uncommented, next commented... - // #2 is a bit buggy... - // terms to keep in the stream... - '\\s*('+[ - '\\n', - '--', - ].join('|')+')', - //*/ - - //* lexer comments... - '\\s*\\([^\\)]*\\)\\s*', - '\\s*--.*[\\n$]', - //*/ - - // quoted strings... - '\\s*"([^"]*)"\\s*', - "\\s*'([^']*)'\\s*", - - // quote... - '\\s*(\\\\)', - - // whitespace... - '\\s+', - ].join('|'), - 'm') - - -// helpers... -// XXX should these skip quoted ends? -var collect = function(start, end){ - return function(context){ - var res = start ? [start] : [] - var code = context.code - var cur = code.shift() - res.push(cur) - while(cur != end && code.length > 0){ - cur = code.shift() - res.push(cur) - } - return res - } -} -var drop = function(start, end){ - var collector = collect(start, end) - //return function(context){ collector(context) } - return function(context){ console.log('XXX', collector(context).join(' ')) } -} - - -// pre-processor namespace... -var PRE_NAMESPACE = { - // comment... - // syntax: -- ... \n - // - // drop everything until '\n' - // - // NOTE: this depends on explicit '\n' words... - //'--': drop('--', '\n'), - '(': drop('(', ')'), - - // XXX use the real reader... - // block... - // syntax: [ ... ] - // NOTE: the shorthand ']]' will close ALL the open blocks. - // XXX should ']]' be smart enough to look ahead and close only the - // blocks not explicitly closed later??? - // ..see below for more... - '[': function(context){ - var block = [] - var code = context.code - var cur = code.splice(0, 1)[0] - while(cur != ']' && cur != ']]' && code.length > 0){ - if(cur == '['){ - cur = this['['](context) - } - block.push(cur) - cur = code.splice(0, 1)[0] - } - // we need this to be able to jump out of all the nested blocks, - // thus we'll keep the ']]' in code and remove it explicitly - // later... - if(cur == ']]'){ - // XXX should we look ahead and count the explicitly closed - // via ']' and ']]' blocks??? - // ...at this point this seems a bit complex... - // ...if there are more than one ']]' in a structure - // this might stop being deterministic... - code.splice(0, 0, cur) - } - if(code.length == 0 && cur != ']' && cur != ']]'){ - console.error('Did not find expected "]".') - } - return block - }, - // drop the closing words... - ']]': function(context){}, - ']': function(context){ console.error('Unexpected "]".') }, - - // XXX macros are not recursive... - 'macro:': function(context){ - var ident = context.code.splice(0, 1) - var cur = context.code.splice(0, 1) - - // as we do not have blocks yet we need to manually collect one ;) - if(cur[0] == '['){ - cur = [ this['['](context) ] - } - - this[ident] = cur[0] - }, - - // a no op... - '\n': function(){ }, -} - - -// main namespace... -var NAMESPACE = { - // constants... - 'true': true, - 'false': false, - 'null': 'null', - - 'nop': function(){}, - - 'is?': function(context){ - return context.stack.pop() === context.stack.pop() }, - - // XXX experimental... - // flip the code and stack... - // ... -- ... - '_flip': function(context){ - var stack = context.stack - context.stack = context.code.reverse() - context.code = stack.reverse() - }, - - // swap heads of stack and code - // ... ns nc -- ... - '_swapN': function(context){ - var c = context.stack.pop() - var s = context.stack.pop() - var l = context.stack.length - - // get the stack/code sections to swap... - var s_c = context.stack.splice(l-s, l) - var c_c = context.code.splice(0, c) - var l = context.stack.length - - // we need to pad e_c and c_c to requested length... - s_c = s_c.length < s ? s_c.concat(Array( s - s_c.length )) : s_c - c_c = c_c.length < c ? c_c.concat(Array( c - c_c.length )) : c_c - - // XXX we also need to shove something more sensible in the - // padding that undefined... - - context.code.splice.apply(context.code, [0, 0].concat(s_c)) - context.stack.splice.apply(context.stack, [l, 0].concat(c_c)) - }, - - // encapsulate stack to a block... - // ... -- [ ... ] - 's2b': function(context){ - context.stack = [ context.stack ] }, - // expand block to stack... - // NOTE: this will append the block contents to stack, w.o. replacing - // stack contents. this is different to _s2b - // ... [ ... ] -- ... ... - 'b2s': function(context){ - var c = context.stack.pop() - c = c === undefined ? [] : c - context.stack.splice.apply(context.stack, [context.stack.length, 0].concat(c)) - }, - 'print': function(context){ - console.log('>>>', context.stack[context.stack.length-1]) }, - - // turn a sting into a lexical list... - // c -- b - // XXX BUG see code... - 'lex': function(context){ - code = context.stack.pop() - if(typeof(code) == typeof('abc')){ - // XXX BUG: '"aaa" "bbb"' translates to ['"aaa"', '" "', '"bbb"'] - // i.e. quotes w/o whitespace are eaten... - if(/^\s*(['"]).*\1\s*$/m.test(code)){ - code = code.split(/^\s*(['"])(.*)\1\s*$/m)[2] - } - - //console.log(code) - - var res = [] - code = code - // split by strings whitespace and block comments... - .split(SPLITTER) - // parse numbers... - .map(function(e){ - // numbers... - if(/^[-+]?[0-9]+\.[0-9]+$/.test(e)){ - e = parseFloat(e) - } else if(/^[-+]?[0-9]+$/.test(e)){ - e = parseInt(e) - } - return e - }) - // remove undefined groups... - .filter(function(e){ - // NOTE: in JS 0 == '' is true ;) - return e !== undefined && e !== '' - }) - } - return code - }, - // pre-process a lexical list... - // a -- b - 'prep': function(context){ - var code = context.stack.pop() - - return run({ - stack: [], - code: code, - ns: context.pre_ns, - pre_ns: {}, - }).stack - }, - - // s c -- s - '_exec': function(context){ - // block... - var b = context.stack.pop() - if(typeof(b) == typeof([]) && b && b.constructor.name == 'Array'){ - b = b.slice() - } else { - b = [ b ] - } - // stack... - var s = context.stack.pop() - var res = run({ - stack: s, - code: b, - // NOTE: this can have side-effects on the context... - ns: context.ns, - pre_ns: context.pre_ns - }) - // XXX is this the right way to go? - context.ns = res.ns - context.pre_ns = res.pre_ns - return res.stack - }, - // quote - push the next elem as-is to stack... - // -- x - '\\': function(context){ - return context.code.splice(0, 1)[0] }, - - // comparisons and logic... - // a b -- c - 'and': function(context){ - var b = context.stack.pop() - var a = context.stack.pop() - if(a){ - return b - } else { - return a - } - }, - // a b -- c - 'or': function(context){ - var b = context.stack.pop() - var a = context.stack.pop() - if(a){ - return a - } else { - return b - } - }, - // x -- b - 'not': function(context){ - return !context.stack.pop() }, - // a b -- c - 'gt': function(context){ - return context.stack.pop() < context.stack.pop() }, - // a b -- c - 'eq': function(context){ - return context.stack.pop() == context.stack.pop() }, - - // stack operations... - // ... x -- x ... - 'rot': function(context){ - context.stack.splice(0, 0, context.stack.pop()) }, - // x ... -- ... x - 'tor': function(context){ - context.stack.push(context.stack.shift()) }, - // a b -- b a - 'swap': function(context){ - return context.stack.splice(context.stack.length-2, 1)[0] }, - // x -- x x - 'dup': function(context){ - return context.stack[context.stack.length-1] }, - // x -- x' - // NOTE: this will do a deep copy... - 'clone': function(context){ - return JSON.parse(JSON.stringify(context.stack.pop())) }, - // x -- - 'drop': function(context){ - context.stack.pop() }, - - // a -- b - // NOTE: all names are also strings so moo string? and 'moo' string? - // are the same... - 'string?': function(context){ - return typeof(context.stack.pop()) == typeof('str') }, - - // basic number operations... - // a -- b - 'number?': function(context){ - return typeof(context.stack.pop()) == typeof(123) }, - // a b -- c - 'add': function(context){ - return context.stack.pop() + context.stack.pop() }, - 'sub': function(context){ - return - context.stack.pop() + context.stack.pop() }, - 'mul': function(context){ - return context.stack.pop() * context.stack.pop() }, - 'div': function(context){ - return 1/context.stack.pop() * context.stack.pop() }, - - // block/list operations... - 'block?': function(context){ - var e = context.stack.pop() - return typeof(e) == typeof([]) && e && e.constructor.name == 'Array' - }, - // b n -- b e - 'at': function(context){ - var i = context.stack.pop() - if(i < 0){ - var l = context.stack[context.stack.length-1] - return l[l.length + i] - } - return context.stack[context.stack.length-1][i] - }, - // b e n -- b - 'to': function(context){ - var i = context.stack.pop() - var e = context.stack.pop() - if(i < 0){ - var l = context.stack[context.stack.length-1] - l[l.length + i] = e - } else { - context.stack[context.stack.length-1][i] = e - } - }, - // b e n -- b - 'before': function(context){ - var i = context.stack.pop() - var e = context.stack.pop() - if(i < 0){ - var l = context.stack[context.stack.length-1] - l.splice(l.length + i, 0, e) - } else { - context.stack[context.stack.length-1].splice(i, 0, e) - } - }, - // b -- b e - 'pop': function(context){ - return context.stack[context.stack.length-1].pop() }, - // b -- b l - 'len': function(context){ - return context.stack[context.stack.length-1].length }, - // b c -- b - 'map': function(context){ - var c = context.stack.pop() - var b = context.stack[context.stack.length-1] - for(var i=0; i < b.length; i++){ - // exec block in a separate context... - var res = run({ - //stack: [b, i, b[i], c], - stack: [b[i], c], - code: ['exec'], - // NOTE: this can have side-effects on the context... - ns: context.ns - }).stack - var l = res.length - if(l == 0){ - b.splice(i, 1) - i-- - } else { - b.splice.apply(b, [i, 1].concat(res)) - i += l - 1 - } - } - }, - - - // object stuff... - '{}': function(){ return {} }, - - 'object?': function(context){ - var o = context.stack[context.stack.length - 1] - return o && o.constructor === Object - }, - - // set item... - // o k v -- o - 'item!': function(context){ - var v = context.stack.pop() - var k = context.stack.pop() - var o = context.stack[context.stack.length - 1] - - o[k] = v - }, - - // test item... - // o k -- o t - 'item?': function(context){ - return context.stack.pop() in context.stack[context.stack.length - 1] }, - - // get item... - // o k -- o v - 'item': function(context){ - var k = context.stack.pop() - return context.stack[context.stack.length - 1][k] }, - - // remove/pop item from object... - // o k -- o v - 'popitem': function(context){ - var k = context.stack.pop() - var o = context.stack[context.stack.length - 1] - - var v = o[k] - delete o[k] - - return v - }, - - // o -- k - 'keys': function(context){ - return Object.keys(context.stack.pop()) }, - - // make a prototype of b... - // a b -- b - // NOTE: if a is false, reset prototype... - 'proto!': function(context){ - var b = context.stack.pop() - var a = context.stack.pop() - b.__proto__ = a === false ? {}.__proto__ : a - return b - }, - - // o -- p - // XXX what should this be: - // {} getproto - 'proto': function(context){ - return context.stack.pop().__proto__ }, - - // -- o - 'ns': function(context){ - return context.ns }, - // o -- - 'ns!': function(context){ - context.ns = context.stack.pop() }, -} - - -// NOTE: hate how JS handles multi-line strings... -var BOOTSTRAP = -`------------------------------------------------------------------------------- - - [S]lang is a [s]imple and complete [S]tack [lang]uage. - - Slang was designed for three main reasons: - - a means to experiment with several aspects of language design, - - an educational tool, to illustrate several programming language - concepts in a simple, hands-on manner, - - fun! - - - -------------------------------------------------------------------------------- - - Slang Basics - ------------ - - The system consists of: - - Stack - - Code - - Namespace - - basic runtime - - { NAMESPACE } - ^ - | - [ .. STACK .. ] <-- runtime -- [ .. CODE .. ] - - - A namespace is a basic key/value store. - - The runtime "reads" entities from the code stream one by one and depending on - whether an entity exists in the namespace it is either pushed on the stack - or evaluated. - - The evaluated entity is traditionally called a "word" (function in non-stack - languages). The only thing that makes a word different from any other entity - is that it matches a key in the namespace, as mentioned above. - - In Slang evaluation is done simply by executing the value of the matched - key/value pair in the namespace. An over-simplified way to explain - evaluation is to say that the content of the value is pushed to the - code stream to be read right away, that\'s almost it, if we skip a - couple of details (see: _exec, exec and for details see: eval) - - The system contains two types of words: - - Host words -- defined by the host system, - - User words -- defined within the system (like this bootstrap code). - - Words may read and affect any of the three system parts: - - Stack - - Code - - Namespace - - Traditioannly, in stack languages words affect only the stack, this is - one of the motivations to implement Slang, that is, to experiment with - different ways to go with stack languages. - - - TODO: add topological comparison/diff - - - ------------------------------------------------------------------------------ - - Stack effect notation - --------------------- - - Traditionally, stack languages use a "stack effect" notation to document how - words affect the stack state, a kind of before-after transformation. here is - a basic example showing how the word "add" works: - - stack code - | 1 2 add - 1 | 2 add - 1 2 | add - 1 2 [add] (a) - 3 | (b) - - - Here the stack effect represents the difference between two states: the - moment when the word is "read" (a) and the stack state after it is - evaluated (b) and is written like this: - - ( a b -- c ) - - - But, due to the fact that in Slang all three of the stack, code and namespace - can be affected by words, we need an extended stack effect notation. to - include at least the second most common case, the "code effect". - To illustrate, here is an example of a word that has a simple code effect, - the "+": - - stack code - | 1 + 2 - 1 | + 2 - 1 [+] 2 (a) - 3 | (b) - - - Here we see that in addition to affecting the stack, 2 is "pulled" from the - code stream. To indicate this we will use "|" that splits the stack (left) - and code (right) states, and write the stack effect for the word "+" like - this: - - ( a | b -- c | ) - - - NOTE: this notation is currently used as a means to documenting words and is - not interpreted in any way. - - - -------------------------------------------------------------------------------- - - Blocks / Lists - -------------- - - Basic words for block manipulation: - - Get block length - - [ 1 2 3 ] len - -> [ 1 2 3 ] 3 - - - Pop element form block tail - - [ 1 2 3 ] pop - -> [ 1 2 ] 3 - - - Push element to block tail - - [ 1 2 3 ] 4 push - -> [ 1 2 3 4 ] - - - NOTE: all indexes can be negative values, these will indicate the - position relative to the tail, -1 being the last element. - - Get element at position (0) - - [ 1 2 3 ] -1 at - -> [ 1 2 3 ] 3 - - - Put element (123) at position (0) - - [ 1 2 3 ] 123 0 to - -> [ 123 2 3 ] - - - Put element (123) before position (0) - - [ 1 2 3 ] 123 0 before - -> [ 123 1 2 3 ] - - - Like before but puts the element after position - - [ 1 2 3 ] 123 0 after - -> [ 1 123 2 3 ] - - - Expand block to stack -- "block 2 stack" - - [ 1 2 3 ] b2s - -> 1 2 3 - - - Map a block/word to each element in a block - - [ 1 2 3 ] [ 1 add ] map - -> [ 2 3 4 ] - - the returned value (stack) of the input block is put into the result - block, this enables us to both remove (empty stack) and expand (more - than one value) the resulting list... - - [ 1 2 3 4 ] [ dup ] map - -> [ 1 1 2 2 3 3 4 4 ] - - [ 1 2 3 4 ] [ dup 2 gt ? [ ] else [ . ] ] map - -> [ 3 4 ] - - - this enables us to construct words like filter, which makes the code - in the last example more readable: - - [ 1 2 3 4 ] [ 2 gt ] filter - -> [ 3 4 ] - - Reduce enables us to take a list and "reduce" it to a single value... - - [ 1 2 3 4 ] \\add reduce - -> 10 - - -------------------------------------------------------------------------------- - - Objects and namespaces - ---------------------- - - Get the namespace object... - - ns -> namespace - - - Set attribute (key-value pair) on object... - - o x 123 item! - -> o - - Since almost all object words return the original object we can chain - object operations like this: - - Create a variable word o and p and set them to empty objects... - - ns - o {} item! - p {} item! - . - - Get attribute x value... - - o x item - -> 123 - - Test if attribute x exists... - - o x item? - -> true - - Get block of attribute idents... - - o keys - -> [ ... ] - - Get and remove an attribute value from o... - - o x popitem - -> 123 - - Set prototype of o to p - - o p proto! - -> o - - Get prototype of o - - o proto - -> p - - -------------------------------------------------------------------------------- -s2b drop -- cleanup after docs... -ns {} proto! ns! . -- keep new words in a seporate context... --- --- With that out of the way, let\'s start with the bootstrap... - --- prepare the basic syntax for defining words... -ns - -- Some shorthands... - . ( x -- ) - [ drop ] item! - rot2 ( .. x y -- x y .. ) - [ rot rot ] item! - tor2 ( x y .. -- .. x y ) - [ tor tor ] item! - - -- Friendly exec... - exec ( b -- ... ) - [ s2b pop _exec b2s ] item! - -- Create a word... - word! ( w b -- ) - [ rot2 ns tor2 item! . ] item! - -- Word definition... - -- syntax: :: - :: ( | w b -- | ) - [ \\word! \\exec 2 2 _swapN ] item! -. - - --- misc... - -:: true? ( a -- b ) [ not not true eq ] -:: false? ( a -- b ) [ not true? ] - --- we already have gt and eq, now let\'s define the rest... -:: ne ( a b -- c ) [ eq not ] -:: lt ( a b -- c ) [ swap gt ] -:: ge ( a b -- c ) [ lt not ] -:: le ( a b -- c ) [ gt not ] - -:: inc ( a -- b ) [ 1 add ] -:: dec ( a -- b ) [ 1 sub ] -:: ! ( a -- b ) [ [ dup 1 ne ] ? [ dup 1 sub ! mul ] ] - - - --- Stack/code manipulation... - -:: _swap ( x | y -- y | x ) [ 1 1 _swapN ] -:: _push ( x | -- | x ) [ 0 _swapN ] -:: _pull ( | x -- x | ) [ 0 swap _swapN ] - -:: eval ( c -- ... ) [ lex prep exec ] --- like exec but will run a block in current context... -:: b2c [ len rot b2s tor 0 _swapN ] - -:: swap2 ( a _ b -- b _ a ) [ swap rot swap tor swap ] -:: dup2 ( a b -- a b a b ) [ dup swap2 dup rot swap2 tor swap ] - --- this is here for devel use only -:: _clear ( ... -- ) [ s2b print drop ] -:: _stack_size ( -- l ) [ s2b len swap b2s tor ] - - - --- Flow control... - --- Classic conditional word: --- [ cond ] [ A ] [ B ] if --- --- A bit too "polish" in my view ;) -:: if ( cond a b -- ... ) [ rot rot exec true? tor and tor or exec ] - --- Ternary operator, this can take two forms: --- COND ? A --- COND ? A else B --- --- We will define this in stages, first the helpers: --- run then block and drop \'else B\' if it exists... -:: _run_then ( a x | -- ... | x ) - ( a else | b -- ... | ) - [ \\exec swap dup \\else eq [ (drop else) drop \\drop _swap 6 ] and - [ (run as-is) 1 _push 4 ] or - b2s 0 _swapN ] --- if \'else B\' exists, run it, else cleanup... -:: _run_else ( a | -- | a ) - ( b else | b -- ... | ) - [ drop dup \\else eq [ drop \\exec _swap 4 ] and - [ 1 _push 2 ] or - b2s 0 _swapN ] --- And now the main operator... --- NOTE: this may actually have one of three stack effects... -:: ? ( c | a -- | ) - ( c | a -- ... | ) - ( c | a else b -- ... | ) - [ exec [ _run_then 1 ] and [ swap _run_else 2 ] or b2s 2 _swapN ] - - - --- List/block 2\'nd gen stuff... - --- make a new block instance shorthand... -:: [] [ [ ] clone ] - --- insert element after index... -:: after ( b e i -- b ) [ - -- special case, when at end, need to push the alement after it... - dup [ -1 eq ] ? - [ . push ] - else - [ inc before ]] - --- NOTE: the "]]" in the last definition, it\'s a shorthand, it closes --- ALL the open blocks to this point. --- ...thus it can be used ONLY as the very last word in a set. - --- push element to tail of block... -:: push ( b e -- b ) [ swap len rot swap tor to ] - --- Replace a pattern (p) in block with value (v)... --- NOTE: this will replace ALL patterns... -:: replace ( l v p -- l ) [ - swap - [ . \\VALUE ] clone - swap 2 to - rot - -- XXX for some reason ? without else messes things up... - [ dup \\PATTERN eq ? VALUE_BLOCK else [ ] ] clone - swap 2 to - tor 5 to - map ] - --- Filter the block via a condition... --- --- the condition block must have the folowing stack effect: elem -- bool -:: filter ( b c -- b ) [ - -- prepare the condition... - [ dup \\TEST exec ] clone - swap TEST replace - -- prepare the template... - [ TEST ? [ ] else [ . ] ] clone - swap TEST replace - map ] - -:: reduce ( L b -- s ) [ - rot clone - -- empty list, reduction is null... - [ len 0 eq ] ? - [ . tor . null ] - -- reduction of list of len 1 is it\'s content, so just pop it... - else [ [ len 1 eq ] ? - [ tor . b2s ] - -- and now recursively reduce the elements till the list is 1 in length... - -- XXX ugly - else [ - pop rot pop rot - [] tor push tor push - -- get and run the block... - tor dup clone rot _exec - -- process the result... - pop rot . tor push tor - reduce ]] - --- Create a block containing a range of numbers form 0 to n-1... -:: range ( n -- b ) [ - -- initial state... - [ dup number? ] ? - [ [] swap ] - -- get first elem... - else - [ 0 at ] - -- we got to the end... - [ dup 0 eq ] ? - drop - -- dec push new and continue... - else - [ 1 sub 0 before range ]] - --- Sum up the elements of a block... -:: sum ( L -- s ) [ [ add ] reduce ] - - --- Meta-word examples (experimental)... - --- Here is an infix operator example... --- :: + ( a | b -- c | ) [ \\exec 2 0 _swapN \\exec \\add 2 1 _swapN ] --- now let\'s make a meta function to make things shorter... -:: infix: ( | op word -- | ) [ - [ - -- format the word definition... - -- NAME WORD -> :: NAME WORD - s2b \\:: -2 before b2s - - -- our template... - -- exec the left side... - [ \\exec 2 0 _swapN - -- exec the right side and arragne the args for WORD... - \\exec \\WORD 2 1 _swapN ] clone - -- get the WORD and insert it into the template above (opsition 8)... - swap WORD replace - - -- push to code / run - 3 0 _swapN - -- swap the arguments and the code to be executed... - ] \\exec 2 2 _swapN ] - --- Now making a word/2 an infix operator is trivial... --- NOTE: these are at this point stupid and do not support priorities... -infix: + add -infix: - sub -infix: * mul -infix: / div - --- these need more thought... -infix: == eq -infix: != ne -infix: > gt -infix: < lt -infix: <= le -infix: >= ge - --- experimental... -infix: = word! - - --- Prefix operation definition... --- Example: --- :: echo: ( | txt -- | ) [ \\_flip \\print _flip ] --- swap stack and code untill the block finishes and consumes it's arguments --- then swap them back... -:: prefix: ( | op word -- | ) [ - [ - -- format the word definition... - -- NAME WORD -> :: NAME WORD - s2b \\:: -2 before b2s - - -- the code template - [ \\_flip \\exec \\WORD _flip ] clone - swap WORD replace - 3 0 _swapN - ] \\exec 2 2 _swapN ] - - - --- Tests and examples... - --- Mandatory "hello word" word example... -:: hi ( -- ) [ "Hello World!" print drop ] - --- Create a block containg a range of numbers from f to t, inclusive... -:: range/2 ( f t -- b ) - [ dup2 swap sub swap . inc range swap [] swap push \\+ 0 before map ] - --- this will enable us to create ranges via 0 .. 4 -infix: .. range/2 - ---:: range/3 ( a n s -- b ) --- [ swap range swap [] swap push \\* 0 before map ] - --- Execute block in a context... --- synctx: context: -prefix: context: [ ns {} proto! ns! exec ns proto ns! ] - - -` - - -var STARTUP = [[], BOOTSTRAP, 'lex', 'prep', '_exec', 'drop'] - - -// build bootstrap... -var CONTEXT = { - stack: [], - code: STARTUP.slice(), - ns: NAMESPACE, - pre_ns: PRE_NAMESPACE, -} - - -// run bootstrap... -run(CONTEXT) - - -// convenience... -function _slang(code, context){ - context = context == null ? CONTEXT : context - - context.code = code - return run(context).stack -} - - -function slang(code, context){ - context = context == null ? CONTEXT : context - - if(typeof(code) == typeof('abc')){ - code = [ '\\', code, 'lex', 'prep', 'exec' ] - } else { - code = [ code, 'prep', 'exec' ] - } - - context.code = code - return run(context).stack -} - - - -/********************************************************** RSlang ***/ -/* -var RS_PRE_NAMESPACE = { - // XXX using the ";" here just for the experiment, in the real thing - // if this thing pans out, that is, use indent... (a-la make/Python) - // XXX this reads ahead at the moment, but it must read back... - ';': function(context){ - var line = [] - var code = context.code - var cur = code.splice(0, 1)[0] - while(cur != ';' && code.length > 0){ - line.push(cur) - cur = code.splice(0, 1)[0] - } - - context.code.splice.apply(context.code, [0, 0].concat(line.reverse())) - }, - - '[': PRE_NAMESPACE['['], - 'macro:': PRE_NAMESPACE['macro:'], -} - -RS_CONTEXT = { - stack: [], - code: STARTUP.slice(), - ns: NAMESPACE, - pre_ns: PRE_NAMESPACE, -} - -// NOTE: we use the traditional bootstrap for this... -run(RS_CONTEXT) - -RS_CONTEXT.pre_ns = RS_PRE_NAMESPACE - -function rslang(code, context){ - context = context == null ? RS_CONTEXT : context - - return slang(code, context) -} -//*/ - - - -/********************************************************************** -* vim:set ts=4 sw=4 spell : */