grammar-well 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,109 @@
1
1
  # Grammar Well
2
2
  A cross-platform grammar compiler and interpreter. That aims to facilitate a simple way to create and evaluate custom grammars on the front-end and back-end. Formerly a TypeScript port of [Nearley](https://github.com/kach/nearley).
3
3
 
4
+ # Quick Start
5
+ ### Install
6
+ `npm i grammar-well`
7
+
8
+ ### Example
9
+
10
+ ```Javascript
11
+ import { Compile, Parse } from 'grammar-well';
12
+
13
+ async function GrammarWellRunner(source, input) {
14
+ function Evalr(source) {
15
+ const module = { exports: null };
16
+ eval(source);
17
+ return module.exports;
18
+ }
19
+ const compiled = await Compile(source, { exportName: 'grammar' });
20
+ return Parse(Evalr(compiled)(), input, { algorithm: 'earley' });
21
+ }
22
+
23
+
24
+ const source = `lexer: {{
25
+ start: "json"
26
+
27
+ json ->
28
+ - when: /\s+/ tag: "space"
29
+ - when: /-?(?:[0-9]|[1-9][0-9]+)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?\b/ tag: "number"
30
+ - when: /"(?:\\["bfnrt\/\\]|\\u[a-fA-F0-9]{4}|[^"\\])*"/ tag: "string"
31
+ - when: "{" tag: "{"
32
+ - when: "}" tag: "}"
33
+ - when: "[" tag: "["
34
+ - when: "]" tag: "]"
35
+ - when: "," tag: ","
36
+ - when: ":" tag: ":"
37
+ - when: "true" tag: "true"
38
+ - when: "false" tag: "false"
39
+ - when: "null" tag: "null"
40
+ }}
41
+
42
+ grammar: {{
43
+ json -> _ (object | array) _ : {{ $1[0] }}
44
+
45
+ object -> "{" _ "}" : {{ {} }}
46
+ | "{" _ pair (_ "," _ pair)* _ "}" : \${ extractObject }
47
+
48
+ array -> "[" _ "]" : {{ [] }}
49
+ | "[" _ value (_ "," _ value)* _ "]" : \${ extractArray }
50
+
51
+ value : {{ $0 }} ->
52
+ object
53
+ | array
54
+ | number
55
+ | string
56
+ | "true" : {{ true }}
57
+ | "false" : {{ false }}
58
+ | "null" : {{ null }}
59
+
60
+ number -> $number : {{ parseFloat($0.value) }}
61
+
62
+ string -> $string : {{ JSON.parse($0.value) }}
63
+
64
+ pair -> key:k _ ":" _ value:v : {{ [$k, $v] }}
65
+
66
+ key -> string : {{ $0 }}
67
+
68
+ _ -> $space? : {{ null }}
69
+ }}
70
+
71
+ head: \${
72
+
73
+ function extractPair(kv, output) {
74
+ if(kv[0]) { output[kv[0]] = kv[1]; }
75
+ }
76
+
77
+ function extractObject({data}) {
78
+ let output = {};
79
+
80
+ extractPair(data[2], output);
81
+
82
+ for (let i in data[3]) {
83
+ extractPair(data[3][i][3], output);
84
+ }
85
+
86
+ return output;
87
+ }
88
+
89
+ function extractArray({data}) {
90
+ let output = [data[2]];
91
+
92
+ for (let i in data[3]) {
93
+ output.push(data[3][i][3]);
94
+ }
95
+
96
+ return output;
97
+ }
98
+ }
99
+ `
100
+
101
+ const input = `{"a":"string","b":true,"c":2}`
102
+
103
+
104
+ console.log(await GrammarWellRunner(source, input))
105
+ ```
106
+
4
107
  # Warning
5
108
  A lot has changed and documentation needs to be written. For now here's the Grammar Well grammar file that parses Garmmar Well's syntax.
6
109
 
package/bootstrap.ts CHANGED
@@ -9,8 +9,8 @@ const BaseDir = './src/grammars';
9
9
  try {
10
10
  console.log(fullpath(file))
11
11
  if (/\.gwell$/.test(file)) {
12
- const json = await Compile(read(file), { format: 'json' });
13
- const js = await Compile(read(file), { exportName: 'grammar', format: 'esmodule' });
12
+ const json = await Compile(read(file), { template: 'json' });
13
+ const js = await Compile(read(file), { exportName: 'grammar', template: 'esmodule' });
14
14
  write(file.replace(/.gwell$/, '.json'), json);
15
15
  write(file.replace(/.gwell$/, '.js'), js);
16
16
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "grammar-well",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Cross-platform Parser written in TypeScript",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
7
7
  "prebuild": "tsc --build --clean",
8
8
  "build": "tsc --build",
9
- "bootstrap": "ts-node bootstrap.ts",
10
- "testing": "ts-node testing.ts",
9
+ "bootstrap": "tsx bootstrap.ts",
10
+ "testing": "tsx testing.ts",
11
11
  "benchmark": "node tests/performance/benchmark.js",
12
12
  "test": "nyc mocha tests/**/*.spec.ts",
13
13
  "profile": "node tests/performance/profile.js"
@@ -25,19 +25,20 @@
25
25
  "devDependencies": {
26
26
  "@types/chai": "^4.3.4",
27
27
  "@types/mocha": "^10.0.1",
28
- "@types/node": "^18.11.18",
29
- "@typescript-eslint/eslint-plugin": "^5.49.0",
30
- "@typescript-eslint/parser": "^5.49.0",
28
+ "@types/node": "^18.15.0",
29
+ "@typescript-eslint/eslint-plugin": "^5.54.1",
30
+ "@typescript-eslint/parser": "^5.54.1",
31
31
  "babel-cli": "^6.26.0",
32
32
  "babel-preset-env": "^1.7.0",
33
33
  "benny": "^3.7.1",
34
34
  "chai": "^4.3.7",
35
- "eslint": "^8.33.0",
36
- "expect": "^29.4.1",
35
+ "eslint": "^8.36.0",
36
+ "expect": "^29.5.0",
37
37
  "mocha": "^10.2.0",
38
38
  "nyc": "^15.1.0",
39
39
  "ts-node": "^10.9.1",
40
- "typescript": "^4.9.4",
40
+ "tsx": "^3.12.3",
41
+ "typescript": "^4.9.5",
41
42
  "yaml": "^2.2.1"
42
43
  },
43
44
  "keywords": [
@@ -49,4 +50,4 @@
49
50
  "grammar",
50
51
  "language"
51
52
  ]
52
- }
53
+ }
@@ -1,4 +1,4 @@
1
- import { CompileOptions, GrammarBuilderContext, OutputFormat, LanguageDirective, ConfigDirective, GrammarBuilderSymbolRepeat, GrammarBuilderExpression, GeneratorGrammarRule, GrammarDirective, ImportDirective, LexerDirective, GrammarBuilderSymbolSubexpression, GrammarTypeLiteral, GeneratorGrammarSymbol, GrammarBuilderSymbol, GrammarBuilderRule } from "../typings";
1
+ import { CompileOptions, GrammarBuilderContext, TemplateFormat, LanguageDirective, ConfigDirective, GrammarBuilderSymbolRepeat, GrammarBuilderExpression, GeneratorGrammarRule, GrammarDirective, ImportDirective, LexerDirective, GrammarBuilderSymbolSubexpression, GrammarTypeLiteral, GeneratorGrammarSymbol, GrammarBuilderSymbol, GrammarBuilderRule } from "../typings";
2
2
 
3
3
  import { Parser } from "../parser/parser";
4
4
  import { FileSystemResolver } from "./import-resolver";
@@ -18,7 +18,7 @@ const BuiltInRegistry = {
18
18
  string,
19
19
  whitespace,
20
20
  }
21
- const OutputFormats = {
21
+ const TemplateFormats = {
22
22
  _default: JavascriptOutput,
23
23
  object: (grammar, exportName) => ({ grammar, exportName }),
24
24
  json: JSONFormatter,
@@ -26,6 +26,7 @@ const OutputFormats = {
26
26
  javascript: JavascriptOutput,
27
27
  module: ESMOutput,
28
28
  esmodule: ESMOutput,
29
+ esm: ESMOutput,
29
30
  ts: TypescriptFormat,
30
31
  typescript: TypescriptFormat
31
32
  }
@@ -33,7 +34,7 @@ const OutputFormats = {
33
34
  export async function Compile(rules: string | LanguageDirective | (LanguageDirective[]), config: CompileOptions = {}) {
34
35
  const builder = new GrammarBuilder(config);
35
36
  await builder.import(rules as any);
36
- return builder.export(config.format);
37
+ return builder.export(config.template);
37
38
  }
38
39
 
39
40
  export class GrammarBuilder {
@@ -51,11 +52,11 @@ export class GrammarBuilder {
51
52
  this.generator.state.grammar.uuids = this.context.uuids;
52
53
  }
53
54
 
54
- export<T extends OutputFormat = '_default'>(format: T, name: string = 'GWLanguage'): ReturnType<typeof OutputFormats[T]> {
55
+ export<T extends TemplateFormat = '_default'>(format: T, name: string = 'GWLanguage'): ReturnType<typeof TemplateFormats[T]> {
55
56
  const grammar = this.generator.state;
56
57
  const output = format || grammar.config.preprocessor || '_default';
57
- if (OutputFormats[output]) {
58
- return OutputFormats[output](this.generator, name);
58
+ if (TemplateFormats[output]) {
59
+ return TemplateFormats[output](this.generator, name);
59
60
  }
60
61
  throw new Error("No such preprocessor: " + output)
61
62
  }
package/src/typings.ts CHANGED
@@ -12,10 +12,10 @@ export interface CompileOptions {
12
12
  resolver?: ImportResolverConstructor;
13
13
  resolverInstance?: ImportResolver;
14
14
  exportName?: string;
15
- format?: OutputFormat;
15
+ template?: TemplateFormat;
16
16
  }
17
17
 
18
- export type OutputFormat = '_default' | 'object' | 'json' | 'js' | 'javascript' | 'module' | 'esmodule' | 'ts' | 'typescript'
18
+ export type TemplateFormat = '_default' | 'object' | 'json' | 'js' | 'javascript' | 'module' | 'esmodule' | 'esm' | 'ts' | 'typescript'
19
19
 
20
20
  export interface GrammarBuilderContext {
21
21
  alreadyCompiled: Set<string>;
@@ -4,7 +4,7 @@ import { Dictionary, GrammarRuleSymbol } from "../typings";
4
4
  export class Collection<T> {
5
5
  categorized: Dictionary<Dictionary<number>> = {};
6
6
  private uncategorized = new Map<T, number>();
7
- private items: T[] = [];
7
+ items: T[] = [];
8
8
 
9
9
  constructor(ref: T[] = []) {
10
10
  for (const s of ref) {
@@ -23,6 +23,17 @@ export class Collection<T> {
23
23
  return this.items[typeof id == 'string' ? parseInt(id) : id];
24
24
  }
25
25
 
26
+ has(ref: T) {
27
+ const c = this.resolve(ref);
28
+ if (c)
29
+ return (c.key in this.categorized[c.category])
30
+ return this.uncategorized.has(ref);
31
+ }
32
+
33
+ redirect(source: T, target: T) {
34
+ this.items[this.encode(source)] = target;
35
+ }
36
+
26
37
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
27
38
  resolve(_: T): { category: keyof Collection<T>['categorized'], key: string } | void { }
28
39
 
@@ -63,7 +74,7 @@ export class SymbolCollection extends Collection<GrammarRuleSymbol>{
63
74
  return { category: 'literalS', key: symbol.literal }
64
75
  } else if ('token' in symbol) {
65
76
  return { category: 'token', key: symbol.token }
66
- } else if ('test' in symbol) {
77
+ } else if (symbol instanceof RegExp) {
67
78
  return { category: 'regex', key: symbol.toString() }
68
79
  } else if (typeof symbol == 'function') {
69
80
  return { category: 'function', key: symbol.toString() }
@@ -117,4 +128,48 @@ export class Matrix<T> {
117
128
  }
118
129
  }
119
130
 
120
- type GetCallbackOrValue<T> = T extends (...args: any) => any ? ReturnType<T> : T;
131
+ export function Flatten(obj: any[] | { [key: string]: any }): FlatObject {
132
+ const collection = new Collection();
133
+ function Traverse(ref: any) {
134
+ if (collection.has(ref)) {
135
+ return collection.encode(ref)
136
+ }
137
+ if (Array.isArray(ref)) {
138
+ collection.redirect(ref, ref.map(v => Traverse(v)));
139
+ } else if (typeof ref === 'object') {
140
+ const o = {};
141
+ for (const k in ref) {
142
+ o[k] = Traverse(ref[k])
143
+ }
144
+ collection.redirect(ref, o);
145
+ } else if (typeof ref === 'function') {
146
+ return collection.encode(ref.toString());
147
+ }
148
+ return collection.encode(ref);
149
+ }
150
+ Traverse(obj);
151
+ return collection.items as any;
152
+ }
153
+
154
+ export function Unflatten(items: FlatObject) {
155
+ const visited = new Set();
156
+ function Traverse(id: number) {
157
+ if (visited.has(id)) {
158
+ return items[id];
159
+ }
160
+ visited.add(id);
161
+ if (Array.isArray(items[id])) {
162
+ return (items[id] as any[]).map(v => Traverse(id));
163
+ } else if (typeof items[id] === 'object') {
164
+ for (const k in items[id] as { [key: string]: any }) {
165
+ items[id][k] = Traverse(id[k])
166
+ }
167
+ }
168
+ return items[id];
169
+ }
170
+ return Traverse(0);
171
+ }
172
+
173
+ type FlatObject = (boolean | number | string | (number[]) | { [key: string]: number })[];
174
+
175
+ type GetCallbackOrValue<T> = T extends (...args: any) => any ? ReturnType<T> : T;
package/src/utility/lr.ts CHANGED
@@ -5,10 +5,9 @@ import { Collection, SymbolCollection } from "./general";
5
5
  export class CanonicalCollection {
6
6
  rules: Collection<GrammarRule> = new Collection();
7
7
  states: { [key: string]: State } = Object.create(null)
8
- symbols: SymbolCollection;
9
-
10
- constructor(public grammar: LanguageGrammar & { symbols?: SymbolCollection }) {
11
- this.symbols = grammar.symbols || new SymbolCollection();
8
+ symbols: SymbolCollection = new SymbolCollection();
9
+
10
+ constructor(public grammar: LanguageGrammar) {
12
11
  const augmented = { name: Symbol() as unknown as string, symbols: [grammar.start] }
13
12
  grammar.rules[augmented.name] = [augmented];
14
13
  this.addState([{ rule: augmented, dot: 0 }]);