The Gram lifecycle
Under the hood, Gram is not a single monolith. It is composed of a series of specific packages that process a recipe step-by-step.
Understanding this lifecycle is crucial if you want to integrate Gram deeply into your own applications or build new tools on top of it.
The pipeline
Section titled “The pipeline”The compilation of a Gram recipe follows a strict, one-way pipeline:
-
Parsing (
Section titled “Parsing (@gram-lang/parser)”@gram-lang/parser)The journey begins here. The Parser takes a raw string of text (your
.gramfile) and uses an OhmJS grammar to validate the syntax. If the syntax is valid, it generates an Abstract Syntax Tree (AST). This AST contains zero logic—it is merely a structured representation of the text. -
Module Resolution & Composition (
Section titled “Module Resolution & Composition (@gram-lang/modules)”@gram-lang/modules)If the recipe contains
@useimport directives,@gram-lang/modulestraverses the dependency graph (detecting cycles, measuring sub-recipe yields, and scoping intermediate variables) and splices the modules into a single, self-contained composed AST. A single-file recipe with no imports passes straight through. -
Compilation (
Section titled “Compilation (@gram-lang/kitchen)”@gram-lang/kitchen)The composed AST is handed off to the Kitchen. The Kitchen is responsible for the structural logic of the recipe:
- Scoping & Scheduling: Resolving intermediate variables and computing optimized execution timelines with ALAP (As Late As Possible) scheduling.
- Time Metrics: Computing
activeTime,cookTime,preparationTime, andtotalTime(preparationTime + cookTime) by mapping all timers. - Shopping List Generation: Aggregating ingredients, summing quantities, and resolving composite ingredients (e.g., combining zest and juice into whole lemons).
The output is a logically sound, compiled recipe — a
CompilationResult(orComposedCompilationResult) object. -
Enrichment (
Section titled “Enrichment (@gram-lang/analyzer)”@gram-lang/analyzer)The Analyzer takes the compiled recipe and cross-references it with your project’s
ingredients.yamldatabase. This is where the physical world meets the digital code:- Mass Standardization: Converts volumes (cups, tbsp) into accurate gram weights using specific ingredient densities.
- Yield Calculation: Calculates purchasing weight versus edible weight.
- Shopping List Aggregation: Resolves database aliases (e.g.
beurre/butter) and merges cross-unit quantities into a single gram total when possible, so Kitchen’s raw per-unit grouping becomes the final, deduplicated list. - Nutritional Estimation: Computes calories and macronutrients based on the standardized masses.
- Baker’s Percentages: Expresses every ingredient’s mass as a percentage of a reference ingredient (typically flour).
Each of these feature sets can be individually enabled or disabled by the host application.
-
Presentation (
Section titled “Presentation (@gram-lang/renderer or custom)”@gram-lang/rendereror custom)Finally, the fully enriched recipe is ready to be displayed. You can use the official
@gram-lang/rendererto generate Markdown, a standard web view, or a self-contained print-ready HTML document — or you can consume the JSON directly in your own React, Vue, or Mobile frontend.
Supporting infrastructure (@gram-lang/i18n & @gram-lang/format)
Section titled “Supporting infrastructure (@gram-lang/i18n & @gram-lang/format)”@gram-lang/i18n: Serves as the single source of truth for unit and time normalization dictionaries, unit conversion tables (UNIT_CONVERSIONS), duration multipliers (TIME_TO_MINUTES), and stable category keys (CATEGORY_KEYS).@gram-lang/format: Provides the canonical code formatting engine (13 deterministic rules) shared across the CLI and Language Server.
Package architecture
Section titled “Package architecture”This separation of concerns ensures that each package is highly focused and reusable. For example, if you are building an editor extension that only needs to highlight syntax, you only need to import @gram-lang/parser.
flowchart LR
subgraph Input ["Input"]
Raw["📄 Raw Text<br/><code>.gram file</code>"]
end
subgraph Pipeline ["Core Pipeline"]
direction LR
Parser["⚡ <b>@gram-lang/parser</b><br/>AST & Grammar"]
Modules["📦 <b>@gram-lang/modules</b><br/>Graph & Composition"]
Kitchen["⚙️ <b>@gram-lang/kitchen</b><br/>DAG & Scheduling"]
Analyzer["🔬 <b>@gram-lang/analyzer</b><br/>Physics & DB"]
Parser --> Modules --> Kitchen --> Analyzer
end
subgraph Output ["Presentation"]
Renderer["🎨 <b>@gram-lang/renderer</b><br/>HTML / UI / Markdown"]
end
subgraph Infra ["Infrastructure"]
Format["✍️ <b>@gram-lang/format</b><br/>Code Formatting"]
I18n["🌐 <b>@gram-lang/i18n</b><br/>Units & Dictionaries"]
end
Raw --> Parser
Analyzer --> Renderer
Format -. Formats .-> Raw
I18n -. Dictionaries .-> Kitchen
I18n -. Conversions .-> Analyzer