Skip to main content

Parsing

The GLYPH JavaScript SDK provides parsers to convert GLYPH format strings back to GValue objects.

Packed Parser

Parse packed format (positional encoding) back to GValue:
import { parsePacked, Schema } from 'glyph-js';

const schema = /* your schema */;

// Parse dense format
const value1 = parsePacked('Team@(^t:ARS Arsenal EPL)', schema);

// Parse with bitmap (sparse optionals)
const value2 = parsePacked(
  'Match@{bm=0b101}(^m:123 2025-12-19T20:00:00Z 2 1)',
  schema
);

// Access parsed values
console.log(value1.get('name')?.asStr()); // "Arsenal"
console.log(value2.get('homeScore')?.asInt()); // 2

Packed Format Syntax

The packed format uses positional encoding:
TypeName@(value1 value2 value3)
With optional bitmap for sparse fields:
TypeName@{bm=0b101}(required1 required2 optional1 optional3)
Bitmap encoding:
  • 0b prefix indicates binary
  • Bits are ordered LSB-first (right to left)
  • Each bit corresponds to an optional field
  • 1 = field is present, 0 = field is null

Nested Structs

Packed format supports nested structures:
const nested = parsePacked(
  'Match@(^m:123 Team@(^t:ARS Arsenal EPL) Team@(^t:LIV Liverpool EPL) 2 1)',
  schema
);

const homeTeam = nested.get('home')?.asStruct();
const homeName = homeTeam?.fields.find(f => f.key === 'name')?.value.asStr();

Tabular Parser

Parse tabular format (optimized for lists) back to GValue:
import { parseTabular, TabularParseResult } from 'glyph-js';

const input = `
@tab Team [t n l]
^t:ARS Arsenal EPL
^t:LIV Liverpool EPL
^t:MCI "Man City" EPL
@end
`;

const result: TabularParseResult = parseTabular(input, schema);

console.log(result.typeName); // "Team"
console.log(result.columns);  // ["t", "n", "l"]
console.log(result.rows.length); // 3

// Access row data
for (const row of result.rows) {
  const name = row.get('name')?.asStr();
  const league = row.get('league')?.asStr();
  console.log(`${name} - ${league}`);
}

Tabular Format Syntax

Tabular format is optimized for lists of structs:
@tab TypeName [col1 col2 col3]
value1 value2 value3
value4 value5 value6
@end
Column identifiers:
  • Field name: name
  • Wire key: n (if defined in schema)
  • Field ID: #2

Cell Formats

Tabular cells support all GLYPH value types:
const input = `
@tab Event [id time score active]
^e:1 2025-12-19T20:00:00Z 2 t
^e:2 2025-12-20T15:00:00Z ∅ f
^e:3 2025-12-21T18:30:00Z 1 t
@end
`;

const result = parseTabular(input, schema);
Supported formats:
  • Null: , null, nil, none
  • Boolean: t/true, f/false
  • Integer: 42, -17
  • Float: 3.14, 1.5e-3
  • String: bare tokens or "quoted strings"
  • Ref: ^prefix:value
  • Time: ISO 8601 format
  • List: [item1 item2 item3]
  • Map: {key1=val1 key2=val2}
  • Nested packed: Team@(^t:ARS Arsenal EPL)

Escaped Strings

Quoted strings support standard escape sequences:
const input = `
@tab Message [id text]
^m:1 "Hello\nWorld"
^m:2 "Quote: \"test\""
^m:3 "Tab:\tseparated"
@end
`;

const result = parseTabular(input, schema);
const text = result.rows[0].get('text')?.asStr();
// "Hello\nWorld" (actual newline)

Header Parser

Parse GLYPH metadata headers:
import { parseHeader, Header } from 'glyph-js';

// Parse header line
const header1 = parseHeader('@lyph v2.6 @schema#a3f5b2c1 @mode=packed');

if (header1) {
  console.log(header1.version);   // "v2.6"
  console.log(header1.schemaId);  // "a3f5b2c1"
  console.log(header1.mode);      // "packed"
}

// Parse with additional metadata
const header2 = parseHeader(
  '@glyph v2.6 @schema#abc123 @mode=tabular @keys=wire @target=^doc:main'
);

if (header2) {
  console.log(header2.keyMode);   // "wire"
  console.log(header2.target);    // { prefix: "doc", value: "main" }
}

Header Fields

version
string
required
GLYPH version (e.g., “v2.6”)
schemaId
string
Schema hash for version validation
mode
'auto' | 'struct' | 'packed' | 'tabular' | 'patch'
Encoding mode hint
keyMode
'wire' | 'name' | 'fid'
Field key encoding mode
target
RefID
Target document reference (for patches)

Parse Options

Configure parser behavior:
import { ParseOptions } from 'glyph-js';

const options: ParseOptions = {
  schema: schema,      // Schema for type validation
  tolerant: false      // Strict parsing (throw on errors)
};

const value = parsePacked(input, schema);
schema
Schema
Schema for type hints and validation (required for most operations)
tolerant
boolean
default:"false"
Enable tolerant mode (continue parsing on recoverable errors)

Error Handling

Parsers throw descriptive errors on invalid input:
try {
  const value = parsePacked('InvalidFormat', schema);
} catch (error) {
  console.error(error.message);
  // "unknown type: InvalidFormat" (at pos 13)
}

try {
  const result = parseTabular(badInput, schema);
} catch (error) {
  console.error(error.message);
  // "row has 2 values, expected 3"
}
Common parse errors:
  • Unknown type name
  • Type mismatch
  • Invalid syntax
  • Unexpected end of input
  • Row length mismatch (tabular)
  • Unknown column name (tabular)
  • Unterminated string
  • Invalid number format

Value Types

Scalar Parsing

The parser automatically detects value types:
// Null


// Boolean
t, f, true, false

// Integer
0, 42, -17

// Float
3.14, -0.5, 1.5e-3, 2e10

// String (bare)
Hello, simple_token, path/to/file

// String (quoted)
"Hello World", "Quote: \"test\""

// Ref
^user:123, ^t:ARS, ^"complex:ref:with:colons"

// Time
2025-12-19T20:00:00Z, 2025-01-15T14:30:00.123Z

Container Parsing

Parse nested containers:
// List
[1 2 3]
[^t:ARS ^t:LIV ^t:MCI]
["Alice" "Bob" "Charlie"]

// Map
{name=Arsenal league=EPL}
{id=^t:ARS founded=1886}

// Struct (v1 format)
Team{id=^t:ARS name=Arsenal league=EPL}

// Packed struct
Team@(^t:ARS Arsenal EPL)

// Sum
Result:Ok("success")
Option:Some(42)

Complete Example

Parse a complete GLYPH document with header:
import { 
  parseHeader, 
  parseTabular, 
  Schema,
  SchemaBuilder,
  t 
} from 'glyph-js';

// Define schema
const schema = new SchemaBuilder()
  .addPackedStruct('Player', 'v1')
    .field('id', t.id(), { fid: 1, wireKey: 'i' })
    .field('name', t.str(), { fid: 2, wireKey: 'n' })
    .field('team', t.str(), { fid: 3, wireKey: 't' })
    .field('goals', t.int(), { fid: 4, wireKey: 'g' })
  .build();

// Parse document
const document = `
@lyph v2.6 @schema#${schema.hash} @mode=tabular

@tab Player [i n t g]
^p:1 "Bukayo Saka" Arsenal 14
^p:2 "Mohamed Salah" Liverpool 18
^p:3 "Erling Haaland" "Man City" 27
@end
`;

const lines = document.trim().split('\n');

// Parse header
const header = parseHeader(lines[0]);
if (header?.schemaId !== schema.hash) {
  throw new Error('Schema mismatch');
}

// Parse tabular data
const tableStart = lines.findIndex(l => l.startsWith('@tab'));
const tableLines = lines.slice(tableStart).join('\n');
const result = parseTabular(tableLines, schema);

console.log(`Parsed ${result.rows.length} players`);

for (const player of result.rows) {
  const name = player.get('name')?.asStr();
  const team = player.get('team')?.asStr();
  const goals = player.get('goals')?.asInt();
  console.log(`${name} (${team}): ${goals} goals`);
}

Performance

The parser is optimized for streaming and handles large datasets efficiently:
  • Single-pass parsing: No backtracking
  • Zero-copy strings: Direct string slices where possible
  • Lazy evaluation: Values parsed on demand
  • Incremental tabular: Process rows as they arrive
// Parse large tabular dataset
const largeInput = generateLargeTabular(10000); // 10k rows
const start = performance.now();
const result = parseTabular(largeInput, schema);
const elapsed = performance.now() - start;

console.log(`Parsed ${result.rows.length} rows in ${elapsed.toFixed(2)}ms`);
// Typical: ~5-10ms for 10k rows

Next Steps

Core Types

Learn about GValue and Schema

Streaming

Incremental validation for real-time parsing

Build docs developers (and LLMs) love