- 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 : */