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
- Link to jump into minibar
- 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
- Enter string/float/int literals
- UI issue with DisambPhrasebookEng
- more prominence to Disamb-linearizations
- 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
- 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");
}
@@ -74,3 +74,13 @@ body.syntax_editor {
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="editor.css" />
</head>
<body class=syntax_editor>
<body class="syntax-editor">
<h2>Syntax Editor</h2>
<div id=minibar></div>
<div id=editor></div>
<div id="minibar"></div>
<div id="editor"></div>
<noscript>This page doesn't works unless JavaScript is enabled.</noscript>
<hr />
<small class="modtime">
John J. Camilleri, November 2012
</small>
<div id="debug" class="hidden"></div>
<!-- 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/pgf_online.js"></script>

View File

@@ -15,7 +15,7 @@ new EditorMenu
Editor.redraw_tree();
Editor.get_refinements();
*/
function Editor(server,opts) {
function Editor(gm,opts) {
var t = this;
/* --- Configuration ---------------------------------------------------- */
@@ -57,16 +57,34 @@ function Editor(server,opts) {
]);
/* --- Client state initialisation -------------------------------------- */
this.server = server;
this.gm = gm;
this.server = gm.server;
this.ast = null;
this.grammar = null;
this.startcat = null;
this.languages = [];
/* --- Register Grammar Manager hooks ----------------------------------- */
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 ----------------------------- */
this.menu = new EditorMenu(this, this.options);
/* --- Shutdown the editor nicely --------------------------------------- */
/* --- Other basic stuff ------------------------------------------------ */
this.shutdown = function() {
clear(this.container);
this.container.classList.remove("editor");
@@ -102,26 +120,17 @@ Editor.prototype.get_ast=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 */
Editor.prototype.change_grammar=function(grammar_info) {
var t = this;
t.grammar = grammar_info;
var startcat0 = t.options.initial.startcat
if (elem(startcat0, grammar_info.categories))
t.startcat = startcat0;
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();
// TODO
Editor.prototype.initialize_from=function(opts) {
var t=this;
if (opts.startcat)
t.options.initial_startcat=opts.startcat;
t.change_grammar();
if (opts.abstr)
t.import_ast(opts.abstr);
}
// Called after changing grammar or startcat
@@ -247,7 +256,7 @@ Editor.prototype.update_linearisation=function(){
return hasPrefix(conc,abs) ? conc.substr(abs.length) : conc;
}
function row(lang, lin) {
var langname = langpart(lang, t.grammar.name);
var langname = langpart(lang, t.gm.grammar.name);
var btn = button(langname, function(){
bind(t.options.lin_action,t)(lin,lang);
});
@@ -264,7 +273,7 @@ Editor.prototype.update_linearisation=function(){
var tbody=empty("tbody");
for (i in data) {
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))
}
}

View File

@@ -34,39 +34,64 @@ function EditorMenu(editor,opts) {
random_button: button("Random", function(){
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 (this.options.show.grammar_menu) {
appendChildren(this.container, [text(" Grammar: "), grammar_menu]);
grammar_menu.onchange = bind(this.change_grammar,this);
}
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.grammar_menu) {
appendChildren(t.container, [text(" Grammar: "), t.ui.grammar_menu]);
t.ui.grammar_menu.onchange = function(){
var grammar_url = t.ui.grammar_menu.value;
t.gm.change_grammar(grammar_url);
}
}
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 -------------------------------------- */
this.editor = editor;
this.gm = editor.gm;
this.server = editor.server;
/* --- Main program, this gets things going ----------------------------- */
this.server.get_grammarlists(bind(this.show_grammarlist,this));
/* --- Register Grammar Manager hooks ----------------------------------- */
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 --------------------------------------------------------- */
// Basically called once, when initializing
// Copied from minibar.js
EditorMenu.prototype.show_grammarlist=function(dir,grammar_names,dir_count) {
// show the grammar list
EditorMenu.prototype.hook_onload=function(dir,grammar_names,dir_count) {
debug("EditorMenu: onload");
var t=this;
var first_time=t.ui.grammar_menu.options.length == 0;
if(first_time) {
@@ -85,7 +110,7 @@ EditorMenu.prototype.show_grammarlist=function(dir,grammar_names,dir_count) {
var grammar0=t.options.initial.grammar;
if(!grammar0) grammar0=t.grammars[0];
t.ui.grammar_menu.value=grammar0;
t.change_grammar();
// t.change_grammar();
}
// Wait at most 1.5s before showing the grammar menu.
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
EditorMenu.prototype.change_grammar=function() {
EditorMenu.prototype.hook_change_grammar=function() {
debug("EditorMenu: change grammar");
var t=this;
var grammar_url = t.ui.grammar_menu.value;
t.server.switch_to_other_grammar(grammar_url, function() {
t.server.grammar_info(function(grammar){
t.update_startcat_menu(grammar);
t.update_language_menu(t.ui.to_menu, grammar);
// Call in main Editor object
t.editor.change_grammar(grammar);
});
});
}
/* --- Start category menu -------------------------------------------------- */
// Called each time the current grammar is changed!
// Copied from minibar_input.js
// Called from hook_change_grammar
EditorMenu.prototype.update_startcat_menu=function(grammar) {
var t=this;
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 -------------------------------------------------- */
// Called each time the current grammar is changed!
// Copied from minibar_support.js
// Called from hook_change_grammar
EditorMenu.prototype.update_language_menu=function(menu,grammar) {
var t = this;
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",
// startcat: "Proposition",
// languages: ["Eng","Swe","Ita"],
// abstr: "PropOpenDate (SuperlPlace TheMostExpensive School) Tomorrow",
// node_id: null
// abstr: "PropOpenDate (SuperlPlace TheMostExpensive School) Tomorrow"
// },
show: {
grammar_menu: true,
@@ -35,8 +34,6 @@ if(window.Minibar) // Minibar loaded?
},
// get us back to the editor!
abstract_action: function(tree) {
//var minibar=this;
// how to get hold of new minibar?
var editor_options = {
target: "editor",
initial: {
@@ -52,13 +49,14 @@ if(window.Minibar) // Minibar loaded?
editor.hide();
editor.minibar=new Minibar(server,minibar_options);
//editor.minibar.editor = editor; // :S
editor.minibar.show()
editor.minibar.show();
}
if(/^\?\/tmp\//.test(location.search)) {
var args=decodeURIComponent(location.search.substr(1)).split(" ")
if(args[0]) server_options.grammars_url=args[0];
}
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);