loft grammar cheat-sheet (v0.4)

One statement per line. # starts a comment. Blank lines are ignored.

Statement order across the file never matters — references resolve across the whole file. Within a line, slot order is fixed: category → type → [id] → placement (see Line anatomy).

A model may span multiple .loft files: every file in a project directory compiles as ONE model with ONE namespace — reference names across files bare, with no import lines, and file layout never changes meaning (folders navigate, never scope).

Start here — build in this order

Everything below this section is reference. This section is method. Most broken models are correctly-spelled statements written in the wrong order and measured from the wrong thing.

# 1. datums first — the framework everything else is measured from
level L1 elev 0' height 10'-0"
grid A vertical   at 0'
grid B vertical   at A + 24'-0"         # the bay width, not grid B's coordinate
grid C vertical   at B + 30'-0"
grid 1 horizontal at 0'
grid 2 horizontal at 1 + 20'-0"

# 2. then hosts — walls today (floors and roofs later); frame first, then the line
# traveling (A,1) -> (C,1), heading east; right = south
wall EXT-1 W1 from (A, 1) to (C, 1) justify left

# 3. then what they host — a typed product, placed along the host off a datum
door 3070 D1 in W1 at 4' past B      # the 3070 doortype states the size once, below

Line anatomy — how a declaration reads

Every declaration is category → type → [id] → placement, with a slot dropped where the category doesn't carry it. One of the two leading name slots is always required, so the first name after the keyword is never ambiguous.

# element: the type leads and is REQUIRED; the id follows, optional
wall EXT-1 W1 from (A, 1) to (C, 1) justify left
wall STUD4 from 1 to 2 on A         # anonymous — id omitted, type still leads

# datum: no type — the REQUIRED id leads (a datum exists to be referenced)
level L1 elev 0' height 10'-0"
grid A vertical   at 0'

# door/window: the type leads here too (and is required); the id follows, optional
door 3070 D1 in W1 at 4' past B      # the 3070 doortype states the size once, below

# opening: the type-less cutter — a hole has no product identity, so its
# dimensions live on the line; id optional, like walls
opening O1 in W1 at 14' width 3' height 7' sill 4'

Lengths

Always carry units. Bare numbers are never lengths.

12'   8"   12'-6"   12'-6 1/2"   27.5'   4.5"   5/8"   3-5/8"   12ft   8in

Coordinates

(x, y), plan view: positive X is right (east), positive Y is up the page (north).

Each slot is a length literal or a position expression over datums:

(12', 28')     (A, 1)     (A + 14', 1)     (B - 4', 2)     (A + (B - A)/2, 1)

Expressions

Anywhere a position or distance goes, a static expression works: + - * /, parentheses, datum references.

grid A.5 vertical at A + (B - A)/2       # midpoint between grids A and B
grid D   vertical at C + 12'             # chained off C
door 3070 D4 in W1 at (2 - 1)/2          # centered: half the 1-to-2 span

Project & units

project "Sample Project"      # optional
units imperial                # optional; imperial is the v0 default

Level

Exactly one level in v0:

level L1 elev 0' height 10'-0"

Grids

grid A vertical   at 0'
grid 3 horizontal at 28'-0"
grid B.5 vertical at B + 12'-6"

Wall types

walltype STUD4 width 4.5" note "3-5/8 stud + gyp ea side"
walltype EXT-1 layers [brick 4", air 1", core: stud 6", gyp 5/8"]

Door & window types

The category requires; the type binds. Every door/window type must satisfy width and height — by fixing a value (stated once, every placement follows) or delegating it (the word instance: each placement supplies it).

doortype 3070 width 3'-0" height 7'-0"                # fixed: a product fact, stated once
doortype STOREFRONT width instance height 8'-0"       # width delegated to each placement
windowtype W-5040 width 5'-0" height 4'-0"
doortype HM-A width 3'-0" height 7'-0" u-fire-rating 90 u-hardware instance text required

Walls

A wall's line is stated one of two ways — by its endpoints, or by a carrier it rides plus two extents that cut it. Both are first-class; pick whichever states the facts you already know.

Carrier + extents is not a partition trick — an exterior wall running along a grid is the case it fits best. It is how a line-based element is placed, and walls are simply the only one so far.

# by endpoints
wall EXT-1 W1 from (A, 1) to (C, 1) justify left

# by carrier + extents — the carrier fixes the line, two transverse datums cut it
wall EXT-1 W2 from 1 to 2 on A justify left      # ON grid A — no offset, no side
wall STUD4 W7 from 1 to 2 at 4' right of W2      # held 4' off wall W2
wall STUD4 from 1 to W5.end at 9' left of B      # grid carrier; a wall's line datum cuts too

Element datums (anchors & accessors)

Every element projects datums you can reference — <id>.<datum>, kind-qualified when ambiguous (wall.W1.end).

wall EXT-1 W4 from W1.end to (C, 2)    # chain: W4 follows W1
wall STUD4 W5 from (A + 12', 1) to (A + 12', 2) top W1.top
door 3070 D5 in W1 at 6' past D1.jamb   # measured from D1's jamb, not its center

Doors

door 3070 D1 in W1 at 4' past B      # the 3070 doortype states the size once, below
door 3070 D2 in W5 at 2' past 1 swing left
door 3070 D3 in W5 at 3' short of 2
door STOREFRONT D6 in W1 at 20' width 6'-8"   # STOREFRONT delegates width; the placement supplies it

Windows

window W-5040 WIN1 in W2 at 6'-0" sill 3'-0"
window W-5040 WIN2 in W2 at 4' past WIN1   # 4' clear of WIN1's jamb

Openings

opening O1 in W1 at 14' width 3' height 7' sill 4'

Rooms

room R1 label "OFFICE 210" at (7', 14')   # point label; boundaries are future

Reserved keywords

Every major building category is reserved for future versions — using one is an error (not silently ignored). Highlights:

component  import  export  const  view  refplane  section  sheet  schedule
floor  roof  ceiling  stair  ramp  railing  column  beam  shaft
curtainwall  duct  pipe  conduit  furniture  site  topo  zone  space
floortype  rooftype  materialtype  hatchtype  layertype  …