diff --git a/langs/jibberish/jibberish.parsers b/langs/jibberish/jibberish.parsers index 777ff3880..891309bc9 100644 --- a/langs/jibberish/jibberish.parsers +++ b/langs/jibberish/jibberish.parsers @@ -9,8 +9,10 @@ integerAtom onoffAtom enum on off atomAtom +keywordAtom topLevelPropertyAtom paint constant.language + extends keywordAtom opSymbolAtom paint keyword.operator.arithmetic diff --git a/package.json b/package.json index e48e88583..d839823ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scrollsdk", - "version": "99.0.0", + "version": "99.1.0", "description": "This npm package includes the Particles class, the Parsers compiler-compiler, a Parsers IDE, and more, all implemented in Particles, Parsers, and TypeScript.", "types": "./built/scrollsdk.node.d.ts", "main": "./products/Particle.js", diff --git a/parsers/Parsers.test.ts b/parsers/Parsers.test.ts index aed940592..b75db4115 100755 --- a/parsers/Parsers.test.ts +++ b/parsers/Parsers.test.ts @@ -651,6 +651,16 @@ anyAtom` } } +testParticles.informationContent = equal => { + // Test bit atom + const bitProgram = makeJibberishProgram("lightbulbState on") + const bitParticle = bitProgram.particleAt(0) + const atom = bitParticle.parsedAtoms[1] + equal(atom.bitsRequired, 1, "bit atom should have 1 bit of information") + equal(bitParticle.parsedAtoms[0].bitsRequired, 0, "cue word should have 0 bits of information. particle is fixed.") + equal(bitParticle.bitsRequired, 1, "bit particle should have 1 bit of information") +} + testParticles.duplicateParsers = equal => { // Arrange/Act const anyProgram = makeJibberishProgram(`type foo diff --git a/parsers/Parsers.ts b/parsers/Parsers.ts index 5970892e6..ba10aa719 100644 --- a/parsers/Parsers.ts +++ b/parsers/Parsers.ts @@ -228,6 +228,22 @@ abstract class ParserBackedParticle extends Particle { return newIndex } + /** + * Returns the total information bits required to represent this particle and all its subparticles. + * This is calculated as the sum of: + * 1. Information bits of all atoms in this particle + * 2. Information bits of all subparticles (recursive) + */ + get bitsRequired(): number { + // Get information bits for all atoms in this particle + const atomBits = this.parsedAtoms.map(atom => atom.bitsRequired).reduce((sum, bits) => sum + bits, 0) + + // Recursively get information bits from all subparticles + const subparticleBits = this.map(child => (child).bitsRequired).reduce((sum, bits) => sum + bits, 0) + + return atomBits + subparticleBits + } + getSubparticleInstancesOfParserId(parserId: particlesTypes.parserId): ParserBackedParticle[] { return this.particleIndex[parserId] || [] } @@ -773,6 +789,13 @@ abstract class AbstractParsersBackedAtom { this._parserDefinitionParser = parserDefinitionParser } + get optionCount() { + return this._typeDef.optionCount + } + get bitsRequired() { + return Math.log2(this.optionCount) + } + getAtom() { return this._particle.getAtom(this._index) } @@ -909,6 +932,10 @@ class ParsersBitAtom extends AbstractParsersBackedAtom { return atom === "0" || atom === "1" } + get optionCount() { + return 2 + } + static defaultPaint = "constant.numeric" _synthesizeAtom() { @@ -944,6 +971,12 @@ class ParsersIntegerAtom extends ParsersNumberAtom { return num.toString() === atom } + get optionCount() { + const minVal = parseInt(this.min) || -Infinity + const maxVal = parseInt(this.max) || Infinity + return maxVal - minVal + 1 + } + static defaultPaint = "constant.numeric.integer" _synthesizeAtom(seed: number) { @@ -969,6 +1002,14 @@ class ParsersFloatAtom extends ParsersNumberAtom { return !isNaN(num) && /^-?\d*(\.\d+)?([eE][+-]?\d+)?$/.test(atom) } + get optionCount() { + // For floats, we'll estimate based on typical float32 precision + // ~7 decimal digits of precision + const minVal = parseInt(this.min) || -Infinity + const maxVal = parseInt(this.max) || Infinity + return (maxVal - minVal) * Math.pow(10, 7) + } + static defaultPaint = "constant.numeric.float" _synthesizeAtom(seed: number) { @@ -999,6 +1040,10 @@ class ParsersBooleanAtom extends AbstractParsersBackedAtom { return this._trues.has(str) || this._falses.has(str) } + get optionCount() { + return 2 + } + static defaultPaint = "constant.language" _synthesizeAtom() { @@ -1045,6 +1090,10 @@ class ParsersKeywordAtom extends ParsersAnyAtom { _synthesizeAtom() { return this._parserDefinitionParser.cueIfAny } + + get optionCount() { + return 1 + } } class ParsersExtraAtomAtomTypeAtom extends AbstractParsersBackedAtom { @@ -1542,6 +1591,12 @@ class atomTypeDefinitionParser extends AbstractExtendibleParticle { return options } + get optionCount() { + const enumOptions = this._getEnumOptions() + if (enumOptions) return enumOptions.length + return Infinity + } + private _getEnumFromAtomTypeOptions(program: ParserBackedParticle) { const particle = this._getParticleFromExtended(ParsersConstants.enumFromAtomTypes) return particle ? Object.keys((particle.getParticle(ParsersConstants.enumFromAtomTypes))._getEnumFromAtomTypes(program)) : undefined diff --git a/particle/Particle.ts b/particle/Particle.ts index e8ef8fbb5..1c70991c2 100644 --- a/particle/Particle.ts +++ b/particle/Particle.ts @@ -3094,7 +3094,7 @@ class Particle extends AbstractParticle { return str ? indent + str.replace(/\n/g, indent) : "" } - static getVersion = () => "99.0.0" + static getVersion = () => "99.1.0" static fromDisk(path: string): Particle { const format = this._getFileFormat(path) diff --git a/products/Parsers.js b/products/Parsers.js index 89b68b2fb..e62d4cc65 100644 --- a/products/Parsers.js +++ b/products/Parsers.js @@ -179,6 +179,19 @@ class ParserBackedParticle extends Particle { } return newIndex } + /** + * Returns the total information bits required to represent this particle and all its subparticles. + * This is calculated as the sum of: + * 1. Information bits of all atoms in this particle + * 2. Information bits of all subparticles (recursive) + */ + get bitsRequired() { + // Get information bits for all atoms in this particle + const atomBits = this.parsedAtoms.map(atom => atom.bitsRequired).reduce((sum, bits) => sum + bits, 0) + // Recursively get information bits from all subparticles + const subparticleBits = this.map(child => child.bitsRequired).reduce((sum, bits) => sum + bits, 0) + return atomBits + subparticleBits + } getSubparticleInstancesOfParserId(parserId) { return this.particleIndex[parserId] || [] } @@ -629,6 +642,12 @@ class AbstractParsersBackedAtom { this._atomTypeId = atomTypeId this._parserDefinitionParser = parserDefinitionParser } + get optionCount() { + return this._typeDef.optionCount + } + get bitsRequired() { + return Math.log2(this.optionCount) + } getAtom() { return this._particle.getAtom(this._index) } @@ -728,6 +747,9 @@ class ParsersBitAtom extends AbstractParsersBackedAtom { const atom = this.getAtom() return atom === "0" || atom === "1" } + get optionCount() { + return 2 + } _synthesizeAtom() { return Utils.getRandomString(1, "01".split("")) } @@ -757,6 +779,11 @@ class ParsersIntegerAtom extends ParsersNumberAtom { if (isNaN(num)) return false return num.toString() === atom } + get optionCount() { + const minVal = parseInt(this.min) || -Infinity + const maxVal = parseInt(this.max) || Infinity + return maxVal - minVal + 1 + } _synthesizeAtom(seed) { return Utils.randomUniformInt(parseInt(this.min), parseInt(this.max), seed).toString() } @@ -776,6 +803,13 @@ class ParsersFloatAtom extends ParsersNumberAtom { const num = parseFloat(atom) return !isNaN(num) && /^-?\d*(\.\d+)?([eE][+-]?\d+)?$/.test(atom) } + get optionCount() { + // For floats, we'll estimate based on typical float32 precision + // ~7 decimal digits of precision + const minVal = parseInt(this.min) || -Infinity + const maxVal = parseInt(this.max) || Infinity + return (maxVal - minVal) * Math.pow(10, 7) + } _synthesizeAtom(seed) { return Utils.randomUniformFloat(parseFloat(this.min), parseFloat(this.max), seed).toString() } @@ -801,6 +835,9 @@ class ParsersBooleanAtom extends AbstractParsersBackedAtom { const str = atom.toLowerCase() return this._trues.has(str) || this._falses.has(str) } + get optionCount() { + return 2 + } _synthesizeAtom() { return Utils.getRandomString(1, ["1", "true", "t", "yes", "0", "false", "f", "no"]) } @@ -836,6 +873,9 @@ class ParsersKeywordAtom extends ParsersAnyAtom { _synthesizeAtom() { return this._parserDefinitionParser.cueIfAny } + get optionCount() { + return 1 + } } ParsersKeywordAtom.defaultPaint = "keyword" class ParsersExtraAtomAtomTypeAtom extends AbstractParsersBackedAtom { @@ -1227,6 +1267,11 @@ class atomTypeDefinitionParser extends AbstractExtendibleParticle { options.sort((a, b) => b.length - a.length) return options } + get optionCount() { + const enumOptions = this._getEnumOptions() + if (enumOptions) return enumOptions.length + return Infinity + } _getEnumFromAtomTypeOptions(program) { const particle = this._getParticleFromExtended(ParsersConstants.enumFromAtomTypes) return particle ? Object.keys(particle.getParticle(ParsersConstants.enumFromAtomTypes)._getEnumFromAtomTypes(program)) : undefined diff --git a/products/Parsers.ts.browser.js b/products/Parsers.ts.browser.js index 85fc141f6..ed362627c 100644 --- a/products/Parsers.ts.browser.js +++ b/products/Parsers.ts.browser.js @@ -177,6 +177,19 @@ class ParserBackedParticle extends Particle { } return newIndex } + /** + * Returns the total information bits required to represent this particle and all its subparticles. + * This is calculated as the sum of: + * 1. Information bits of all atoms in this particle + * 2. Information bits of all subparticles (recursive) + */ + get bitsRequired() { + // Get information bits for all atoms in this particle + const atomBits = this.parsedAtoms.map(atom => atom.bitsRequired).reduce((sum, bits) => sum + bits, 0) + // Recursively get information bits from all subparticles + const subparticleBits = this.map(child => child.bitsRequired).reduce((sum, bits) => sum + bits, 0) + return atomBits + subparticleBits + } getSubparticleInstancesOfParserId(parserId) { return this.particleIndex[parserId] || [] } @@ -627,6 +640,12 @@ class AbstractParsersBackedAtom { this._atomTypeId = atomTypeId this._parserDefinitionParser = parserDefinitionParser } + get optionCount() { + return this._typeDef.optionCount + } + get bitsRequired() { + return Math.log2(this.optionCount) + } getAtom() { return this._particle.getAtom(this._index) } @@ -726,6 +745,9 @@ class ParsersBitAtom extends AbstractParsersBackedAtom { const atom = this.getAtom() return atom === "0" || atom === "1" } + get optionCount() { + return 2 + } _synthesizeAtom() { return Utils.getRandomString(1, "01".split("")) } @@ -755,6 +777,11 @@ class ParsersIntegerAtom extends ParsersNumberAtom { if (isNaN(num)) return false return num.toString() === atom } + get optionCount() { + const minVal = parseInt(this.min) || -Infinity + const maxVal = parseInt(this.max) || Infinity + return maxVal - minVal + 1 + } _synthesizeAtom(seed) { return Utils.randomUniformInt(parseInt(this.min), parseInt(this.max), seed).toString() } @@ -774,6 +801,13 @@ class ParsersFloatAtom extends ParsersNumberAtom { const num = parseFloat(atom) return !isNaN(num) && /^-?\d*(\.\d+)?([eE][+-]?\d+)?$/.test(atom) } + get optionCount() { + // For floats, we'll estimate based on typical float32 precision + // ~7 decimal digits of precision + const minVal = parseInt(this.min) || -Infinity + const maxVal = parseInt(this.max) || Infinity + return (maxVal - minVal) * Math.pow(10, 7) + } _synthesizeAtom(seed) { return Utils.randomUniformFloat(parseFloat(this.min), parseFloat(this.max), seed).toString() } @@ -799,6 +833,9 @@ class ParsersBooleanAtom extends AbstractParsersBackedAtom { const str = atom.toLowerCase() return this._trues.has(str) || this._falses.has(str) } + get optionCount() { + return 2 + } _synthesizeAtom() { return Utils.getRandomString(1, ["1", "true", "t", "yes", "0", "false", "f", "no"]) } @@ -834,6 +871,9 @@ class ParsersKeywordAtom extends ParsersAnyAtom { _synthesizeAtom() { return this._parserDefinitionParser.cueIfAny } + get optionCount() { + return 1 + } } ParsersKeywordAtom.defaultPaint = "keyword" class ParsersExtraAtomAtomTypeAtom extends AbstractParsersBackedAtom { @@ -1225,6 +1265,11 @@ class atomTypeDefinitionParser extends AbstractExtendibleParticle { options.sort((a, b) => b.length - a.length) return options } + get optionCount() { + const enumOptions = this._getEnumOptions() + if (enumOptions) return enumOptions.length + return Infinity + } _getEnumFromAtomTypeOptions(program) { const particle = this._getParticleFromExtended(ParsersConstants.enumFromAtomTypes) return particle ? Object.keys(particle.getParticle(ParsersConstants.enumFromAtomTypes)._getEnumFromAtomTypes(program)) : undefined diff --git a/products/Particle.browser.js b/products/Particle.browser.js index 626a83efd..1e84d1fcf 100644 --- a/products/Particle.browser.js +++ b/products/Particle.browser.js @@ -2598,7 +2598,7 @@ Particle.iris = `sepal_length,sepal_width,petal_length,petal_width,species 4.9,2.5,4.5,1.7,virginica 5.1,3.5,1.4,0.2,setosa 5,3.4,1.5,0.2,setosa` -Particle.getVersion = () => "99.0.0" +Particle.getVersion = () => "99.1.0" class AbstractExtendibleParticle extends Particle { _getFromExtended(cuePath) { const hit = this._getParticleFromExtended(cuePath) diff --git a/products/Particle.js b/products/Particle.js index a4043d9cc..e744226e6 100644 --- a/products/Particle.js +++ b/products/Particle.js @@ -2588,7 +2588,7 @@ Particle.iris = `sepal_length,sepal_width,petal_length,petal_width,species 4.9,2.5,4.5,1.7,virginica 5.1,3.5,1.4,0.2,setosa 5,3.4,1.5,0.2,setosa` -Particle.getVersion = () => "99.0.0" +Particle.getVersion = () => "99.1.0" class AbstractExtendibleParticle extends Particle { _getFromExtended(cuePath) { const hit = this._getParticleFromExtended(cuePath) diff --git a/products/jibberish.browser.js b/products/jibberish.browser.js index 699cca5a3..740d6a48b 100644 --- a/products/jibberish.browser.js +++ b/products/jibberish.browser.js @@ -41,8 +41,10 @@ integerAtom onoffAtom enum on off atomAtom +keywordAtom topLevelPropertyAtom paint constant.language + extends keywordAtom opSymbolAtom paint keyword.operator.arithmetic diff --git a/products/jibberish.nodejs.js b/products/jibberish.nodejs.js index 1372869a8..d1099bbc6 100755 --- a/products/jibberish.nodejs.js +++ b/products/jibberish.nodejs.js @@ -47,8 +47,10 @@ integerAtom onoffAtom enum on off atomAtom +keywordAtom topLevelPropertyAtom paint constant.language + extends keywordAtom opSymbolAtom paint keyword.operator.arithmetic diff --git a/releaseNotes.scroll b/releaseNotes.scroll index f04c7e7ea..c317e71b4 100644 --- a/releaseNotes.scroll +++ b/releaseNotes.scroll @@ -20,6 +20,10 @@ node_modules/scroll-cli/microlangs/changes.parsers thinColumns 4 +📦 99.1.0 2024-12-01 +🎉 new `bitsRequired` method +🎉 started adding infra for built-in complexity measurements + 📦 99.0.0 2024-11-30 🎉 Fusion now handles all 4 parser passes ⚠️ BREAKING: (no one should be affected). Fusion API changed. No longer any `parseCode` method.