UNPKG

7.29 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.getKeys = void 0;
4class EnvRec extends Array {
5 constructor(parent, namespace) {
6 super();
7 this._parent = parent;
8 this._children = [];
9 if (this._parent) {
10 this._parent._children.push(this);
11 }
12 this._namespace = namespace;
13 this._byType = {};
14 this._byLocation = {};
15 this._byProductionName = {};
16 this._byAoid = {};
17 this._keys = new Set();
18 }
19 // this mutates each item to fill in missing properties
20 push(...items) {
21 for (const item of items) {
22 pushKey(this._byType, item.type, item);
23 pushKey(this._byLocation, item.location, item);
24 if (item.type === 'clause' && item.aoid) {
25 const op = {
26 type: 'op',
27 aoid: item.aoid,
28 refId: item.id,
29 location: item.location,
30 referencingIds: [],
31 };
32 this.push(op);
33 }
34 if (item.type === 'op') {
35 this._byAoid[item.aoid] = item;
36 this._keys.add(item.aoid);
37 }
38 if (item.type === 'production') {
39 this._byProductionName[item.name] = item;
40 }
41 if (item.type === 'term') {
42 for (const key of getKeys(item)) {
43 this._keys.add(key);
44 }
45 }
46 }
47 return super.push(...items);
48 }
49}
50// Map, etc. returns array.
51Object.defineProperty(EnvRec, Symbol.species, { value: Array });
52/*@internal*/
53class Biblio {
54 constructor(location) {
55 this._byId = {};
56 this._location = location;
57 this._root = new EnvRec(undefined, 'global');
58 this._nsToEnvRec = { global: this._root };
59 this.createNamespace(location, 'global');
60 }
61 byId(id) {
62 return this._byId[id];
63 }
64 byNamespace(ns) {
65 const env = this._nsToEnvRec[ns];
66 if (!env) {
67 throw new Error('Namespace ' + ns + ' not found');
68 }
69 return env;
70 }
71 byProductionName(name, ns) {
72 ns = ns || this._location;
73 return this.lookup(ns, env => env._byProductionName[name]);
74 }
75 byAoid(aoid, ns) {
76 ns = ns || this._location;
77 return this.lookup(ns, env => env._byAoid[aoid]);
78 }
79 getDefinedWords(ns) {
80 const result = Object.create(null);
81 for (const type of ['term', 'op']) {
82 // note that the `seen` set is not shared across types
83 // this is dumb but is the current semantics: ops always clobber terms
84 const seen = new Set();
85 let current = this._nsToEnvRec[ns];
86 while (current) {
87 const entries = current._byType[type] || [];
88 for (const entry of entries) {
89 let keys;
90 if (type === 'term') {
91 if (entry.term === 'type') {
92 // this is a dumb kludge necessitated by ecma262 dfn'ing both "type" and "Type"
93 // the latter originally masked the former, so that it didn't actually end up linking all usages of the word "type"
94 // we've changed the logic a bit so that masking no longer happens, and consequently the autolinker adds a bunch of spurious links
95 // this can be removed once ecma262 no longer dfn's it and we update ecma262-biblio.json
96 continue;
97 }
98 keys = getKeys(entry).flatMap(key => {
99 if (/^[a-z]/.test(key)) {
100 // include capitalized variant of words starting with lowercase letter
101 return [key, key[0].toUpperCase() + key.slice(1)];
102 }
103 return key;
104 });
105 }
106 else {
107 keys = [entry.aoid];
108 }
109 for (const key of keys) {
110 if (!seen.has(key)) {
111 seen.add(key);
112 result[key] = entry;
113 }
114 }
115 }
116 current = current._parent;
117 }
118 }
119 return result;
120 }
121 lookup(ns, cb) {
122 let env = this._nsToEnvRec[ns];
123 if (!env) {
124 throw new Error('Namespace ' + ns + ' not found');
125 }
126 while (env) {
127 const result = cb(env);
128 if (result) {
129 return result;
130 }
131 env = env._parent;
132 }
133 return undefined;
134 }
135 add(entry, ns) {
136 ns = ns || this._location;
137 const env = this._nsToEnvRec[ns];
138 entry.namespace = ns;
139 // @ts-ignore
140 entry.location = entry.location || '';
141 // @ts-ignore
142 entry.referencingIds = entry.referencingIds || [];
143 env.push(entry);
144 if (entry.id) {
145 if ({}.hasOwnProperty.call(this, entry.id)) {
146 throw new Error('Duplicate biblio entry ' + JSON.stringify(entry.id) + '.');
147 }
148 this._byId[entry.id] = entry;
149 }
150 }
151 createNamespace(ns, parent) {
152 const existingNs = this._nsToEnvRec[ns];
153 if (existingNs) {
154 if (existingNs._parent._namespace === parent) {
155 return;
156 }
157 else {
158 throw new Error('Namespace ' + ns + ' already in use.');
159 }
160 }
161 if (!parent) {
162 throw new Error('Cannot create namespace without parent');
163 }
164 const parentEnv = this._nsToEnvRec[parent];
165 if (!parentEnv) {
166 throw new Error('Cannot find namespace with name ' + parent);
167 }
168 if (!ns) {
169 throw new Error('Cannot create namespace without a name');
170 }
171 const env = new EnvRec(parentEnv, ns);
172 this._nsToEnvRec[ns] = env;
173 }
174 addExternalBiblio(biblio) {
175 Object.keys(biblio).forEach(site => {
176 biblio[site].forEach(entry => {
177 entry.location = site;
178 this.add(entry, 'global');
179 });
180 });
181 }
182 keysForNamespace(ns) {
183 return this._nsToEnvRec[ns]._keys;
184 }
185 toJSON() {
186 let root = [];
187 function addEnv(env) {
188 root = root.concat(env);
189 env._children.forEach(addEnv);
190 }
191 addEnv(this.byNamespace(this._location));
192 return root;
193 }
194 dump() {
195 dumpEnv(this._root);
196 }
197}
198exports.default = Biblio;
199function dumpEnv(env) {
200 console.log('## ' + env._namespace);
201 console.log(env.map(entry => JSON.stringify(entry)).join(', '));
202 env._children.forEach(child => {
203 dumpEnv(child);
204 });
205}
206function pushKey(arr, key, value) {
207 if (arr[key] === undefined) {
208 arr[key] = [];
209 }
210 arr[key].push(value);
211}
212function getKeys(entry) {
213 return [entry.term, ...(entry.variants || [])].map(v => v.replace(/\s+/g, ' '));
214}
215exports.getKeys = getKeys;