binary-parser
Advanced tools
Comparing version 1.1.6 to 1.2.0
@@ -34,2 +34,5 @@ //======================================================================================== | ||
var aliasRegistry = {}; | ||
var FUNCTION_PREFIX = '___parser_'; | ||
var BIT_RANGE = []; | ||
@@ -68,2 +71,3 @@ (function() { | ||
this.constructorFn = null; | ||
this.alias = null; | ||
}; | ||
@@ -103,2 +107,8 @@ | ||
Parser.prototype.namely = function(alias) { | ||
aliasRegistry[alias] = this; | ||
this.alias = alias; | ||
return this; | ||
} | ||
Parser.prototype.skip = function(length, options) { | ||
@@ -133,3 +143,3 @@ if (options && options.assert) { | ||
Parser.prototype.array = function(varName, options) { | ||
if (!options.readUntil && !options.length) { | ||
if (!options.readUntil && !options.length && !options.lengthInBytes) { | ||
throw new Error('Length option of array is not defined.'); | ||
@@ -140,3 +150,4 @@ } | ||
} | ||
if (typeof options.type === 'string' && Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.type]) < 0) { | ||
if ((typeof options.type === 'string') && !aliasRegistry[options.type] | ||
&& Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.type]) < 0) { | ||
throw new Error('Specified primitive type "' + options.type + '" is not supported.'); | ||
@@ -163,6 +174,7 @@ } | ||
if (typeof options.choices[key] === 'string' && Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.choices[key]]) < 0) { | ||
if ((typeof options.choices[key] === 'string') && !aliasRegistry[options.choices[key]] | ||
&& (Object.keys(PRIMITIVE_TYPES).indexOf(NAME_MAP[options.choices[key]]) < 0)) { | ||
throw new Error('Specified primitive type "' + options.choices[key] + '" is not supported.'); | ||
} | ||
}); | ||
}, this); | ||
@@ -176,3 +188,4 @@ return this.setNextParser('choice', varName, options); | ||
} | ||
if (!(options.type instanceof Parser)) { | ||
if (!(options.type instanceof Parser) && !aliasRegistry[options.type]) { | ||
throw new Error('Type option of nest must be a Parser object.'); | ||
@@ -212,2 +225,24 @@ } | ||
ctx.pushCode('if (!Buffer.isBuffer(buffer)) {'); | ||
ctx.generateError('"argument buffer is not a Buffer object"'); | ||
ctx.pushCode('}'); | ||
if (!this.alias) { | ||
this.addRawCode(ctx); | ||
} else { | ||
this.addAliasedCode(ctx); | ||
} | ||
if (this.alias) { | ||
ctx.pushCode('return {0}(0).result;', FUNCTION_PREFIX + this.alias); | ||
} else { | ||
ctx.pushCode('return vars;'); | ||
} | ||
return ctx.code; | ||
}; | ||
Parser.prototype.addRawCode = function(ctx) { | ||
ctx.pushCode('var offset = 0;'); | ||
if (this.constructorFn) { | ||
@@ -218,14 +253,39 @@ ctx.pushCode('var vars = new constructorFn();'); | ||
} | ||
ctx.pushCode('var offset = 0;'); | ||
ctx.pushCode('if (!Buffer.isBuffer(buffer)) {'); | ||
ctx.generateError('"argument buffer is not a Buffer object"'); | ||
ctx.pushCode('}'); | ||
this.generate(ctx); | ||
this.resolveReferences(ctx); | ||
ctx.pushCode('return vars;'); | ||
}; | ||
return ctx.code; | ||
Parser.prototype.addAliasedCode = function(ctx) { | ||
ctx.pushCode('function {0}(offset) {', FUNCTION_PREFIX + this.alias); | ||
if (this.constructorFn) { | ||
ctx.pushCode('var vars = new constructorFn();'); | ||
} else { | ||
ctx.pushCode('var vars = {};'); | ||
} | ||
this.generate(ctx); | ||
ctx.markResolved(this.alias); | ||
this.resolveReferences(ctx); | ||
ctx.pushCode('return { offset: offset, result: vars };'); | ||
ctx.pushCode('}'); | ||
return ctx; | ||
}; | ||
Parser.prototype.resolveReferences = function(ctx) { | ||
var references = ctx.getUnresolvedReferences(); | ||
ctx.markRequested(references); | ||
references.forEach(function(alias) { | ||
var parser = aliasRegistry[alias]; | ||
parser.addAliasedCode(ctx); | ||
}); | ||
}; | ||
Parser.prototype.compile = function() { | ||
@@ -259,2 +319,5 @@ this.compiled = new Function('buffer', 'callback', 'constructorFn', this.getCode()); | ||
// if this is a nested parser | ||
} else if (this.type === 'Nest') { | ||
size = this.options.type.sizeOf(); | ||
} else if (!this.type) { | ||
@@ -474,2 +537,3 @@ size = 0; | ||
var length = ctx.generateOption(this.options.length); | ||
var lengthInBytes = ctx.generateOption(this.options.lengthInBytes); | ||
var type = this.options.type; | ||
@@ -491,2 +555,4 @@ var counter = ctx.generateTmpVariable(); | ||
ctx.pushCode('for (var {0} = 0; offset < buffer.length; {0}++) {', counter); | ||
} else if (lengthInBytes !== undefined) { | ||
ctx.pushCode('for (var {0} = offset; offset - {0} < {1}; ) {', counter, lengthInBytes); | ||
} else { | ||
@@ -497,4 +563,11 @@ ctx.pushCode('for (var {0} = 0; {0} < {1}; {0}++) {', counter, length); | ||
if (typeof type === 'string') { | ||
ctx.pushCode('var {0} = buffer.read{1}(offset);', item, NAME_MAP[type]); | ||
ctx.pushCode('offset += {0};', PRIMITIVE_TYPES[NAME_MAP[type]]); | ||
if (!aliasRegistry[type]) { | ||
ctx.pushCode('var {0} = buffer.read{1}(offset);', item, NAME_MAP[type]); | ||
ctx.pushCode('offset += {0};', PRIMITIVE_TYPES[NAME_MAP[type]]); | ||
} else { | ||
var tempVar = ctx.generateTmpVariable(); | ||
ctx.pushCode('var {0} = {1}(offset);', tempVar, FUNCTION_PREFIX + type); | ||
ctx.pushCode('var {0} = {1}.result; offset = {1}.offset;', item, tempVar); | ||
if (type !== this.alias) ctx.addReference(type); | ||
} | ||
} else if (type instanceof Parser) { | ||
@@ -523,4 +596,11 @@ ctx.pushCode('var {0} = {};', item); | ||
if (typeof type === 'string') { | ||
ctx.pushCode('{0} = buffer.read{1}(offset);', ctx.generateVariable(this.varName), NAME_MAP[type]); | ||
ctx.pushCode('offset += {0};', PRIMITIVE_TYPES[NAME_MAP[type]]); | ||
if (!aliasRegistry[type]) { | ||
ctx.pushCode('{0} = buffer.read{1}(offset);', ctx.generateVariable(this.varName), NAME_MAP[type]); | ||
ctx.pushCode('offset += {0};', PRIMITIVE_TYPES[NAME_MAP[type]]); | ||
} else { | ||
var tempVar = ctx.generateTmpVariable(); | ||
ctx.pushCode('var {0} = {1}(offset);', tempVar, FUNCTION_PREFIX + type); | ||
ctx.pushCode('{0} = {1}.result; offset = {1}.offset;', ctx.generateVariable(this.varName), tempVar); | ||
if (type !== this.alias) ctx.addReference(type); | ||
} | ||
} else if (type instanceof Parser) { | ||
@@ -556,6 +636,13 @@ ctx.pushPath(varName); | ||
var nestVar = ctx.generateVariable(this.varName); | ||
ctx.pushCode('{0} = {};', nestVar); | ||
ctx.pushPath(this.varName); | ||
this.options.type.generate(ctx); | ||
ctx.popPath(); | ||
if (this.options.type instanceof Parser) { | ||
ctx.pushCode('{0} = {};', nestVar); | ||
ctx.pushPath(this.varName); | ||
this.options.type.generate(ctx); | ||
ctx.popPath(); | ||
} else if (aliasRegistry[this.options.type]) { | ||
var tempVar = ctx.generateTmpVariable(); | ||
ctx.pushCode('var {0} = {1}(offset);', tempVar, FUNCTION_PREFIX + this.options.type); | ||
ctx.pushCode('{0} = {1}.result; offset = {1}.offset;', nestVar, tempVar); | ||
if (this.options.type !== this.alias) ctx.addReference(this.options.type); | ||
} | ||
}; | ||
@@ -562,0 +649,0 @@ |
@@ -15,2 +15,3 @@ //======================================================================================== | ||
this.tmpVariableCount = 0; | ||
this.references = {}; | ||
}; | ||
@@ -61,3 +62,3 @@ | ||
var args = Array.prototype.slice.call(arguments); | ||
this.code += Context.interpolate.apply(this, args) + '\n'; | ||
@@ -82,2 +83,24 @@ }; | ||
Context.prototype.addReference = function(alias) { | ||
if (this.references[alias]) return; | ||
this.references[alias] = { resolved: false, requested: false }; | ||
}; | ||
Context.prototype.markResolved = function(alias) { | ||
this.references[alias].resolved = true; | ||
}; | ||
Context.prototype.markRequested = function(aliasList) { | ||
aliasList.forEach(function(alias) { | ||
this.references[alias].requested = true; | ||
}.bind(this)); | ||
}; | ||
Context.prototype.getUnresolvedReferences = function() { | ||
var references = this.references; | ||
return Object.keys(this.references).filter(function(alias) { | ||
return !references[alias].resolved && !references[alias].requested; | ||
}); | ||
}; | ||
//---------------------------------------------------------------------------------------- | ||
@@ -84,0 +107,0 @@ // private methods |
{ | ||
"name": "binary-parser", | ||
"version": "1.1.6", | ||
"version": "1.2.0", | ||
"description": "Blazing-fast binary parser builder", | ||
@@ -5,0 +5,0 @@ "main": "lib/binary_parser.js", |
135
README.md
@@ -150,5 +150,7 @@ # Binary-parser | ||
If it's a string, you have to choose from [u]int{8, 16, 32}{le, be}. | ||
- `length` - (either `length` or `readUntil` is required) Length of the array. Can be a number, string or a function. | ||
- `length` - (either `length`, `lengthInBytes`, or `readUntil` is required) Length of the array. Can be a number, string or a function. | ||
Use number for statically sized arrays. | ||
- `readUntil` - (either `length` or `readUntil` is required) If `'eof'`, then this parser | ||
- `lengthInBytes` - (either `length`, `lengthInBytes`, or `readUntil` is required) Length of the array expressed in bytes. Can be a number, string or a function. | ||
Use number for statically sized arrays. | ||
- `readUntil` - (either `length`, `lengthInBytes`, or `readUntil` is required) If `'eof'`, then this parser | ||
reads until the end of `Buffer` object. If function it reads until the function returns true. | ||
@@ -177,5 +179,24 @@ | ||
// Dynamically sized array (with stop-check on parsed item) | ||
// Statically sized array | ||
.array('data4', { | ||
type: 'int32', | ||
lengthInBytes: 16 | ||
}) | ||
// Dynamically sized array (reference another variable) | ||
.uint8('dataLengthInBytes') | ||
.array('data5', { | ||
type: 'int32', | ||
lengthInBytes: 'dataLengthInBytes' | ||
}) | ||
// Dynamically sized array (with some calculation) | ||
.array('data6', { | ||
type: 'int32', | ||
lengthInBytes: function() { return this.dataLengthInBytes - 4; } // other fields are available through this | ||
}) | ||
// Dynamically sized array (with stop-check on parsed item) | ||
.array('data7', { | ||
type: 'int32', | ||
readUntil: function(item, buffer) { return item === 42 } // stop when specific item is parsed. buffer can be used to perform a read-ahead. | ||
@@ -185,3 +206,3 @@ }); | ||
// Use user defined parser object | ||
.array('data5', { | ||
.array('data8', { | ||
type: userDefinedParser, | ||
@@ -244,2 +265,85 @@ length: 'dataLength' | ||
### namely(alias) | ||
Set an alias to this parser, so there will be an opportunity to refer to it by name in methods like `.array`, `.nest` and `.choice`, instead of requirement to have an instance of it. | ||
Especially, the parser may reference itself: | ||
```javascript | ||
var stop = new Parser(); | ||
var parser = | ||
new Parser().namely('self') // use 'self' to refer to the parser itself | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: stop, | ||
1: 'self', | ||
2: Parser.start().nest('left', { type: 'self' }) | ||
.nest('right', { type: 'self' }), | ||
3: Parser.start().nest('one', { type: 'self' }) | ||
.nest('two', { type: 'self' }) | ||
.nest('three', { type: 'self' }) | ||
} | ||
}); | ||
// 2 | ||
// / \ | ||
// 3 1 | ||
// / | \ \ | ||
// 1 0 2 0 | ||
// / / \ | ||
// 0 1 0 | ||
// / | ||
// 0 | ||
var buffer = new Buffer([ 2, | ||
/* left -> */ 3, | ||
/* one -> */ 1, /* -> */ 0, | ||
/* two -> */ 0, | ||
/* three -> */ 2, | ||
/* left -> */ 1, /* -> */ 0, | ||
/* right -> */ 0, | ||
/* right -> */ 1, /* -> */ 0 ]); | ||
parser.parse(buffer); | ||
``` | ||
For most of the cases there is almost no difference to the instance-way of referencing, but this method provides the way to parse recursive trees, where each node could reference the node of the same type from the inside. | ||
Also, when you reference a parser using its instance twice, the generated code will contain two similar parts of the code included, while with the named approach, it will include a function with a name, and will just call this function for every case of usage. | ||
NB: This style could lead to circular references and infinite recursion, to avoid this, ensure that every possible path has its end. Also, this recursion is not tail-optimized, so could lead to memory leaks when it goes too deep. | ||
An example of referencing other patches: | ||
```javascript | ||
// the line below registers the name 'self', so we will be able to use it in | ||
// `twoCells` as a reference | ||
var parser = Parser.start().namely('self'); | ||
var stop = Parser.start().namely('stop'); | ||
var twoCells = Parser.start().namely('twoCells') | ||
.nest('left', { type: 'self' }) | ||
.nest('right', { type: 'stop' }) | ||
parser | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: 'stop', | ||
1: 'self', | ||
2: 'twoCells' | ||
} | ||
}); | ||
var buffer = new Buffer([ 2, | ||
/* left */ 1, 1, 0, | ||
/* right */ 0 ]); | ||
parser.parse(buffer); | ||
``` | ||
### compile() | ||
@@ -299,24 +403,1 @@ Compile this parser on-the-fly and cache its result. Usually, there is no need to | ||
Pull requests with fixes and improvements are welcomed! | ||
## License | ||
The MIT License (MIT) | ||
Copyright (c) 2013-2014 Keichi Takahashi <keichi.t@me.com> | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
@@ -22,2 +22,17 @@ var assert = require('assert'); | ||
}); | ||
it('should parse array of primitive types with lengthInBytes', function(){ | ||
var parser = | ||
Parser.start() | ||
.uint8('length') | ||
.array('message', { | ||
lengthInBytes: 'length', | ||
type: 'uint8' | ||
}); | ||
var buffer = new Buffer([12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
length: 12, | ||
message: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] | ||
}); | ||
}); | ||
it('should parse array of user defined types', function(){ | ||
@@ -45,2 +60,66 @@ var elementParser = new Parser() | ||
}); | ||
it('should parse array of user defined types with lengthInBytes', function(){ | ||
var elementParser = new Parser() | ||
.uint8('key') | ||
.int16le('value'); | ||
var parser = | ||
Parser.start() | ||
.uint16le('length') | ||
.array('message', { | ||
lengthInBytes: 'length', | ||
type: elementParser | ||
}); | ||
var buffer = new Buffer([0x06, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
length: 0x06, | ||
message: [ | ||
{key: 0xca, value: 1234}, | ||
{key: 0xbe, value: 1235} | ||
] | ||
}); | ||
}); | ||
it('should parse array of user defined types with lengthInBytes literal', function(){ | ||
var elementParser = new Parser() | ||
.uint8('key') | ||
.int16le('value'); | ||
var parser = | ||
Parser.start() | ||
.array('message', { | ||
lengthInBytes: 0x06, | ||
type: elementParser | ||
}); | ||
var buffer = new Buffer([0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
message: [ | ||
{key: 0xca, value: 1234}, | ||
{key: 0xbe, value: 1235} | ||
] | ||
}); | ||
}); | ||
it('should parse array of user defined types with lengthInBytes function', function(){ | ||
var elementParser = new Parser() | ||
.uint8('key') | ||
.int16le('value'); | ||
var parser = | ||
Parser.start() | ||
.uint16le('length') | ||
.array('message', { | ||
lengthInBytes: function() { return this.length; }, | ||
type: elementParser | ||
}); | ||
var buffer = new Buffer([0x06, 0x00, 0xca, 0xd2, 0x04, 0xbe, 0xd3, 0x04]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
length: 0x06, | ||
message: [ | ||
{key: 0xca, value: 1234}, | ||
{key: 0xbe, value: 1235} | ||
] | ||
}); | ||
}); | ||
it('should parse array of arrays', function(){ | ||
@@ -182,2 +261,67 @@ var rowParser = | ||
}); | ||
it('should be able to go into recursion', function(){ | ||
var parser = | ||
Parser.start().namely('self') | ||
.uint8('length') | ||
.array('data', { | ||
type: 'self', | ||
length: 'length' | ||
}); | ||
var buffer = new Buffer([ 1, 1, 1, 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
length: 1, | ||
data: [ { | ||
length: 1, | ||
data: [ { | ||
length: 1, | ||
data: [ { length: 0, data: [] } ] | ||
} ] | ||
} ] | ||
}); | ||
}); | ||
it('should be able to go into even deeper recursion', function(){ | ||
var parser = | ||
Parser.start().namely('self') | ||
.uint8('length') | ||
.array('data', { | ||
type: 'self', | ||
length: 'length' | ||
}); | ||
// 2 | ||
// / \ | ||
// 3 1 | ||
// / | \ \ | ||
// 1 0 2 0 | ||
// / / \ | ||
// 0 1 0 | ||
// / | ||
// 0 | ||
var buffer = new Buffer([ 2, | ||
/* 0 */ 3, | ||
/* 0 */ 1, | ||
/* 0 */ 0, | ||
/* 1 */ 0, | ||
/* 2 */ 2, | ||
/* 0 */ 1, | ||
/* 0 */ 0, | ||
/* 1 */ 0, | ||
/* 1 */ 1, | ||
/* 0 */ 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
length: 2, | ||
data: [ { | ||
length: 3, | ||
data: [ { length: 1, data: [ { length: 0, data: [] } ] }, | ||
{ length: 0, data: [] }, | ||
{ length: 2, data: [ { length: 1, data: [ { length: 0, data: [] } ] }, | ||
{ length: 0, data: [] } ] } ] | ||
}, { | ||
length: 1, | ||
data: [ { length: 0, data: [] } ] | ||
} ] | ||
}); | ||
}); | ||
}); | ||
@@ -266,2 +410,184 @@ | ||
}); | ||
it('should be able to go into recursion', function(){ | ||
var stop = Parser.start(); | ||
var parser = | ||
Parser.start().namely('self') | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: stop, | ||
1: 'self' | ||
} | ||
}); | ||
var buffer = new Buffer([ 1, 1, 1, 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
type: 1, | ||
data: { | ||
type: 1, | ||
data: { | ||
type: 1, | ||
data: { type: 0, data: {} } | ||
} | ||
} | ||
}); | ||
}); | ||
it('should be able to go into recursion with simple nesting', function(){ | ||
var stop = Parser.start(); | ||
var parser = | ||
Parser.start().namely('self') | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: stop, | ||
1: 'self', | ||
2: Parser.start().nest('left', { type: 'self' }) | ||
.nest('right', { type: stop }) | ||
} | ||
}); | ||
var buffer = new Buffer([ 2, | ||
/* left */ 1, 1, 0, | ||
/* right */ 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
type: 2, | ||
data: { | ||
left: { | ||
type: 1, | ||
data: { type: 1, data: { type: 0, data: {} } }, | ||
}, | ||
right: { } | ||
} | ||
}); | ||
}); | ||
it('should be able to refer to other parsers by name', function(){ | ||
var parser = Parser.start().namely('self'); | ||
var stop = Parser.start().namely('stop'); | ||
var twoCells = Parser.start().namely('twoCells') | ||
.nest('left', { type: 'self' }) | ||
.nest('right', { type: 'stop' }) | ||
parser | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: 'stop', | ||
1: 'self', | ||
2: 'twoCells' | ||
} | ||
}); | ||
var buffer = new Buffer([ 2, | ||
/* left */ 1, 1, 0, | ||
/* right */ 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
type: 2, | ||
data: { | ||
left: { | ||
type: 1, | ||
data: { type: 1, data: { type: 0, data: {} } }, | ||
}, | ||
right: { } | ||
} | ||
}); | ||
}); | ||
it('should be able to refer to other parsers both directly and by name', function(){ | ||
var parser = Parser.start().namely('self'); | ||
var stop = Parser.start(); | ||
var twoCells = Parser.start().nest('left', { type: 'self' }) | ||
.nest('right', { type: stop }) | ||
parser | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: stop, | ||
1: 'self', | ||
2: twoCells | ||
} | ||
}); | ||
var buffer = new Buffer([ 2, | ||
/* left */ 1, 1, 0, | ||
/* right */ 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
type: 2, | ||
data: { | ||
left: { | ||
type: 1, | ||
data: { type: 1, data: { type: 0, data: {} } }, | ||
}, | ||
right: { } | ||
} | ||
}); | ||
}); | ||
it('should be able to go into recursion with complex nesting', function(){ | ||
var stop = Parser.start(); | ||
var parser = | ||
Parser.start().namely('self') | ||
.uint8('type') | ||
.choice('data', { | ||
'tag': 'type', | ||
'choices': { | ||
0: stop, | ||
1: 'self', | ||
2: Parser.start().nest('left', { type: 'self' }) | ||
.nest('right', { type: 'self' }), | ||
3: Parser.start().nest('one', { type: 'self' }) | ||
.nest('two', { type: 'self' }) | ||
.nest('three', { type: 'self' }) | ||
} | ||
}); | ||
// 2 | ||
// / \ | ||
// 3 1 | ||
// / | \ \ | ||
// 1 0 2 0 | ||
// / / \ | ||
// 0 1 0 | ||
// / | ||
// 0 | ||
var buffer = new Buffer([ 2, | ||
/* left -> */ 3, | ||
/* one -> */ 1, /* -> */ 0, | ||
/* two -> */ 0, | ||
/* three -> */ 2, | ||
/* left -> */ 1, /* -> */ 0, | ||
/* right -> */ 0, | ||
/* right -> */ 1, /* -> */ 0 ]); | ||
assert.deepEqual(parser.parse(buffer), { | ||
type: 2, | ||
data: { | ||
left: { | ||
type: 3, | ||
data: { | ||
one: { type: 1, data: { type: 0, data: {} } }, | ||
two: { type: 0, data: {} }, | ||
three: { type: 2, | ||
data: { | ||
left: { type: 1, data: { type: 0, data: {} } }, | ||
right: { type: 0, data: {} } | ||
} }, | ||
} | ||
}, | ||
right: { | ||
type: 1, | ||
data: { type: 0, data: {} } | ||
} | ||
} | ||
}); | ||
}); | ||
}); | ||
@@ -268,0 +594,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133133
17
1869
399