Skip to content

Commit eaa210f

Browse files
authored
Merge pull request #166 from Rzial/master
Added parsing context variables
2 parents 3f236b6 + 6cb81ea commit eaa210f

File tree

3 files changed

+273
-28
lines changed

3 files changed

+273
-28
lines changed

README.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -429,10 +429,10 @@ parser.parse(buffer);
429429
```
430430

431431
### wrapped(name[, options])
432-
Read data then wrap it by transforming it by a function for further parsing.
433-
It works similarly to a buffer where it reads a block of data. But instead of returning the buffer it
432+
Read data then wrap it by transforming it by a function for further parsing.
433+
It works similarly to a buffer where it reads a block of data. But instead of returning the buffer it
434434
will pass it on to a parser for further processing.
435-
- `wrapper` - (Required) A function taking a buffer and returning a buffer (`(x: Buffer | Uint8Array ) => Buffer | Uint8Array`)
435+
- `wrapper` - (Required) A function taking a buffer and returning a buffer (`(x: Buffer | Uint8Array ) => Buffer | Uint8Array`)
436436
transforming the buffer into a buffer expected by `type`.
437437
- `type` - (Required) A `Parser` object to parse the result of wrapper.
438438
- `length ` - (either `length` or `readUntil` is required) Length of the
@@ -455,11 +455,11 @@ var textParser = Parser.start()
455455
var mainParser = Parser.start()
456456
// Read length of the data to wrap
457457
.uint32le('length')
458-
// Read wrapped data
458+
// Read wrapped data
459459
.wrapped('wrappedData', {
460460
// Indicate how much data to read, like buffer()
461461
length: 'length',
462-
// Define function to pre-process the data buffer
462+
// Define function to pre-process the data buffer
463463
wrapper: function (buffer) {
464464
// E.g. decompress data and return it for further parsing
465465
return zlib.inflateRawSync(buffer);
@@ -519,6 +519,42 @@ These options can be used in all parsers.
519519
});
520520
```
521521

522+
### Context variables
523+
You can use some special fields while parsing to traverse your structure. These
524+
context variables will be removed after the parsing process:
525+
- `$parent` - This field references the parent structure. This variable will be
526+
`null` while parsing the root structure.
527+
```js
528+
var parser = new Parser()
529+
.nest("header", {
530+
type: new Parser().uint32("length"),
531+
})
532+
.array("data", {
533+
type: "int32",
534+
length: function() {
535+
return this.$parent.header.length
536+
}
537+
})
538+
```
539+
- `$root` - This field references the root structure.
540+
```js
541+
var parser = new Parser()
542+
.nest("header", {
543+
type: new Parser().uint32("length"),
544+
})
545+
.nest("data", {
546+
type: new Parser()
547+
.uint32("value")
548+
.array("data", {
549+
type: "int32",
550+
length: function() {
551+
return this.$root.header.length
552+
}
553+
}),
554+
})
555+
556+
```
557+
522558
## Examples
523559
See `example/` for real-world examples.
524560

lib/binary_parser.ts

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -457,11 +457,11 @@ export class Parser {
457457
return this;
458458
}
459459

460-
skip(length: number, options?: ParserOptions) {
460+
skip(length: ParserOptions['length'], options?: ParserOptions) {
461461
return this.seek(length, options);
462462
}
463463

464-
seek(relOffset: number, options?: ParserOptions) {
464+
seek(relOffset: ParserOptions['length'], options?: ParserOptions) {
465465
if (options && options.assert) {
466466
throw new Error('assert option on seek is not allowed.');
467467
}
@@ -571,7 +571,7 @@ export class Parser {
571571
return this.setNextParser('choice', varName as string, options);
572572
}
573573

574-
nest(varName: string | ParserOptions, options: ParserOptions) {
574+
nest(varName: string | ParserOptions, options?: ParserOptions) {
575575
if (typeof options !== 'object' && typeof varName === 'object') {
576576
options = varName;
577577
varName = null;
@@ -670,34 +670,38 @@ export class Parser {
670670

671671
private addRawCode(ctx: Context) {
672672
ctx.pushCode('var offset = 0;');
673-
674-
if (this.constructorFn) {
675-
ctx.pushCode('var vars = new constructorFn();');
676-
} else {
677-
ctx.pushCode('var vars = {};');
678-
}
673+
ctx.pushCode(
674+
`var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};`
675+
);
676+
ctx.pushCode('vars.$parent = null;');
677+
ctx.pushCode('vars.$root = vars;');
679678

680679
this.generate(ctx);
681680

682681
this.resolveReferences(ctx);
683682

683+
ctx.pushCode('delete vars.$parent;');
684+
ctx.pushCode('delete vars.$root;');
684685
ctx.pushCode('return vars;');
685686
}
686687

687688
private addAliasedCode(ctx: Context) {
688-
ctx.pushCode(`function ${FUNCTION_PREFIX + this.alias}(offset) {`);
689-
690-
if (this.constructorFn) {
691-
ctx.pushCode('var vars = new constructorFn();');
692-
} else {
693-
ctx.pushCode('var vars = {};');
694-
}
689+
ctx.pushCode(
690+
`function ${FUNCTION_PREFIX + this.alias}(offset, parent, root) {`
691+
);
692+
ctx.pushCode(
693+
`var vars = ${this.constructorFn ? 'new constructorFn()' : '{}'};`
694+
);
695+
ctx.pushCode('vars.$parent = parent || null;');
696+
ctx.pushCode('vars.$root = root || vars;');
695697

696698
this.generate(ctx);
697699

698700
ctx.markResolved(this.alias);
699701
this.resolveReferences(ctx);
700702

703+
ctx.pushCode('delete vars.$parent;');
704+
ctx.pushCode('delete vars.$root;');
701705
ctx.pushCode('return { offset: offset, result: vars };');
702706
ctx.pushCode('}');
703707

@@ -1088,18 +1092,28 @@ export class Parser {
10881092
);
10891093
ctx.pushCode(`offset += ${PRIMITIVE_SIZES[type as PrimitiveTypes]};`);
10901094
} else {
1095+
const parentVar = ctx.generateVariable();
10911096
const tempVar = ctx.generateTmpVariable();
1092-
ctx.pushCode(`var ${tempVar} = ${FUNCTION_PREFIX + type}(offset);`);
1097+
ctx.pushCode(
1098+
`var ${tempVar} = ${
1099+
FUNCTION_PREFIX + type
1100+
}(offset, ${parentVar}, ${parentVar}.$root);`
1101+
);
10931102
ctx.pushCode(
10941103
`var ${item} = ${tempVar}.result; offset = ${tempVar}.offset;`
10951104
);
10961105
if (type !== this.alias) ctx.addReference(type);
10971106
}
10981107
} else if (type instanceof Parser) {
1108+
const parentVar = ctx.generateVariable();
10991109
ctx.pushCode(`var ${item} = {};`);
11001110

11011111
ctx.pushScope(item);
1112+
ctx.pushCode(`${item}.$parent = ${parentVar};`);
1113+
ctx.pushCode(`${item}.$root = ${parentVar}.$root;`);
11021114
type.generate(ctx);
1115+
ctx.pushCode(`delete ${item}.$parent`);
1116+
ctx.pushCode(`delete ${item}.$root`);
11031117
ctx.popScope();
11041118
}
11051119

@@ -1136,7 +1150,11 @@ export class Parser {
11361150
ctx.pushCode(`offset += ${PRIMITIVE_SIZES[type as PrimitiveTypes]}`);
11371151
} else {
11381152
const tempVar = ctx.generateTmpVariable();
1139-
ctx.pushCode(`var ${tempVar} = ${FUNCTION_PREFIX + type}(offset);`);
1153+
ctx.pushCode(
1154+
`var ${tempVar} = ${
1155+
FUNCTION_PREFIX + type
1156+
}(offset, ${varName}.$parent, ${varName}.$root);`
1157+
);
11401158
ctx.pushCode(
11411159
`${varName} = ${tempVar}.result; offset = ${tempVar}.offset;`
11421160
);
@@ -1151,8 +1169,14 @@ export class Parser {
11511169

11521170
private generateChoice(ctx: Context) {
11531171
const tag = ctx.generateOption(this.options.tag);
1172+
const nestVar = ctx.generateVariable(this.varName);
1173+
11541174
if (this.varName) {
1155-
ctx.pushCode(`${ctx.generateVariable(this.varName)} = {};`);
1175+
ctx.pushCode(`${nestVar} = {};`);
1176+
1177+
const parentVar = ctx.generateVariable();
1178+
ctx.pushCode(`${nestVar}.$parent = ${parentVar};`);
1179+
ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`);
11561180
}
11571181
ctx.pushCode(`switch(${tag}) {`);
11581182
Object.keys(this.options.choices).forEach((tag) => {
@@ -1169,22 +1193,37 @@ export class Parser {
11691193
ctx.generateError(`"Met undefined tag value " + ${tag} + " at choice"`);
11701194
}
11711195
ctx.pushCode('}');
1196+
1197+
if (this.varName) {
1198+
ctx.pushCode(`delete ${nestVar}.$parent`);
1199+
ctx.pushCode(`delete ${nestVar}.$root`);
1200+
}
11721201
}
11731202

11741203
private generateNest(ctx: Context) {
11751204
const nestVar = ctx.generateVariable(this.varName);
11761205

11771206
if (this.options.type instanceof Parser) {
11781207
if (this.varName) {
1208+
const parentVar = ctx.generateVariable();
11791209
ctx.pushCode(`${nestVar} = {};`);
1210+
ctx.pushCode(`${nestVar}.$parent = ${parentVar};`);
1211+
ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`);
11801212
}
11811213
ctx.pushPath(this.varName);
11821214
this.options.type.generate(ctx);
11831215
ctx.popPath(this.varName);
1216+
if (this.varName) {
1217+
ctx.pushCode(`delete ${nestVar}.$parent`);
1218+
ctx.pushCode(`delete ${nestVar}.$root`);
1219+
}
11841220
} else if (aliasRegistry[this.options.type]) {
1221+
const parentVar = ctx.generateVariable();
11851222
const tempVar = ctx.generateTmpVariable();
11861223
ctx.pushCode(
1187-
`var ${tempVar} = ${FUNCTION_PREFIX + this.options.type}(offset);`
1224+
`var ${tempVar} = ${
1225+
FUNCTION_PREFIX + this.options.type
1226+
}(offset, ${parentVar}, ${parentVar}.$root);`
11881227
);
11891228
ctx.pushCode(
11901229
`${nestVar} = ${tempVar}.result; offset = ${tempVar}.offset;`
@@ -1285,14 +1324,22 @@ export class Parser {
12851324
ctx.pushCode(`offset = ${offset};`);
12861325

12871326
if (this.options.type instanceof Parser) {
1327+
const parentVar = ctx.generateVariable();
12881328
ctx.pushCode(`${nestVar} = {};`);
1329+
ctx.pushCode(`${nestVar}.$parent = ${parentVar};`);
1330+
ctx.pushCode(`${nestVar}.$root = ${parentVar}.$root;`);
12891331
ctx.pushPath(this.varName);
12901332
this.options.type.generate(ctx);
12911333
ctx.popPath(this.varName);
1334+
ctx.pushCode(`delete ${nestVar}.$parent`);
1335+
ctx.pushCode(`delete ${nestVar}.$root`);
12921336
} else if (aliasRegistry[this.options.type]) {
1337+
const parentVar = ctx.generateVariable();
12931338
const tempVar = ctx.generateTmpVariable();
12941339
ctx.pushCode(
1295-
`var ${tempVar} = ${FUNCTION_PREFIX + this.options.type}(offset);`
1340+
`var ${tempVar} = ${
1341+
FUNCTION_PREFIX + this.options.type
1342+
}(offset, ${parentVar}, ${parentVar}.$root);`
12961343
);
12971344
ctx.pushCode(
12981345
`${nestVar} = ${tempVar}.result; offset = ${tempVar}.offset;`

0 commit comments

Comments
 (0)