// I thank God for allowing me to program. let stack = []; let dictionary = {}; let temporary = {}; let immediate = {}; let source = ''; let pointer = 0; let w = dictionary; let last_defined = 'none'; let out = console.log; let root = {}; //vc function error (...stuff) { throw new Error(stuff.map(x=>x === undefined? 'nil' : x.toString()).join(' ')); } function read_word () { let word = ''; let chr = source[pointer++]; while (chr !== undefined && chr.match(/\s/)) { chr = source[pointer++]; } while (chr !== undefined && !chr.match(/\s/)) { word += chr; chr = source[pointer++]; } if (word !== '') { return word.toLowerCase(); } } function read_word_case () { let word = ''; let chr = source[pointer++]; while (chr !== undefined && chr.match(/\s/)) { chr = source[pointer++]; } while (chr !== undefined && !chr.match(/\s/)) { word += chr; chr = source[pointer++]; } if (word !== '') { return word; } } function put (...stuff) { stack.push(...stuff); } function get () { if (stack.length) { return stack.pop(); } else { error('stack underflow', 1); } } function get2 () { if (stack.length > 1) { return stack.splice(-2); } else { error('stack underflow', 2); } } function get3 () { if (stack.length > 2) { return stack.splice(-3); } else { error('stack underflow', 3); } } function get4 () { if (stack.length > 3) { return stack.splice(-4); } else { error('stack underflow', 4); } } function getn (num) { if (stack.length >= num) { return stack.splice(-num); } else { error('stack underflow', num); } } function interpret_element (element) { if (temporary[element]) { temporary[element](); } else if (dictionary[element]) { dictionary[element](); } else if (element.match(/^-?\d+\.?\d*$/)) { put(parseFloat(element)); } else if (element.startsWith("'")) { put(element.substr(1)); } else if (element.startsWith('.')) { if (element.endsWith('!')) { let [obj, value] = get2(); if (typeof obj != 'object') { error('dot! notation in a non object element', obj); } set_object(obj, element.substr(1, element.length - 2).split('.'), value); } else { let obj = get(); if (typeof obj != 'object') { error('dot notation in a non object element', obj); } put(access_object(obj, element.substr(1).split('.'))); } } else if (element.match(/\./)) { if (element.endsWith('!')) { let keys = element.substr(0, element.length - 1).split('.'); interpret_element(keys.shift()); let [value, obj] = get2(); if (typeof obj != 'object') { error('dot! notation in a non object element', obj); } set_object(obj, keys, value); } else { let keys = element.split('.'); interpret_element(keys.shift()); let obj = get(); if (typeof obj != 'object') { error('dot notation in a non object element', obj); } put(access_object(obj, keys)); } } else { error('word does not exist', element); } } //ccs function compile_element (element) { if (temporary[element]) { return temporary[element]; } else if (dictionary[element]) { if (element in immediate) { dictionary[element](1); if (immediate[element]) { return get(); } } else { return dictionary[element]; } } else if (element.match(/^-?\d+\.?\d*$/)) { let num = parseFloat(element); return () => { put(num) }; } else if (element.startsWith("'")) { let str = element.substr(1); return () => { put(str) }; } else if (element.startsWith('.')) { if (element.endsWith('!')) { let keys = element.substr(1, element.length - 2).split('.'); return () => { let [obj, value] = get2(); if (typeof obj != 'object') { error('dot! notation in a non object element', obj); } set_object(obj, keys, value); }; } else { let keys = element.substr(1).split('.'); return () => { let obj = get(); if (typeof obj != 'object') { error('dot notation in a non object element', obj); } put(access_object(obj, keys)); }; } } else if (element.match(/\./)) { if (element.endsWith('!')) { let keys = element.substr(0, element.length - 1).split('.'); let word = compile_element(keys.shift()); return () => { word(); let [value, obj] = get2(); if (typeof obj != 'object') { error('dot! notation in a non object element', obj); } set_object(obj, keys, value); } } else { let keys = element.split('.'); let word = compile_element(keys.shift()); return () => { word(); let obj = get(); if (typeof obj != 'object') { error('dot notation in a non object element', obj); } put(access_object(obj, keys)); }; } } else { error('word cannot be compiled', element); } } function read_words (delimiter) { let words = []; let word; while ((word = read_word()) != delimiter) { if (word === undefined) { error('did not find a terminating', delimiter); } if (word.match(/^-?\d+\.?\d*$/)) { words.push(parseFloat(word)); } else { words.push(word); } } return words; } function read_words_case (delimiter) { let words = []; let word; while ((word = read_word_case()) != delimiter) { if (word === undefined) { error('did not find a terminating', delimiter); } if (word.match(/^-?\d+\.?\d*$/)) { words.push(parseFloat(word)); } else { words.push(word); } } return words; } function compile_words (delimiter) { let words = []; let word; while ((word = read_word()) != delimiter) { if (word === undefined) { error('did not find a terminating', delimiter); } let element = compile_element(word); if (element) { words.push(element); } } return words; } function read_name (...err) { let word = read_word(); if (word === undefined) { error(...err); } else { return word; } } function read_name_case (...err) { let word = read_word_case(); if (word === undefined) { error(...err); } else { return word; } } function block (delimiter) { let words = compile_words(delimiter); return () => { words.forEach((x)=>x()) } } function interpret_string (string) { let word; let old = source; let oldp = pointer; source = string; pointer = 0; while ((word = read_word()) !== undefined) { interpret_element(word); } source = old; pointer = oldp; } function repl () { process.stdin.on('data', (data) => { data = data.toString(); if (data == "\n") { process.exit(); } else { try { interpret_string(data); } catch (e) { console.error(e); } console.log(stack); } }); } function get_string (...msg) { let str = get(); if (typeof str != 'string') { error(...msg); } return str; } function get_number (...msg) { let num = get(); if (typeof num != 'number') { error(...msg); } return num; } function compile (array, word) { let code = compile_element(word); if (code) { array.push(code); } } function with_out (fun, code) { let old = out; out = fun; code(); out = old; } //ims function immediate_words () { ['list', 'object', '."', '"', '{', 'bind', 'if'].forEach((x)=>{immediate[x]=1}); [':', 'immediate', 'immediate*'].forEach((x)=>{immediate[x]=0}); } function reader_word (word_name, code, err) { w[word_name] = (x) => { let name = read_name(err); if (x) { put(()=>{code(name)}); } else { code(name); } }; immediate[word_name] = 1; } function read_string (delimiter, err) { let str = ''; let chr; while ((chr = source[pointer++]) != delimiter) { if (chr === undefined) { error(err); } if (chr == '\\') { chr = source[pointer++]; if (chr === undefined) { error('backlash escape sequence at the end of source code'); } if (chr == 'a') { str += "\a"; } else if (chr == 't') { str += "\t"; } else if (chr == 'n') { str += "\n"; } else if (chr == 'r') { str += "\r"; } else if (chr == 'e') { str += "\e"; } else if (chr == '0') { str += "\0"; } else { str += chr; } } else { str += chr; } } return str; } function interpolate_string (str) { let result = ''; for (let i = 0; i < str.length; i++) { let chr = str[i]; if (chr == '~') { if (i == str.length - 1) { return result + '~'; } chr = str[++i]; if (chr == 'a') { let value = get(); if (Array.isArray(value)) { result += value.join(', '); } else { result += value.toString(); } } else if (chr == '%') { result += "\n"; } else if (chr == '{') { let end = str.indexOf('}~'); if (end == -1) { error('~{ directive in string without terminating }~'); } interpret_string(str.substr(++i, end - i)); result += get().toString(); i = end + 1; } } else { result += chr; } } return result; } function access_object (obj, keys) { let last = keys.length - 1; let last_key = keys[last]; for (let i = 0; i < last; i++) { obj = obj[keys[i]]; } if (typeof obj[last_key] == 'function') { return obj[last_key].bind(obj); } else { return obj[last_key]; } } function set_object (obj, keys, value) { let last = keys.length - 1; for (let i = 0; i < last; i++) { obj = obj[keys[i]]; } obj[keys[last]] = value; } function interpolate_list (list) { return list.map((x)=> { if (typeof x == 'string') { if (x == ',') { return get(); } else if (x == ',,') { return ','; } else if (x.startsWith(',')) { interpret_element(x.substr(1)); return get(); } else { return x; } } else { if (Array.isArray(x)) { return interpolate_list(x); } else { return x; } } }); } function get_delimiter (delimiter) { if (delimiter == '{') { return '}'; } else if (delimiter == '(') { return ')'; } else if (delimiter == '[') { return ']'; } else if (delimiter == '<') { return '>'; } else { return delimiter; } } //wds w[':'] = (x) => { let name = read_name(': did not read a name'); if (x) { temporary[name] = block(';'); } else { last_defined = name; dictionary[name] = block(';'); temporary = {}; } }; w['-'] = () => { let two = get_number('- did not receive a number'); let one = get_number('- did not receive a number'); put(one - two); }; w['/'] = () => { let two = get_number('% did not receive a number'); let one = get_number('% did not receive a number'); put(one / two); }; w['mod'] = () => { let two = get_number('mod did not receive a number'); let one = get_number('mod did not receive a number'); put(one % two); }; w['*'] = () => { let two = get_number('x did not receive a number'); let one = get_number('x did not receive a number'); put(one * two); }; w['..'] = () => { let two = get_string('.. did not receive a string'); let one = get_string(); put(one + two); }; w['+'] = () => { let two = get_number('+ did not receive a number'); let one = get_number('+ did not receive a number'); put(one + two); }; w.times = () => { let [code, times] = get2(); for (let i = 0; i < times; i++) { code(); } }; w['times*'] = () => { let [code, times] = get2(); for (let i = 0; i < times; i++) { put(i); code(); } }; w.word = () => { put(dictionary[get()]) }; w.log = () => { console.log(get()) }; w.qw = (x) => { let words = read_words_case(get_delimiter(read_name_case('qw did not read a delimiter'))); if (x) { put(()=>{put(words)}) } else { put(words); } }; w['{'] = (x) => { if (x) { let code = block('}'); put(()=>{put(code)}); } else { put(block('}')); } }; w.eval = ()=>{get()()}; w.bind = (x) => { let word_name = read_name('bind did not read a name'); let value; if (x) { put(()=>{value=get()}); temporary[word_name] = () => { put(value) }; } else { dictionary[word_name] = () => { put(value) }; value = get(); } }; w.r = ()=>{stack=[]}; w.immediate = () => { immediate[last_defined] = 0 }; w['immediate*'] = () => { immediate[last_defined] = 1 }; w.if = (x) => { let condition = block('then'); let word; let t = []; let f = []; let compiled = t; while ((word = read_word()) != 'endif') { if (word === undefined) { error('if did not find endif'); } if (word == 'else') { compiled = f; continue; } compile(compiled, word); } let code = () => { condition(); let value = get(); if (value) { for (let sub of t) { sub(); } } else { for (let sub of f) { sub(); } } }; if (x) { put(code); } else { code(); } }; w.compiling = (x) => { put(x) }; w.pr = () => { out(get() + "\n") }; w.ps = () => { out(get() + ' ') }; w.pri = () => { out(get()) }; w.cr = () => { out("\n") }; w.sp = () => { out(' ') }; w.buffer = () => { let str = ''; with_out((x) => { str += x }, get()); put(str); }; w.root = ()=>{put(root)}; w.dictionary = ()=>{put(dictionary)}; w['reset-dictionary'] = ()=>{dictionary = Object.assign({}, root); immediate = {}; immediate_words()}; reader_word('see', (name) => { out(dictionary[name].toString() + '\n'); }, 'see did not read a word name'); w['."'] = (x) => { let str = read_string('"', '." did not find a terminating "'); if (x) { put(()=>{out(interpolate_string(str))}); } else { out(interpolate_string(str)); } }; w['"'] = (x) => { let str = read_string('"', '" did not find a terminating "'); if (x) { put(()=>{put(interpolate_string(str))}); } else { put(interpolate_string(str)); } }; w.list = (x) => { let delimiter = get_delimiter(read_name_case('list did not read a delimiter')); let word; let list = []; let positions = []; let funs = {}; while ((word = read_word_case()) != delimiter) { if (word === undefined) { error('list did not find', delimiter); } if (word == 'list' || word == '"') { if (x) { dictionary[word](x); list.push(undefined); funs[list.length - 1] = get(); positions.push(list.length - 1); } else { dictionary[word](); list.push(get()); } } else if (word.match(/^-?\d+\.?\d*$/)) { list.push(parseFloat(word)); } else { list.push(word); } } if (x) { put(()=> { for (let position of positions) { funs[position](); list[position] = get(); } put(interpolate_list(list)) }); } else { put(interpolate_list(list)); } }; Object.assign(root, dictionary); immediate_words(); repl();