CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-parsimmon

A monadic LL(infinity) parser combinator library

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

binary-parsing.mddocs/

Binary Data Parsing

Specialized parsers for binary data formats, buffers, and bit-level operations. These parsers work with Node.js Buffer objects and provide precise control over binary data interpretation.

Capabilities

Basic Binary Parsers

Fundamental parsers for binary data structures.

/**
 * Parse a specific byte value
 * @param {number} b - Byte value to match (0-255)
 * @returns {Parser} Parser that matches the specific byte
 */
Parsimmon.Binary.byte(b);

/**
 * Parse a buffer of specified length
 * @param {number} length - Number of bytes to read
 * @returns {Parser} Parser that returns Buffer of specified length
 */
Parsimmon.Binary.buffer(length);

/**
 * Parse an encoded string of specified length
 * @param {string} encoding - Character encoding (utf8, ascii, etc.)
 * @param {number} length - Number of bytes to read
 * @returns {Parser} Parser that returns decoded string
 */
Parsimmon.Binary.encodedString(encoding, length);

Usage Examples:

// Parse specific bytes
const magicNumber = Parsimmon.Binary.byte(0x89)
  .then(Parsimmon.Binary.byte(0x50))
  .then(Parsimmon.Binary.byte(0x4E))
  .then(Parsimmon.Binary.byte(0x47));

// Parse PNG signature
const pngSig = Parsimmon.seq(
  Parsimmon.Binary.byte(0x89),
  Parsimmon.Binary.byte(0x50),
  Parsimmon.Binary.byte(0x4E),
  Parsimmon.Binary.byte(0x47),
  Parsimmon.Binary.byte(0x0D),
  Parsimmon.Binary.byte(0x0A),
  Parsimmon.Binary.byte(0x1A),
  Parsimmon.Binary.byte(0x0A)
);

// Parse raw buffer data
const header = Parsimmon.Binary.buffer(16);
const data = header.parse(someBuffer);

// Parse encoded strings
const utf8String = Parsimmon.Binary.encodedString("utf8", 10);
const asciiString = Parsimmon.Binary.encodedString("ascii", 20);

Integer Parsers

Parsers for various integer formats with different endianness.

/**
 * Parse unsigned big-endian integer
 * @param {number} length - Byte length (1-6)
 * @returns {Parser} Parser that returns unsigned integer
 */
Parsimmon.Binary.uintBE(length);

/**
 * Parse unsigned little-endian integer
 * @param {number} length - Byte length (1-6)
 * @returns {Parser} Parser that returns unsigned integer
 */
Parsimmon.Binary.uintLE(length);

/**
 * Parse signed big-endian integer
 * @param {number} length - Byte length (1-6)
 * @returns {Parser} Parser that returns signed integer
 */
Parsimmon.Binary.intBE(length);

/**
 * Parse signed little-endian integer
 * @param {number} length - Byte length (1-6)
 * @returns {Parser} Parser that returns signed integer
 */
Parsimmon.Binary.intLE(length);

Pre-defined Integer Parsers:

// Unsigned big-endian
Parsimmon.Binary.uint8BE;  // 1-byte unsigned
Parsimmon.Binary.uint16BE; // 2-byte unsigned
Parsimmon.Binary.uint32BE; // 4-byte unsigned

// Unsigned little-endian
Parsimmon.Binary.uint8LE;  // 1-byte unsigned
Parsimmon.Binary.uint16LE; // 2-byte unsigned
Parsimmon.Binary.uint32LE; // 4-byte unsigned

// Signed big-endian
Parsimmon.Binary.int8BE;   // 1-byte signed
Parsimmon.Binary.int16BE;  // 2-byte signed
Parsimmon.Binary.int32BE;  // 4-byte signed

// Signed little-endian
Parsimmon.Binary.int8LE;   // 1-byte signed
Parsimmon.Binary.int16LE;  // 2-byte signed
Parsimmon.Binary.int32LE;  // 4-byte signed

Usage Examples:

// Parse various integer sizes
const header = Parsimmon.seqObj(
  ["magic", Parsimmon.Binary.uint32BE],
  ["version", Parsimmon.Binary.uint16BE],
  ["flags", Parsimmon.Binary.uint8BE],
  ["count", Parsimmon.Binary.uint32LE]
);

// Parse file header with mixed endianness
const fileHeader = Parsimmon.seqMap(
  Parsimmon.Binary.uint32BE,  // File size (big-endian)
  Parsimmon.Binary.uint16LE,  // Format version (little-endian)
  Parsimmon.Binary.uint16BE,  // Flags (big-endian)
  (size, version, flags) => ({ size, version, flags })
);

// Parse signed values
const coordinate = Parsimmon.seqMap(
  Parsimmon.Binary.int16BE,
  Parsimmon.Binary.int16BE,
  (x, y) => ({ x, y })
);

Floating Point Parsers

Parsers for IEEE 754 floating point numbers.

/**
 * Parse 32-bit big-endian float
 * @returns {Parser} Parser that returns 32-bit float
 */
Parsimmon.Binary.floatBE();

/**
 * Parse 32-bit little-endian float
 * @returns {Parser} Parser that returns 32-bit float
 */
Parsimmon.Binary.floatLE();

/**
 * Parse 64-bit big-endian double
 * @returns {Parser} Parser that returns 64-bit double
 */
Parsimmon.Binary.doubleBE();

/**
 * Parse 64-bit little-endian double
 * @returns {Parser} Parser that returns 64-bit double
 */
Parsimmon.Binary.doubleLE();

Usage Examples:

// Parse 3D coordinates as floats
const vertex = Parsimmon.seqMap(
  Parsimmon.Binary.floatLE(),
  Parsimmon.Binary.floatLE(),
  Parsimmon.Binary.floatLE(),
  (x, y, z) => ({ x, y, z })
);

// Parse scientific data with doubles
const measurement = Parsimmon.seqObj(
  ["timestamp", Parsimmon.Binary.doubleBE()],
  ["value", Parsimmon.Binary.doubleBE()],
  ["error", Parsimmon.Binary.floatBE()]
);

// Parse mixed numeric data
const record = Parsimmon.seqMap(
  Parsimmon.Binary.uint32BE,    // ID
  Parsimmon.Binary.floatLE(),   // Temperature
  Parsimmon.Binary.floatLE(),   // Humidity
  Parsimmon.Binary.doubleBE(),  // Timestamp
  (id, temp, humidity, time) => ({
    id, temperature: temp, humidity, timestamp: time
  })
);

Bit-level Parsing

Advanced parsers for bit-level data manipulation.

/**
 * Parse sequence of bit fields
 * @param {number[]} alignments - Array of bit counts for each field
 * @returns {Parser} Parser that returns array of bit field values
 */
Parsimmon.Binary.bitSeq(alignments);

/**
 * Parse bit fields into named object
 * @param {Array} namedAlignments - Array of [name, bits] pairs or bit counts
 * @returns {Parser} Parser that returns object with named bit fields
 */
Parsimmon.Binary.bitSeqObj(namedAlignments);

Usage Examples:

// Parse IP header flags (3 bits total)
const ipFlags = Parsimmon.Binary.bitSeq([1, 1, 1]); // Reserved, DF, MF
ipFlags.parse(Buffer.from([0x40])); // [0, 1, 0] - Don't Fragment set

// Parse bit fields with names
const tcpFlags = Parsimmon.Binary.bitSeqObj([
  ["fin", 1],
  ["syn", 1], 
  ["rst", 1],
  ["psh", 1],
  ["ack", 1],
  ["urg", 1],
  ["ece", 1],
  ["cwr", 1]
]);

// Parse complex bit structures
const bitmapHeader = Parsimmon.Binary.bitSeqObj([
  ["signature", 16],
  ["fileSize", 32],
  4, // reserved
  4, // reserved
  ["dataOffset", 32]
]);

// Parse packed data structures
const packedCoord = Parsimmon.Binary.bitSeqObj([
  ["x", 10],    // X coordinate (10 bits)
  ["y", 10],    // Y coordinate (10 bits)
  ["z", 10],    // Z coordinate (10 bits)
  ["flags", 2]  // Status flags (2 bits)
]);

Binary Data Validation

Examples of validating binary data during parsing.

Usage Examples:

// Validate magic numbers
const validateMagic = (expected) => (value) => {
  if (value === expected) return Parsimmon.succeed(value);
  return Parsimmon.fail(`expected magic ${expected.toString(16)}, got ${value.toString(16)}`);
};

const jpegHeader = Parsimmon.Binary.uint16BE
  .chain(validateMagic(0xFFD8))
  .desc("JPEG magic number");

// Validate ranges
const validateRange = (min, max) => (value) => {
  if (value >= min && value <= max) return Parsimmon.succeed(value);
  return Parsimmon.fail(`value ${value} out of range [${min}, ${max}]`);
};

const rgbColor = Parsimmon.seqMap(
  Parsimmon.Binary.uint8BE.chain(validateRange(0, 255)),
  Parsimmon.Binary.uint8BE.chain(validateRange(0, 255)),
  Parsimmon.Binary.uint8BE.chain(validateRange(0, 255)),
  (r, g, b) => ({ r, g, b })
);

// Parse with length validation
const lengthPrefixedString = Parsimmon.Binary.uint16BE.chain(length => {
  if (length > 1024) return Parsimmon.fail("string too long");
  return Parsimmon.Binary.encodedString("utf8", length);
});

// Parse arrays with count
const parseArray = (elementParser) => {
  return Parsimmon.Binary.uint32BE.chain(count => {
    if (count > 10000) return Parsimmon.fail("array too large");
    return elementParser.times(count);
  });
};

const intArray = parseArray(Parsimmon.Binary.int32BE);

Complex Binary Structures

Examples of parsing complex binary file formats.

Usage Examples:

// Parse TGA image header
const tgaHeader = Parsimmon.seqObj(
  ["idLength", Parsimmon.Binary.uint8BE],
  ["colorMapType", Parsimmon.Binary.uint8BE], 
  ["imageType", Parsimmon.Binary.uint8BE],
  ["colorMapSpec", Parsimmon.seqObj(
    ["firstEntryIndex", Parsimmon.Binary.uint16LE],
    ["colorMapLength", Parsimmon.Binary.uint16LE],
    ["colorMapEntrySize", Parsimmon.Binary.uint8BE]
  )],
  ["imageSpec", Parsimmon.seqObj(
    ["xOrigin", Parsimmon.Binary.uint16LE],
    ["yOrigin", Parsimmon.Binary.uint16LE],
    ["width", Parsimmon.Binary.uint16LE],
    ["height", Parsimmon.Binary.uint16LE],
    ["pixelDepth", Parsimmon.Binary.uint8BE],
    ["imageDescriptor", Parsimmon.Binary.uint8BE]
  )]
);

// Parse WAV file header
const wavHeader = Parsimmon.seqObj(
  ["riff", Parsimmon.Binary.buffer(4)], // "RIFF"
  ["fileSize", Parsimmon.Binary.uint32LE],
  ["wave", Parsimmon.Binary.buffer(4)], // "WAVE"
  ["fmt", Parsimmon.Binary.buffer(4)],  // "fmt "
  ["fmtSize", Parsimmon.Binary.uint32LE],
  ["audioFormat", Parsimmon.Binary.uint16LE],
  ["numChannels", Parsimmon.Binary.uint16LE],
  ["sampleRate", Parsimmon.Binary.uint32LE],
  ["byteRate", Parsimmon.Binary.uint32LE],
  ["blockAlign", Parsimmon.Binary.uint16LE],
  ["bitsPerSample", Parsimmon.Binary.uint16LE]
);

// Parse network packet
const ethernetFrame = Parsimmon.seqObj(
  ["destination", Parsimmon.Binary.buffer(6)],
  ["source", Parsimmon.Binary.buffer(6)],
  ["etherType", Parsimmon.Binary.uint16BE],
  ["payload", Parsimmon.Binary.buffer(46)] // Minimum payload
);

Install with Tessl CLI

npx tessl i tessl/npm-parsimmon

docs

binary-parsing.md

combinators.md

core-parsers.md

index.md

language-creation.md

string-parsers.md

transformation.md

tile.json