1
0
forked from GitHub/gf-core
This commit is contained in:
krangelov
2019-06-24 12:14:15 +02:00
9 changed files with 3250 additions and 735 deletions

View File

@@ -0,0 +1,7 @@
# Deprecation notice
As of June 2019, this JavaScript version of the GF runtime is considered deprecated,
in favour of the TypeScript version in `../typescript/gflib.ts`.
If you don't want/need TypeScript and are just looking for a ready-to-use JavaScript version,
see `../typescript/js/gflib.js` which is generated directly from the TypeScript version.

View File

@@ -1,38 +1,38 @@
function GFGrammar(abstract, concretes) { function GFGrammar(abstract, concretes) {
this.abstract = abstract; this.abstract = abstract;
this.concretes = concretes; this.concretes = concretes;
} }
/* Translates a string from any concrete syntax to all concrete syntaxes. /* Translates a string from any concrete syntax to all concrete syntaxes.
Uses the start category of the grammar. Uses the start category of the grammar.
*/ */
GFGrammar.prototype.translate = function (input, fromLang, toLang) { GFGrammar.prototype.translate = function (input, fromLang, toLang) {
var outputs = new Object(); var outputs = new Object();
var fromConcs = this.concretes; var fromConcs = this.concretes;
if (fromLang) { if (fromLang) {
fromConcs = new Object(); fromConcs = new Object();
fromConcs[fromLang] = this.concretes[fromLang]; fromConcs[fromLang] = this.concretes[fromLang];
} }
var toConcs = this.concretes; var toConcs = this.concretes;
if (toLang) { if (toLang) {
toConcs = new Object(); toConcs = new Object();
toConcs[toLang] = this.concretes[toLang]; toConcs[toLang] = this.concretes[toLang];
} }
for (var c1 in fromConcs) { for (var c1 in fromConcs) {
var concrete = this.concretes[c1]; var concrete = this.concretes[c1];
var trees = concrete.parseString(input, this.abstract.startcat); var trees = concrete.parseString(input, this.abstract.startcat);
if (trees.length > 0) { if (trees.length > 0) {
outputs[c1] = new Array(); outputs[c1] = new Array();
for (var i in trees) { for (var i in trees) {
outputs[c1][i] = new Object(); outputs[c1][i] = new Object();
for (var c2 in toConcs) { for (var c2 in toConcs) {
outputs[c1][i][c2] = this.concretes[c2].linearize(trees[i]); outputs[c1][i][c2] = this.concretes[c2].linearize(trees[i]);
} }
} }
} }
} }
return outputs; return outputs;
} }
@@ -47,56 +47,56 @@ String.prototype.setTag = function (tag) { this.tag = tag; };
/* Abstract syntax trees */ /* Abstract syntax trees */
function Fun(name) { function Fun(name) {
this.name = name; this.name = name;
this.args = new Array(); this.args = new Array();
for (var i = 1; i < arguments.length; i++) { for (var i = 1; i < arguments.length; i++) {
this.args[i-1] = arguments[i]; this.args[i-1] = arguments[i];
} }
} }
Fun.prototype.print = function () { return this.show(0); } ; Fun.prototype.print = function () { return this.show(0); } ;
Fun.prototype.show = function (prec) { Fun.prototype.show = function (prec) {
if (this.isMeta()) { if (this.isMeta()) {
if (isUndefined(this.type)) { if (isUndefined(this.type)) {
return '?'; return '?';
} else { } else {
var s = '?:' + this.type; var s = '?:' + this.type;
if (prec > 0) { if (prec > 0) {
s = "(" + s + ")" ; s = "(" + s + ")" ;
} }
return s; return s;
} }
} else { } else {
var s = this.name; var s = this.name;
var cs = this.args; var cs = this.args;
for (var i in cs) { for (var i in cs) {
s += " " + (isUndefined(cs[i]) ? "undefined" : cs[i].show(1)); s += " " + (isUndefined(cs[i]) ? "undefined" : cs[i].show(1));
} }
if (prec > 0 && cs.length > 0) { if (prec > 0 && cs.length > 0) {
s = "(" + s + ")" ; s = "(" + s + ")" ;
} }
return s; return s;
} }
}; };
Fun.prototype.getArg = function (i) { Fun.prototype.getArg = function (i) {
return this.args[i]; return this.args[i];
}; };
Fun.prototype.setArg = function (i,c) { Fun.prototype.setArg = function (i,c) {
this.args[i] = c; this.args[i] = c;
}; };
Fun.prototype.isMeta = function() { Fun.prototype.isMeta = function() {
return this.name == '?'; return this.name == '?';
} ; } ;
Fun.prototype.isComplete = function() { Fun.prototype.isComplete = function() {
if (this.isMeta()) { if (this.isMeta()) {
return false; return false;
} else { } else {
for (var i in this.args) { for (var i in this.args) {
if (!this.args[i].isComplete()) { if (!this.args[i].isComplete()) {
return false; return false;
} }
} }
return true; return true;
} }
} ; } ;
Fun.prototype.isLiteral = function() { Fun.prototype.isLiteral = function() {
return (/^[\"\-\d]/).test(this.name); return (/^[\"\-\d]/).test(this.name);
@@ -127,98 +127,98 @@ Fun.prototype.isEqual = function(obj) {
/* Type annotation */ /* Type annotation */
function GFAbstract(startcat, types) { function GFAbstract(startcat, types) {
this.startcat = startcat; this.startcat = startcat;
this.types = types; this.types = types;
} }
GFAbstract.prototype.addType = function(fun, args, cat) { GFAbstract.prototype.addType = function(fun, args, cat) {
this.types[fun] = new Type(args, cat); this.types[fun] = new Type(args, cat);
} ; } ;
GFAbstract.prototype.getArgs = function(fun) { GFAbstract.prototype.getArgs = function(fun) {
return this.types[fun].args; return this.types[fun].args;
} }
GFAbstract.prototype.getCat = function(fun) { GFAbstract.prototype.getCat = function(fun) {
return this.types[fun].cat; return this.types[fun].cat;
}; };
GFAbstract.prototype.annotate = function(tree, type) { GFAbstract.prototype.annotate = function(tree, type) {
if (tree.name == '?') { if (tree.name == '?') {
tree.type = type; tree.type = type;
} else { } else {
var typ = this.types[tree.name]; var typ = this.types[tree.name];
for (var i in tree.args) { for (var i in tree.args) {
this.annotate(tree.args[i], typ.args[i]); this.annotate(tree.args[i], typ.args[i]);
} }
} }
return tree; return tree;
} ; } ;
GFAbstract.prototype.handleLiterals = function(tree, type) { GFAbstract.prototype.handleLiterals = function(tree, type) {
if (tree.name != '?') { if (tree.name != '?') {
if (type == "String" || type == "Int" || type == "Float") { if (type == "String" || type == "Int" || type == "Float") {
tree.name = type + "_Literal_" + tree.name; tree.name = type + "_Literal_" + tree.name;
} else { } else {
var typ = this.types[tree.name]; var typ = this.types[tree.name];
for (var i in tree.args) { for (var i in tree.args) {
this.handleLiterals(tree.args[i], typ.args[i]); this.handleLiterals(tree.args[i], typ.args[i]);
} }
} }
} }
return tree; return tree;
} ; } ;
/* Hack to get around the fact that our SISR doesn't build real Fun objects. */ /* Hack to get around the fact that our SISR doesn't build real Fun objects. */
GFAbstract.prototype.copyTree = function(x) { GFAbstract.prototype.copyTree = function(x) {
var t = new Fun(x.name); var t = new Fun(x.name);
if (!isUndefined(x.type)) { if (!isUndefined(x.type)) {
t.type = x.type; t.type = x.type;
} }
var cs = x.args; var cs = x.args;
if (!isUndefined(cs)) { if (!isUndefined(cs)) {
for (var i in cs) { for (var i in cs) {
t.setArg(i, this.copyTree(cs[i])); t.setArg(i, this.copyTree(cs[i]));
} }
} }
return t; return t;
} ; } ;
GFAbstract.prototype.parseTree = function(str, type) { GFAbstract.prototype.parseTree = function(str, type) {
return this.annotate(this.parseTree_(str.match(/[\w\'\.\"]+|\(|\)|\?|\:/g), 0), type); return this.annotate(this.parseTree_(str.match(/[\w\'\.\"]+|\(|\)|\?|\:/g), 0), type);
} ; } ;
GFAbstract.prototype.parseTree_ = function(tokens, prec) { GFAbstract.prototype.parseTree_ = function(tokens, prec) {
if (tokens.length == 0 || tokens[0] == ")") { return null; } if (tokens.length == 0 || tokens[0] == ")") { return null; }
var t = tokens.shift(); var t = tokens.shift();
if (t == "(") { if (t == "(") {
var tree = this.parseTree_(tokens, 0); var tree = this.parseTree_(tokens, 0);
tokens.shift(); tokens.shift();
return tree; return tree;
} else if (t == '?') { } else if (t == '?') {
var tree = this.parseTree_(tokens, 0); var tree = this.parseTree_(tokens, 0);
return new Fun('?'); return new Fun('?');
} else { } else {
var tree = new Fun(t); var tree = new Fun(t);
if (prec == 0) { if (prec == 0) {
var c, i; var c, i;
for (i = 0; (c = this.parseTree_(tokens, 1)) !== null; i++) { for (i = 0; (c = this.parseTree_(tokens, 1)) !== null; i++) {
tree.setArg(i,c); tree.setArg(i,c);
} }
} }
return tree; return tree;
} }
} ; } ;
function Type(args, cat) { function Type(args, cat) {
this.args = args; this.args = args;
this.cat = cat; this.cat = cat;
} }
/* Linearization */ /* Linearization */
function GFConcrete(flags, productions, functions, sequences, startCats, totalFIds) { function GFConcrete(flags, productions, functions, sequences, startCats, totalFIds) {
this.flags = flags; this.flags = flags;
this.productions = productions; this.productions = productions;
this.functions = functions; this.functions = functions;
this.sequences = sequences; this.sequences = sequences;
this.startCats = startCats; this.startCats = startCats;
this.totalFIds = totalFIds; this.totalFIds = totalFIds;
this.pproductions = productions; this.pproductions = productions;
this.lproductions = new Object(); this.lproductions = new Object();
for (var fid in productions) { for (var fid in productions) {
for (var i in productions[fid]) { for (var i in productions[fid]) {
@@ -231,11 +231,11 @@ function GFConcrete(flags, productions, functions, sequences, startCats, totalFI
rule.fun = fun; rule.fun = fun;
var register = function (args, key, i) { var register = function (args, key, i) {
if (i < args.length) { if (i < args.length) {
var c = 0; var c = 0;
var arg = args[i].fid; var arg = args[i].fid;
for (var k in productions[arg]) { for (var k in productions[arg]) {
var rule = productions[arg][k]; var rule = productions[arg][k];
if (rule.id == "Coerce") { if (rule.id == "Coerce") {
register(args,key + "_" + rule.arg,i+1); register(args,key + "_" + rule.arg,i+1);
@@ -245,18 +245,18 @@ function GFConcrete(flags, productions, functions, sequences, startCats, totalFI
if (c == 0) if (c == 0)
register(args,key + "_" + arg,i+1); register(args,key + "_" + arg,i+1);
} else { } else {
var set = lproductions[key]; var set = lproductions[key];
if (set == null) { if (set == null) {
set = new Array(); set = new Array();
lproductions[key] = set; lproductions[key] = set;
} }
set.push({fun: fun, fid: fid}); set.push({fun: fun, fid: fid});
} }
} }
register(rule.args,rule.fun.name,0); register(rule.args,rule.fun.name,0);
} }
} }
} }
@@ -271,26 +271,26 @@ GFConcrete.prototype.linearizeSyms = function (tree, tag) {
var res = new Array(); var res = new Array();
if (tree.isString()) { if (tree.isString()) {
var sym = new SymKS(tree.name); var sym = new SymKS(tree.name);
sym.tag = tag; sym.tag = tag;
res.push({fid: -1, table: [[sym]]}); res.push({fid: -1, table: [[sym]]});
} else if (tree.isInt()) { } else if (tree.isInt()) {
var sym = new SymKS(tree.name); var sym = new SymKS(tree.name);
sym.tag = tag; sym.tag = tag;
res.push({fid: -2, table: [[sym]]}); res.push({fid: -2, table: [[sym]]});
} else if (tree.isFloat()) { } else if (tree.isFloat()) {
var sym = new SymKS(tree.name); var sym = new SymKS(tree.name);
sym.tag = tag; sym.tag = tag;
res.push({fid: -3, table: [[sym]]}); res.push({fid: -3, table: [[sym]]});
} else if (tree.isMeta()) { } else if (tree.isMeta()) {
// TODO: Use lindef here // TODO: Use lindef here
var cat = this.startCats[tree.type]; var cat = this.startCats[tree.type];
var sym = new SymKS(tree.name); var sym = new SymKS(tree.name);
sym.tag = tag; sym.tag = tag;
for (var fid = cat.s; fid <= cat.e; fid++) { for (var fid = cat.s; fid <= cat.e; fid++) {
res.push({fid: fid, table: [[sym]]}); res.push({fid: fid, table: [[sym]]});
} }
} else { } else {
var cs = new Array(); var cs = new Array();
@@ -347,7 +347,7 @@ GFConcrete.prototype.syms2toks = function (syms) {
case "KP": case "KP":
for (var j in sym.tokens) { for (var j in sym.tokens) {
ts.push(this.tagIt(sym.tokens[j],sym.tag)); ts.push(this.tagIt(sym.tokens[j],sym.tag));
} }
break; break;
} }
} }
@@ -370,32 +370,32 @@ GFConcrete.prototype.tagAndLinearize = function (tree) {
return this.syms2toks(res[0].table[0]); return this.syms2toks(res[0].table[0]);
} }
GFConcrete.prototype.unlex = function (ts) { GFConcrete.prototype.unlex = function (ts) {
if (ts.length == 0) { if (ts.length == 0) {
return ""; return "";
} }
var noSpaceAfter = /^[\(\-\[]/; var noSpaceAfter = /^[\(\-\[]/;
var noSpaceBefore = /^[\.\,\?\!\)\:\;\-\]]/; var noSpaceBefore = /^[\.\,\?\!\)\:\;\-\]]/;
var s = ""; var s = "";
for (var i = 0; i < ts.length; i++) { for (var i = 0; i < ts.length; i++) {
var t = ts[i]; var t = ts[i];
var after = i < ts.length-1 ? ts[i+1] : null; var after = i < ts.length-1 ? ts[i+1] : null;
s += t; s += t;
if (after != null && !t.match(noSpaceAfter) if (after != null && !t.match(noSpaceAfter)
&& !after.match(noSpaceBefore)) { && !after.match(noSpaceBefore)) {
s += " "; s += " ";
} }
} }
return s; return s;
}; };
GFConcrete.prototype.tagIt = function (obj, tag) { GFConcrete.prototype.tagIt = function (obj, tag) {
if (isString(obj)) { if (isString(obj)) {
var o = new String(obj); var o = new String(obj);
o.setTag(tag); o.setTag(tag);
return o; return o;
} else { } else {
var me = arguments.callee; var me = arguments.callee;
if (arguments.length == 2) { if (arguments.length == 2) {
me.prototype = obj; me.prototype = obj;
var o = new me(); var o = new me();
@@ -416,28 +416,28 @@ function isNumber(a) { return typeof a == 'number' && isFinite(a); }
function isFunction(a) { return typeof a == 'function'; } function isFunction(a) { return typeof a == 'function'; }
function dumpObject (obj) { function dumpObject (obj) {
if (isUndefined(obj)) { if (isUndefined(obj)) {
return "undefined"; return "undefined";
} else if (isString(obj)) { } else if (isString(obj)) {
return '"' + obj.toString() + '"'; // FIXME: escape return '"' + obj.toString() + '"'; // FIXME: escape
} else if (isBoolean(obj) || isNumber(obj)) { } else if (isBoolean(obj) || isNumber(obj)) {
return obj.toString(); return obj.toString();
} else if (isArray(obj)) { } else if (isArray(obj)) {
var x = "["; var x = "[";
for (var i in obj) { for (var i in obj) {
x += dumpObject(obj[i]); x += dumpObject(obj[i]);
if (i < obj.length-1) { if (i < obj.length-1) {
x += ","; x += ",";
} }
} }
return x + "]"; return x + "]";
} else { } else {
var x = "{"; var x = "{";
for (var y in obj) { for (var y in obj) {
x += y + "=" + dumpObject(obj[y]) + ";" ; x += y + "=" + dumpObject(obj[y]) + ";" ;
} }
return x + "}"; return x + "}";
} }
} }
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@@ -447,11 +447,11 @@ function dumpObject (obj) {
GFConcrete.prototype.showRules = function () { GFConcrete.prototype.showRules = function () {
var ruleStr = new Array(); var ruleStr = new Array();
ruleStr.push(""); ruleStr.push("");
for (var i = 0, j = this.rules.length; i < j; i++) { for (var i = 0, j = this.rules.length; i < j; i++) {
ruleStr.push(this.rules[i].show()); ruleStr.push(this.rules[i].show());
} }
return ruleStr.join(""); return ruleStr.join("");
}; };
GFConcrete.prototype.tokenize = function (string) { GFConcrete.prototype.tokenize = function (string) {
var inToken = false; var inToken = false;
@@ -460,20 +460,20 @@ GFConcrete.prototype.tokenize = function (string) {
for (var i = 0; i < string.length; i++) { for (var i = 0; i < string.length; i++) {
if ( string.charAt(i) == ' ' // space if ( string.charAt(i) == ' ' // space
|| string.charAt(i) == '\f' // form feed || string.charAt(i) == '\f' // form feed
|| string.charAt(i) == '\n' // newline || string.charAt(i) == '\n' // newline
|| string.charAt(i) == '\r' // return || string.charAt(i) == '\r' // return
|| string.charAt(i) == '\t' // horizontal tab || string.charAt(i) == '\t' // horizontal tab
|| string.charAt(i) == '\v' // vertical tab || string.charAt(i) == '\v' // vertical tab
|| string.charAt(i) == String.fromCharCode(160) // &nbsp; || string.charAt(i) == String.fromCharCode(160) // &nbsp;
) { ) {
if (inToken) { if (inToken) {
end = i-1; end = i-1;
inToken = false; inToken = false;
tokens.push(string.substr(start,end-start+1)); tokens.push(string.substr(start,end-start+1));
} }
} else { } else {
if (!inToken) { if (!inToken) {
start = i; start = i;
inToken = true; inToken = true;
@@ -490,93 +490,93 @@ GFConcrete.prototype.tokenize = function (string) {
return tokens; return tokens;
}; };
GFConcrete.prototype.parseString = function (string, cat) { GFConcrete.prototype.parseString = function (string, cat) {
var tokens = this.tokenize(string); var tokens = this.tokenize(string);
var ps = new ParseState(this, cat); var ps = new ParseState(this, cat);
for (var i in tokens) { for (var i in tokens) {
if (!ps.next(tokens[i])) if (!ps.next(tokens[i]))
return new Array(); return new Array();
} }
return ps.extractTrees(); return ps.extractTrees();
}; };
/** /**
* Generate list of suggestions given an input string * Generate list of suggestions given an input string
*/ */
GFConcrete.prototype.complete = function (input, cat) { GFConcrete.prototype.complete = function (input, cat) {
// Parameter defaults // Parameter defaults
if (input == null) input = ""; if (input == null) input = "";
if (cat == null) cat = grammar.abstract.startcat; if (cat == null) cat = grammar.abstract.startcat;
// Tokenise input string & remove empty tokens // Tokenise input string & remove empty tokens
tokens = input.trim().split(' '); tokens = input.trim().split(' ');
for (var i = tokens.length - 1; i >= 0; i--) { for (var i = tokens.length - 1; i >= 0; i--) {
if (tokens[i] == "") { tokens.splice(i, 1); } if (tokens[i] == "") { tokens.splice(i, 1); }
} }
// Capture last token as it may be partial // Capture last token as it may be partial
current = tokens.pop(); current = tokens.pop();
if (current == null) current = ""; if (current == null) current = "";
// Init parse state objects. // Init parse state objects.
// ps2 is used for testing whether the final token is parsable or not. // ps2 is used for testing whether the final token is parsable or not.
var ps = new ParseState(this, cat); var ps = new ParseState(this, cat);
var ps2 = new ParseState(this, cat); var ps2 = new ParseState(this, cat);
// Iterate over tokens, feed one by one to parser // Iterate over tokens, feed one by one to parser
for (var i = 0; i < tokens.length ; i++) { for (var i = 0; i < tokens.length ; i++) {
if (!ps.next(tokens[i])) { if (!ps.next(tokens[i])) {
return new Array(); // Incorrect parse, nothing to suggest return new Array(); // Incorrect parse, nothing to suggest
} }
ps2.next(tokens[i]); // also consume token in ps2 ps2.next(tokens[i]); // also consume token in ps2
} }
// Attempt to also parse current, knowing it may be incomplete // Attempt to also parse current, knowing it may be incomplete
if (ps2.next(current)) { if (ps2.next(current)) {
ps.next(current); ps.next(current);
tokens.push(current); tokens.push(current);
current = ""; current = "";
} }
delete(ps2); // don't need this anymore delete(ps2); // don't need this anymore
// Parse is successful so far, now get suggestions // Parse is successful so far, now get suggestions
var acc = ps.complete(current); var acc = ps.complete(current);
// Format into just a list of strings & return // Format into just a list of strings & return
// (I know the multiple nesting looks horrible) // (I know the multiple nesting looks horrible)
var suggs = new Array(); var suggs = new Array();
if (acc.value) { if (acc.value) {
// Iterate over all acc.value[] // Iterate over all acc.value[]
for (var v = 0; v < acc.value.length; v++) { for (var v = 0; v < acc.value.length; v++) {
// Iterate over all acc.value[].seq[] // Iterate over all acc.value[].seq[]
for (var s = 0; s < acc.value[v].seq.length; s++) { for (var s = 0; s < acc.value[v].seq.length; s++) {
if (acc.value[v].seq[s].tokens == null) continue; if (acc.value[v].seq[s].tokens == null) continue;
// Iterate over all acc.value[].seq[].tokens // Iterate over all acc.value[].seq[].tokens
for (var t = 0; t < acc.value[v].seq[s].tokens.length; t++) { for (var t = 0; t < acc.value[v].seq[s].tokens.length; t++) {
suggs.push( acc.value[v].seq[s].tokens[t] ); suggs.push( acc.value[v].seq[s].tokens[t] );
} }
} }
} }
} }
// Note: return used tokens too // Note: return used tokens too
return { 'consumed' : tokens, 'suggestions' : suggs }; return { 'consumed' : tokens, 'suggestions' : suggs };
} }
// Apply Object Definition // Apply Object Definition
function Apply(fun, args) { function Apply(fun, args) {
this.id = "Apply"; this.id = "Apply";
this.fun = fun; this.fun = fun;
this.args = args; this.args = args;
} }
Apply.prototype.show = function (cat) { Apply.prototype.show = function (cat) {
var recStr = new Array(); var recStr = new Array();
recStr.push(cat, " -> ", fun.name, " [", this.args, "]"); recStr.push(cat, " -> ", fun.name, " [", this.args, "]");
return recStr.join(""); return recStr.join("");
}; };
Apply.prototype.isEqual = function (obj) { Apply.prototype.isEqual = function (obj) {
if (this.id != obj.id || this.fun != obj.fun || this.args.length != obj.args.length) if (this.id != obj.id || this.fun != obj.fun || this.args.length != obj.args.length)
return false; return false;
for (var i in this.args) { for (var i in this.args) {
@@ -588,37 +588,37 @@ Apply.prototype.isEqual = function (obj) {
}; };
function PArg() { function PArg() {
this.fid = arguments[arguments.length-1]; this.fid = arguments[arguments.length-1];
if (arguments.length > 1) if (arguments.length > 1)
this.hypos = arguments.slice(0,arguments.length-1); this.hypos = arguments.slice(0,arguments.length-1);
} }
// Coerce Object Definition // Coerce Object Definition
function Coerce(arg) { function Coerce(arg) {
this.id = "Coerce"; this.id = "Coerce";
this.arg = arg; this.arg = arg;
} }
Coerce.prototype.show = function (cat) { Coerce.prototype.show = function (cat) {
var recStr = new Array(); var recStr = new Array();
recStr.push(cat, " -> _ [", this.args, "]"); recStr.push(cat, " -> _ [", this.args, "]");
return recStr.join(""); return recStr.join("");
}; };
// Const Object Definition // Const Object Definition
function Const(lit, toks) { function Const(lit, toks) {
this.id = "Const"; this.id = "Const";
this.lit = lit; this.lit = lit;
this.toks = toks; this.toks = toks;
} }
Const.prototype.show = function (cat) { Const.prototype.show = function (cat) {
var recStr = new Array(); var recStr = new Array();
recStr.push(cat, " -> ", lit.print()); recStr.push(cat, " -> ", lit.print());
return recStr.join(""); return recStr.join("");
}; };
Const.prototype.isEqual = function (obj) { Const.prototype.isEqual = function (obj) {
if (this.id != obj.id || this.lit.isEqual(obj.lit) || this.toks.length != obj.toks.length) if (this.id != obj.id || this.lit.isEqual(obj.lit) || this.toks.length != obj.toks.length)
return false; return false;
for (var i in this.toks) { for (var i in this.toks) {
@@ -638,41 +638,41 @@ function CncFun(name,lins) {
// Object to represent argument projections in grammar rules // Object to represent argument projections in grammar rules
function SymCat(i, label) { function SymCat(i, label) {
this.id = "Arg"; this.id = "Arg";
this.i = i; this.i = i;
this.label = label; this.label = label;
} }
SymCat.prototype.getId = function () { return this.id; }; SymCat.prototype.getId = function () { return this.id; };
SymCat.prototype.getArgNum = function () { return this.i }; SymCat.prototype.getArgNum = function () { return this.i };
SymCat.prototype.show = function () { SymCat.prototype.show = function () {
var argStr = new Array(); var argStr = new Array();
argStr.push(this.i, this.label); argStr.push(this.i, this.label);
return argStr.join("."); return argStr.join(".");
}; };
// Object to represent terminals in grammar rules // Object to represent terminals in grammar rules
function SymKS() { function SymKS() {
this.id = "KS"; this.id = "KS";
this.tokens = arguments; this.tokens = arguments;
} }
SymKS.prototype.getId = function () { return this.id; }; SymKS.prototype.getId = function () { return this.id; };
SymKS.prototype.show = function () { SymKS.prototype.show = function () {
var terminalStr = new Array(); var terminalStr = new Array();
terminalStr.push('"', this.tokens, '"'); terminalStr.push('"', this.tokens, '"');
return terminalStr.join(""); return terminalStr.join("");
}; };
// Object to represent pre in grammar rules // Object to represent pre in grammar rules
function SymKP(tokens,alts) { function SymKP(tokens,alts) {
this.id = "KP"; this.id = "KP";
this.tokens = tokens; this.tokens = tokens;
this.alts = alts; this.alts = alts;
} }
SymKP.prototype.getId = function () { return this.id; }; SymKP.prototype.getId = function () { return this.id; };
SymKP.prototype.show = function () { SymKP.prototype.show = function () {
var terminalStr = new Array(); var terminalStr = new Array();
terminalStr.push('"', this.tokens, '"'); terminalStr.push('"', this.tokens, '"');
return terminalStr.join(""); return terminalStr.join("");
}; };
function Alt(tokens, prefixes) { function Alt(tokens, prefixes) {
@@ -682,15 +682,15 @@ function Alt(tokens, prefixes) {
// Object to represent pre in grammar rules // Object to represent pre in grammar rules
function SymLit(i,label) { function SymLit(i,label) {
this.id = "Lit"; this.id = "Lit";
this.i = i; this.i = i;
this.label = label; this.label = label;
} }
SymLit.prototype.getId = function () { return this.id; }; SymLit.prototype.getId = function () { return this.id; };
SymLit.prototype.show = function () { SymLit.prototype.show = function () {
var argStr = new Array(); var argStr = new Array();
argStr.push(this.i, this.label); argStr.push(this.i, this.label);
return argStr.join("."); return argStr.join(".");
}; };
// Parsing // Parsing
@@ -812,35 +812,35 @@ ParseState.prototype.next = function (token) {
*/ */
ParseState.prototype.complete = function (currentToken) { ParseState.prototype.complete = function (currentToken) {
// Initialise accumulator for suggestions // Initialise accumulator for suggestions
var acc = this.items.lookup(currentToken); var acc = this.items.lookup(currentToken);
if (acc == null) if (acc == null)
acc = new Trie(); acc = new Trie();
this.process( this.process(
// Items // Items
this.items.value, this.items.value,
// Deal with literal categories // Deal with literal categories
function (fid) { function (fid) {
// Always return null, as suggested by Krasimir // Always return null, as suggested by Krasimir
return null; return null;
}, },
// Takes an array of tokens and populates the accumulator // Takes an array of tokens and populates the accumulator
function (tokens, item) { function (tokens, item) {
if (currentToken == "" || tokens[0].indexOf(currentToken) == 0) { //if begins with... if (currentToken == "" || tokens[0].indexOf(currentToken) == 0) { //if begins with...
var tokens1 = new Array(); var tokens1 = new Array();
for (var i = 1; i < tokens.length; i++) { for (var i = 1; i < tokens.length; i++) {
tokens1[i-1] = tokens[i]; tokens1[i-1] = tokens[i];
} }
acc.insertChain1(tokens1, item); acc.insertChain1(tokens1, item);
} }
} }
); );
// Return matches // Return matches
return acc; return acc;
} }
ParseState.prototype.extractTrees = function() { ParseState.prototype.extractTrees = function() {
this.process( this.items.value this.process( this.items.value

View File

@@ -0,0 +1,18 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["plugin:@typescript-eslint/recommended"],
"rules": {
"indent": "off",
"@typescript-eslint/indent": ["warn", 2],
"@typescript-eslint/no-use-before-define": ["error", {
"functions": false,
"classes": false
}],
"semi": "off",
"@typescript-eslint/semi": ["warn", "never"],
"quotes": ["warn", "single"],
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
}
}

View File

@@ -0,0 +1,31 @@
# GF TypeScript Runtime
`gflib.ts` is a TypeScript implementation of the GF runtime.
It is ported from an older JavaScript implementation [`gflib.js`](../javascript/gflib.js),
with some improvements.
Importantly, all **future** updates will only be made to this TypeScript version.
## Applicability
This runtime allows you use GF in pure JavaScript, and thus have GF-powered apps without the need for a server backend.
However, it has not been actively maintained as the other runtimes have been.
So its features are limited and it is not efficient, making it really only useful for smaller grammars.
## Using
`gflib.ts` can be transpiled to JavaScript by running `tsc` in this folder (of course you need TypeScript installed).
It has no module dependencies.
You can then include the generated `js/gflib.js` file in your application as usual.
_This generated JavaScript version is also included under version control,
to make it easy for someone to use without having to install TypeScript._
Your GF grammar should be compiled with: `gf --make --output-format=js`
For an example of a working web application using the JavaScript runtime, see `../javascript`.
## What happened to `gflib.d.ts`?
There was once a file here called `gflib.d.ts`, which contained TypeScript type definitions for the **old** `gflib.js`.
Since the runtime is now ported to TypeScript, those type definitions are no longer necessary (plus they contained many errors).

View File

@@ -1,337 +0,0 @@
/**
* gflib.dt.s
*
* by John J. Camilleri
*
* TypeScript type definitions for the "original" JS GF runtime (GF:src/runtime/javascript/gflib.js)
*/
// Note: the String prototype is extended with:
// String.prototype.tag = "";
// String.prototype.setTag = function (tag) { this.tag = tag; };
/**
* A GF grammar is one abstract and multiple concretes
*/
declare class GFGrammar {
abstract: GFAbstract
concretes: {[key: string]: GFConcrete}
constructor(abstract: GFAbstract, concretes: {[key: string]: GFConcrete})
translate(
input: string,
fromLang: string,
toLang: string
): {[key: string]: {[key: string]: string}}
}
/**
* Abstract Syntax Tree
*/
declare class Fun {
name: string
args: Fun[]
constructor(name: string, ...args: Fun[])
print(): string
show(): string
getArg(i: number): Fun
setArg(i: number, c: Fun): void
isMeta(): boolean
isComplete(): boolean
isLiteral(): boolean
isString(): boolean
isInt(): boolean
isFloat(): boolean
isEqual(obj: any): boolean
}
/**
* Abstract syntax
*/
declare class GFAbstract {
startcat: string
types: {[key: string]: Type} // key is function name
constructor(startcat: string, types: {[key: string]: Type})
addType(fun: string, args: string[], cat: string): void
getArgs(fun: string): string[]
getCat(fun: string): string
annotate(tree: Fun, type: string): Fun
handleLiterals(tree: Fun, type: Type): Fun
copyTree(x: Fun): Fun
parseTree(str: string, type: string): Fun
parseTree_(tokens: string[], prec: number): Fun
}
/**
* Type
*/
declare class Type {
args: string[]
cat: string
constructor(args: string[], cat: string)
}
type ApplyOrCoerce = Apply | Coerce
/**
* Concrete syntax
*/
declare class GFConcrete {
flags: {[key: string]: string}
productions: {[key: number]: ApplyOrCoerce[]}
functions: CncFun[]
sequences: Array<Array<Sym>>
startCats: {[key: string]: {s: number, e: number}}
totalFIds: number
pproductions: {[key: number]: ApplyOrCoerce[]}
lproductions: {[key: string]: {fid: FId, fun: CncFun}}
constructor(
flags: {[key: string]: string},
productions: {[key: number]: ApplyOrCoerce[]},
functions: CncFun[],
sequences: Array<Array<Sym>>,
startCats: {[key: string]: {s: number, e: number}},
totalFIds: number
)
linearizeSyms(tree: Fun, tag: string): Array<{fid: FId, table: any}>
syms2toks(syms: Sym[]): string[]
linearizeAll(tree: Fun): string[]
linearize(tree: Fun): string
tagAndLinearize(tree: Fun): string[]
unlex(ts: string): string
tagIt(obj: any, tag: string): any
// showRules(): string // Uncaught TypeError: Cannot read property 'length' of undefined at gflib.js:451
tokenize(string: string): string[]
parseString(string: string, cat: string): Fun[]
complete(
input: string,
cat: string
): {consumed: string[], suggestions: string[]}
}
/**
* Function ID
*/
type FId = number
/**
* Apply
*/
declare class Apply {
id: string
fun: FId
args: PArg[]
constructor(fun: FId, args: PArg[])
show(cat: string): string
isEqual(obj: any): boolean
}
/**
* PArg
*/
declare class PArg {
fid: FId
hypos: any[]
constructor(fid: FId, ...hypos: any[])
}
/**
* Coerce
*/
declare class Coerce {
id: string
arg: FId
constructor(arg: FId)
show(cat: string): string
}
/**
* Const
*/
declare class Const {
id: string
lit: Fun
toks: any[]
constructor(lit: Fun, toks: any[])
show(cat: string): string
isEqual(obj: any): boolean
}
/**
* CncFun
*/
declare class CncFun {
name: string
lins: FId[]
constructor(name: string, lins: FId[])
}
type Sym = SymCat | SymKS | SymKP | SymLit
/**
* SymCat
*/
declare class SymCat {
id: string
i: number
label: number
constructor(i: number, label: number)
getId(): string
getArgNum(): number
show(): string
}
/**
* SymKS
*/
declare class SymKS {
id: string
tokens: string[]
constructor(...tokens: string[])
getId(): string
show(): string
}
/**
* SymKP
*/
declare class SymKP {
id: string
tokens: string[]
alts: Alt[]
constructor(tokens: string[], alts: Alt[])
getId(): string
show(): string
}
/**
* Alt
*/
declare class Alt {
tokens: string[]
prefixes: string[]
constructor(tokens: string[], prefixes: string[])
}
/**
* SymLit
*/
declare class SymLit {
id: string
i: number
label: number
constructor(i: number, label: number)
getId(): string
show(): string
}
/**
* Trie
*/
declare class Trie {
value: any
items: Trie[]
insertChain(keys, obj): void
insertChain1(keys, obj): void
lookup(key, obj): any
isEmpty(): boolean
}
/**
* ParseState
*/
declare class ParseState {
concrete: GFConcrete
startCat: string
items: Trie
chart: Chart
constructor(concrete: GFConcrete, startCat: string)
next(token: string): boolean
complete(correntToken: string): Trie
extractTrees(): any[]
process(
agenda,
literalCallback: (fid: FId) => any,
tokenCallback: (tokens: string[], item: any) => any
): void
}
/**
* Chart
*/
declare class Chart {
active: any
actives: {[key: number]: any}
passive: any
forest: {[key: number]: ApplyOrCoerce[]}
nextId: number
offset: number
constructor(concrete: GFConcrete)
lookupAC(fid: FId,label)
lookupACo(offset, fid: FId, label)
labelsAC(fid: FId)
insertAC(fid: FId, label, items): void
lookupPC(fid: FId, label, offset)
insertPC(fid1: FId, label, offset, fid2: FId): void
shift(): void
expandForest(fid: FId): any[]
}
/**
* ActiveItem
*/
declare class ActiveItem {
offset: number
dot: number
fun: CncFun
seq: Array<Sym>
args: PArg[]
fid: FId
lbl: number
constructor(
offset: number,
dot: number,
fun: CncFun,
seq: Array<Sym>,
args: PArg[],
fid: FId,
lbl: number
)
isEqual(obj: any): boolean
shiftOverArg(i: number, fid: FId): ActiveItem
shiftOverTokn(): ActiveItem
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "none",
"target": "es3",
"strict": true,
"noImplicitAny": true,
"removeComments": true,
"outDir": "js",
"sourceMap": true
},
"compileOnSave": true
}