@ohm-js/wasm 0.1.0

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.
@@ -0,0 +1,104 @@
1
+ import test from 'ava';
2
+ import {readFile} from 'node:fs/promises';
3
+ import {dirname, join} from 'node:path';
4
+ import {performance} from 'node:perf_hooks';
5
+ import {fileURLToPath} from 'node:url';
6
+ import * as ohm from 'ohm-js';
7
+ import es5js from '../../../examples/ecmascript/index.js';
8
+
9
+ import {WasmMatcher} from '../src/index.js';
10
+ import es5fac from './data/_es5.js';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const datadir = join(__dirname, 'data');
14
+
15
+ function ES5Matcher(rules) {
16
+ // `rules` is an object with the raw rule bodies.
17
+ // The WasmMatcher constructor requires a grammar object, so we create
18
+ // one and manually add the rules in.
19
+ const g = ohm.grammar('ES5 { start = }');
20
+ for (const key of Object.keys(rules)) {
21
+ g.rules[key] = {
22
+ body: rules[key],
23
+ formals: [],
24
+ description: key,
25
+ primitive: false,
26
+ };
27
+ }
28
+ // Since this is called with `new`, we can't use `await` here.
29
+ return WasmMatcher.fromGrammar(g);
30
+ }
31
+
32
+ async function es5Matcher() {
33
+ return es5fac({
34
+ Terminal: ohm.pexprs.Terminal,
35
+ RuleApplication: ohm.pexprs.Apply,
36
+ Sequence: ohm.pexprs.Seq,
37
+ Choice: ohm.pexprs.Alt,
38
+ Repetition: ohm.pexprs.Star,
39
+ Not: ohm.pexprs.Not,
40
+ Range: ohm.pexprs.Range,
41
+ any: ohm.pexprs.any,
42
+ end: ohm.pexprs.end,
43
+ Matcher: ES5Matcher,
44
+ });
45
+ }
46
+
47
+ const matchWithInput = (m, str) => (m.setInput(str), m.match());
48
+
49
+ test('basic es5 examples', async t => {
50
+ const m = await es5Matcher();
51
+ t.is(matchWithInput(m, 'x = 3;'), 1);
52
+ t.is(matchWithInput(m, 'function foo() { return 1; }'), 1);
53
+ });
54
+
55
+ test('html5shiv', async t => {
56
+ const html5shivPath = join(datadir, '_html5shiv-3.7.3.js');
57
+ const source = await readFile(html5shivPath, 'utf8');
58
+
59
+ const m = await es5Matcher();
60
+ let start = performance.now();
61
+ es5js.grammar.match(source);
62
+ t.log(`html5shiv (Ohm) match time: ${(performance.now() - start).toFixed(2)}ms`);
63
+ start = performance.now();
64
+ t.is(matchWithInput(m, source), 1);
65
+ t.log(`html5shiv (Wasm) match time: ${(performance.now() - start).toFixed(2)}ms`);
66
+ });
67
+
68
+ test('unparsing', async t => {
69
+ const source = String.raw`
70
+ var obj = {_nm: "Thomas", "full-name": "Thomas Müller", name: function() { return this._nm; }};
71
+ var arr = [1, "hello", true, null, {x: 2}];
72
+ function Car(brand) { this.brand = brand; }
73
+ Car.prototype.start = function() { return this.brand + " started"; };
74
+ var car = new Car("BMW");
75
+ (function(x) { console.log("IIFE: " + x); })(42);
76
+ for (var i in obj) if (obj.hasOwnProperty(i)) console.log(i);
77
+ var result = obj.name === "John" ? "match" : "no match";
78
+ try {
79
+ var test = typeof arr instanceof Array && !false || 5 + 3 * 2;
80
+ } catch (e) {
81
+ } finally {
82
+ }
83
+ var counter = (function() { var n = 0; return function() { return ++n; }; })();
84
+ /\d+/.test("123") && console.log(counter());
85
+ `;
86
+
87
+ const m = await es5Matcher();
88
+ t.is(matchWithInput(m, source), 1);
89
+
90
+ let unparsed = '';
91
+
92
+ let pos = 0;
93
+ function walk(node) {
94
+ if (node.isTerminal()) {
95
+ unparsed += source.slice(pos, pos + node.matchLength);
96
+ pos += node.matchLength;
97
+ }
98
+ for (const child of node.children) {
99
+ walk(child);
100
+ }
101
+ }
102
+ walk(m.getCstRoot());
103
+ t.is(unparsed, source);
104
+ });
@@ -0,0 +1,27 @@
1
+ /* global URL */
2
+
3
+ import test from 'ava';
4
+ import fs from 'node:fs';
5
+ import * as ohm from 'ohm-js';
6
+ import {performance} from 'perf_hooks';
7
+
8
+ import {WasmMatcher} from '../src/index.js';
9
+
10
+ const matchWithInput = (m, str) => (m.setInput(str), m.match());
11
+
12
+ const scriptRel = relPath => new URL(relPath, import.meta.url);
13
+ const grammarSource = fs.readFileSync(scriptRel('data/_liquid-html.ohm'), 'utf8');
14
+ const input = fs.readFileSync(scriptRel('data/_book-review.liquid'), 'utf8');
15
+
16
+ // eslint-disable-next-line ava/no-skip-test
17
+ test.skip('basic matching', async t => {
18
+ const ns = ohm.grammars(grammarSource);
19
+ let start = performance.now();
20
+ t.is(ns.LiquidHTML.match(input).succeeded(), true); // Trigger fillInputBuffer
21
+ t.log(`Ohm.js: ${(performance.now() - start).toFixed(2)}ms`);
22
+
23
+ const m = await WasmMatcher.fromGrammar(ns.LiquidHTML);
24
+ start = performance.now();
25
+ t.is(matchWithInput(m, input), 1);
26
+ t.log(`Wasm: ${(performance.now() - start).toFixed(2)}ms`);
27
+ });