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
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 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
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)
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" }
}
GLYPH version (e.g., “v2.6”)
Schema hash for version validation
mode
'auto' | 'struct' | 'packed' | 'tabular' | 'patch'
Encoding mode hint
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 for type hints and validation (required for most operations)
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`);
}
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