UNPKG

force-lang

Version:

a modern forth lang compatible with NodeJS

329 lines (319 loc) 8.29 kB
const log = require('bunny-logger'); var TokenStream = require('./token-stream'); const read = require('./read'); const env = require('./env'); //const NativeLib = require('./native_lib'); const loadfile = require('./load-file'); const err = require('./error'); class Eval{ constructor(){ this.mode = 'interpret'; this.s = env.s; this._load_lib = true; //if(this._load_lib) this.load_lib(); } async load_lib(){ var x = await loadfile.load(__dirname + '/lib.j'); this.eval(x); } load_lib_sync(){ var x = loadfile.loadsync(__dirname + '/lib.j'); this.eval(x); } see_func(func_name){ var x=env.lookup(func_name); if(!x){ log.info('no word found...'); return; } switch(x._datum._type){ case 'TC_NATIVE_FUNC': log.info(`: ${x._name}`); log.info('<native func> ;'); break; case 'TC_COMP_FUNC': log.info(`: ${x._name}`); process.stdout.write(` `); for(var n of x._datum._datum){ process.stdout.write(`${n._datum} `); } log.info(''); break; default: log.info('not a word...'); break; } } set_mode(x){ if(x === 'interpret' || x === 'compile'){ this.mode = x; } } where_to_str(where){ var str = ''; if(where.file) str += 'in ' + where.file +' '; str += 'at ' + where.line +','+ where.col; return str; } eval_lambda(stream, list) { this.mode = 'compile'; var y; var body = []; //var func_name = read.read(stream); var level=0; var list_copy=list; if(list) { while(y=list_copy.shift()){ if(y._datum=='(') level++; if(y._datum==')') { if(level==0) { this.mode = 'interpret'; break; } level--; } body.push(y); } } else { while((y=read.read(stream))!=false){ if(y._datum=='(') level++; if(y._datum==')') { if(level==0) { this.mode = 'interpret'; break; } level--; } body.push(y); } } env.s.push({_type: 'TC_LAMBDA_FUNC', _datum: body}); return list_copy; } eval_if(stream, list){ //log.info('if.:.'); var y; var body = []; var else_body = []; var then_body = []; var level=0; if(list){ //var list_copy=JSON.parse(JSON.stringify(list)); var list_copy=list; while(y=list_copy.shift()){ if(y._datum=='if') {level++;} if(y._datum=='then') { if(level==0){then_body = body; break;}else{level--;}} if(y._datum=='else') { if(level==0) {else_body = body; body = []; continue;}} body.push(y); } } else { while((y=read.read(stream))!=false){ if(y._datum=='if') {level++;} if(y._datum=='then') { if(level==0){then_body = body; break;}else{level--;}} if(y._datum=='else') { if(level==0) {else_body = body; body = []; continue;}} body.push(y); } } //log.info({else_body}); //log.info({then_body}); if(env.is_true(env.s.pop())){ this.eval_parsed(then_body); } else { this.eval_parsed(else_body); } return list_copy; } eval_while(stream, list){ //log.info('while.:.'); var y; var body = []; var test_body = []; var while_body = []; var level=0; var list_copy=list; if(list){ // //var list_copy=JSON.parse(JSON.stringify(list)); // var list_copy=list; while(y=list_copy.shift()){ if(y._datum=='begin') {level++;} if(y._datum=='repeat') { if(level==0){while_body = body; break;}else{level--;}} if(y._datum=='while') { if(level==0) {test_body = body; body = []; continue;}} body.push(y); } } else { while((y=read.read(stream))!=false){ if(y._datum=='begin') {level++;} if(y._datum=='repeat') { if(level==0){while_body = body; break;}else{level--;}} if(y._datum=='while') { if(level==0) {test_body = body; body = []; continue;}} body.push(y); } } //log.info({test_body}); //log.info({while_body}); while(true){ this.eval_parsed(test_body); if(!env.is_true(env.s.pop())) break; this.eval_parsed(while_body); } return list_copy; } eval_case(stream, list){ //log.info('case.:.'); var y; var body = []; var test_body = []; var case_body = []; var case_list = []; var level=0; var list_copy=list; if(list){ while(y=list_copy.shift()){ if(y._datum=='case') {level++;} if(y._datum=='endcase') { if(level==0){break;}else{level--;}} if(y._datum=='of') { if(level==0) {test_body = body; body = []; continue;}} if(y._datum=='endof') { if(level==0) {case_body = body; body = []; case_list.push({test_body, case_body});continue;}} body.push(y); } } else { while((y=read.read(stream))!=false){ if(y._datum=='case') {level++;} if(y._datum=='endcase') { if(level==0){break;}else{level--;}} if(y._datum=='of') { if(level==0) {test_body = body; body = []; continue;}} if(y._datum=='endof') { if(level==0) {case_body = body; body = []; case_list.push({test_body, case_body});continue;}} body.push(y); } } //log.info({case_list}); //let x = env.s.pop(); for(var item of case_list){ //log.info(item.test_body); this.eval_parsed(item.test_body); let y = env.s.pop(); if(env.is_true(y) /*|| x._datum == y._datum*/){ //log.info(item.case_body);//???? this.eval_parsed(item.case_body); break; } } return list_copy; } eval_parsed_step(e){ var y; if(this.mode == 'interpret'){ if(e._type == 'TC_NUM') this.s.push(e); if(e._type == 'TC_STR') this.s.push(e); if(e._type == 'TC_JSON') this.s.push(e); if(e._type == 'TC_BOOL') this.s.push(e); if(e._type == 'TC_WORD'){ if(y=env.lookup(e._datum)){ switch(y._datum._type){ case 'TC_NATIVE_FUNC': err.add_stack(e); y._datum._datum.call(); err.pop_stack(); break; case 'TC_COMP_FUNC': err.add_stack(e); this.eval_parsed(y._datum._datum); err.pop_stack(); break; case 'TC_FUNC_JS': //this.eval_parsed(y._datum._datum); log.info('js func...'); break; default: this.s.push(y); //log.error(`unknown type ${y._datum._type} for ${y._name}`); //log.error(this.where_to_str(y)); break; } }else{ env.s.push(err.throw(`word not found '${e._datum}' ${this.where_to_str(e._where)}`)); } } if(err.require_handle(` ${e._datum} ${this.where_to_str(e._where)}`)) this.eval('handle');; } if(this.mode == 'compile'){ if(e._type == 'TC_WORD'){ if(e._datum == ';'){ this.mode = 'interpret'; return; } } } } eval_parsed(e){ var item; var list_copy=JSON.parse(JSON.stringify(e)); //for(var item of e){ while(item=list_copy.shift()){ if(item._datum == '('){ list_copy=this.eval_lambda(null,list_copy); continue; } if(item._datum == 'if'){ list_copy=this.eval_if(null,list_copy); continue; } if(item._datum == 'begin'){ list_copy=this.eval_while(null,list_copy); continue; } if(item._datum == 'case'){ list_copy=this.eval_case(null,list_copy); continue; } if(item._datum == ';' || item._datum == 'exit') break; this.eval_parsed_step(item); } } eval(e, filename=null){ var stream = read.tokenize(e); var x; while((x=read.read(stream, filename))!=false){ //log.info(x); if(x._type == 'TC_WORD'){ if(x._datum == 'var'){ var newvar = read.read(stream); env.set(newvar._datum, {_type: 'TC_NUM', _datum: 0}, 'TC_VAR', newvar._where); continue; } if(x._datum == ':'){ this.mode = 'compile'; var y; var body = []; var func_name = read.read(stream); while((y=read.read(stream))!=false){ body.push(y); if(y._datum==';') {this.mode = 'interpret'; break;} } env.set(func_name._datum, {_type: 'TC_COMP_FUNC', _datum: body}, 'TC_COMP_FUNC', func_name._where); continue; } if(x._datum == '('){ this.eval_lambda(stream); continue; } if(x._datum == 'see'){ var func_name = read.read(stream); this.see_func(func_name._datum); continue; } if(x._datum == 'if'){ this.eval_if(stream); continue; } if(x._datum == 'begin'){ this.eval_while(stream); continue; } if(x._datum == 'case'){ this.eval_case(stream); continue; } } this.eval_parsed_step(x); } //this.s.print(); //env.print_debug(); } }; module.exports = new Eval();