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