Porting of JS runtime to TypeScript (gflib.ts) complete

This commit is contained in:
John J. Camilleri
2019-06-07 13:58:23 +02:00
parent f2e03bfc51
commit ffcdaa921f
4 changed files with 121 additions and 73 deletions

1
src/runtime/typescript/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.js

View File

@@ -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).

View File

@@ -6,18 +6,10 @@
* A port of the pure JavaScript runtime (/src/runtime/javascript/gflib.js) into TypeScript * 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 * 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 abstract: GFAbstract
public concretes: {[key: string]: GFConcrete} public concretes: {[key: string]: GFConcrete}
@@ -62,7 +54,7 @@ export class GFGrammar {
/** /**
* Abstract Syntax Tree * Abstract Syntax Tree
*/ */
export class Fun { class Fun {
public name: string public name: string
public args: Fun[] public args: Fun[]
public type?: string public type?: string
@@ -408,7 +400,7 @@ class GFConcrete {
case 'KS': case 'KS':
case 'KP': { case 'KP': {
let sym = sym0 as SymKS | SymKP let sym = sym0 as SymKS | SymKP
toks.push(this.tagIt(sym,tag)) toks.push(sym.tagWith(tag))
break break
} }
} }
@@ -428,14 +420,14 @@ class GFConcrete {
case 'KS': { case 'KS': {
let sym = sym0 as SymKS let sym = sym0 as SymKS
for (let j in sym.tokens) { for (let j in sym.tokens) {
ts.push(this.tagIt(sym.tokens[j],sym.tag)) ts.push(sym.tokens[j].tagWith(sym.tag))
} }
break break
} }
case 'KP': { case 'KP': {
let sym = sym0 as SymKP let sym = sym0 as SymKP
for (let j in sym.tokens) { for (let j in sym.tokens) {
ts.push(this.tagIt(sym.tokens[j],sym.tag)) ts.push(sym.tokens[j].tagWith(sym.tag))
} }
break break
} }
@@ -482,21 +474,21 @@ class GFConcrete {
return s return s
} }
private tagIt(obj: Taggable, tag: string): Taggable { // private tagIt(obj: Taggable, tag: string): Taggable {
if (isString(obj)) { // if (isString(obj)) {
let o = new String(obj) // let o = new String(obj)
o.setTag(tag) // o.setTag(tag)
return o // return o
} else { // } else {
let me = arguments.callee // let me = arguments.callee
if (arguments.length == 2) { // if (arguments.length == 2) {
me.prototype = obj // me.prototype = obj
let o = new me() // let o = new me()
o.tag = tag // o.tag = tag
return o // return o
} // }
} // }
} // }
// public showRules(): string { // public showRules(): string {
// let ruleStr = [] // 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 * Function ID
*/ */
@@ -783,6 +795,11 @@ class SymKS implements Taggable {
terminalStr.push('"', this.tokens, '"') terminalStr.push('"', this.tokens, '"')
return terminalStr.join('') 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, '"') terminalStr.push('"', this.tokens, '"')
return terminalStr.join('') return terminalStr.join('')
} }
public tagWith(tag: string): SymKS {
this.tag = tag
return this
}
} }
/** /**
@@ -1423,52 +1445,48 @@ class ActiveItem {
* Utilities * Utilities
*/ */
/* /* eslint-disable @typescript-eslint/no-explicit-any */
eslint-disable
@typescript-eslint/no-explicit-any,
@typescript-eslint/no-unused-vars
*/
/* from Remedial JavaScript by Douglas Crockford, http://javascript.crockford.com/remedial.html */ /* from Remedial JavaScript by Douglas Crockford, http://javascript.crockford.com/remedial.html */
function isString(a: any): boolean { // function isString(a: any): boolean {
return typeof a == 'string' // return typeof a == 'string'
} // }
function isArray(a: any): boolean { // function isArray(a: any): boolean {
return a && typeof a == 'object' && a.constructor == Array // return a && typeof a == 'object' && a.constructor == Array
} // }
function isUndefined(a: any): boolean { function isUndefined(a: any): boolean {
return typeof a == 'undefined' return typeof a == 'undefined'
} }
function isBoolean(a: any): boolean { // function isBoolean(a: any): boolean {
return typeof a == 'boolean' // return typeof a == 'boolean'
} // }
function isNumber(a: any): boolean { // function isNumber(a: any): boolean {
return typeof a == 'number' && isFinite(a) // return typeof a == 'number' && isFinite(a)
} // }
function isFunction(a: any): boolean { // function isFunction(a: any): boolean {
return typeof a == 'function' // return typeof a == 'function'
} // }
function dumpObject (obj: any): string { // function dumpObject (obj: any): string {
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)) {
let x = '[' // let x = '['
for (let i = 0; i < obj.length; i++) { // for (let i = 0; i < obj.length; i++) {
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 {
let x = '{' // let x = '{'
for (let y in obj) { // for (let y in obj) {
x += y + '=' + dumpObject(obj[y]) + ';' // x += y + '=' + dumpObject(obj[y]) + ';'
} // }
return x + '}' // return x + '}'
} // }
} // }

View File

@@ -1,5 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "none",
"target": "es3",
"noImplicitAny": true "noImplicitAny": true
} },
"compileOnSave": true
} }