Skip to content

Commit c981740

Browse files
authored
Merge pull request keichi#68 from keichi/omit-varname
Flattened data structure for "nest" types
2 parents d7078bb + e823090 commit c981740

File tree

4 files changed

+66
-26
lines changed

4 files changed

+66
-26
lines changed

README.md

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ console.log(ipHeader.parse(buf));
7676
Constructs a Parser object. Returned object represents a parser which parses
7777
nothing.
7878

79-
### parse(buffer[, callback])
79+
### parse(buffer)
8080
Parse a `Buffer` object `buffer` with this parser and return the resulting
8181
object. When `parse(buffer)` is called for the first time, parser code is
8282
compiled on-the-fly and internally cached.
@@ -85,7 +85,7 @@ compiled on-the-fly and internally cached.
8585
Set the constructor function that should be called to create the object
8686
returned from the `parse` method.
8787

88-
### [u]int{8, 16, 32}{le, be}(name [,options])
88+
### [u]int{8, 16, 32}{le, be}(name[, options])
8989
Parse bytes as an integer and store it in a variable named `name`. `name`
9090
should consist only of alphanumeric characters and start with an alphabet.
9191
Number of bits can be chosen from 8, 16 and 32. Byte-ordering can be either
@@ -102,12 +102,12 @@ var parser = new Parser()
102102
.int16be("c");
103103
```
104104

105-
### bit\[1-32\](name [,options])
105+
### bit\[1-32\](name[, options])
106106
Parse bytes as a bit field and store it in variable `name`. There are 32
107107
methods from `bit1` to `bit32` each corresponding to 1-bit-length to
108108
32-bits-length bit field.
109109

110-
### {float, double}{le, be}(name [,options])
110+
### {float, double}{le, be}(name[, options])
111111
Parse bytes as an floating-point value and store it in a variable named
112112
`name`. `name` should consist only of alphanumeric characters and start with
113113
an alphabet.
@@ -120,10 +120,10 @@ var parser = new Parser()
120120
.doublele("b");
121121
```
122122

123-
### string(name [,options])
123+
### string(name[, options])
124124
Parse bytes as a string. `name` should consist only of alpha numeric
125-
characters and start with an alphabet. `options` is an object; following
126-
options are available:
125+
characters and start with an alphabet. `options` is an object which can have
126+
the following keys:
127127

128128
- `encoding` - (Optional, defaults to `utf8`) Specify which encoding to use.
129129
`"utf8"`, `"ascii"`, `"hex"` and else are valid. See
@@ -139,10 +139,10 @@ options are available:
139139
- `stripNull` - (Optional, must be used with `length`) If true, then strip
140140
null characters from end of the string
141141

142-
### buffer(name [,options])
142+
### buffer(name[, options])
143143
Parse bytes as a buffer. `name` should consist only of alpha numeric
144-
characters and start with an alphabet. `options` is an object; following
145-
options are available:
144+
characters and start with an alphabet. `options` is an object which can have
145+
the following keys:
146146

147147
- `clone` - (Optional, defaults to `false`) By default,
148148
`buffer(name [,options])` returns a new buffer which references the same
@@ -156,9 +156,9 @@ options are available:
156156
- `readUntil` - (either `length` or `readUntil` is required) If `"eof"`, then
157157
this parser will read till it reaches end of the `Buffer` object.
158158

159-
### array(name [,options])
160-
Parse bytes as an array. `options` is an object; following options are
161-
available:
159+
### array(name, options)
160+
Parse bytes as an array. `options` is an object which can have the following
161+
keys:
162162

163163
- `type` - (Required) Type of the array element. Can be a string or an user
164164
defined Parser object. If it's a string, you have to choose from [u]int{8,
@@ -232,18 +232,18 @@ var parser = new Parser()
232232
});
233233
```
234234

235-
### choice(name [,options])
236-
Choose one parser from several choices according to a field value. Combining
237-
`choice` with `array` is useful for parsing a typical
238-
[Type-Length-Value](http://en.wikipedia.org/wiki/Type-length-value) styled
239-
format.
235+
### choice([name,] options)
236+
Choose one parser from multiple parsers according to a field value and store
237+
its parsed result to key `name`. If `name` is null or omitted, the result of
238+
the chosen parser is directly embedded into the current object. `options` is
239+
an object which can have the following keys:
240240

241241
- `tag` - (Required) The value used to determine which parser to use from the
242242
`choices` Can be a string pointing to another field or a function.
243243
- `choices` - (Required) An object which key is an integer and value is the
244244
parser which is executed when `tag` equals the key value.
245245
- `defaultChoice` - (Optional) In case of the tag value doesn't match any of
246-
`choices` use this parser.
246+
`choices`, this parser is used.
247247

248248
```javascript
249249
var parser1 = ...;
@@ -260,9 +260,13 @@ var parser = new Parser().uint8("tagValue").choice("data", {
260260
});
261261
```
262262

263-
### nest(name [,options])
264-
Nest a parser in this position. Parse result of the nested parser is stored in the variable
265-
`name`.
263+
Combining `choice` with `array` is an idiom to parse
264+
[TLV](http://en.wikipedia.org/wiki/Type-length-value)-based formats.
265+
266+
### nest([name,] options)
267+
Execute an inner parser and store its result to key `name`. If `name` is null
268+
or omitted, the result of the inner parser is directly embedded into the
269+
current object. `options` is an object which can have the following keys:
266270

267271
- `type` - (Required) A `Parser` object.
268272

example/tar.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ var tarHeader = new Parser()
2525
.skip(12);
2626

2727
var tarItem = new Parser()
28-
.nest("header", {
28+
.nest({
2929
type: tarHeader
3030
})
3131
.skip(function() {
32-
return Math.ceil(this.header.size / 512) * 512;
32+
return Math.ceil(this.size / 512) * 512;
3333
});
3434

3535
var tarArchive = new Parser().array("files", {

lib/binary_parser.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,14 @@ Parser.prototype.choice = function(varName, options) {
170170
options = varName;
171171
varName = null;
172172
}
173+
173174
if (!options.tag) {
174175
throw new Error("Tag option of array is not defined.");
175176
}
176177
if (!options.choices) {
177178
throw new Error("Choices option of array is not defined.");
178179
}
180+
179181
Object.keys(options.choices).forEach(function(key) {
180182
if (isNaN(parseInt(key, 10))) {
181183
throw new Error("Key of choices must be a number.");
@@ -203,13 +205,22 @@ Parser.prototype.choice = function(varName, options) {
203205
};
204206

205207
Parser.prototype.nest = function(varName, options) {
208+
if (arguments.length == 1 && typeof varName === "object") {
209+
options = varName;
210+
varName = null;
211+
}
212+
206213
if (!options.type) {
207214
throw new Error("Type option of nest is not defined.");
208215
}
209-
210216
if (!(options.type instanceof Parser) && !aliasRegistry[options.type]) {
211217
throw new Error("Type option of nest must be a Parser object.");
212218
}
219+
if (!(options.type instanceof Parser) && !varName) {
220+
throw new Error(
221+
"options.type must be a object if variable name is omitted."
222+
);
223+
}
213224

214225
return this.setNextParser("nest", varName, options);
215226
};
@@ -701,8 +712,11 @@ Parser.prototype.generateChoice = function(ctx) {
701712

702713
Parser.prototype.generateNest = function(ctx) {
703714
var nestVar = ctx.generateVariable(this.varName);
715+
704716
if (this.options.type instanceof Parser) {
705-
ctx.pushCode("{0} = {};", nestVar);
717+
if (this.varName) {
718+
ctx.pushCode("{0} = {};", nestVar);
719+
}
706720
ctx.pushPath(this.varName);
707721
this.options.type.generate(ctx);
708722
ctx.popPath(this.varName);

test/composite_parser.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,28 @@ describe("Composite parser", function() {
882882
name: "John Doe"
883883
});
884884
});
885+
886+
it("should 'flatten' output when using null varName", function() {
887+
var parser = new Parser()
888+
.string("s1", { zeroTerminated: true })
889+
.nest(null, {
890+
type: new Parser().string("s2", { zeroTerminated: true })
891+
});
892+
893+
var buf = Buffer.from("foo\0bar\0");
894+
895+
assert.deepEqual(parser.parse(buf), { s1: "foo", s2: "bar" });
896+
});
897+
898+
it("should 'flatten' output when omitting varName", function() {
899+
var parser = new Parser().string("s1", { zeroTerminated: true }).nest({
900+
type: new Parser().string("s2", { zeroTerminated: true })
901+
});
902+
903+
var buf = Buffer.from("foo\0bar\0");
904+
905+
assert.deepEqual(parser.parse(buf), { s1: "foo", s2: "bar" });
906+
});
885907
});
886908

887909
describe("Buffer parser", function() {

0 commit comments

Comments
 (0)