演算子定義できるパーサとインタプリタ
ほぼ、C言語の演算子が定義できてて+αの演算子がついてる言語です。
最初構文木を作って、それをインタプリタで動くいう。
なにが、新しいのかというと、プログラムは数値や識別子以外は演算子でつながってるってこと。
演算子でつながってるだけなので、構文木もそんな風になってます。
あとは、説明めんどくさいので。いいや。
とにかく、S式に代わると言い張っていいんじゃないのかなぁと思えるくらいになってきたんじゃないかと思います。
いつも、そう思ってるのだけど、これ以上シンプルで分かりやすくってのは難しいと思います。
ってところまで来たと思いますけど、どうでしょうか。
このインタプリタ部分をもっと拡張すると俺的に理想に近い言語になりそうです。
1+2*3;4
が
["xox",["xox",1,"+",["xox",2,"*",3]],";",4]
となる〜っていう言語です。xoxは左結合の2項演算子の意味。
{a:1;b:2;c:a+b;}
がjavascriptのオブジェクトとして
{a:1,b:2,c:3}
という値を返してみたりしてますけど、関数とかは実装できてないところです。
以下ソース
<script> var regs = {}; Object.prototype.toString = function() { var str = []; for (var i in this) str.push(i+":"+this[i]) return "{"+str.join(",")+"}" } Array.prototype.toString = function(){return "["+this.join(",")+"]"} Operators.prototype.getFunction = function(m,op,op2) { return { "pxp":function(b){return ["pxp",op,b,op2]}, "xpxp":function(a,b){return ["xpxp",a,op,b,op2]}, "xoy":function(a,b){return ["xoy",a,op,b]}, "xox":function(a,b){return ["xox", a,op,b]}, "oy":function(a){return ["oy",op,a]}, "xo":function(a){return ["xo",a,op]} }[m]; } function Operators(m, p,op,op2, n,f) { this.priority = p this.xoy = {} this.xox = {} this.xo = {} this.oy = {} this.xpxp = {} this.xpxp2 = {} this.pxp = {} this.pxp2 = {} this.next = n if(m=="xpxp")xpxp2[op]=op2 if(m=="pxp")pxp2[op]=op2 if (m) this[m][op] = f ? f : this.getFunction(m,op,op2) } function escape(str) { return str.replace(/([\^\\\+\-\/\*\?\|\[\]\(\)\{\}\,\.\?])/g,"\\$1") } Operators.add = function(a) { if (regs[a.length]==null)regs[a.length] = "" regs[a.length] += escape(a)+"|" } Operators.ends = {"":""} Operators.prototype.set = function (no, mode, op,op2,f) {with(this){ if (priority == no) { Operators.add(op) if(mode=="xpxp"||mode=="pxp"){ this[mode+"2"][op]=op2 Operators.ends[op2]=1 Operators.add(op2) } this[mode][op] = f ? f : this.getFunction(mode,op,op2,f) return this } if (next == null) { Operators.add(op) return next = new Operators(mode, no, op,op2, null,f) } if (priority < no && next.priority > no) { Operators.add(op) return next = new Operators(mode, no, op,op2, next,f) } return next.set(no, mode, op,op2,f) }} var ops = new Operators(0, null) ops.set(240,"xox",".") ops.set(240,"xox","->") ops.set(240,"pxp","{","}") ops.set(240,"pxp","(",")") ops.set(240,"pxp","[","]") ops.set(240,"xpxp","{","}") ops.set(240,"xpxp","(",")") ops.set(240,"xpxp","[","]") ops.set(230,"xo","--") ops.set(230,"xo","++") //ops.set(230,"oy","cast") //ops.set(230,"oy","sizeof") ops.set(230,"oy","!") ops.set(230,"oy","~") ops.set(230,"oy","-") ops.set(230,"oy","+") ops.set(230,"oy","*") ops.set(230,"oy","&") ops.set(230,"oy","--") ops.set(230,"oy","++") ops.set(220,"xox","%") ops.set(220,"xox","/") ops.set(220,"xox","*") ops.set(210,"xox","-") ops.set(210,"xox","+") ops.set(200,"xox",">>") ops.set(200,"xox","<<") ops.set(190,"xox","=>") ops.set(190,"xox","<=") ops.set(190,"xox",">") ops.set(190,"xox","<") ops.set(180,"xox","!=") ops.set(180,"xox","==") ops.set(170,"xox","&") ops.set(160,"xox","^") ops.set(150,"xox","|") ops.set(140,"xox","&&") ops.set(130,"xox","||") ops.set(120,"xox","?") ops.set(110,"xoy","=") ops.set(110,"xoy","*=") ops.set(110,"xoy","/=") ops.set(110,"xoy","%=") ops.set(110,"xoy","+=") ops.set(110,"xoy","-=") ops.set(110,"xoy","<<=") ops.set(110,"xoy",">>=") ops.set(110,"xoy","&=") ops.set(110,"xoy","^=") ops.set(110,"xoy","|=") ops.set(110,"xoy",":=") ops.set(115,"xoy",":") ops.set(105,"xox","else") ops.set(100,"xox",",") ops.set(100,"xo",",") ops.set(90,"xox",";") ops.set(90,"xo",";") function parse(str) { var regl = []; for (var i in regs) { regl.push(i) } regl = regl.sort().reverse() var regstr = ""; for (var i = 0; i < regl.length; i++) { regstr += regs[regl[i]] } var reg = new RegExp('^[ \r\n\t]*(\#[^\r\n]*([ \r\n\t]+|$))*('+regstr +'[_a-zA-Z][_a-zA-Z0-9]*|[0-9]+|$)') function peek() { return str.match(reg)[3] } function pop() { return peek(),str = RegExp.rightContext,RegExp.$3 } function exp() { return expn(ops.next) } function expn(ops) { if (ops == null) return fact() var p = peek() var c if (ops.oy[p]) { pop() c = ops.oy[p](expn(ops)) } else if (ops.pxp[p]) { pop() c = peek() == ops.pxp2[p] ? null : exp() var p2 if ((p2=pop()) != ops.pxp2[p]) throw "error "+c2+" "+ops.pxp2[p] +":"+p2 c = ops.pxp[p](c) } else { c = expn(ops.next) } p = peek() while (ops.xox[p] || ops.xoy[p] || ops.xo[p] || ops.xpxp[p]) { pop() var p2 = peek() if(ops.xox[p] && Operators.ends[p2]==null) c = ops.xox[p](c,expn(ops.next)) else if(ops. xo[p]) c = ops.xo[p](c) else if(ops.xoy[p]) c = ops.xoy[p](c,expn(ops)) else if(ops.xpxp[p]) { var c2 = peek() == ops.xpxp2[p] ? null : exp() var p2 if ((p2=pop()) != ops.xpxp2[p]) throw "error "+c2+" "+ops.xpxp2[p] +":"+p2 c = ops.xpxp[p](c,c2) } else { throw "error" } p = peek() } return c } function fact() { var c = pop() if(c.match(/^[0-9]/)) c = Number(c) if(Operators.ends[c]) throw "error"; return c } try{ return exp() }catch(e){ alert(e) throw e } } function setEnv(env,name,data) { if(name in env) return env[name] = data, data if(env.parent) return setEnv(env.parent, name,data) throw "error" } function getEnv(env,name) { if(name in env) return env[name] if(env.parent) return getEnv(env.parent,name) throw "error: undefined value "+name } function xo(data,env) { switch(data[2]) { case ";": return eval(data[1],env) } throw "error" } function xox(data,env) { switch(data[2]) { case ".": return eval(data[1],env)[data[3]] case "+": return eval(data[1],env)+eval(data[3],env) case "-": return eval(data[1],env)-eval(data[3],env) case "*": return eval(data[1],env)*eval(data[3],env) case "/": return eval(data[1],env)/eval(data[3],env) case ";": eval(data[1],env); return eval(data[3],env) case ",": return [eval(data[1],env),eval(data[3],env)] } throw "error" } function xoy(data,env) { switch(data[2]) { case "=": return setEnv(env, data[1], eval(data[3],env)) case ":": return env[data[1]] = eval(data[3],env) } throw "error" } function xpxp(data,env) { switch(data[2]) { case "{": if(data[1] instanceof Array && data[1][0]=="pxp" && data[1][1]=="(") { return ["fun", data, {parent:env}] } case "(": var f = eval(data[1],env); alert(f) case "[": alert("koko");return eval(data[1],env)[eval(data[3],env)]; } throw "error" } function pxp(data,env) { switch(data[1]) { case "{": var nenv = {parent:env}; eval(data[2],nenv); delete nenv.parent ; return nenv } throw "error" } function fun(data,env) { data[1] // 引数リスト } function eval(data,env) { if(env == null)env={} if(data instanceof Array) { switch(data[0]) { case "xox": return xox(data,env) case "xoy": return xoy(data,env) case "xo": return xo(data,env) case "pxp": return pxp(data,env) case "xpxp": return xpxp(data,env) case "fun": return fun(data,env) } } // alert(data+" "+typeof(data)) if(typeof(data)=="string") { return getEnv(env, data) } return data } function run(str) { try{ var p = parse(str) alert(p) return eval(p) }catch(e){ return e } } function c() { document.getElementById("out").value = run(document.getElementById("in").value) } function log(str){ document.write(str+"<br>") } </script> <textarea cols=80 rows=5 id=in></textarea><input type=button onclick=c()><br> <textarea cols=80 rows=25 id=out> </textarea>