diff --git a/Slang/slang.html b/Slang/slang.html
new file mode 100755
index 0000000..e69dd24
--- /dev/null
+++ b/Slang/slang.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Slang/slang.js b/Slang/slang.js
new file mode 100755
index 0000000..4ad3f76
--- /dev/null
+++ b/Slang/slang.js
@@ -0,0 +1,504 @@
+/**********************************************************************
+*
+*
+*
+**********************************************************************/
+
+
+/*********************************************************************/
+
+function run(context){
+
+ var stack = context.stack
+ stack = stack == null ? [] : stack
+ context.stack = stack
+
+ var ns = context.ns
+
+ 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 ns){
+ var word = ns[cur]
+ // low-level word...
+ if(typeof(word) == typeof(function(){})){
+ var res = ns[cur](context)
+
+ // hi-level word...
+ } else if(typeof(word) == typeof([]) && word.constructor.name == 'Array'){
+ // XXX add isolation with a continuation...
+ context.code.splice.apply(context.code, [0, 0].concat(word))
+ var res = null
+
+ // variable...
+ } else {
+ res = word
+ }
+
+ if(res != null){
+ context.stack.push(res)
+ }
+
+ // everything else...
+ } else {
+ context.stack.push(cur)
+ }
+ }
+
+ return context
+}
+
+var SPLITTER = /\s*\([^\)]*\)\s*|\s*--.*[\n$]|\s*(".*")\s*|\s*('.*')\s*|\s+/m
+
+
+// pre-processor namespace...
+var PRE_NAMESPACE = {
+ // XXX use the real reader...
+ // block...
+ // syntax: [ ... ]
+ '[': function(context){
+ var block = []
+ var code = context.code
+ var cur = code.splice(0, 1)[0]
+ while(cur != ']' && code.length > 0){
+ if(cur == '['){
+ cur = this['['](context)
+ }
+ block.push(cur)
+ cur = code.splice(0, 1)[0]
+ }
+ if(code.length == 0 && cur != ']'){
+ console.error('Did not find expected "]".')
+ }
+ return block
+ },
+ '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]
+ },
+}
+
+
+// main namespace...
+var NAMESPACE = {
+ // constants...
+ 'true': true,
+ 'false': false,
+ // this is mutable, so we'll need to create a new instance each time
+ '[]': function(){ return [] },
+
+ 'nop': function(){},
+
+ // 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()
+ 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(/^(['"]).*\1$/m.test(code)){
+ code = code.split(/^(['"])(.*)\1$/m)[2]
+ }
+ 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
+ },
+
+ // word definition...
+ // syntax: ::
+ // --
+ '::': function(context){
+ var ident = context.code.splice(0, 1)
+ var cur = context.code.splice(0, 1)
+
+ this[ident] = cur[0]
+ },
+
+ // s c -- s
+ '_exec': function(context){
+ // block...
+ var b = context.stack.pop()
+ if(typeof(b) == typeof([]) && 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
+ }).stack
+ return res
+ },
+ // quote - push the next elem as-is to stack...
+ // -- x
+ '\\': function(context){
+ var code = context.code
+ return 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 --
+ 'drop': function(context){
+ context.stack.pop()
+ },
+
+ // basic number operations...
+ // a -- b
+ 'isNumber': 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 context.stack.pop() / context.stack.pop()
+ },
+
+ // block/list operations...
+ 'isBlock': function(context){
+ var e = context.stack.pop()
+ return typeof(e) == typeof([]) && 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 + 1, 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
+ 'each': 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], 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
+ }
+ }
+ },
+}
+
+
+var BOOTSTRAP = '\n'+
+':: _swap ( x -- y ) [ 1 1 _swapN ]\n'+
+':: _push ( x -- y ) [ 0 _swapN ]\n'+
+':: _pull ( x -- y ) [ 0 swap _swapN ]\n'+
+'\n'+
+':: exec ( b -- ... ) [ s2b pop _exec b2s ]\n'+
+':: eval ( c -- ... ) [ lex prep exec ]\n'+
+'-- like exec but will run a block in current context...\n'+
+':: b2c [ len rot b2s tor 0 _swapN ]\n'+
+'\n'+
+':: . ( x -- ) [ print drop ]\n'+
+'\n'+
+':: swap2 ( a _ b -- b _ a ) [ swap rot swap tor swap ]\n'+
+'\n'+
+':: isT ( a -- b ) [ not not true eq ]\n'+
+':: isF ( a -- b ) [ not isT ]\n'+
+//'\n'+
+//':: if ( cond a b -- ... ) [ rot rot exec isT tor and tor or exec ]\n'+
+'\n'+
+'-- [ ] ? [ ] \n'+
+'-- [ ] ? [ ] else [ ] \n'+
+':: _run_else [ drop dup \\ else eq [ drop \\ exec _swap 4 ] and\n'+
+' [ 1 _push 2 ] or\n'+
+' b2s 0 _swapN ]\n'+
+':: _run_then [ \\ exec swap dup \\ else eq [ (drop else) drop \\ drop _swap 6 ] and\n'+
+' [ (run as-is) 1 _push 4 ] or\n'+
+' b2s 0 _swapN ]\n'+
+// XXX BUG: if no code after this will break...
+// Ex:
+// -- this will not exec [ B ]...
+// [ A ] ? [ B ]
+// -- while this will...
+// [ A ] ? [ B ] nop
+':: ? [ exec [ _run_then 1 ] and [ swap _run_else 2 ] or b2s 2 _swapN ]\n'+
+'\n'+
+'\n'+
+'-- list/block 2\'nd gen stuff...\n'+
+':: push ( b e i -- b ) [ -1 before ]\n'+
+'\n'+
+'\n'+
+'-- experimental...\n'+
+'-- NOTE: these are at this point stupid and do not support priorities or grouping...\n'+
+'-- NOTE: these have both stack and code effect, in genera the operations are of \n'+
+'-- the form: A op B\n'+
+':: + [ \\ add _swap ]\n'+
+':: - [ \\ sub _swap ]\n'+
+':: * [ \\ mul _swap ]\n'+
+':: / [ \\ div _swap ]\n'+
+'\n'+
+':: == [ \\ eq _swap ]\n'+
+':: > [ \\ gt _swap ]\n'+
+'\n'+
+'\n'+
+'-- this is here for devel use only\n'+
+':: _clear ( * -- ) [ s2b drop ] \n'+
+':: _stack_size ( -- l ) [ s2b len swap b2s tor ] \n'+
+'\n'+
+'\n'+
+'-- tests and examples...\n'+
+':: hi ( -- ) [ "Hello World!" print drop ]\n'+
+//'-- NOTE: nop at the end is a stub to fix a bug in ? else ...\n'+
+//':: ! ( a -- b ) [ [ dup 1 eq not ] ? [ dup 1 sub ! mul ] nop ]\n'+
+':: ! ( a -- b ) [ [ dup 1 eq not ] ? [ dup 1 sub ! mul ] ]\n'+
+':: range ( n -- b ) [\n'+
+' -- initial state...\n'+
+' [ dup isNumber ] ? \n'+
+' [ [] swap ]\n'+
+' -- get first elem...\n'+
+' else\n'+
+' [ 0 at ]\n'+
+' -- we got to the end...\n'+
+' [ dup 0 eq ] ? \n'+
+' [ drop ]\n'+
+' -- dec push new and continue...\n'+
+' else\n'+
+' [ 1 sub 0 before range ] ]\n'+
+':: range2 ( n s -- b )\n'+
+' [ swap range swap [] swap push \\ * 0 before each ]\n'+
+'\n'+
+''
+
+
+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], 'b2s', 'prep', 'exec' ]
+ }
+
+ context.code = code
+ return run(context).stack
+}
+
+
+
+/**********************************************************************
+* vim:set ts=4 sw=4 : */