| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-26 06:13:44 +04:00
										 |  |  | /* XXX for some odd reason this breaks the interpreter... | 
					
						
							|  |  |  | Array.prototype.toString = function(){ | 
					
						
							|  |  |  | 	return '[ ' + this.join(', ') + ' ]' | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function run(context){ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 	context.stack = context.stack == null ? [] : context.stack | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	while(context.code.length > 0){ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var cur = context.code.splice(0, 1)[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// exit...
 | 
					
						
							|  |  |  | 		if(typeof(cur) == typeof('abc') && cur == '_exit'){ | 
					
						
							|  |  |  | 			return context | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// word
 | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 		} else if(typeof(cur) == typeof('abc') && cur in context.ns){ | 
					
						
							|  |  |  | 			var word = context.ns[cur] | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			// low-level word...
 | 
					
						
							|  |  |  | 			if(typeof(word) == typeof(function(){})){ | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 				var res = context.ns[cur](context) | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// hi-level word...
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 			} else if(typeof(word) == typeof([]) && word && word.constructor.name == 'Array'){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 				// XXX add isolation with a continuation...
 | 
					
						
							|  |  |  | 				context.code.splice.apply(context.code, [0, 0].concat(word)) | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 				var res = undefined | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// variable...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 				var res = word | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 			if(res !== undefined){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 				context.stack.push(res) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// everything else...
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			context.stack.push(cur) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return context | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-08 19:28:31 +04:00
										 |  |  | // XXX make this add '\n' / EOL words to the stream...
 | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | //var SPLITTER = /\s*\([^\)]*\)\s*|\s*--.*[\n$]|\s*"([^"]*)"\s*|\s*'([^']*)'\s*|\s+/m
 | 
					
						
							|  |  |  | var SPLITTER = RegExp([ | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 			/* 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...
 | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 			// terms to keep in the stream...
 | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 			'\\s*('+[ | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 				'\\n', | 
					
						
							|  |  |  | 				'--', | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 			].join('|')+')', | 
					
						
							|  |  |  | 			//*/
 | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 			//* lexer comments...
 | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 			'\\s*\\([^\\)]*\\)\\s*', | 
					
						
							|  |  |  | 			'\\s*--.*[\\n$]', | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 			//*/
 | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// quoted strings...
 | 
					
						
							|  |  |  | 			'\\s*"([^"]*)"\\s*', | 
					
						
							|  |  |  | 			"\\s*'([^']*)'\\s*", | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 			// quote...
 | 
					
						
							|  |  |  | 			'\\s*(\\\\)', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 			// whitespace...
 | 
					
						
							|  |  |  | 			'\\s+', | 
					
						
							|  |  |  | 		].join('|'), | 
					
						
							|  |  |  | 		'm') | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | // helpers...
 | 
					
						
							|  |  |  | // XXX should these skip quoted ends?
 | 
					
						
							| 
									
										
										
										
											2016-12-22 18:04:24 +03:00
										 |  |  | var collect = function(start, end){ | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 	return function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-22 18:04:24 +03:00
										 |  |  | 		var res = start ? [start] : [] | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 		var code = context.code | 
					
						
							|  |  |  | 		var cur = code.shift() | 
					
						
							|  |  |  | 		res.push(cur) | 
					
						
							|  |  |  | 		while(cur != end && code.length > 0){ | 
					
						
							| 
									
										
										
										
											2016-12-22 18:04:24 +03:00
										 |  |  | 			cur = code.shift() | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 			res.push(cur) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-12-22 18:04:24 +03:00
										 |  |  | var drop = function(start, end){ | 
					
						
							|  |  |  | 	var collector = collect(start, end) | 
					
						
							|  |  |  | 	//return function(context){ collector(context) }
 | 
					
						
							|  |  |  | 	return function(context){ console.log('XXX', collector(context).join(' ')) } | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | // pre-processor namespace...
 | 
					
						
							|  |  |  | var PRE_NAMESPACE = { | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 	// comment...
 | 
					
						
							|  |  |  | 	// syntax: -- ... \n
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// drop everything until '\n'
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this depends on explicit '\n' words...
 | 
					
						
							| 
									
										
										
										
											2016-12-22 18:04:24 +03:00
										 |  |  | 	//'--': drop('--', '\n'),
 | 
					
						
							|  |  |  | 	'(': drop('(', ')'), | 
					
						
							| 
									
										
										
										
											2014-02-10 05:26:24 +04:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// XXX use the real reader...
 | 
					
						
							|  |  |  | 	// block...
 | 
					
						
							|  |  |  | 	// syntax: [ ... ]
 | 
					
						
							| 
									
										
										
										
											2014-02-10 18:10:40 +04:00
										 |  |  | 	// 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...
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	'[': function(context){ | 
					
						
							|  |  |  | 		var block = [] | 
					
						
							|  |  |  | 		var code = context.code | 
					
						
							|  |  |  | 		var cur = code.splice(0, 1)[0] | 
					
						
							| 
									
										
										
										
											2014-02-10 17:53:13 +04:00
										 |  |  | 		while(cur != ']' && cur != ']]' && code.length > 0){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			if(cur == '['){ | 
					
						
							|  |  |  | 				cur = this['['](context) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			block.push(cur) | 
					
						
							|  |  |  | 			cur = code.splice(0, 1)[0] | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-02-10 18:10:40 +04:00
										 |  |  | 		// 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...
 | 
					
						
							| 
									
										
										
										
											2014-02-10 17:53:13 +04:00
										 |  |  | 		if(cur == ']]'){ | 
					
						
							| 
									
										
										
										
											2014-02-10 18:10:40 +04:00
										 |  |  | 			// XXX should we look ahead and count the explicitly closed
 | 
					
						
							|  |  |  | 			// 		via ']' and ']]' blocks???
 | 
					
						
							|  |  |  | 			// 		...at this point this seems a bit complex...
 | 
					
						
							| 
									
										
										
										
											2014-09-25 21:27:28 +04:00
										 |  |  | 			// 		...if there are more than one ']]' in a structure
 | 
					
						
							| 
									
										
										
										
											2014-02-10 18:10:40 +04:00
										 |  |  | 			//		this might stop being deterministic...
 | 
					
						
							| 
									
										
										
										
											2014-02-10 17:53:13 +04:00
										 |  |  | 			code.splice(0, 0, cur) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if(code.length == 0 && cur != ']' && cur != ']]'){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			console.error('Did not find expected "]".') | 
					
						
							|  |  |  | 		}  | 
					
						
							|  |  |  | 		return block | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2014-02-10 17:53:13 +04:00
										 |  |  | 	// drop the closing words...
 | 
					
						
							|  |  |  | 	']]': function(context){}, | 
					
						
							| 
									
										
										
										
											2014-02-10 17:38:46 +04:00
										 |  |  | 	']': function(context){ console.error('Unexpected "]".') }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-30 08:52:12 +04:00
										 |  |  | 	// XXX macros are not recursive...
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	'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] | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2014-02-08 19:28:31 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// a no op...
 | 
					
						
							| 
									
										
										
										
											2016-12-22 07:55:06 +03:00
										 |  |  | 	'\n': function(){ }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // main namespace...
 | 
					
						
							|  |  |  | var NAMESPACE = { | 
					
						
							|  |  |  | 	// constants...
 | 
					
						
							|  |  |  | 	'true': true, | 
					
						
							|  |  |  | 	'false': false, | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 	'null': 'null', | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	'nop': function(){},  | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 	'is?': function(context){  | 
					
						
							|  |  |  | 		return context.stack.pop() === context.stack.pop() }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// 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){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		context.stack = [ context.stack ] }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// 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() | 
					
						
							| 
									
										
										
										
											2013-12-30 06:06:45 +04:00
										 |  |  | 		c = c === undefined ? [] : c | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 		context.stack.splice.apply(context.stack, [context.stack.length, 0].concat(c)) | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	'print': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		console.log('>>>', context.stack[context.stack.length-1]) }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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...
 | 
					
						
							| 
									
										
										
										
											2013-07-23 00:37:17 +04:00
										 |  |  | 			if(/^\s*(['"]).*\1\s*$/m.test(code)){ | 
					
						
							|  |  |  | 				code = code.split(/^\s*(['"])(.*)\1\s*$/m)[2] | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2013-07-23 00:37:17 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-26 06:13:44 +04:00
										 |  |  | 			//console.log(code)
 | 
					
						
							| 
									
										
										
										
											2013-07-23 00:37:17 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			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() | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		if(typeof(b) == typeof([]) && b && b.constructor.name == 'Array'){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 			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 | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 		// XXX is this the right way to go?
 | 
					
						
							|  |  |  | 		context.ns = res.ns | 
					
						
							|  |  |  | 		context.pre_ns = res.pre_ns | 
					
						
							|  |  |  | 		return res.stack | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	// quote - push the next elem as-is to stack...
 | 
					
						
							|  |  |  | 	// -- x
 | 
					
						
							|  |  |  | 	'\\': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.code.splice(0, 1)[0] }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return !context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// a b -- c
 | 
					
						
							|  |  |  | 	'gt': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack.pop() < context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// a b -- c
 | 
					
						
							|  |  |  | 	'eq': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack.pop() == context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// stack operations...
 | 
					
						
							|  |  |  | 	// ... x -- x ...
 | 
					
						
							|  |  |  | 	'rot': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		context.stack.splice(0, 0, context.stack.pop()) }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// x ... -- ... x
 | 
					
						
							|  |  |  | 	'tor': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		context.stack.push(context.stack.shift()) }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// a b -- b a
 | 
					
						
							|  |  |  | 	'swap': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack.splice(context.stack.length-2, 1)[0] }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// x -- x x
 | 
					
						
							|  |  |  | 	'dup': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack[context.stack.length-1] }, | 
					
						
							| 
									
										
										
										
											2013-12-30 06:06:45 +04:00
										 |  |  | 	// x -- x'
 | 
					
						
							|  |  |  | 	// NOTE: this will do a deep copy...
 | 
					
						
							|  |  |  | 	'clone': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return JSON.parse(JSON.stringify(context.stack.pop())) }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// x --
 | 
					
						
							|  |  |  | 	'drop': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-12 02:07:34 +04:00
										 |  |  | 	// a -- b
 | 
					
						
							|  |  |  | 	// NOTE: all names are also strings so moo string? and 'moo' string?
 | 
					
						
							|  |  |  | 	// 		are the same...
 | 
					
						
							|  |  |  | 	'string?': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return typeof(context.stack.pop()) == typeof('str') }, | 
					
						
							| 
									
										
										
										
											2014-02-12 02:07:34 +04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// basic number operations...
 | 
					
						
							|  |  |  | 	// a -- b
 | 
					
						
							| 
									
										
										
										
											2014-02-12 02:07:34 +04:00
										 |  |  | 	'number?': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return typeof(context.stack.pop()) == typeof(123) }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// a b -- c
 | 
					
						
							|  |  |  | 	'add': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack.pop() + context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	'sub': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return - context.stack.pop() + context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	'mul': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack.pop() * context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	'div': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return 1/context.stack.pop() * context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// block/list operations...
 | 
					
						
							| 
									
										
										
										
											2014-02-12 02:07:34 +04:00
										 |  |  | 	'block?': function(context){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 		var e = context.stack.pop() | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return typeof(e) == typeof([]) && e && e.constructor.name == 'Array' | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	// 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] | 
					
						
							| 
									
										
										
										
											2014-02-10 09:05:10 +04:00
										 |  |  | 			l.splice(l.length + i, 0, e) | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			context.stack[context.stack.length-1].splice(i, 0, e) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	// b -- b e
 | 
					
						
							|  |  |  | 	'pop': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack[context.stack.length-1].pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// b -- b l
 | 
					
						
							|  |  |  | 	'len': function(context){ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return context.stack[context.stack.length-1].length }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	// b c -- b
 | 
					
						
							| 
									
										
										
										
											2014-02-10 09:05:10 +04:00
										 |  |  | 	'map': function(context){ | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 		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({ | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 				//stack: [b, i, b[i], c],
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 				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 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// object stuff...
 | 
					
						
							|  |  |  | 	'{}': function(){ return {} },  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	'object?': function(context){ | 
					
						
							|  |  |  | 		var o = context.stack[context.stack.length - 1] | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 		return o && o.constructor === Object | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 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 | 
					
						
							|  |  |  |    	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 	// test item...
 | 
					
						
							|  |  |  | 	// o k -- o t
 | 
					
						
							|  |  |  | 	'item?': function(context){  | 
					
						
							|  |  |  | 		return context.stack.pop() in context.stack[context.stack.length - 1] }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 	// get item...
 | 
					
						
							|  |  |  | 	// o k -- o v
 | 
					
						
							|  |  |  | 	'item': function(context){  | 
					
						
							| 
									
										
										
										
											2016-12-18 16:40:57 +03:00
										 |  |  | 		var k = context.stack.pop() | 
					
						
							|  |  |  | 		return context.stack[context.stack.length - 1][k] }, | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 	'keys': function(context){  | 
					
						
							|  |  |  | 		return Object.keys(context.stack.pop()) }, | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 	'proto': function(context){  | 
					
						
							|  |  |  | 		return context.stack.pop().__proto__ }, | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// -- o
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 	'ns': function(context){  | 
					
						
							|  |  |  | 		return context.ns }, | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | 	// o --
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:08:03 +03:00
										 |  |  | 	'ns!': function(context){  | 
					
						
							|  |  |  | 		context.ns = context.stack.pop() }, | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-23 01:57:43 +04:00
										 |  |  | // NOTE: hate how JS handles multi-line strings...
 | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  | 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! | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ------------------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 17:38:18 +03:00
										 |  |  |  Slang Basics | 
					
						
							|  |  |  |  ------------ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  |  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 | 
					
						
							| 
									
										
										
										
											2017-08-25 17:38:18 +03:00
										 |  |  | 	- Namespace | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |  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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ----------------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 17:38:18 +03:00
										 |  |  |  Stack effect notation | 
					
						
							|  |  |  |  --------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  |  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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ------------------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 17:38:18 +03:00
										 |  |  |  Blocks / Lists | 
					
						
							|  |  |  |  -------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  |  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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ------------------------------------------------------------------------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 17:38:18 +03:00
										 |  |  |  Objects and namespaces | 
					
						
							|  |  |  |  ---------------------- | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 13:33:13 +03:00
										 |  |  |  Get the namespace object... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ns		-> namespace | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  Set attribute (key-value pair) on object... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		o x 123 item! | 
					
						
							|  |  |  | 				-> o | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-25 13:33:13 +03:00
										 |  |  |  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! | 
					
						
							|  |  |  | 		. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  |  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 | 
					
						
							| 
									
										
										
										
											2017-09-29 02:06:29 +03:00
										 |  |  | 	-- Some shorthands... | 
					
						
							| 
									
										
										
										
											2017-01-06 05:54:39 +03:00
										 |  |  | 	. ( 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: :: <ident> <value> | 
					
						
							|  |  |  | 	:: ( | 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: <block> | 
					
						
							|  |  |  | prefix: context: [ ns {} proto! ns! exec ns proto ns! ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | `
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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')){ | 
					
						
							| 
									
										
										
										
											2013-07-23 00:37:17 +04:00
										 |  |  | 		code = [ '\\', code, 'lex', 'prep', 'exec' ] | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2013-07-23 00:37:17 +04:00
										 |  |  | 		code = [ code, 'prep', 'exec' ] | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	context.code = code | 
					
						
							|  |  |  | 	return run(context).stack | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-20 23:23:29 +04:00
										 |  |  | /********************************************************** RSlang ***/ | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2013-07-20 23:23:29 +04:00
										 |  |  | 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) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-12-09 20:41:45 +03:00
										 |  |  | //*/
 | 
					
						
							| 
									
										
										
										
											2013-07-20 23:23:29 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-26 02:44:11 +04:00
										 |  |  | /********************************************************************** | 
					
						
							| 
									
										
										
										
											2014-02-10 18:10:40 +04:00
										 |  |  | * vim:set ts=4 sw=4 spell :                                                */ |