Syntax editor: now uses common GrammarManager object

This commit is contained in:
john.j.camilleri
2012-11-23 15:03:36 +00:00
parent fe9b368b90
commit 7fe922d5a6
7 changed files with 284 additions and 93 deletions

View File

@@ -0,0 +1,173 @@
/* --- Grammar Manager object ----------------------------------------------- */
/*
This object stores the state for:
- grammar
- startcat
- languages
Hooks which actions can be hooked to:
- onload
- change_grammar
- change_startcat
- change_languages
*/
function GrammarManager(server,opts) {
var t = this;
/* --- Configuration ---------------------------------------------------- */
// default values
this.options = {
initial: {}
};
this.hooks = {
onload: [
function(gm){ debug("default action: onload"); }
],
change_grammar: [
function(grammar){ debug("default action: change grammar"); }
],
change_startcat: [
function(startcat){ debug("default action: change startcat"); }
],
change_languages: [
function(languages){ debug("default action: change languages"); }
]
}
// Apply supplied options
// if(opts) for(var o in opts) this.options[o]=opts[o];
/* --- Client state initialisation -------------------------------------- */
this.server = server;
this.grammar = null; // current grammar
// this.grammars=[];
// this.grammar_dirs=[];
this.startcat = null; // current startcat
this.languages = []; // current languages (empty means all langs)
/* --- Main program, this gets things going ----------------------------- */
this.init=function(){
this.server.get_grammarlists(bind(this.onload,this));
}
this.init();
}
//
//GrammarManager.prototype.update_grammar_list=function(dir,grammar_names,dir_count) {
GrammarManager.prototype.onload=function(dir,grammar_names,dir_count) {
var t=this;
t.grammars=[];
t.grammar_dirs=[];
t.grammar_dirs.push(dir);
t.grammars=t.grammars.concat(grammar_names.map(function(g){return dir+g}));
var grammar0=t.options.initial.grammar || t.grammars[0];
t.change_grammar(grammar0);
// Execute hooked actions
t.run_actions("onload",dir,grammar_names,dir_count);
}
/* --- Registering / unregistering actions to hooks ------------------------- */
GrammarManager.prototype.register_action=function(hook,action) {
this.hooks[hook].push(action);
}
GrammarManager.prototype.unregister_action=function(hook,action) {
// TODO!
}
// Execute actions for a given hook
// TODO: any number of arguments
GrammarManager.prototype.run_actions=function(hook,arg1,arg2,arg3) {
var acts = this.hooks[hook];
for (f in acts) {
acts[f](arg1,arg2,arg3);
}
}
/* --- Grammar -------------------------------------------------------------- */
// API
GrammarManager.prototype.change_grammar=function(grammar_url) {
var t=this;
t.server.switch_to_other_grammar(grammar_url, function() {
t.server.grammar_info(function(grammar){
// Set internal state
t.grammar = grammar;
// Call internal functions
t.update_startcat(grammar);
t.update_language_list(grammar);
// Execute hooked actions
t.run_actions("change_grammar",grammar);
});
});
}
/* --- Start category ------------------------------------------------------- */
// Internal
// Sets default startcat for grammar
GrammarManager.prototype.update_startcat=function(grammar) {
var t=this;
var cats=grammar.categories;
var startcat0 = t.options.initial.startcat;
if (elem(startcat0, cats))
t.startcat = startcat0;
else
t.startcat = grammar.startcat;
}
// API
GrammarManager.prototype.change_startcat=function(startcat) {
var t = this;
// Set internal state
t.startcat = startcat;
// Call internal functions
// ...
// Execute hooked actions
t.run_actions("change_startcat",startcat);
}
/* --- Languages ------------------------------------------------------------ */
// Internal
// Sets default languages for grammar
GrammarManager.prototype.update_language_list=function(grammar) {
var t = this;
function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng"
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
// Replace the options in the menu with the languages in the grammar
var langs=grammar.languages;
for(var i=0; i<langs.length; i++) {
var ln=langs[i].name;
if(!hasPrefix(ln,"Disamb")) {
var lp=langpart(ln,grammar.name);
if (elem(lp, t.options.initial.languages)) {
t.languages.push(ln); // or lp?
}
}
}
}
// API
GrammarManager.prototype.change_languages=function(languages) {
var t = this;
// Set internal state
t.languages = languages;
// Call internal functions
// ...
// Execute hooked actions
t.run_actions("change_languages",languages);
}

View File

@@ -31,20 +31,15 @@ An improved version of the [old syntax editor][1].
## TODO ## TODO
- Link to jump into minibar - Wrap a subtree
- Compatibility with grammars with dependent category types - Compatibility with grammars with dependent category types
- Clicking on tokens to select tree node - Clicking on tokens to select tree node
- try to retain subtree when replacing node
- Use local caching - Use local caching
- Clipboard of trees
- Usage of printnames
- Enter string/float/int literals - Enter string/float/int literals
- UI issue with DisambPhrasebookEng
- more prominence to Disamb-linearizations - more prominence to Disamb-linearizations
- ambiguity: (optionally) parse all the resulting linearizations/variants and point out those which are ambiguous - ambiguity: (optionally) parse all the resulting linearizations/variants and point out those which are ambiguous
- try to retain subtree when replacing node
- add undo/redo (or back/forward) navigation - add undo/redo (or back/forward) navigation
- structure fridge magnets some more (eg newline before the magnet whose first letter is different) - structure fridge magnets some more (eg newline before the magnet whose first letter is different)
- The formal-looking funs and cats are not linked/aligned to the linearizations.
Maybe a possible UI could be where the user is
clicking on the linearization (in a chosen language) and the tree is
drawn under it (from top to bottom, not from left to right as
currently). So that the alignment of words to functions is always
explicit. But maybe this is not doable.

View File

@@ -1,4 +1,4 @@
body.syntax_editor { body.syntax-editor {
background: #ccc url("../minibar/brushed-metal.png"); background: #ccc url("../minibar/brushed-metal.png");
} }
@@ -74,3 +74,13 @@ body.syntax_editor {
background: white; background: white;
} }
#debug
{
font: 10px monospace;
white-space: pre;
color: #333;
margin: 1em 0;
border: 1px dashed #999;
padding: 1em;
}

View File

@@ -7,18 +7,20 @@
<link rel="stylesheet" type="text/css" href="../minibar/minibar.css" /> <link rel="stylesheet" type="text/css" href="../minibar/minibar.css" />
<link rel="stylesheet" type="text/css" href="editor.css" /> <link rel="stylesheet" type="text/css" href="editor.css" />
</head> </head>
<body class=syntax_editor> <body class="syntax-editor">
<h2>Syntax Editor</h2> <h2>Syntax Editor</h2>
<div id=minibar></div> <div id="minibar"></div>
<div id=editor></div> <div id="editor"></div>
<noscript>This page doesn't works unless JavaScript is enabled.</noscript> <noscript>This page doesn't works unless JavaScript is enabled.</noscript>
<hr /> <hr />
<small class="modtime"> <small class="modtime">
John J. Camilleri, November 2012 John J. Camilleri, November 2012
</small> </small>
<div id="debug" class="hidden"></div>
<!-- Common --> <!-- Common -->
<script type="text/javascript" src="../js/grammar_manager.js"></script>
<script type="text/javascript" src="../js/support.js"></script> <script type="text/javascript" src="../js/support.js"></script>
<script type="text/javascript" src="../js/pgf_online.js"></script> <script type="text/javascript" src="../js/pgf_online.js"></script>

View File

@@ -15,7 +15,7 @@ new EditorMenu
Editor.redraw_tree(); Editor.redraw_tree();
Editor.get_refinements(); Editor.get_refinements();
*/ */
function Editor(server,opts) { function Editor(gm,opts) {
var t = this; var t = this;
/* --- Configuration ---------------------------------------------------- */ /* --- Configuration ---------------------------------------------------- */
@@ -57,16 +57,34 @@ function Editor(server,opts) {
]); ]);
/* --- Client state initialisation -------------------------------------- */ /* --- Client state initialisation -------------------------------------- */
this.server = server; this.gm = gm;
this.server = gm.server;
this.ast = null; this.ast = null;
this.grammar = null;
this.startcat = null; /* --- Register Grammar Manager hooks ----------------------------------- */
this.languages = []; this.gm.register_action("change_grammar",function(grammar){
debug("Editor: change grammar");
var startcat0 = t.options.initial.startcat;
if (elem(startcat0, grammar.categories))
t.startcat = startcat0;
else
t.startcat = null;
t.get_grammar_constructors(bind(t.start_fresh,t));
});
this.gm.register_action("change_startcat",function(startcat){
debug("Editor: change startcat");
t.startcat = startcat;
t.start_fresh();
});
this.gm.register_action("change_languages",function(languages){
debug("Editor: change languages");
t.update_linearisation();
});
/* --- Main program, this gets things going ----------------------------- */ /* --- Main program, this gets things going ----------------------------- */
this.menu = new EditorMenu(this, this.options); this.menu = new EditorMenu(this, this.options);
/* --- Shutdown the editor nicely --------------------------------------- */ /* --- Other basic stuff ------------------------------------------------ */
this.shutdown = function() { this.shutdown = function() {
clear(this.container); clear(this.container);
this.container.classList.remove("editor"); this.container.classList.remove("editor");
@@ -102,26 +120,17 @@ Editor.prototype.get_ast=function() {
} }
Editor.prototype.get_startcat=function() { Editor.prototype.get_startcat=function() {
return this.startcat || this.grammar.startcat; return this.gm.startcat;
} }
/* --- These get called from EditorMenu, or some custom code */ // TODO
Editor.prototype.initialize_from=function(opts) {
Editor.prototype.change_grammar=function(grammar_info) { var t=this;
var t = this; if (opts.startcat)
t.grammar = grammar_info; t.options.initial_startcat=opts.startcat;
var startcat0 = t.options.initial.startcat t.change_grammar();
if (elem(startcat0, grammar_info.categories)) if (opts.abstr)
t.startcat = startcat0; t.import_ast(opts.abstr);
else
t.startcat = null;
t.get_grammar_constructors(bind(t.start_fresh,t));
}
Editor.prototype.change_startcat=function(startcat) {
var t = this;
t.startcat = startcat;
t.start_fresh();
} }
// Called after changing grammar or startcat // Called after changing grammar or startcat
@@ -247,7 +256,7 @@ Editor.prototype.update_linearisation=function(){
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc; return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
} }
function row(lang, lin) { function row(lang, lin) {
var langname = langpart(lang, t.grammar.name); var langname = langpart(lang, t.gm.grammar.name);
var btn = button(langname, function(){ var btn = button(langname, function(){
bind(t.options.lin_action,t)(lin,lang); bind(t.options.lin_action,t)(lin,lang);
}); });
@@ -264,7 +273,7 @@ Editor.prototype.update_linearisation=function(){
var tbody=empty("tbody"); var tbody=empty("tbody");
for (i in data) { for (i in data) {
var lang = data[i].to; var lang = data[i].to;
if (t.languages.length < 1 || elem(lang, t.languages)) { if (t.gm.languages.length < 1 || elem(lang, t.gm.languages)) {
tbody.appendChild(row(lang, data[i].text)) tbody.appendChild(row(lang, data[i].text))
} }
} }

View File

@@ -34,39 +34,64 @@ function EditorMenu(editor,opts) {
random_button: button("Random", function(){ random_button: button("Random", function(){
t.editor.generate_random(); t.editor.generate_random();
}), }),
debug_toggle: button("⚙", function(){
var sel = element("debug");
if (sel.classList.contains("hidden"))
sel.classList.remove("hidden")
else
sel.classList.add("hidden")
}),
}; };
with(this.ui) { if (t.options.show.grammar_menu) {
if (this.options.show.grammar_menu) { appendChildren(t.container, [text(" Grammar: "), t.ui.grammar_menu]);
appendChildren(this.container, [text(" Grammar: "), grammar_menu]); t.ui.grammar_menu.onchange = function(){
grammar_menu.onchange = bind(this.change_grammar,this); var grammar_url = t.ui.grammar_menu.value;
} t.gm.change_grammar(grammar_url);
if (this.options.show.startcat_menu) {
appendChildren(this.container, [text(" Startcat: "), startcat_menu]);
startcat_menu.onchange = bind(this.change_startcat,this);
}
if (this.options.show.to_menu) {
appendChildren(this.container, [text(" To: "), to_toggle, to_menu]);
to_menu.onchange = bind(this.change_language,this);
}
appendChildren(this.container, [clear_button]);
if (this.options.show.random_button) {
appendChildren(this.container, [random_button]);
} }
} }
if (t.options.show.startcat_menu) {
appendChildren(t.container, [text(" Startcat: "), t.ui.startcat_menu]);
t.ui.startcat_menu.onchange = function(){
var startcat = t.ui.startcat_menu.value;
t.gm.change_startcat(startcat);
}
}
if (t.options.show.to_menu) {
appendChildren(t.container, [text(" To: "), t.ui.to_toggle, t.ui.to_menu]);
t.ui.to_menu.onchange = function(){
var languages = new Array();
for (i in t.ui.to_menu.options) {
var opt = t.ui.to_menu.options[i];
if (opt.selected)
languages.push(opt.value);
}
t.gm.change_languages(languages);
}
}
appendChildren(t.container, [t.ui.clear_button]);
if (t.options.show.random_button) {
appendChildren(t.container, [t.ui.random_button]);
}
appendChildren(t.container, [t.ui.debug_toggle]);
/* --- Client state initialisation -------------------------------------- */ /* --- Client state initialisation -------------------------------------- */
this.editor = editor; this.editor = editor;
this.gm = editor.gm;
this.server = editor.server; this.server = editor.server;
/* --- Main program, this gets things going ----------------------------- */ /* --- Register Grammar Manager hooks ----------------------------------- */
this.server.get_grammarlists(bind(this.show_grammarlist,this)); this.gm.register_action("onload", bind(this.hook_onload, this));
this.gm.register_action("change_grammar", bind(this.hook_change_grammar, this));
// this.gm.register_action("change_startcat", bind(this.hook_change_startcat, this));
// this.gm.register_action("change_languages", bind(this.hook_change_languages, this));
} }
/* --- Grammar menu --------------------------------------------------------- */ /* --- Grammar menu --------------------------------------------------------- */
// Basically called once, when initializing // show the grammar list
// Copied from minibar.js EditorMenu.prototype.hook_onload=function(dir,grammar_names,dir_count) {
EditorMenu.prototype.show_grammarlist=function(dir,grammar_names,dir_count) { debug("EditorMenu: onload");
var t=this; var t=this;
var first_time=t.ui.grammar_menu.options.length == 0; var first_time=t.ui.grammar_menu.options.length == 0;
if(first_time) { if(first_time) {
@@ -85,7 +110,7 @@ EditorMenu.prototype.show_grammarlist=function(dir,grammar_names,dir_count) {
var grammar0=t.options.initial.grammar; var grammar0=t.options.initial.grammar;
if(!grammar0) grammar0=t.grammars[0]; if(!grammar0) grammar0=t.grammars[0];
t.ui.grammar_menu.value=grammar0; t.ui.grammar_menu.value=grammar0;
t.change_grammar(); // t.change_grammar();
} }
// Wait at most 1.5s before showing the grammar menu. // Wait at most 1.5s before showing the grammar menu.
if(first_time) t.timeout=setTimeout(pick_first_grammar,1500); if(first_time) t.timeout=setTimeout(pick_first_grammar,1500);
@@ -93,24 +118,21 @@ EditorMenu.prototype.show_grammarlist=function(dir,grammar_names,dir_count) {
} }
// Copied from minibar.js // Copied from minibar.js
EditorMenu.prototype.change_grammar=function() { EditorMenu.prototype.hook_change_grammar=function() {
debug("EditorMenu: change grammar");
var t=this; var t=this;
var grammar_url = t.ui.grammar_menu.value; var grammar_url = t.ui.grammar_menu.value;
t.server.switch_to_other_grammar(grammar_url, function() { t.server.switch_to_other_grammar(grammar_url, function() {
t.server.grammar_info(function(grammar){ t.server.grammar_info(function(grammar){
t.update_startcat_menu(grammar); t.update_startcat_menu(grammar);
t.update_language_menu(t.ui.to_menu, grammar); t.update_language_menu(t.ui.to_menu, grammar);
// Call in main Editor object
t.editor.change_grammar(grammar);
}); });
}); });
} }
/* --- Start category menu -------------------------------------------------- */ /* --- Start category menu -------------------------------------------------- */
// Called each time the current grammar is changed! // Called from hook_change_grammar
// Copied from minibar_input.js
EditorMenu.prototype.update_startcat_menu=function(grammar) { EditorMenu.prototype.update_startcat_menu=function(grammar) {
var t=this; var t=this;
var menu=this.ui.startcat_menu; var menu=this.ui.startcat_menu;
@@ -129,16 +151,9 @@ EditorMenu.prototype.update_startcat_menu=function(grammar) {
// } // }
} }
//
EditorMenu.prototype.change_startcat=function() {
var new_startcat = this.ui.startcat_menu.value;
this.editor.change_startcat(new_startcat);
}
/* --- Langugage (to) menu -------------------------------------------------- */ /* --- Langugage (to) menu -------------------------------------------------- */
// Called each time the current grammar is changed! // Called from hook_change_grammar
// Copied from minibar_support.js
EditorMenu.prototype.update_language_menu=function(menu,grammar) { EditorMenu.prototype.update_language_menu=function(menu,grammar) {
var t = this; var t = this;
function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng" function langpart(conc,abs) { // langpart("FoodsEng","Foods") == "Eng"
@@ -162,14 +177,3 @@ EditorMenu.prototype.update_language_menu=function(menu,grammar) {
} }
} }
//
EditorMenu.prototype.change_language=function() {
this.editor.languages = new Array();
for (i in this.ui.to_menu.options) {
var opt = this.ui.to_menu.options[i];
if (opt.selected)
this.editor.languages.push(opt.value);
}
this.editor.update_linearisation();
}

View File

@@ -8,8 +8,7 @@ var editor_options = {
// grammar: "http://localhost:41296/grammars/Phrasebook.pgf", // grammar: "http://localhost:41296/grammars/Phrasebook.pgf",
// startcat: "Proposition", // startcat: "Proposition",
// languages: ["Eng","Swe","Ita"], // languages: ["Eng","Swe","Ita"],
// abstr: "PropOpenDate (SuperlPlace TheMostExpensive School) Tomorrow", // abstr: "PropOpenDate (SuperlPlace TheMostExpensive School) Tomorrow"
// node_id: null
// }, // },
show: { show: {
grammar_menu: true, grammar_menu: true,
@@ -35,8 +34,6 @@ if(window.Minibar) // Minibar loaded?
}, },
// get us back to the editor! // get us back to the editor!
abstract_action: function(tree) { abstract_action: function(tree) {
//var minibar=this;
// how to get hold of new minibar?
var editor_options = { var editor_options = {
target: "editor", target: "editor",
initial: { initial: {
@@ -52,13 +49,14 @@ if(window.Minibar) // Minibar loaded?
editor.hide(); editor.hide();
editor.minibar=new Minibar(server,minibar_options); editor.minibar=new Minibar(server,minibar_options);
//editor.minibar.editor = editor; // :S //editor.minibar.editor = editor; // :S
editor.minibar.show() editor.minibar.show();
} }
if(/^\?\/tmp\//.test(location.search)) { if(/^\?\/tmp\//.test(location.search)) {
var args=decodeURIComponent(location.search.substr(1)).split(" ") var args=decodeURIComponent(location.search.substr(1)).split(" ")
if(args[0]) server_options.grammars_url=args[0]; if(args[0]) server_options.grammars_url=args[0];
} }
var server = pgf_online(server_options); var server = pgf_online(server_options);
var editor = new Editor(server, editor_options); // var editor = new Editor(server, editor_options);
var gm = new GrammarManager(server);
var editor = new Editor(gm, editor_options);