liveBook · Manning

Identifier

As explained in Building User-Friendly DSLs

An identifier is a value that uniquely distinguishes an AST object so it can be indexed and referenced during persistence operations.

An identifier is a value that uniquely distinguishes an AST object so it can be indexed and referenced during persistence operations. Store it directly on each object under the id key, alongside its concept and settings. section 7.2

IDs solve the reference problem in JSON serialization. Replace a JavaScript object reference such as { ref: targetObject } with { refId: targetObject.id }, preventing reference cycles from breaking JSON.stringify. section 7.2 section 7.6

A practical identifier workflow:

  • Generate an ID when creating each AST object, rather than assigning it only during serialization. section 7.2
  • Use a string containing random characters; uniqueness matters more than the particular value type. section 7.2
  • Index objects by ID during deserialization so refId values can resolve back to reference objects. section 7.6
  • Keep generated identifiers separate from JavaScript identifiers derived from business names such as “rental period” and rentalPeriod. section 8.4.1

Rental AST

Creating a Rental record type gives it an ID, while nested Data Attribute and Number objects receive their own IDs. A reference from rentalPriceAfterDiscount to rentalPriceBeforeDiscount can therefore serialize as a refId and later resolve to its target. section 7.2 section 7.6

{ 
    id: "<unique-ID>",       #1
    concept: "<concept>", 
    settings: { 
        // ... 
    } 
}

Inspect the compact AST shape showing the id field attached directly to each object. section 7.2

Listing 7.11 Code for a newId function that generates a random ID
const { nanoid } = require("nanoid")
const newId = () => nanoid(10)   #1

Inspect the newId function, which generates ten-character random identifiers with nanoid. section 7.2

Listing 7.15 Transforming an AST into a cycle-free “almost-AST”
const serialize = (value) => {
    if (isAstObject(value)) {
        const serializedAstObject = {
            id: value.id,
            concept: value.concept,
            settings: {}
        }
        for (const propertyName in value.settings) {
            serializedAstObject.settings[propertyName] =
                 serialize(value.settings[propertyName])
        }
        return serializedAstObject
    }
    if (isAstReferenceObject(value)) {
        return ({  #1
            refId: isAstObject(value.ref)    #2
                     ? value.ref.id
                     : undefined
        })
    }
    if (Array.isArray(value)) {
        return value.map(serialize)
    }
    return value
}
module.exports.serialize = serialize

Inspect the serializer transforming object references into serializable refId values. section 7.2

How Building User-Friendly DSLs covers it

How deep it goes
Explains representation, generation, reference substitution, and ID-indexed resolution, while deferring collision-proof production identifiers.
Examples
Uses the Rental AST, nanoid-generated values, nested Data Attribute objects, and refId-based references.
What it assumes
Assumes JavaScript object references and JSON serialization, while establishing AST identity and reference handling explicitly.
What you can do after
Build it from scratch by assigning IDs, serializing references as refId values, and resolving them during deserialization.
How it connects
Connects AST objects with reference relations, serialization, deserialization, JSON, and JavaScript identifiers.

Also explained in

Compare how 5 books explain identifier →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Building User-Friendly DSLs. The section links point to the parts of the book it draws on.