From 314052f8d3b380c9b1562aafdbaf4af445e7fd82 Mon Sep 17 00:00:00 2001 From: "john.j.camilleri" Date: Mon, 3 Dec 2012 14:02:47 +0000 Subject: [PATCH] Syntax editor: add wrap feature --- src/www/syntax-editor/ast.js | 25 ++++- src/www/syntax-editor/editor.js | 149 ++++++++++++++++++--------- src/www/syntax-editor/editor_menu.js | 7 +- 3 files changed, 129 insertions(+), 52 deletions(-) diff --git a/src/www/syntax-editor/ast.js b/src/www/syntax-editor/ast.js index 20e0c7019..5aa55d230 100644 --- a/src/www/syntax-editor/ast.js +++ b/src/www/syntax-editor/ast.js @@ -35,6 +35,10 @@ function NodeID(x) { return new NodeID( this ); } + // Return NodeID as string + this.toString = function() { + return this.id.toString(); + } } /* --- Abstract Syntax Tree (with state)------------------------------------- */ @@ -42,7 +46,7 @@ function NodeID(x) { function ASTNode(data) { for (var d in data) this[d]=data[d]; this.children = []; - for (var c in data.children) { + if (data) for (var c in data.children) { this.children.push( new ASTNode(data.children[c]) ); } this.hasChildren = function(){ @@ -103,6 +107,10 @@ function AST(fun, cat) { this.currentNode.cat = c; } + this.hasParent = function() { + return this.currentID.get().length > 1; + } + // Add a single type dependency at current node this.addDep = function(k, type) { // Add unassigned type variable to current @@ -115,7 +123,7 @@ function AST(fun, cat) { return node; } - // Add a single fun at current node + // Add a node as child of current node this.add = function(fun, cat) { var node = newNode(fun,cat); this._add(this.currentID, node); @@ -128,6 +136,19 @@ function AST(fun, cat) { x.children.push(node); } + // Wrap the current node inside another node + this.wrap = function(typeobj, childid) { + var subtree = new ASTNode(this.currentNode); + this.currentNode.fun = typeobj.name.join(" "); + this.currentNode.cat = typeobj.ret; + this.currentNode.children = []; + for (var i in typeobj.args) { + this.add(null, typeobj.args[i]); + } + this.currentNode.children[i] = subtree; + return subtree; + } + // Determine if current node is writable (empty/no children) this.is_writable=function() { var cn = this.currentNode; diff --git a/src/www/syntax-editor/editor.js b/src/www/syntax-editor/editor.js index fc60443ef..b8642457a 100644 --- a/src/www/syntax-editor/editor.js +++ b/src/www/syntax-editor/editor.js @@ -142,6 +142,29 @@ Editor.prototype.start_fresh=function () { /* --- Functions for handling tree manipulation ----------------------------- */ + +Editor.prototype.add_refinement=function(t,fun,callback,disable_destructive) { + // var t = this; + // hide refinement if identical to current fun? + + var opt = span_class("refinement", text(fun)); + opt.onclick = bind(function(){ + callback(this.innerHTML) + }, opt); + + // If refinement would be destructive, disable it + if (disable_destructive) { + var blank = t.ast.is_writable(); + var typeobj = t.lookup_fun(fun); + var inplace = t.ast.fits_in_place(typeobj); + if (!blank && !inplace) { + opt.classList.add("disabled"); + } + } + + t.ui.refinements.appendChild(opt); +} + // Show refinements for given cat (usually that of current node) Editor.prototype.get_refinements=function(cat) { var t = this; @@ -155,23 +178,8 @@ Editor.prototype.get_refinements=function(cat) { var cont = function(data){ clear(t.ui.refinements); for (var pi in data.producers) { - // hide refinement if identical to current fun? - var fun = data.producers[pi]; - var opt = span_class("refinement", text(fun)); - opt.onclick = bind(function(){ - t.select_refinement(this.innerHTML) - }, opt); - - // If refinement would be destructive, disable it - var blank = t.ast.is_writable(); - var typeobj = t.lookup_fun(fun); - var inplace = t.ast.fits_in_place(typeobj); - if (!blank && !inplace) { - opt.classList.add("disabled"); - } - - t.ui.refinements.appendChild(opt); + t.add_refinement(t, fun, bind(t.select_refinement,t), true); } }; var err = function(data){ @@ -238,6 +246,84 @@ Editor.prototype.update_current_node=function(newID) { } } +// Clear current node and all its children +Editor.prototype.clear_node = function() { + var t = this; + t.ast.removeChildren(); + t.ast.setFun(null); + t.redraw_tree(); + t.get_refinements(); +} + +// Show wrap candidates +Editor.prototype.wrap_candidates = function() { + var t = this; + + // we need to end with this + var cat = t.ast.getCat(); + + // if no parent, then cat can be anything as long + // as the current tree fits somewhere + var refinements = []; + for (var i in t.grammar_constructors.funs) { + var obj = t.grammar_constructors.funs[i]; + if (elem(cat, obj.args)) { + if (!t.ast.hasParent() || obj.ret==cat) { + refinements.push(obj); + } + } + } + + if (refinements.length == 0) { + alert("No functions exist which can wrap the selected node."); + return; + } + + t.ui.refinements.innerHTML = "Wrap with: "; + for (var i in refinements) { + var fun = refinements[i].name; + t.add_refinement(t, fun, bind(t.wrap,t), false); + } +} + +// Wrap the current node inside another function +Editor.prototype.wrap = function(fun,childid) { + var t = this; + + var typeobj = t.grammar_constructors.funs[fun]; + + // do actual replacement + t.ast.wrap(typeobj, childid); + + // refresh stuff + t.redraw_tree(); + t.update_linearisation(); + t.ast.toNextHole(); + t.update_current_node(); +} + +// Generate random subtree from current node +Editor.prototype.generate_random = function() { + var t = this; + t.ast.removeChildren(); + var args = { + cat: t.ast.getCat(), + limit: 1 + }; + if (!args.cat) { + alert("Missing category at current node"); + return; + } + var cont = function(data){ + var tree = data[0].tree; + t.import_ast(tree); + }; + var err = function(data){ + alert("Error"); + }; + server.get_random(args, cont, err); +} + Editor.prototype.redraw_tree=function() { var t = this; var elem = node; // function from support.js @@ -298,37 +384,6 @@ Editor.prototype.update_linearisation=function(){ }); } -// Clear current node and all its children -Editor.prototype.clear_node = function() { - var t = this; - t.ast.removeChildren(); - t.ast.setFun(null); - t.redraw_tree(); - t.get_refinements(); -} - -// Generate random subtree from current node -Editor.prototype.generate_random = function() { - var t = this; - t.ast.removeChildren(); - var args = { - cat: t.ast.getCat(), - limit: 1 - }; - if (!args.cat) { - alert("Missing category at current node"); - return; - } - var cont = function(data){ - var tree = data[0].tree; - t.import_ast(tree); - }; - var err = function(data){ - alert("Error"); - }; - server.get_random(args, cont, err); -} - // Import AST from string representation, setting at current node Editor.prototype.import_ast = function(abstr) { var t = this; diff --git a/src/www/syntax-editor/editor_menu.js b/src/www/syntax-editor/editor_menu.js index 57000e551..83375d68d 100644 --- a/src/www/syntax-editor/editor_menu.js +++ b/src/www/syntax-editor/editor_menu.js @@ -28,9 +28,9 @@ function EditorMenu(editor,opts) { multiple: "multiple", class: "hidden" }), - // wrap_button: button("Wrap", function(){ - // t.editor.wrap(); - // }), + wrap_button: button("Wrap", function(){ + t.editor.wrap_candidates(); + }), clear_button: button("Clear", function(){ t.editor.clear_node(); }), @@ -72,6 +72,7 @@ function EditorMenu(editor,opts) { } } appendChildren(t.container, [t.ui.clear_button]); + appendChildren(t.container, [t.ui.wrap_button]); if (t.options.show.random_button) { appendChildren(t.container, [t.ui.random_button]); }