// 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 = {}; let variable = {}; let hook = {}; //vc function error (...stuff) { let code = source.substr(0, pointer); throw new Error([code, ...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', 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}); ['is', ':', '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 convert_tokens (string) { let result = ''; for (let i = 0; i < string.length; i++) { let chr = string[i]; if (chr == '-') { chr = string[++i]; if (chr === undefined) { return result; } result += chr.toUpperCase(); } else { result += chr; } } return result; } function access_object (obj, keys) { let last = keys.length - 1; let last_key = convert_tokens(keys[last]); for (let i = 0; i < last; i++) { obj = obj[convert_tokens(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[convert_tokens(keys[i])]; } obj[convert_tokens(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; } } function set_var (name, value) { variable[name] = value; if (hook[name]) { for (let i = hook[name].length - 1; i >= 0; i--) { put(value); hook[name][i](); } } } async function load_tags () { for (let tag of [...document.getElementsByTagName('oh')]) { let src = tag.getAttribute('src'); if (src) { let res = await fetch(src); if (res.ok) { let text = await res.text(); interpret_string(text); } else { error('could not fetch file', src); } } interpret_string(tag.textContent); } } function init () { if (typeof window == 'undefined') { repl(); } else { let style = document.createElement('style'); document.head.appendChild(style); style.sheet.insertRule('oh,ohh{display:none}'); window.addEventListener('load', browser); } } function browser () { let ctx; let animation; let timed = {}; let canvas; w.repl = () => { let socket = new WebSocket('ws://' + get()); let log = console.log; let winerr = window.onerror; socket.addEventListener('open', (event) => { console.log = window.onerror = error = (...x) => { socket.send(x.toString()) }; socket.send('ok'); }); socket.addEventListener('message', (event) => { try { interpret_string(event.data); } catch (e) { socket.send(e); } socket.send('[' + stack.toString() + ']'); }); socket.addEventListener('close', () => { console.log = log; window.onerror = winerr; }); }; w.document = () => { put(document) }; w.body = () => { put(document.body) }; w.append = () => { get().appendChild(get()) }; w.dom = () => { put(document.createElement(get())) }; w.clear = () => { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) }; w.canvas = () => { canvas = document.createElement('canvas'); document.body.appendChild(canvas); ctx = canvas.getContext('2d'); canvas.width = innerWidth; canvas.height = innerHeight; document.body.style.overflow = 'hidden'; document.body.style.padding = '0'; document.body.style.margin = '0'; window.addEventListener('resize', ()=>{canvas.width = innerWidth; canvas.height = innerHeight;}); w.canvas = () => { put(canvas) }; }; w.rect = () => { ctx.fillRect(...get4()) }; w.color = () => { ctx.fillStyle = get() }; w.ctx = () => { put(ctx) }; w.width = () => { put(innerWidth) }; w.height = () => { put(innerHeight) }; w.animation = () => { let code = get(); if (animation) { cancelAnimationFrame(animation); } function loop () { code(); animation = requestAnimationFrame(loop); } loop(); }; w.game = async () => { let load = dictionary['game-load']; undefined_error(load, 'game-load is not defined'); let update = dictionary['game-update']; undefined_error(update, 'game-update is not defined'); let draw = dictionary['game-draw']; undefined_error(draw, 'game-draw is not defined'); }; load_tags(); } function undefined_error (test, error) { if (test === undefined) { error(error); } } function load_images(obj, callback) { Promise.all(Object.entries(obj).map(([name, url]) => { return new Promise((resolve, reject) => { let image = new Image() image.src = url image.onload = () => { obj[name] = image; resolve(); } image.onerror = () => reject(new Error(`Failed to load image: ${url}`)) }); })).then(callback).catch(error); } //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.greater = () => { let two = get_number('greater did not receive a number'); let one = get_number('greater did not receive a number'); put(one > two); }; w.less = () => { let two = get_number('less did not receive a number'); let one = get_number('less did not receive a number'); put(one < two); }; w['greater-or-equal'] = () => { let two = get_number('greater-or-equal did not receive a number'); let one = get_number('greater-or-equal did not receive a number'); put(one >= two); }; w['less-or-equal'] = () => { let two = get_number('less-or-equal did not receive a number'); let one = get_number('less-or-equal 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['/'] = () => { 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.print = () => { 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 == '"' || word == 'object') { 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)); } }; w.object = (x) => { let delimiter = get_delimiter(read_word_case('object did not read a delimiter')); let obj = {}; let key; let delayed = {}; while ((key = read_word_case()) !== delimiter) { if (key === undefined) { error('object did not find', delimiter); } let value = read_word(); if (value === undefined) { error('key', key, 'did not receive a value'); } if (value == '"' || value == 'list' || value == 'object') { if (x) { dictionary[value](x); delayed[key] = get(); } else { dictionary[value](); obj[key] = get(); } } else if (value == '{') { dictionary['{'](); if (x) { delayed[key] = get(); } else { get()(); obj[key] = get(); } } else if (value.match(/^\-?\d+\.?\d*$/)) { obj[key] = parseFloat(value); } else if (value == 'nil') { obj[key] = undefined; } else { obj[key] = value; } } if (x) { put(() => { for (let key of Object.keys(delayed)) { delayed[key](); obj[key] = get(); } put(Object.assign({}, obj)); }); } else { put(Object.assign({}, obj)); } }; w.dup = () => {let x = get(); put(x,x)}; w.drop = get; w.is = (x) => { let name = read_name('is did not read a name'); dictionary[name] = ()=>{put(variable[name])}; if (x) { put(()=>{set_var(name, get())}); } else { set_var(name, get()); } }; reader_word('remove-hook', (name) => { let num = get(); if (hook[name] && hook[name][num]) { hook[name].splice(num, 1); } else { error('hook', num, name, 'does not exist'); } }); reader_word('hook*', (name) => { if (hook[name]) { hook[name].push(get()); put(hook[name].length-1); } else { hook[name] = [get()]; put(0); } }); reader_word('hook-once', (name) => { if (hook[name]) { let code = get(); let id = hook[name].length; hook[name].push(()=>{code(); hook[name].splice(id, 1)}); } else { let code = get(); hook[name] = [()=>{code(); hook[name].splice(0, 1)}]; } }); reader_word('hook', (name) => { if (hook[name]) { hook[name].push(get()); } else { hook[name] = [get()]; } }); reader_word('set', (name) => { set_var(name, get()); }); reader_word('get', (name) => { put(variable[name]); }); w['assert-stack'] = () => { let num = get(); if (stack.length !== num) { error('stack does not have', num, 'elements'); } }; w.properties = () => { let [obj, names] = get2(); for (let name of names) { put(obj[name]); } }; w['increment-by'] = () => { let [obj, key, value] = get3(); obj[key] += value; }; w['decrement-by'] = () => { let [obj, key, value] = get3(); obj[key] -= value; }; w.decrement = () => { let [obj, key] = get2(); obj[key]--; }; w.increment = () => { let [obj, key] = get2(); obj[key]++; }; Object.assign(root, dictionary); immediate_words(); init();