Skip to content

Commit 55c787b

Browse files
committed
BREAKING CHANGE: Remove Namespaces
1 parent d30b2db commit 55c787b

File tree

12 files changed

+46
-123
lines changed

12 files changed

+46
-123
lines changed

doc/api-reference.md

-12
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,6 @@ Instantiate the Grammar defined by `source`. If specified, `optNamespace` is the
1414

1515
Create a new Namespace containing Grammar instances for all of the grammars defined in `source`. If `optNamespace` is specified, it will be the prototype of the new Namespace.
1616

17-
## Namespace objects
18-
19-
When instantiating a grammar that refers to another grammar -- e.g. `MyJava <: Java { keyword += "async" }` -- the supergrammar name ('Java') is resolved to a grammar by looking up the name in a Namespace. In Ohm/JS, Namespaces are a plain old JavaScript objects, and an object literal like `{Java: myJavaGrammar}` can be passed to any API that expects a Namespace. For convenience, Ohm also has the following methods for working with namespaces:
20-
21-
<b><pre class="api">ohm.namespace(optProps?: object)</pre></b>
22-
23-
Create a new namespace. If `optProps` is specified, all of its properties will be copied to the new namespace.
24-
25-
<b><pre class="api">ohm.extendNamespace(namespace: object, optProps?: object)</pre></b>
26-
27-
Create a new namespace which inherits from `namespace`. If `optProps` is specified, all of its properties will be copied to the new namespace.
28-
2917
## Grammar objects
3018

3119
A Grammar instance `g` has the following methods:

packages/cli/src/commands/generateBundles/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ function generateRecipe(grammarPath, grammars, writer, isEsm) {
7373
"'use strict';const ohm=require('ohm-js');";
7474

7575
// If it's a single-grammar source file, the default export is the grammar.
76-
// Otherwise, the export is a (possibly empty) Namespace containing the grammars.
76+
// Otherwise, the export is a (possibly empty) object containing the grammars.
7777
if (!isSingleGrammar) {
78-
output += 'const result=ohm.createNamespace();';
78+
output += 'const result={};';
7979
}
8080
for (const [name, grammar] of Object.entries(grammars)) {
8181
const {superGrammar} = grammar;

packages/ohm-js/index.d.ts

-19
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,6 @@ declare namespace ohm {
5353
*/
5454
function grammars(source: string, namespace?: Namespace): Namespace;
5555

56-
/**
57-
* grammarsFromScriptElements was removed in Ohm v16.0. See
58-
* https://ohmjs.org/d/gfs for more info.
59-
* @deprecated
60-
*/
61-
function grammarsFromScriptElements(nodeList?: unknown, namespace?: Namespace): Namespace;
62-
63-
/**
64-
* Create a new namespace. If props is specified, all of its properties
65-
* will be copied to the new namespace.
66-
*/
67-
function namespace(props?: Object): Namespace;
68-
69-
/**
70-
* Create a new namespace which inherits from namespace. If props is
71-
* specified, all of its properties will be copied to the new namespace.
72-
*/
73-
function extendNamespace(namespace: Namespace, props?: Object): Namespace;
74-
7556
/**
7657
* A Namespace is a dictionary of Grammars
7758
*/

packages/ohm-js/scripts/data/index.d.ts.template

-19
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,6 @@ declare namespace ohm {
5252
*/
5353
function grammars(source: string, namespace?: Namespace): Namespace;
5454

55-
/**
56-
* grammarsFromScriptElements was removed in Ohm v16.0. See
57-
* https://ohmjs.org/d/gfs for more info.
58-
* @deprecated
59-
*/
60-
function grammarsFromScriptElements(nodeList?: unknown, namespace?: Namespace): Namespace;
61-
62-
/**
63-
* Create a new namespace. If props is specified, all of its properties
64-
* will be copied to the new namespace.
65-
*/
66-
function namespace(props?: Object): Namespace;
67-
68-
/**
69-
* Create a new namespace which inherits from namespace. If props is
70-
* specified, all of its properties will be copied to the new namespace.
71-
*/
72-
function extendNamespace(namespace: Namespace, props?: Object): Namespace;
73-
7455
/**
7556
* A Namespace is a dictionary of Grammars
7657
*/

packages/ohm-js/src/Namespace.js

-37
This file was deleted.

packages/ohm-js/src/buildGrammar.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import * as pexprs from './pexprs.js';
77

88
const superSplicePlaceholder = Object.create(pexprs.PExpr.prototype);
99

10+
function namespaceHas(ns, name) {
11+
// Look for an enumerable property, anywhere in the prototype chain.
12+
for (const prop in ns) {
13+
if (prop === name) return true;
14+
}
15+
return false;
16+
}
17+
1018
// Returns a Grammar instance (i.e., an object with a `match` method) for
1119
// `tree`, which is the concrete syntax tree of a user-written grammar.
1220
// The grammar will be assigned into `namespace` under the name of the grammar
@@ -26,12 +34,12 @@ export function buildGrammar(match, namespace, optOhmGrammarForTesting) {
2634
},
2735
Grammar(id, s, _open, rules, _close) {
2836
const grammarName = id.visit();
29-
decl = builder.newGrammar(grammarName, namespace);
37+
decl = builder.newGrammar(grammarName);
3038
s.child(0) && s.child(0).visit();
3139
rules.children.map(c => c.visit());
3240
const g = decl.build();
3341
g.source = this.source.trimmed();
34-
if (grammarName in namespace) {
42+
if (namespaceHas(namespace, grammarName)) {
3543
throw errors.duplicateGrammarDeclaration(g, namespace);
3644
}
3745
namespace[grammarName] = g;
@@ -43,7 +51,7 @@ export function buildGrammar(match, namespace, optOhmGrammarForTesting) {
4351
if (superGrammarName === 'null') {
4452
decl.withSuperGrammar(null);
4553
} else {
46-
if (!namespace || !(superGrammarName in namespace)) {
54+
if (!namespace || !namespaceHas(namespace, superGrammarName)) {
4755
throw errors.undeclaredGrammar(superGrammarName, namespace, n.source);
4856
}
4957
decl.withSuperGrammar(namespace[superGrammarName]);

packages/ohm-js/src/errors.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {assert} from './common.js';
2-
import {Namespace} from './Namespace.js';
32
import * as pexprs from './pexprs-main.js';
43

54
// --------------------------------------------------------------------
@@ -50,10 +49,7 @@ export function grammarSyntaxError(matchFailure) {
5049

5150
export function undeclaredGrammar(grammarName, namespace, interval) {
5251
const message = namespace ?
53-
'Grammar ' +
54-
grammarName +
55-
' is not declared in namespace ' +
56-
Namespace.toString(namespace) :
52+
`Grammar ${grammarName} is not declared in namespace '${namespace}'` :
5753
'Undeclared grammar ' + grammarName;
5854
return createError(message, interval);
5955
}

packages/ohm-js/src/main.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {buildGrammar} from './buildGrammar.js';
33
import * as common from './common.js';
44
import * as errors from './errors.js';
55
import {Grammar} from './Grammar.js';
6-
import {Namespace} from './Namespace.js';
76
import * as pexprs from './pexprs.js';
87
import * as util from './util.js';
98

@@ -44,7 +43,7 @@ export function grammar(source, optNamespace) {
4443
}
4544

4645
export function grammars(source, optNamespace) {
47-
const ns = Namespace.extend(Namespace.asNamespace(optNamespace));
46+
const ns = Object.create(optNamespace || {});
4847
if (typeof source !== 'string') {
4948
// For convenience, detect Node.js Buffer objects and automatically call toString().
5049
if (isBuffer(source)) {
@@ -72,7 +71,6 @@ export function grammarsFromScriptElements(optNodeOrNodeList) {
7271
}
7372

7473
export * from './main-kernel.js';
75-
export const {createNamespace} = Namespace;
7674
export {ohmGrammar};
7775
export {pexprs, util};
7876
export {version} from './version.js';

packages/ohm-js/test/test-main.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import ohm from '../index.mjs';
1111

1212
test('namespaces', t => {
1313
const ns = ohm.grammars('G { start = "foo" }');
14-
t.truthy(ns.G.match('foo'), 'G exists in the namespace and works');
14+
t.is(ns.G.match('foo').succeeded(), true, 'G exists in the namespace and works');
1515

1616
const ns2 = ohm.grammars('ccc { foo = "foo" }', ns);
1717
t.truthy(ns2);
@@ -30,6 +30,31 @@ test('namespaces', t => {
3030
t.deepEqual(ns3.G, ns2.G, 'super grammar is the same');
3131
});
3232

33+
test('plain JS objects as namespaces', t => {
34+
const ns = ohm.grammars('toString { start = "!!!" }');
35+
t.is(ns.toString.match('!!!').succeeded(), true);
36+
37+
t.truthy(ohm.grammar('G <: toString {}', ns));
38+
t.throws(() => ohm.grammar('G <: toString {}', {}), {
39+
message: /Grammar toString is not declared/,
40+
});
41+
42+
const toString = ohm.grammar('toString {}');
43+
t.truthy(ohm.grammar('G <: toString {}', {toString}));
44+
t.truthy(ohm.grammar('G <: toString {}', Object.create({toString})));
45+
46+
const ns2 = {};
47+
Object.defineProperty(ns2, 'G', {
48+
value: ohm.grammar('G {}'),
49+
enumerable: false,
50+
});
51+
t.throws(
52+
() => ohm.grammar('G2 <: G {}', ns2),
53+
{message: /Grammar G is not declared/},
54+
'an own, non-enumerable property is ignored',
55+
);
56+
});
57+
3358
test('instantiating grammars from different types of objects', t => {
3459
let g = ohm.grammar(fs.readFileSync('test/arithmetic.ohm'));
3560
t.is(g.match('1+2').succeeded(), true, 'works with a Buffer from fs.readFileSync()');

packages/ohm-js/test/test-ohm-syntax.js

+4-20
Original file line numberDiff line numberDiff line change
@@ -1368,21 +1368,13 @@ describe('bootstrap', test => {
13681368
});
13691369

13701370
test('it can produce a grammar that works', t => {
1371-
const g = buildGrammar(
1372-
ns.Ohm.match(ohmGrammarSource, 'Grammar'),
1373-
ohm.createNamespace(),
1374-
ns.Ohm,
1375-
);
1371+
const g = buildGrammar(ns.Ohm.match(ohmGrammarSource, 'Grammar'), {}, ns.Ohm);
13761372
assertSucceeds(
13771373
t,
13781374
g.match(ohmGrammarSource, 'Grammar'),
13791375
'Ohm grammar can recognize itself',
13801376
);
1381-
const Arithmetic = buildGrammar(
1382-
g.match(arithmeticGrammarSource, 'Grammar'),
1383-
ohm.createNamespace(),
1384-
g,
1385-
);
1377+
const Arithmetic = buildGrammar(g.match(arithmeticGrammarSource, 'Grammar'), {}, g);
13861378
const s = Arithmetic.createSemantics().addAttribute('v', {
13871379
exp(expr) {
13881380
return expr.v;
@@ -1428,16 +1420,8 @@ describe('bootstrap', test => {
14281420
});
14291421

14301422
test('full bootstrap!', t => {
1431-
const g = buildGrammar(
1432-
ns.Ohm.match(ohmGrammarSource, 'Grammar'),
1433-
ohm.createNamespace(),
1434-
ns.Ohm,
1435-
);
1436-
const gPrime = buildGrammar(
1437-
g.match(ohmGrammarSource, 'Grammar'),
1438-
ohm.createNamespace(),
1439-
g,
1440-
);
1423+
const g = buildGrammar(ns.Ohm.match(ohmGrammarSource, 'Grammar'), {}, ns.Ohm);
1424+
const gPrime = buildGrammar(g.match(ohmGrammarSource, 'Grammar'), {}, g);
14411425
gPrime.namespaceName = g.namespaceName; // make their namespaceName properties the same
14421426
compareGrammars(t, g, gPrime);
14431427
});

packages/ohm-js/test/test-recipes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test('simple grammar recipes', t => {
3131
});
3232

3333
test('grammar recipes with supergrammars', t => {
34-
const ns = ohm.createNamespace();
34+
const ns = {};
3535
ns.G = ohm.grammar('G { start = end }');
3636
ns.G2 = ohm.grammar('G2 <: G { start := "a" }', ns);
3737

packages/packaging-tests/checkExports.mjs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as assert from 'uvu/assert';
22

33
const expectedExports = [
4-
'createNamespace',
54
'grammar',
65
'grammarFromScriptElement',
76
'grammars',

0 commit comments

Comments
 (0)