From ffcdaa921f21aca97da90936e0a57eec93a1f53b Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Fri, 7 Jun 2019 13:58:23 +0200 Subject: [PATCH] Porting of JS runtime to TypeScript (`gflib.ts`) complete --- src/runtime/typescript/.gitignore | 1 + src/runtime/typescript/README.md | 26 +++++ src/runtime/typescript/gflib.ts | 162 +++++++++++++++------------ src/runtime/typescript/tsconfig.json | 5 +- 4 files changed, 121 insertions(+), 73 deletions(-) create mode 100644 src/runtime/typescript/.gitignore create mode 100644 src/runtime/typescript/README.md diff --git a/src/runtime/typescript/.gitignore b/src/runtime/typescript/.gitignore new file mode 100644 index 000000000..a6c7c2852 --- /dev/null +++ b/src/runtime/typescript/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/src/runtime/typescript/README.md b/src/runtime/typescript/README.md new file mode 100644 index 000000000..7f796a63b --- /dev/null +++ b/src/runtime/typescript/README.md @@ -0,0 +1,26 @@ +# 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 small 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 depenedencies. +You can then include the generated `gflib.js` file in your application as usual. + +Your GF grammar should be compiled with: `gf --make --output-format=js` + +## 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). diff --git a/src/runtime/typescript/gflib.ts b/src/runtime/typescript/gflib.ts index 274b46b33..75a0bd385 100644 --- a/src/runtime/typescript/gflib.ts +++ b/src/runtime/typescript/gflib.ts @@ -6,18 +6,10 @@ * A port of the pure JavaScript runtime (/src/runtime/javascript/gflib.js) into TypeScript */ -// Note: the String prototype is extended with: -// String.prototype.tag = ""; -// String.prototype.setTag = function (tag) { this.tag = tag; }; - -interface Taggable { - tag?: string; -} - /** * A GF grammar is one abstract and multiple concretes */ -export class GFGrammar { +class GFGrammar { // eslint-disable-line @typescript-eslint/no-unused-vars public abstract: GFAbstract public concretes: {[key: string]: GFConcrete} @@ -62,7 +54,7 @@ export class GFGrammar { /** * Abstract Syntax Tree */ -export class Fun { +class Fun { public name: string public args: Fun[] public type?: string @@ -408,7 +400,7 @@ class GFConcrete { case 'KS': case 'KP': { let sym = sym0 as SymKS | SymKP - toks.push(this.tagIt(sym,tag)) + toks.push(sym.tagWith(tag)) break } } @@ -428,14 +420,14 @@ class GFConcrete { case 'KS': { let sym = sym0 as SymKS for (let j in sym.tokens) { - ts.push(this.tagIt(sym.tokens[j],sym.tag)) + ts.push(sym.tokens[j].tagWith(sym.tag)) } break } case 'KP': { let sym = sym0 as SymKP for (let j in sym.tokens) { - ts.push(this.tagIt(sym.tokens[j],sym.tag)) + ts.push(sym.tokens[j].tagWith(sym.tag)) } break } @@ -482,21 +474,21 @@ class GFConcrete { return s } - private tagIt(obj: Taggable, tag: string): Taggable { - if (isString(obj)) { - let o = new String(obj) - o.setTag(tag) - return o - } else { - let me = arguments.callee - if (arguments.length == 2) { - me.prototype = obj - let o = new me() - o.tag = tag - return o - } - } - } + // private tagIt(obj: Taggable, tag: string): Taggable { + // if (isString(obj)) { + // let o = new String(obj) + // o.setTag(tag) + // return o + // } else { + // let me = arguments.callee + // if (arguments.length == 2) { + // me.prototype = obj + // let o = new me() + // o.tag = tag + // return o + // } + // } + // } // public showRules(): string { // let ruleStr = [] @@ -617,6 +609,26 @@ class GFConcrete { } } +/** + * A type which can be tagged + */ +interface Taggable { + tag?: string; + tagWith: (tag: string) => Taggable; +} + +/** + * Strings can also be tagged in the same way + */ +interface String { + tag?: string; + tagWith: (tag: string) => string; +} +String.prototype.tagWith = function (tag: string): string { + this.tag = tag + return this +} + /** * Function ID */ @@ -783,6 +795,11 @@ class SymKS implements Taggable { terminalStr.push('"', this.tokens, '"') return terminalStr.join('') } + + public tagWith(tag: string): SymKS { + this.tag = tag + return this + } } /** @@ -805,6 +822,11 @@ class SymKP implements Taggable { terminalStr.push('"', this.tokens, '"') return terminalStr.join('') } + + public tagWith(tag: string): SymKS { + this.tag = tag + return this + } } /** @@ -1423,52 +1445,48 @@ class ActiveItem { * Utilities */ -/* -eslint-disable - @typescript-eslint/no-explicit-any, - @typescript-eslint/no-unused-vars -*/ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* from Remedial JavaScript by Douglas Crockford, http://javascript.crockford.com/remedial.html */ -function isString(a: any): boolean { - return typeof a == 'string' -} -function isArray(a: any): boolean { - return a && typeof a == 'object' && a.constructor == Array -} +// function isString(a: any): boolean { +// return typeof a == 'string' +// } +// function isArray(a: any): boolean { +// return a && typeof a == 'object' && a.constructor == Array +// } function isUndefined(a: any): boolean { return typeof a == 'undefined' } -function isBoolean(a: any): boolean { - return typeof a == 'boolean' -} -function isNumber(a: any): boolean { - return typeof a == 'number' && isFinite(a) -} -function isFunction(a: any): boolean { - return typeof a == 'function' -} -function dumpObject (obj: any): string { - 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)) { - let x = '[' - for (let i = 0; i < obj.length; i++) { - x += dumpObject(obj[i]) - if (i < obj.length-1) { - x += ',' - } - } - return x + ']' - } else { - let x = '{' - for (let y in obj) { - x += y + '=' + dumpObject(obj[y]) + ';' - } - return x + '}' - } -} +// function isBoolean(a: any): boolean { +// return typeof a == 'boolean' +// } +// function isNumber(a: any): boolean { +// return typeof a == 'number' && isFinite(a) +// } +// function isFunction(a: any): boolean { +// return typeof a == 'function' +// } +// function dumpObject (obj: any): string { +// 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)) { +// let x = '[' +// for (let i = 0; i < obj.length; i++) { +// x += dumpObject(obj[i]) +// if (i < obj.length-1) { +// x += ',' +// } +// } +// return x + ']' +// } else { +// let x = '{' +// for (let y in obj) { +// x += y + '=' + dumpObject(obj[y]) + ';' +// } +// return x + '}' +// } +// } diff --git a/src/runtime/typescript/tsconfig.json b/src/runtime/typescript/tsconfig.json index dc8bbb8e1..9a6cb7ac6 100644 --- a/src/runtime/typescript/tsconfig.json +++ b/src/runtime/typescript/tsconfig.json @@ -1,5 +1,8 @@ { "compilerOptions": { + "module": "none", + "target": "es3", "noImplicitAny": true - } + }, + "compileOnSave": true }