1
0
forked from GitHub/gf-core

Syntax editor: in-place replacement of functions

When at a non-leaf node, refinements with identical type signatures
are highlighting and can re placed without destroying the children.
If not, the refinement is greyed and the user is asked to clear
the current subtree first if they wish to replace it.
This aspect of the UI should be polished, but at least it is obvious.
Also, some substantial optimizations can still be made to cache
the processed type signatures (which determine what can be replaced
in-place)
This commit is contained in:
john.j.camilleri
2012-11-30 10:56:42 +00:00
parent 3c900ee6a3
commit 58c3e3db85
5 changed files with 89 additions and 26 deletions

View File

@@ -29,12 +29,15 @@ An improved version of the [old syntax editor][1].
See `editor.html` and `editor_online.js`.
## Bugs
- Change startcat doesn't work when given an initial startcat
## TODO
- Wrap a subtree
- Compatibility with grammars with dependent category types
- Clicking on tokens to select tree node
- try to retain subtree when replacing node
- Use local caching
- Clipboard of trees
- Usage of printnames

View File

@@ -120,6 +120,29 @@ function AST(fun, cat) {
x.children.push(node);
}
// Determine if current node is writable (empty/no children)
this.is_writable=function() {
var current = this.getCurrent();
var blank = current.fun == null || current.children.length == 0;
return blank;
}
// Determine if a fun would fit in a current hole
this.fits_in_place=function(typeobj) {
var current = this.getCurrent();
var inplace = false;
if (typeobj.args.length == current.children.length) {
var matches = 0;
for (var i in typeobj.args) {
if (typeobj.args[i] == current.children[i].cat)
matches++;
}
inplace = matches == current.children.length;
}
return inplace;
}
// Set entire subtree at current node
this.setSubtree = function(node) {
this._setSubtree(this.current, node);
@@ -140,9 +163,9 @@ function AST(fun, cat) {
}
node.children[lid.shift()] = new ASTNode(subtree);
}
}
// Find a node in the tree from its ID
this.find = function(id) {
var lid = undefined
if (Object.prototype.toString.call(id) == "[object Object]") {

View File

@@ -73,6 +73,10 @@ body.syntax-editor {
font: 0.9em sans-serif;
background: white;
}
.refinement.disabled
{
opacity: 0.5;
}
#debug
{

View File

@@ -119,6 +119,7 @@ Editor.prototype.start_fresh=function () {
/* --- Functions for handling tree manipulation ----------------------------- */
// Show refinements for given cat (usually that of current node)
Editor.prototype.get_refinements=function(cat) {
var t = this;
t.ui.refinements.innerHTML = "...";
@@ -131,10 +132,21 @@ Editor.prototype.get_refinements=function(cat) {
var cont = function(data){
clear(t.ui.refinements);
for (pi in data.producers) {
var opt = span_class("refinement", text(data.producers[pi]));
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 def = t.grammar_constructors.funs[fun].def;
var typeobj = AST.parse_type_signature(def);
var inplace = t.ast.fits_in_place(typeobj);
if (!blank && !inplace) {
opt.classList.add("disabled");
}
t.ui.refinements.appendChild(opt);
}
};
@@ -187,35 +199,55 @@ Editor.prototype.get_refinements=function(cat) {
// }
// }
// Select refinement now by default replaces "in-place"
// Case 1: current node is blank/no kids
// Case 2: kids have all same types, perform an in-place replacement
// Case 3: kids have diff types/number, prevent replacement (must clear first)
Editor.prototype.select_refinement=function(fun) {
var t = this;
t.ast.removeChildren();
t.ast.setFun(fun);
// Check if current node is blank or childless (case 1)
var blank = t.ast.is_writable();
// Parse out function arguments
// Check if we can replace in-place (case 2)
var def = t.grammar_constructors.funs[fun].def;
var typeobj = AST.parse_type_signature(def);
var inplace = !blank && t.ast.fits_in_place(typeobj);
// Add dependent type placeholders
if (typeobj.deps.length > 0) {
for (var i in typeobj.deps) {
t.ast.addDep(typeobj.deps[i].id, typeobj.deps[i].type);
if (!blank && !inplace) {
alert("use clear first if you want to replace the subtree");
return;
}
t.ast.setFun(fun);
if (blank) {
t.ast.removeChildren();
// Get new function arguments
var def = t.grammar_constructors.funs[fun].def;
var typeobj = AST.parse_type_signature(def);
// Add dependent type placeholders
if (typeobj.deps.length > 0) {
alert("the syntax editor current doesn't support dependent types");
// for (var i in typeobj.deps) {
// t.ast.addDep(typeobj.deps[i].id, typeobj.deps[i].type);
// }
}
// Add function argument placeholders
if (typeobj.args.length > 0) {
for (var i in typeobj.args) {
t.ast.add(null, typeobj.args[i]);
}
}
}
// Add function argument placeholders
if (typeobj.args.length > 0) {
for (var i in typeobj.args) {
t.ast.add(null, typeobj.args[i]);
}
}
// Update ui
t.redraw_tree();
t.update_linearisation();
// Select next hole & get its refinements
t.ui.refinements.innerHTML = "...";
t.ast.toNextHole();
t.update_current_node();
}
@@ -289,8 +321,8 @@ Editor.prototype.update_linearisation=function(){
});
}
//
Editor.prototype.delete_refinement = function() {
// Clear current node and all its children
Editor.prototype.clear_node = function() {
var t = this;
t.ast.removeChildren();
t.ast.setFun(null);
@@ -342,14 +374,12 @@ Editor.prototype.import_ast = function(abstr) {
}
// Look up information for a function
// This will absolutely fail on dependant types
Editor.prototype.lookup_fun = function(fun) {
var t = this;
var def = t.grammar_constructors.funs[fun].def;
var ix = def.lastIndexOf(" ");
var cat = def.substr(ix).trim();
var typeobj = AST.parse_type_signature(def);
return {
cat: cat
cat: typeobj.ret
}
}

View File

@@ -28,8 +28,11 @@ function EditorMenu(editor,opts) {
multiple: "multiple",
class: "hidden"
}),
// wrap_button: button("Wrap", function(){
// t.editor.wrap();
// }),
clear_button: button("Clear", function(){
t.editor.delete_refinement();
t.editor.clear_node();
}),
random_button: button("Random", function(){
t.editor.generate_random();