UNPKG

mrscheme

Version:

Scheme didactic subset interpreter in JavaScript.

627 lines (517 loc) 18.1 kB
export const TreeLib = {}; import { M$ } from './message101.mjs'; import { PrimitiveError } from './primerror.mjs'; import { TypeFun, TypeVariable, makeTypeBool, TypeList } from "./type101.mjs"; import { ECOTree } from "./ecotree.mjs"; TreeLib.installPrimEnv = function(penv) { /* binary trees */ penv.register("ab-vide",new TreeLib.primitiveABEmpty()); penv.register("ab-noeud",new TreeLib.primitiveABNode()); penv.register("ab-etiquette",new TreeLib.primitiveABValue()); penv.register("ab-gauche",new TreeLib.primitiveABLeft()); penv.register("ab-droit",new TreeLib.primitiveABRight()); penv.register("ab-noeud?",new TreeLib.primitiveABNodeP()); penv.register("ab-affiche",new TreeLib.primitiveABDraw()); /* general trees */ penv.register(M$("ag-noeud").toString(),new TreeLib.primitiveAGNode()); penv.register(M$("ag-etiquette").toString(),new TreeLib.primitiveAGValue()); penv.register(M$("ag-foret").toString(),new TreeLib.primitiveAGForest()); penv.register(M$("ag-affiche").toString(),new TreeLib.primitiveAGDraw()); /* old names penv.register("ab-draw",new TreeLib.primitiveABDraw()); penv.register(M$("gt-node").toString(),new TreeLib.primitiveAGNode()); penv.register(M$("gt-value").toString(),new TreeLib.primitiveAGValue()); penv.register(M$("gt-forest").toString(),new TreeLib.primitiveAGForest()); penv.register(M$("gt-draw").toString(),new TreeLib.primitiveAGDraw()); */ }; /*** Binary trees ***/ TreeLib.TypeBEmpty = function () { this.type = "BEmpty"; this.toString = function() { return "BEmpty"; } this.show = function() { return "ab-vide"; // for users } this.convert = function(other,mvars) { if(other.type=="BEmpty" || other.type=="BTree") { return true; } else if(other.type=="Option") { return other.convert(this,mvars); } else if(other.type=="Sum") { return other.convert(this,mvars); } else if(other.type=="Var") { return mvars.convertVar(this,other.ref); } else { return false; } } this.updateVars = function(mvars,trans) { return new TreeLib.TypeBEmpty(); } this.normalize = function(mvars) { return new TreeLib.TypeBEmpty(); } } TreeLib.BTreeId = 0; TreeLib.BEmptyValue = function() { this.type = "bempty"; this.id = TreeLib.BTreeId; TreeLib.BTreeId++; this.isNumber = false; this.equal = function(other) { if(other==null || other==undefined) { return false; } if(other.type!="bempty") { return false; } return true; } this.copy = function() { return new TreeLib.BEmptyValue(); } this.toHTML = function() { return '<span class="value">'+this.contentToHTML()+'<span class="tooltip">type <strong>'+M$('BEmpty').toString()+'</strong></span></span>'; } this.contentToHTML = function() { return "*ab-empty*"; } this.toString = function() { return "*ab-empty*"; } }; TreeLib.BTreeValue = function(value,left,right) { this.type = "btree"; this.isNumber = false; this.id = TreeLib.BTreeId; TreeLib.BTreeId++; this.value = value; this.left = left; this.right = right; this.equal = function(other) { if(other==null || other==undefined) { return false; } if(other.type!="btree") { return false; } return this.value.equal(other.value) && this.left.equal(other.left) && this.right.equal(other.right); } this.copy = function() { return new TreeLib.BTreeValue(this.value,this.left.copy(),this.right.copy()); } this.toString = function() { return "*"+M$("bin-tree").toString()+":"+this.id+"*"; } this.contentToHTML = function() { return "*"+M$("bin-tree").toString()+":"+this.id+"*"; } this.toHTML = function() { return '<span class="value">'+this.contentToHTML()+'<span class="tooltip">type <strong>'+M$('BTree').toString()+'</strong></span></span>'; } }; TreeLib.BTreeViewId = 0; TreeLib.BTreeView = function(btree) { this.type = "btreeview"; this.isNumber = false; this.id = TreeLib.BTreeViewId; TreeLib.BTreeViewId++; this.btree = btree; this.equal = function(other) { return this===other; } this.toString = function() { return this.btree.toString(); } this.contentToHTML = function() { return '<div class="btreeview" id="btreeview'+this.id+'"></div>'; } this.maxBuild = 0; this.buildNode = function(treeview, parent, btree) { if(btree.type=="btree") { if(btree.id>this.maxBuild) { this.maxBuild=btree.id+1; } var label = ""; var str = btree.value.toString(); for(var i=0;(i<str.length) && (i<8);i++) { label+=str.charAt(i); } if(str.length>8) { label+="..."; } treeview.add(btree.id,parent,label); this.buildNode(treeview,btree.id,btree.left); this.buildNode(treeview,btree.id,btree.right); } else if(btree.type=="bempty") { var id = this.maxBuild; this.maxBuild++; treeview.add(id,parent,"",10,10); } else { throw "not a binary tree (please report)"; } } this.afterOutput = function() { var treeview = new ECOTree('btreeview','btreeview'+this.id); treeview.config.defaultNodeWidth = 64; treeview.config.defaultNodeHeight = 20; treeview.config.iLevelSeparation = 20; treeview.config.iSiblingSeparation = 20; treeview.config.iSubtreeSeparation = 40; treeview.linkType = "B"; this.maxBuild = 0; this.buildNode(treeview,-1,this.btree); treeview.UpdateTree(); } this.toHTML = function() { return '<span class="value">'+this.contentToHTML()+'<span class="tooltip">type <strong>'+M$('BTreeView').toString()+'</strong></span></span>'; } } TreeLib.TypeBTree = function(elemType) { this.type = "BTree"; this.elemType = elemType; this.toString = function() { return "BTree[" + this.elemType.toString() + "]"; } this.show = function() { return "ArbreBinaire[" + this.elemType.show() + "]"; } this.convert = function(other,mvars) { if(other.type=="BEmpty") { return this; } else if(other.type=="BTree") { return this.elemType.convert(other.elemType,mvars); } else if(other.type=="Option") { return other.convert(this,mvars); } else if(other.type=="Sum") { return other.convert(this,mvars); } else if(other.type=="Var") { return mvars.convertVar(this,other.ref); } else { return null; } } this.updateVars = function(mvars,trans) { var nelemType = this.elemType.updateVars(mvars,trans); return new TreeLib.TypeBTree(nelemType); } this.normalize = function(mvars) { var nelemType = this.elemType.normalize(mvars); return new TreeLib.TypeBTree(nelemType); } }; TreeLib.TypeBTreeView = function() { this.type = "BTreeView"; this.toString = function() { return "BTreeView"; } this.show = function() { return "VisuArbreBinaire"; } this.convert = function(other,mvars) { if(other.type=="BTreeView") { return this; } else if(other.type=="Option") { return other.convert(this,mvars); } else if(other.type=="Sum") { return other.convert(this,mvars); } else if(other.type=="Var") { return mvars.convertVar(this,other.ref); } else { return null; } } this.updateVars = function(mvars,trans) { return new TreeLib.TypeBTreeView(); } this.normalize = function(mvars) { return new TreeLib.TypeBTreeView(); } }; TreeLib.primitiveABEmpty = function() { this.typeRepr = new TypeFun(new Array(),new TreeLib.TypeBEmpty()); this.arity = 0; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { return new TreeLib.BEmptyValue(); } } TreeLib.primitiveABNode = function() { this.typeRepr = new TypeFun(new Array(new TypeVariable(0), new TreeLib.TypeBTree(new TypeVariable(0)), new TreeLib.TypeBTree(new TypeVariable(0))) ,new TreeLib.TypeBTree(new TypeVariable(0))); this.arity = 3; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[1].type!="btree" && args[1].type!="bempty") { return new PrimitiveError(expr.get(1),"ab-noeud",args,"Not a binary tree"); } if(args[2].type!="btree" && args[2].type!="bempty") { return new PrimitiveError(expr.get(2),"ab-noeud",args,"Not a binary tree"); } return new TreeLib.BTreeValue(args[0],args[1].copy(),args[2].copy()); } } TreeLib.primitiveABValue = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeBTree(new TypeVariable(0))),new TypeVariable(0)); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type!="btree") { return new PrimitiveError(expr.get(1),"ab-etiquette",args,"Not a binary tree node"); } return args[0].value; } } TreeLib.primitiveABLeft = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeBTree(new TypeVariable(0))),new TreeLib.TypeBTree(new TypeVariable(0))); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type!="btree") { return new PrimitiveError(expr.get(1),"ab-gauche",args,"Not a binary tree node"); } return args[0].left; } } TreeLib.primitiveABRight = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeBTree(new TypeVariable(0))),new TreeLib.TypeBTree(new TypeVariable(0))); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type!="btree") { return new PrimitiveError(expr.get(1),"ab-droit",args,"Not a binary tree node"); } return args[0].right; } } TreeLib.primitiveABNodeP = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeBTree(new TypeVariable(0))),makeTypeBool()); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type=="btree") { return new BoolValue(true); } else if(args[0].type=="bempty") { return new BoolValue(false); } return new PrimitiveError(expr.get(1),"ab-noeud?",args,"Not a binary tree"); } } TreeLib.primitiveABDraw = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeBTree(new TypeVariable(0))),new TreeLib.TypeBTreeView()); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type!="btree" && args[0].type!="bempty") { return new PrimitiveError(expr.get(1),"ab-affiche",args,"Not a binary tree"); } return new TreeLib.BTreeView(args[0]); } } /*** General (n-ary) trees ***/ TreeLib.GTreeId = 0; TreeLib.GTreeValue = function(value,children) { this.type = "gtree"; this.isNumber = false; this.id = TreeLib.GTreeId; TreeLib.GTreeId++; this.value = value; // represented as a scheme list this.children = children; this.equal = function(other) { if(other==null || other==undefined) { return false; } if(other.type!="gtree") { return false; } if(!this.value.equal(other.value)) { return false; } return this.children.equal(other.children); } this.copy = function() { var children_copy = this.children.copy(); return new TreeLib.GTreeValue(this.value, children_copy); } this.toString = function() { return "*"+M$("gen-tree").toString()+":"+this.id+"*"; } this.contentToHTML = function() { return "*"+M$("gen-tree").toString()+":"+this.id+"*"; } this.toHTML = function() { return '<span class="value">'+this.contentToHTML()+'<span class="tooltip">type <strong>'+M$("GTree").toString()+'</strong></span></span>'; } }; TreeLib.GTreeViewId = 0; TreeLib.GTreeView = function(gtree) { this.type = "gtreeview"; this.isNumber = false; this.id = TreeLib.GTreeViewId; TreeLib.GTreeViewId++; this.gtree = gtree; this.equal = function(other) { return this===other; } this.toString = function() { return this.gtree.toString(); } this.contentToHTML = function() { return '<div class="gtreeview" id="gtreeview'+this.id+'"></div>'; } this.maxBuild = 0; this.buildNode = function(treeview, parent,gtree) { if(gtree.type=="gtree") { if(gtree.id>this.maxBuild) { this.maxBuild=gtree.id+1; } var label = ""; var str = gtree.value.toString(); for(var i=0;(i<str.length) && (i<8);i++) { label+=str.charAt(i); } if(str.length>8) { label+="..."; } treeview.add(gtree.id,parent,label); var forest = gtree.children; while(forest.type=="pair") { this.buildNode(treeview,gtree.id,forest.car); forest = forest.cdr; } } else { throw "not a general tree (please report)"; } } this.afterOutput = function() { var treeview = new ECOTree('gtreeview','gtreeview'+this.id); treeview.config.defaultNodeWidth = 64; treeview.config.defaultNodeHeight = 20; treeview.config.iLevelSeparation = 20; treeview.config.iSiblingSeparation = 20; treeview.config.iSubtreeSeparation = 40; treeview.linkType = "B"; this.maxBuild = 0; this.buildNode(treeview,-1,this.gtree); treeview.UpdateTree(); } this.toHTML = function() { return '<span class="value">'+this.contentToHTML()+'<span class="tooltip">type <strong>'+M$("GTree").toString()+'</strong></span></span>'; } } TreeLib.TypeGTree = function(elemType) { this.type = "GTree"; this.elemType = elemType; this.toString = function() { return "GTree[" + this.elemType.toString() + "]"; } this.show = function() { return "ArbreGeneral[" + this.elemType.show() + "]"; } this.convert = function(other,mvars) { if(other.type=="GTree") { return this.elemType.convert(other.elemType,mvars); } else if(other.type=="Option") { return other.convert(this,mvars); } else if(other.type=="Sum") { return other.convert(this,mvars); } else if(other.type=="Var") { return mvars.convertVar(this,other.ref); } else { return false; } } this.updateVars = function(mvars,trans) { var nelemType = this.elemType.updateVars(mvars,trans); return new TreeLib.TypeGTree(nelemType); } this.normalize = function(mvars) { var nelemType = this.elemType.normalize(mvars); return new TreeLib.TypeGTree(nelemType); } } TreeLib.TypeGTreeView = function() { this.type = "GTreeView"; this.toString = function() { return "GTreeView"; } this.show = function() { return "VisuArbreBinaire"; } this.convert = function(other,mvars) { if(other.type=="GTreeView") { return this; } else if(other.type=="Option") { return other.convert(this,mvars); } else if(other.type=="Sum") { return other.convert(this,mvars); } else if(other.type=="Var") { return mvars.convertVar(this,other.ref); } else { return null; } } this.updateVars = function(mvars,trans) { return new TreeLib.TypeGTreeView(); } this.normalize = function(mvars) { return new TreeLib.TypeGTreeView(); } }; TreeLib.primitiveAGNode = function() { this.typeRepr = new TypeFun(new Array(new TypeVariable(0), new TypeList(new TreeLib.TypeGTree(new TypeVariable(0)))) ,new TreeLib.TypeGTree(new TypeVariable(0))); this.arity = 2; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[1].type!="pair" && args[1].type!='nil') { return new PrimitiveError(expr.get(1),M$("gt-node").toString(),args,M$("Not a list")); } var npair = args[1].copy(); var pair = npair; while(pair.type!="nil") { if(pair.car.type!='gtree') { return new PrimitiveError(expr.get(1),M$("gt-node").toString(),args,M$("Not a list of general trees")); } pair = pair.cdr; } return new TreeLib.GTreeValue(args[0],npair); } } TreeLib.primitiveAGValue = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeGTree(new TypeVariable(0))),new TypeVariable(0)); this.arity = 1; this.nary = false; this.exec = function(evaluator, lexenv, expr,args) { if(args[0].type!="gtree") { return new PrimitiveError(expr.get(1),M$("gt-value").toString(),args,M$("Not a general tree node")); } return args[0].value; } } TreeLib.primitiveAGForest = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeGTree(new TypeVariable(0))),new TypeList(new TreeLib.TypeGTree(new TypeVariable(0)))); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type!="gtree") { return new PrimitiveError(expr.get(1),M$("gt-forest").toString(),args,M$("Not a general tree node")); } return args[0].children; } } TreeLib.primitiveAGDraw = function() { this.typeRepr = new TypeFun(new Array(new TreeLib.TypeGTree(new TypeVariable(0))),new TreeLib.TypeGTreeView()); this.arity = 1; this.nary = false; this.exec = function(evaluator,lexenv,expr,args) { if(args[0].type!="gtree") { return new PrimitiveError(expr.get(1),M$("gt-draw").toString(),args,M$("Not a general tree")); } return new TreeLib.GTreeView(args[0]); } }