diff --git a/src/www/gfse/cloud.js b/src/www/gfse/cloud.js index f19b6ee37..18cbaae9f 100644 --- a/src/www/gfse/cloud.js +++ b/src/www/gfse/cloud.js @@ -24,7 +24,7 @@ function upload(g) { [hidden(g.basename+".gf",show_abstract(g))]) for(var i in g.concretes) form.appendChild(hidden(g.basename+g.concretes[i].langcode+".gf", - show_concrete(g.basename)(g.concretes[i]))); + show_concrete(g)(g.concretes[i]))); editor.appendChild(form); form.submit(); form.parentNode.removeChild(form); diff --git a/src/www/gfse/cloud2.js b/src/www/gfse/cloud2.js index 8a6f198f3..4794e4ea0 100644 --- a/src/www/gfse/cloud2.js +++ b/src/www/gfse/cloud2.js @@ -36,7 +36,7 @@ function upload(g) { var cname=g.basename+g.concretes[i].langcode+".gf"; //files.push(cname); form.appendChild(hidden(cname, - show_concrete(g.basename)(g.concretes[i]))); + show_concrete(g)(g.concretes[i]))); } editor.appendChild(form); form.submit(); diff --git a/src/www/gfse/editor.css b/src/www/gfse/editor.css index ec92a8e01..302548371 100644 --- a/src/www/gfse/editor.css +++ b/src/www/gfse/editor.css @@ -17,6 +17,9 @@ div#file, pre.plain { background: white; padding: 0.6ex; } img.cloud, img.right, div.right, div.modtime { float: right; } .modtime { color: #999; white-space: nowrap; } +table.grammar_list { border-collapse: collapse; margin-left: 1.0em; } +table.grammar_list td { padding: 0.4ex 0.25em; } + div.namebar { background: #9df; } div.namebar table { width: 100%; } .namebar h3, .home h3 { margin: 0; color: #009; } @@ -45,7 +48,8 @@ div.template:hover .editable:hover, .deletable:hover { background: #ff9; } -.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete +.extensible:hover .more,.editable:hover > .edit ,.deletable:hover > .delete, + tr.deletable:hover .delete { visibility: visible; } .more { color: green; } diff --git a/src/www/gfse/editor.js b/src/www/gfse/editor.js index 7f2112b6b..d2c41f30f 100644 --- a/src/www/gfse/editor.js +++ b/src/www/gfse/editor.js @@ -35,24 +35,29 @@ function draw_grammar_list() { insertAfter(cloud_download,cloud_upload); } editor.appendChild(home) - var gs=ul([]); function del(i) { return function () { delete_grammar(i); } } function clone(i) { return function (g,b) { clone_grammar(i); } } - for(var i=0;i0 && !local.get(local.count-1)) @@ -291,6 +314,14 @@ function draw_startcat(g) { return indent([kw("flags startcat"),sep(" = "),m]); } +function draw_extends(exts) { + var kw_extends=kw("extends ") + kw_extends.title="This grammar is an extension of the grammars listed here." + return exts && exts.length>0 + ? indent([kw_extends,ident(exts.join(", "))]) + : text("") +} + function draw_abstract(g) { var kw_cat = kw("cat"); kw_cat.title = "The categories (nonterminals) of the grammar are enumerated here. [C.3.2]"; @@ -307,6 +338,7 @@ function draw_abstract(g) { return div_id("file", [kw("abstract "),ident(g.basename),sep(" = "), draw_timestamp(g.abstract), + draw_extends(g.extends), flags, indent([extensible([kw_cat, indent(draw_cats(g))]), @@ -352,21 +384,27 @@ function rename_cat(g,el,cat) { string_editor(el,cat,ren); } +function duplicated(g,kind,orig) { + return orig==g.basename + ? "Same "+kind+" defined twice in this module" + : "Same "+kind+" already defined in "+orig +} + function draw_cats(g) { var cs=g.abstract.cats; var es=[]; - var defined={}; + var defined=inherited_cats(g); function eident(cat) { function ren(g,el) { rename_cat(g,el,cat); } return editable("span",ident(cat),g,ren,"Rename category"); } function check(cat,el) { - return ifError(defined[cat],"Same category named twice",el); + return ifError(defined[cat],duplicated(g,"category",defined[cat]),el); } function del(i) { return function() { delete_cat(g,i); }} for(var i in cs) { es.push(deletable(del(i),check(cs[i],eident(cs[i])),"Delete this category")); - defined[cs[i]]=true; + defined[cs[i]]=g.basename; es.push(sep("; ")); } es.push(more(g,add_cat,"Add more categories")); @@ -419,24 +457,24 @@ function draw_funs(g) { var funs=g.abstract.funs; var es=[]; var dc=defined_cats(g); - var df={}; + var df=inherited_funs(g); function del(i) { return function() { delete_fun(g,i); }} function draw_efun(i,df) { - return editable("span",draw_fun(funs[i],dc,df),g,edit_fun(i),"Edit this function"); + return editable("span",draw_fun(g,funs[i],dc,df),g,edit_fun(i),"Edit this function"); } for(var i in funs) { es.push(node_sortable("fun",funs[i].name,[deletable(del(i),draw_efun(i,df),"Delete this function")])); - df[funs[i].name]=true; + df[funs[i].name]=g.basename; } es.push(more(g,add_fun,"Add a new function")); return es; } -function draw_fun(fun,dc,df) { +function draw_fun(g,fun,dc,df) { function check(el) { return ifError(dc[fun.name], "Function names must be distinct from category names", - ifError(df[fun.name],"Same function defined twice",el)); + ifError(df[fun.name],duplicated(g,"function",df[fun.name]),el)); } return node("span",{}, [check(ident(fun.name)),sep(" : "),draw_type(fun.type,dc)]); @@ -496,6 +534,7 @@ function draw_concrete(g,i) { edit_langcode,"Change language"), kw(" of "),ident(g.basename),sep(" = "), draw_timestamp(conc), + draw_extends((g.extends || []).map(conc_extends(conc))), indent([extensible([kw("open "),draw_opens(g,i)])]), indent([kw_lincat,draw_lincats(g,i)]), indent([kw_lin,draw_lins(g,i)]), @@ -658,7 +697,7 @@ function draw_lincats(g,i) { return node("span",{"class":cls}, [ident(c.cat),sep(" = "),t]); } - var dc=defined_cats(g); + var dc=locally_defined_cats(g,{}); function draw_lincat(c) { var cat=c.cat; var err=!dc[cat]; @@ -821,7 +860,7 @@ function draw_lins(g,ci) { l.push(t); return node("span",{"class":cls},l); } - var df=defined_funs(g); + var df=locally_defined_funs(g,{}); function draw_lin(f) { var fun=f.fun; var err= !df[fun]; @@ -852,6 +891,42 @@ function draw_lins(g,ci) { /* -------------------------------------------------------------------------- */ +function defined_cats(g) { + var grammar_byname=cached_grammar_byname(); + var igs=(g.extends || []).map(grammar_byname) + return all_defined_cats(g,igs) +} + +function inherited_cats(g) { + var grammar_byname=cached_grammar_byname(); + var igs=(g.extends || []).map(grammar_byname) + return all_inherited_cats(igs,{}) +} + +function defined_funs(g) { + var grammar_byname=cached_grammar_byname(); + var igs=(g.extends || []).map(grammar_byname) + return all_defined_funs(g,igs) +} + +function inherited_funs(g) { + var grammar_byname=cached_grammar_byname(); + var igs=(g.extends || []).map(grammar_byname) + return all_inherited_funs(igs,{}) +} + +function cached_grammar_byname() { + var gix={}; + for(var i=0;i ... -> Cat_n -type Grammar = { basename: Id, abstract : Abstract, concretes : [Concrete] } +type Grammar = { basename: ModId, + extends: [ModId], + abstract: Abstract, + concretes: [Concrete] } -type Abstract = { startcat: Cat, cats : [Cat], funs : [ Fun ] } -type Fun = { name:Id, type : Type } +type Abstract = { startcat: Cat, cats: [Cat], funs: [Fun] } +type Fun = { name: FunId, type: Type } -type Concrete = { langcode:Id, - opens:[Id], - params: [{name:Id, rhs:String}], - lincats : [{ cat:Cat, type:Term}], - opers: [{name:Lhs, rhs:Term}], - lins: [{fun:Id, args:[Id], lin:Term}] +type Concrete = { langcode: Id, + opens: [ModId], + params: [{name: Id, rhs: String}], + lincats : [{ cat: Cat, type: Term}], + opers: [{name: Lhs, rhs: Term}], + lins: [{fun: FunId, args: [Id], lin: Term}] } @@ -24,24 +29,42 @@ type Lhs = String -- name and type of oper, type Term = String -- arbitrary GF term (not parsed by the editor) */ -// defined_cats :: [Grammar] -> {Cat=>Bool} -function defined_cats(g) { - var dc={}; +// locally_defined_cats :: Grammar -> {Cat=>Bool} -> {Cat=>Bool} // destr upd +function locally_defined_cats(g,dc) { with(g.abstract) - for(var i in cats) dc[cats[i]]=true; + for(var i in cats) dc[cats[i]]=g.basename; return dc; } -// defined_funs :: [Grammar] -> {Id=>Bool} -function defined_funs(g) { - var df={}; +// all_defined_cats :: Grammar -> [Grammar] -> {Cat=>Bool} +function all_defined_cats(g,igs) { + return all_inherited_cats(igs,locally_defined_cats(g,{})) +} +// all_inherited_cats :: [Grammar] -> {Cat=>Bool} -> {Cat=>Bool} // destr upd +function all_inherited_cats(igs,dc) { + for(var i in igs) dc=locally_defined_cats(igs[i],dc) + return dc; +} + +// locally_defined_funs :: [Grammar] -> {FunId=>ModId} -> {Id=>ModId} // destr upd +function locally_defined_funs(g,df) { with(g.abstract) - for(var i in funs) df[funs[i].name]=true; + for(var i in funs) df[funs[i].name]=g.basename; + return df; +} + +// all_defined_funs :: Grammar -> [Grammar] -> {FunId=>ModId} +function all_defined_funs(g,igs) { + return all_inherited_funs(igs,locally_defined_funs(g,{})) +} +// all_inherited_funs :: [Grammar] -> {FunId=>ModId} -> {FunId=>ModId} // destr upd +function all_inherited_funs(igs,df) { + for(var i in igs) df=locally_defined_funs(igs[i],df) return df; } // Return the type of a named function in the abstract syntax -// function_type :: Grammar -> Id -> Type +// function_type :: Grammar -> FunId -> Type function function_type(g,fun) { with(g.abstract) for(var i in funs) if(funs[i].name==fun) return funs[i].type @@ -187,7 +210,7 @@ function parse_oper(s) { } -/* --- Print as plain text (normal GF concrete syntax) ---------------------- */ +/* --- Print as plain text (normal GF source syntax) ------------------------- */ function show_type(t) { var s=""; @@ -209,7 +232,9 @@ function show_grammar(g) { function show_abstract(g) { // var startcat= g.abstract.cats.length==1 ? g.abstract.cats[0] : g.abstract.startcat; var startcat= g.abstract.startcat || g.abstract.cats[0]; - return "abstract "+g.basename+" = {\n\n" + return "abstract "+g.basename+" = " + +show_extends(g.extends) + +"{\n\n" +"flags coding = utf8 ;\n\n" +show_startcat(startcat) +show_cats(g.abstract.cats) @@ -217,6 +242,10 @@ function show_abstract(g) { +"}\n"; } +function show_extends(exts) { + return exts && exts.length>0 ? exts.join(", ")+" ** " : ""; +} + function show_startcat(startcat) { return startcat ? "flags startcat = "+startcat+";\n\n" : ""; } @@ -228,15 +257,18 @@ function show_cats(cats) { function show_funs(funs) { return show_list("fun",show_fun,funs); } function show_concretes(g) { - return map(show_concrete(g.basename),g.concretes).join("\n\n"); + return map(show_concrete(g),g.concretes).join("\n\n"); } -function show_concrete(basename) { +function conc_extends(conc) { return function(m) { return m+conc.langcode; }} + +function show_concrete(g) { return function(conc) { return "--# -path=.:present\n" - + "concrete "+basename+conc.langcode+" of "+basename+" =" + + "concrete "+g.basename+conc.langcode+" of "+g.basename+" = " + +show_extends((g.extends || []).map(conc_extends(conc))) +show_opens(conc.opens) - +" {\n\nflags coding = utf8 ;\n\n" + +"{\n\nflags coding = utf8 ;\n\n" +show_params(conc.params) +show_lincats(conc.lincats) +show_opers(conc.opers) @@ -252,7 +284,7 @@ function show_list(kw,show1,list) { } function show_opens(opens) { - return opens && opens.length>0 ? "\n\nopen "+opens.join(", ")+" in" : "" + return opens && opens.length>0 ? "\n\nopen "+opens.join(", ")+" in " : "" } function show_params(params) { return show_list("param",show_param,params); }