-
-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
Normal attributes are lazily computed. A stored attribute is initialized | ||
by a separate initialization operation. After initialization, you can read | ||
a stored attribute's value just like a normal (computed) attribute. | ||
*/ | ||
export function addStoredAttribute(semantics, attrName, initSignature, fn) { | ||
semantics.addAttribute(attrName, { | ||
_default() { | ||
throw new Error(`Attribute '${attrName}' not initialized`); | ||
} | ||
Check failure on line 10 in packages/ohm-js/extras/storedAttributes.js GitHub Actions / build (16.x)
Check failure on line 10 in packages/ohm-js/extras/storedAttributes.js GitHub Actions / build (18.x)
|
||
}); | ||
|
||
// Create the attribute setter, which the semantic actions of the init | ||
// operation will close over. | ||
const key = semantics._getSemantics().attributeKeys[attrName]; | ||
const setAttr = (wrapper, val) => { | ||
wrapper._node[key] = val; | ||
return val; | ||
}; | ||
|
||
// Create the init operation. It's the user's responsibility to call it. | ||
semantics.addOperation(initSignature, fn(setAttr)); | ||
return semantics; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import test from 'ava'; | ||
import fs from 'node:fs'; | ||
import {URL} from 'node:url'; | ||
|
||
import {addStoredAttribute} from '../../extras/storedAttributes.js'; | ||
import * as ohm from '../../index.mjs'; | ||
|
||
const grammarPath = new URL('../data/arithmetic.ohm', import.meta.url); | ||
const g = ohm.grammar(fs.readFileSync(grammarPath)); | ||
|
||
test('stored attributes', t => { | ||
const semantics = g.createSemantics(); | ||
const exp = semantics(g.match('3 + 4 - 1')); | ||
|
||
// A meaningless stored attribute, where nodes have a "polarity" depending | ||
// on the type of operation they are involved in. All nodes pass their | ||
// polarity downwards, and the default action gives the child the polarity | ||
// of its parent. | ||
addStoredAttribute(semantics, 'polarity', 'initPolarity(pol)', setPolarity => ({ | ||
AddExp_plus(expA, _, expB) { | ||
setPolarity(this, '+'); | ||
// Note that we explicitly skip initializing the operator. | ||
expA.initPolarity(this.polarity); | ||
expB.initPolarity(this.polarity); | ||
}, | ||
AddExp_minus(expA, _, expB) { | ||
setPolarity(this, '-'); | ||
// Note that we explicitly skip initializing the operator. | ||
expA.initPolarity(this.polarity); | ||
expB.initPolarity(this.polarity); | ||
}, | ||
_default(...children) { | ||
// By default, inherit polarity from the parent node. | ||
setPolarity(this, this.args.pol); | ||
children.forEach(c => c.initPolarity(this.args.pol)); | ||
} | ||
Check failure on line 36 in packages/ohm-js/test/extras/test-storedAttributes.js GitHub Actions / build (16.x)
Check failure on line 36 in packages/ohm-js/test/extras/test-storedAttributes.js GitHub Actions / build (18.x)
|
||
})); | ||
exp.initPolarity('='); | ||
t.is(exp.polarity, '='); // Exp | ||
t.is(exp.child(0).polarity, '='); // AddExp | ||
t.is(exp.child(0).child(0).polarity, '-'); // AddExp_minus | ||
t.is(exp.child(0).child(0).child(0).polarity, '-'); // AddExp | ||
t.is(exp.child(0).child(0).child(0).child(0).polarity, '+'); // AddExp_plus | ||
|
||
t.throws(() => exp.child(0).child(0).child(1).polarity, { | ||
message: 'Attribute \'polarity\' not initialized' | ||
Check failure on line 46 in packages/ohm-js/test/extras/test-storedAttributes.js GitHub Actions / build (16.x)
Check failure on line 46 in packages/ohm-js/test/extras/test-storedAttributes.js GitHub Actions / build (18.x)
|
||
}); | ||
}); |