TCSL — TextToCAD Scripting Language

Language Specification v1.5

Date2026-03-18
Predecessorv1.4 (2026-03-17)
Extension.tcsl
MIMEapplication/vnd.texttocad.tcsl
LicenseCC BY-SA 4.0
EncodingUTF-8

1.Introduction#

TCSL (TextToCAD Scripting Language) is a declarative, statically-typed domain-specific language for describing parametric three-dimensional physical objects. It is designed primarily for modeling case furniture (wardrobes, dressers, cabinets, shelving units) and metal structures (table frames, tube shelving, railings) with exact physical dimensions and units of measurement.

The language serves simultaneously as an executable program, parametric documentation, and a live product description. Every numeric literal in a dimensional context must carry an explicit unit of measurement — a bare number such as 800 without a unit suffix is a syntax error. This design ensures that TCSL code is unambiguous and directly maps to physical fabrication parameters.

TCSL programs are consumed by the TCSL Viewer (a browser-based application combining a text editor, 3D visualizer, cost calculator, and commercial quote generator) and can be transpiled to Python/FreeCAD for manufacturing-grade CAD output. The language is intentionally constrained: there are no loops, conditionals, user-defined functions, or module imports. Complexity is managed through decomposition into multiple files and through the pipeline operator |>, which chains geometric transformations in a single expression.

This specification defines version 1.5 of the language. It is the normative reference for parser implementers, code generators (including LLMs), and tooling developers. For a visual product tour and LLM generation manual, see the companion TCSL Viewer Guide v5.3.

2.Design Priorities#

The language design is governed by six priorities, listed in descending order of importance.

Priority 1 — Physical correctness. Every dimension, position, and angle in the output model must correspond to a physically realizable measurement. The type system enforces dimensional consistency: adding a length to an angle is a compile-time error (§14, TCSL_R001). Units are never inferred — they are always explicit.

Priority 2 — LLM generability. The syntax is designed to be reliably produced by large language models from natural-language descriptions. The fixed four-zone structure (INPUT → LET → GEOMETRY → EXPORT) eliminates ordering ambiguity. Named function arguments, mandatory units, and a small keyword set reduce the space of syntactically valid programs, making few-shot learning effective.

Priority 3 — Human readability. A TCSL file reads as a bill of materials with spatial relationships. Descriptive variable names, inline comments, and the absence of control flow make programs scannable by non-programmers (e.g., furniture makers reviewing a quote).

Priority 4 — Parametric flexibility. The input/let separation allows end users to adjust base dimensions via sliders in the Viewer while derived dimensions recompute automatically. This makes a single TCSL file cover a family of product variants.

Priority 5 — Tooling simplicity. The parser is a single-pass, line-oriented state machine with no backtracking. The entire pipeline from source text to Three.js scene graph is implemented in under 2,000 lines of JavaScript.

Priority 6 — Transpilability. Every valid TCSL program can be mechanically translated to Python/FreeCAD code. The Viewer performs this transpilation client-side, enabling manufacturing-grade output without a server.

3.Conformance#

A conforming TCSL v1.5 implementation must accept all programs that satisfy the grammar and semantic rules defined in this specification, must reject all programs that violate them with an appropriate error code from §14, and must produce geometry that is equivalent (within floating-point tolerance of ±0.001 mm) to the reference semantics described herein. Implementations may extend the language with additional functions or units, provided that all programs valid under this specification retain their defined semantics. Such extensions must be clearly documented and must not alter the meaning of any construct defined here.

4.Notation#

This specification uses Extended Backus-Naur Form (EBNF) as defined in ISO/IEC 14977 to describe the TCSL grammar. In EBNF rules, non-terminal symbols are written in PascalCase, terminal symbols (literal strings) are enclosed in double quotes, optional elements are enclosed in square brackets [ ], repetition (zero or more) is indicated by curly braces { }, alternation by the vertical bar |, and grouping by parentheses ( ). Special sequences delimited by ? ? describe character sets or Unicode properties not easily expressed as literals.

Code examples shown in green-bordered blocks (✓ VALID) are normative — they must be accepted by a conforming parser. Code examples in red-bordered blocks (✗ INVALID) are normative counter-examples — a conforming parser must reject them with the indicated error code.

5.Lexical Structure#

A TCSL source file is a sequence of Unicode characters encoded in UTF-8. The lexer converts this character stream into a sequence of tokens, discarding whitespace and comments. The lexical grammar is specified below.

5.1.Source Encoding#

The source file must be valid UTF-8. A byte order mark (BOM, U+FEFF) at the start of the file is permitted and ignored. Identifiers may contain ASCII letters, digits, and underscores only (see §5.5). Comments and string literals may contain arbitrary Unicode characters.

5.2.Whitespace and Line Termination#

Whitespace characters are: space (U+0020), horizontal tab (U+0009), and carriage return (U+000D). A line terminator is a line feed (U+000A) or the sequence CR LF (U+000D U+000A). TCSL is a line-oriented language: each statement occupies exactly one logical line. There is no line-continuation mechanism — the entire pipeline chain from assignment through all |> operators must appear on a single line.

EBNF
Whitespace     = ?U+0020? | ?U+0009? | ?U+000D? ;
LineTerminator = ?U+000A? | ?U+000D? , "?U+000A?" ;

5.3.Comments#

TCSL supports single-line comments beginning with the # character. Everything from # to the end of the line (exclusive of the line terminator) is treated as a comment and discarded during lexing. There are no multi-line or block comment forms. Comments may appear on their own line or at the end of a statement line. The ## sequence at the start of a line is treated identically to # — double-hash headings are purely cosmetic and carry no semantic meaning.

EBNF
Comment = "#" , { ?any character except LineTerminator? } ;

5.4.Tokens#

After stripping whitespace and comments, the input is partitioned into the following token categories: keywords, identifiers, numeric literals, string literals, boolean literals, unit suffixes, operators, and punctuation. The token categories are described in the subsections that follow.

5.5.Identifiers#

An identifier begins with an ASCII letter (a–z, A–Z) or underscore, followed by zero or more ASCII letters, digits (0–9), or underscores. Identifiers are case-sensitive. The dollar sign $ is not permitted in identifiers — it is reserved as the tag separator (see §10). Identifiers must not collide with keywords (see §5.6).

EBNF
Identifier    = IdentStart , { IdentContinue } ;
IdentStart    = ?ASCII letter? | "_" ;
IdentContinue = IdentStart | ?ASCII digit? ;

5.6.Keywords#

The following identifiers are reserved as keywords and may not be used as variable names, parameter names, or as-labels.

KeywordZonePurpose
inputINPUTDeclares an input parameter with a default value
letLETDeclares a computed variable
exportEXPORTMarks a geometry variable for output
asEXPORTAssigns a human-readable label for the calculator
trueAnyBoolean literal
falseAnyBoolean literal

5.7.Numeric Literals#

A numeric literal is a sequence of one or more digits, optionally containing a single decimal point. Negative numbers are expressed using the unary minus operator, not as negative literals. Scientific notation is not supported. A numeric literal appearing in a dimensional context (constructor argument, transform argument, input declaration of a length or angle) must be immediately followed by a unit suffix (see §5.10). A bare numeric literal without a unit in a dimensional context triggers error TCSL_E001.

EBNF
NumericLiteral = Digits , [ "." , Digits ] ;
Digits         = ?ASCII digit? , { ?ASCII digit? } ;

5.8.String Literals#

String literals are enclosed in double quotes. They support no escape sequences. Strings are used exclusively in input declarations for metadata purposes (e.g., product names). They cannot appear in arithmetic expressions or as function arguments.

5.9.Boolean Literals#

The tokens true and false represent boolean values. Booleans are used in input declarations to control conditional geometry generation in future versions. In v1.5, they are accepted by the parser but have no effect on geometry output.

5.10.Units of Measurement#

A unit suffix is a keyword-like token that appears after a numeric literal (separated by optional whitespace). The following units are recognized. The canonical unit for lengths is millimeters and for angles is degrees — all other units are converted to canonical form during parsing.

UnitDimensionCanonicalConversion Factor
mmLength1.0
cmLengthmm10.0
mLengthmm1000.0
inLengthmm25.4
ftLengthmm304.8
degAngle1.0
radAngledeg57.2957795…
IMPORTANT

Unitless integers are permitted only in two contexts: the count parameter of pattern functions (linear_pattern, circular_pattern), and the input declaration of a counter variable. In all other contexts, a numeric literal without a unit suffix is an error.

5.11.Operators and Punctuation#

SymbolNamePrecedenceAssociativity
|>Pipeline1 (lowest)Left-to-right
+Addition4Left-to-right
Subtraction / unary minus4 / 6Left-to-right / prefix
*Multiplication5Left-to-right
/Division5Left-to-right
=Assignment0Right
( )Grouping / function call
,Argument separator
$Tag separator

6.Type System#

TCSL employs a simple static type system based on dimensional analysis. There are no user-defined types. The type of every expression is inferred at parse time and checked for dimensional consistency.

6.1.Primitive Types#

TypeDescriptionExample
LengthA linear measurement in millimeters (canonical)732 mm
AngleA rotational measurement in degrees (canonical)45 deg
ScalarA dimensionless number (used for counts, ratios, scale factors)4
BooleanA truth valuetrue
StringA double-quoted text value“Oak Natural”
GeometryA 3D solid body (returned by constructors and transforms)box(…)

6.2.Dimensional Analysis#

The type checker propagates dimensions through arithmetic expressions. Addition and subtraction require both operands to have the same dimension — adding a Length to an Angle is error TCSL_R001. Multiplication and division between a Length (or Angle) and a Scalar produce a value with the same dimension as the dimensioned operand. Multiplying two Length values produces an area dimension, which is not a valid type in TCSL and triggers error TCSL_R002.

6.3.Arithmetic Compatibility#

The following table defines the result type for each combination of operator and operand types. Cells marked “ERR” indicate a type error that must be reported at parse time.

LeftOperatorRightResultError Code
Length+LengthLength
LengthLengthLength
Length*ScalarLength
Scalar*LengthLength
Length/ScalarLength
Length+AngleERRTCSL_R001
Length*LengthERRTCSL_R002
Angle+AngleAngle
AngleAngleAngle
Angle*ScalarAngle
Angle/ScalarAngle
Scalar+ScalarScalar
Scalar*ScalarScalar
Scalar/ScalarScalar
Length/LengthScalar
Angle/AngleScalar
Angle+LengthERRTCSL_R001

6.4.Type Coercion#

There is no implicit type coercion in TCSL. Specifically, a Scalar is never automatically promoted to Length. If a function parameter expects a Length, the caller must provide a value with an explicit unit. This rule is the foundation of TCSL’s physical correctness guarantee.

7.Program Structure#

A TCSL program is organized into four sequential zones. The zones must appear in a fixed order. Each zone may contain zero or more statements of its respective type, except EXPORT which requires at least one statement. Comments and blank lines may appear anywhere.

7.1.Zone Ordering#

OrderZoneKeywordRequiredPurpose
1INPUTinputNoDeclare parameters with default values (→ Viewer sliders)
2LETletNoCompute derived values from inputs and constants
3GEOMETRYNoConstruct and transform 3D bodies
4EXPORTexportYes (≥1)Name bodies for output and calculator mapping

An input statement appearing after a let statement is error TCSL_E004. A let statement appearing after a geometry assignment is error TCSL_E005. A geometry assignment appearing after an export statement is error TCSL_E006. A program with zero export statements is error TCSL_E003.

7.2.Pipeline Semantics#

The pipeline operator |> passes the result of the left-hand expression as the implicit first argument to the function call on the right. It is left-associative and has the lowest precedence of all operators. The expression a |> f(x) |> g(y) is equivalent to g(f(a, x), y) where the first argument is the geometry being transformed.

TCSLPipeline chaining example
shelf = box(width = 700 mm, depth = 400 mm, height = 16 mm) |> translate(x = 16 mm, y = 0 mm, z = 300 mm)

7.3.Single-Line Rule#

Every statement — including pipeline chains of arbitrary length — must occupy exactly one logical line. A line break before a |> operator causes the parser to treat the continuation as a new (incomplete) statement, resulting in a parse error. There is no line-continuation character. This constraint is a deliberate design choice that simplifies the parser and ensures unambiguous tokenization.

✗ INVALIDLine break before |> — parse error
panel = box(width = 100 mm, depth = 50 mm, height = 16 mm)
    |> translate(x = 10 mm, y = 0 mm, z = 0 mm)  # ERROR: |> on new line

8.Zone 1 — INPUT#

The INPUT zone declares named parameters with default values. Each input statement introduces a variable that is available in all subsequent zones. In the Viewer, each input variable with a numeric value generates an interactive slider (range 10%–300% of the default value, step 1, debounce 100 ms).

EBNF
InputStatement = "input" , Identifier , "=" , Literal ;
Literal        = NumericLiteral , [ UnitSuffix ]
               | BooleanLiteral
               | StringLiteral ;
✓ VALIDINPUT zone examples
input total_width = 732 mm       # Length — generates slider
input total_depth = 450 mm       # Length — generates slider
input board = 16 mm              # Length — generates slider
input drawer_count = 4           # Scalar (unitless counter)
input has_back_panel = true      # Boolean
IMPORTANT

Do not pre-compute derived values in input declarations. Viewer sliders adjust input values independently — an input inner_width = 700 mm will remain 700 mm even when total_width changes via its slider. Derived values must be placed in the LET zone so they recompute automatically.

9.Zone 2 — LET#

The LET zone declares computed variables. Each let statement binds a name to an arithmetic expression over previously declared variables (from INPUT or earlier LET statements). The expression is evaluated eagerly and the resulting value is immutable. Forward references are not permitted — referencing a variable before its declaration is error TCSL_E013.

EBNF
LetStatement = "let" , Identifier , "=" , ArithExpr ;
ArithExpr    = Term , { ( "+" | "-" ) , Term } ;
Term         = Factor , { ( "*" | "/" ) , Factor } ;
Factor       = Identifier | NumericLiteral , [ UnitSuffix ]
             | "(" , ArithExpr , ")"
             | "-" , Factor ;
✓ VALIDLET zone examples
let inner_width = total_width - 2 * board    # Length - Scalar * Length = Length
let shelf_width = inner_width                   # Simple alias
let back_height = side_height - 2 * board    # For back panel

10.Zone 3 — GEOMETRY#

The GEOMETRY zone is where 3D bodies are created, transformed, and combined. Each statement assigns a geometry value to a name. Bare arithmetic expressions (e.g., x = width - 32 mm) are forbidden in this zone — they must appear in the LET zone. Violation of this rule is error TCSL_E002.

10.1.Constructors#

Constructors create primitive 3D shapes at the origin (0, 0, 0). All arguments are named and mandatory unless otherwise noted.

ConstructorArgumentsOriginDescription
box(width, depth, height)3 × Length(0,0,0) cornerAxis-aligned rectangular solid. plate is an alias.
cylinder(radius, height)2 × Length(0,0,0) base centerCylinder along Z-axis
sphere(radius)1 × Length(0,0,0) centerSphere
✓ VALIDConstructors
side_panel = box(width = board, depth = total_depth, height = side_height)
knob = cylinder(radius = 15 mm, height = 20 mm)
ball = sphere(radius = 10 mm)

10.2.Spatial Transforms#

FunctionArgumentsDescription
translate(x, y, z)3 × Length (all required)Translate by (x, y, z)
rotate(angle, ax, ay, az, cx, cy, cz)1 Angle + 6 LengthRotate by angle around axis (ax,ay,az) through point (cx,cy,cz)
scale(sx, sy, sz)3 × ScalarScale by dimensionless factors along each axis
IMPORTANT

For fasteners (confirmat screws, dowels, cam locks), always use box() with appropriate dimensions rather than cylinder() + rotate(). The box representation avoids rotation complexity and renders more reliably in the Viewer’s Three.js engine.

10.3.Boolean Operations#

Boolean operations combine two geometry bodies into one. They are invoked as pipeline functions taking an other argument that names the tool body.

FunctionArgumentsDescription
union(other)GeometryMerge two bodies into one
cut(other)GeometrySubtract the tool from the target. subtract is an alias.
intersect(other)GeometryKeep only the overlapping volume
✓ VALIDHollow profile tube via cut
tube_outer = box(width = 40 mm, depth = 40 mm, height = 750 mm)
tube_inner = box(width = 36 mm, depth = 36 mm, height = 752 mm) |> translate(x = 2 mm, y = 2 mm, z = -1 mm)
tube_post = tube_outer |> cut(other = tube_inner)

10.4.Patterns (Arrays)#

FunctionArgumentsDescription
linear_pattern(count, dx, dy, dz)1 Scalar + 3 LengthRepeat body count times at equal linear spacing
circular_pattern(count, angle, cx, cy, cz, ax, ay, az)1 Scalar + 1 Angle + 6 LengthRepeat body count times around an axis

10.5.Selectors#

Selectors filter topological entities (edges, faces) from a body for subsequent modification. They are pipeline functions that return a modified body with a selection context.

FunctionArgumentsDescription
select_edges_by_axis(axis)axis: x, y, or zSelect edges parallel to the given axis
select_faces_by_axis(axis, keep)axis: x/y/z, keep: min/maxSelect face at min or max extent on axis

10.6.Modifiers#

FunctionArgumentsDescription
fillet(radius)1 × LengthRound selected edges with given radius
chamfer(distance)1 × LengthBevel selected edges at 45°
shell(thickness, direction)1 × Length, 1 × EnumHollow the body by removing a face and offsetting walls
NOTE

Fillets and chamfers must be applied after all boolean operations. Applying a fillet before a cut may produce unexpected geometry because the boolean engine modifies the edge topology.

11.Zone 4 — EXPORT#

Every geometry variable intended for output must be explicitly exported. The export statement names the variable and assigns a human-readable label via the as keyword. This label is used by the Viewer’s cost calculator and commercial quote generator. A program with zero exports is error TCSL_E003.

EBNF
ExportStatement = "export" , Identifier , [ "as" , AsLabel ] ;
AsLabel         = Identifier ;  (* e.g. Side_LDSP16mm_1 *)

The naming convention for as labels follows the pattern [Element]_[Material][Thickness]_[Number]. The material keyword in the label enables the Viewer’s calculator to automatically classify parts for costing. An export without a material keyword triggers warning TCSL_W001. Certain label keywords also enable Viewer animations: labels containing door, drawer, shutter, flap, or lid cause the Viewer to treat those parts as openable/animatable.

✓ VALIDEXPORT examples
export corpus_left as Side_LDSP16mm_1
export corpus_right as Side_LDSP16mm_2
export facade_drawer as Facade_LDSP16mm_1    # "drawer" → animatable
export tube_post as Post_Tube40x40x2_1
export guide_left as Hardware_1

12.Function Reference#

The following table is the complete list of built-in functions available in TCSL v1.5. All arguments are named. Arguments marked “req” are required; those with a default value are optional.

FunctionCategoryArgumentsReturnsNotes
boxConstructorwidth: L, depth: L, height: LGeometryAlias: plate
cylinderConstructorradius: L, height: LGeometryZ-axis aligned
sphereConstructorradius: LGeometryCenter at origin
translateTransformx: L, y: L, z: LGeometryAll 3 required
rotateTransformangle: A, ax: L, ay: L, az: L, cx: L, cy: L, cz: LGeometryAxis + center
scaleTransformsx: S, sy: S, sz: SGeometryDimensionless
unionBooleanother: GeomGeometry
cutBooleanother: GeomGeometryAlias: subtract
intersectBooleanother: GeomGeometry
linear_patternPatterncount: S, dx: L, dy: L, dz: LGeometry
circular_patternPatterncount: S, angle: A, cx-cz: L, ax-az: LGeometry
select_edges_by_axisSelectoraxis: EnumSelectionx, y, or z
select_faces_by_axisSelectoraxis: Enum, keep: EnumSelectionmin or max
filletModifierradius: LGeometryAfter booleans
chamferModifierdistance: LGeometryAfter booleans
shellModifierthickness: L, direction: EnumGeometryinward/outward

In the table above, L = Length, A = Angle, S = Scalar, Geom = Geometry, Enum = enumeration value (see §13).

13.Enumerations#

Several function parameters accept enumeration values. These are bare identifiers (no quotes) that must match one of the defined values exactly.

axis
Used by selectors. Valid values: x, y, z.
keep
Used by select_faces_by_axis. Valid values: min (face at smallest coordinate on axis), max (face at largest coordinate on axis).
direction
Used by shell. Valid values: inward (shell inward — volume decreases), outward (shell outward — volume increases).

14.Error Codes#

TCSL defines three categories of diagnostics: errors (prefix E, which halt parsing), runtime errors (prefix R, which halt evaluation), and warnings (prefix W, which do not halt). All codes use the format TCSL_Xnnn.

14.1.Parse Errors (E)#

CodeMessageCauseFix
TCSL_E001Expected unit of measurementBare number in dimensional contextAdd mm or deg
TCSL_E002Expected function callBare arithmetic in GEOMETRY zoneMove expression to let
TCSL_E003No export statementProgram has zero exportsAdd at least one export
TCSL_E004Input after letinput appears after letMove input before all let
TCSL_E005Let after geometrylet appears after geometry assignmentMove let before geometry
TCSL_E006Geometry after exportGeometry assignment after exportMove geometry before exports
TCSL_E007Undefined variableReference to undeclared nameDeclare with input or let
TCSL_E008Duplicate identifierSame name declared twiceRename one occurrence
TCSL_E009Unknown functionCall to undefined function nameCheck spelling (see §12)
TCSL_E010Wrong argument countToo few or too many argumentsMatch function signature
TCSL_E011Unknown argument nameNamed argument not in signatureCheck argument names
TCSL_E012Reserved keyword as identifierUsing input, let, etc. as variable nameChoose different name
TCSL_E013Forward referenceVariable used before declarationReorder declarations

14.2.Runtime Errors (R)#

CodeMessageCauseFix
TCSL_R001Incompatible dimensionsAdding Length + AngleFix expression types
TCSL_R002Invalid dimension productLength × Length (area)Use Scalar multiplier
TCSL_R003Division by zeroScalar division by zeroCheck denominator
TCSL_R004Negative dimensionConstructor arg evaluates to ≤ 0Check arithmetic
TCSL_R005Boolean operation failedCSG engine error (degenerate geometry)Adjust tool body overlap

14.3.Warnings (W)#

CodeMessageCauseFix
TCSL_W001No material keyword in as labelexport x as Name_1 without materialAdd LDSP/MDF/HDF/Tube/etc.
TCSL_W002Unexported geometry variableGeometry assigned but never exportedAdd export or remove if tool body
TCSL_W003Unused inputInput variable never referencedRemove or use in let/geometry
TCSL_W004Large dimensionAny dimension > 10,000 mmVerify value is intentional

15.Valid Examples#

The following examples are normative. A conforming TCSL implementation must accept each of them without errors.

15.1.Minimal — Single Shelf#

✓ VALID4 lines · 1 part
input shelf_w = 600 mm
input shelf_d = 250 mm
input shelf_h = 16 mm

shelf_board = box(width = shelf_w, depth = shelf_d, height = shelf_h)

export shelf_board as Shelf_LDSP16mm_1

15.2.Profile Tube 40×40×2 mm#

✓ VALIDHollow rectangular tube via CSG cut
input tube_w = 40 mm
input tube_d = 40 mm
input tube_t = 2 mm
input tube_len = 750 mm

let tube_inner_w = tube_w - 2 * tube_t
let tube_inner_d = tube_d - 2 * tube_t

tube_outer = box(width = tube_w, depth = tube_d, height = tube_len)
tube_inner = box(width = tube_inner_w, depth = tube_inner_d, height = tube_len + 2 mm) |> translate(x = tube_t, y = tube_t, z = -1 mm)
tube_post = tube_outer |> cut(other = tube_inner)

export tube_post as Post_Tube40x40x2_1

16.Invalid Examples#

The following examples are normative counter-examples. A conforming implementation must reject each with the indicated error code.

16.1.Missing Unit TCSL_E001#

✗ INVALID
input width = 800              # ERROR: missing unit (mm, cm, in, ...)

16.2.Bare Arithmetic in GEOMETRY TCSL_E002#

✗ INVALID
input w = 800 mm
input board = 16 mm

inner = w - 2 * board         # ERROR: bare arithmetic in GEOMETRY zone
shelf = box(width = inner, depth = 400 mm, height = board)

export shelf as Shelf_LDSP16mm_1

16.3.Incompatible Dimensions TCSL_R001#

✗ INVALID
input width = 732 mm
input angle = 45 deg

let bad = width + angle          # ERROR: Length + Angle

16.4.Zone Order Violation TCSL_E005#

✗ INVALID
input w = 800 mm

shelf = box(width = w, depth = 400 mm, height = 16 mm)

let inner = w - 32 mm           # ERROR: let after geometry

export shelf as Shelf_1

17.File Format#

A TCSL source file uses the extension .tcsl and the MIME type application/vnd.texttocad.tcsl. The file must be UTF-8 encoded, may optionally start with a BOM, and must use LF or CR+LF line endings. There is no maximum file size defined by this specification, but implementations may impose practical limits.

For multi-file projects (complex products decomposed per §7), the recommended directory structure is as follows.

Directory Structure
project-name/
├── main.tcsl            # Primary file (carcass + exports)
├── drawers.tcsl         # Drawer subsystem
├── hardware.tcsl        # Fasteners and slides
├── doors.tcsl           # Facades and doors
└── README.md            # Assembly notes

18.Design Rationale#

18.1.Why Mandatory Units?#

The Mars Climate Orbiter was lost in 1999 due to a unit mismatch between metric and imperial systems. TCSL prevents this class of error at the language level. Every dimensional value carries its unit through the entire computation, and the type checker ensures dimensional consistency at every operation. The cost is verbosity — 800 mm instead of 800 — but the benefit is that a TCSL program is unambiguous regardless of the reader’s locale or assumptions.

18.2.Why No Loops or Conditionals?#

TCSL targets a specific domain: parametric physical objects with known, fixed topology. A wardrobe has two side panels, a top, a bottom, and some number of shelves — this structure does not vary at runtime. Loops and conditionals would introduce combinatorial complexity that makes LLM generation unreliable and visual inspection difficult. The linear_pattern and circular_pattern functions provide controlled repetition for the cases where it is needed (e.g., a row of shelf brackets).

18.3.Why Single-Line Pipelines?#

The single-line rule eliminates an entire class of ambiguity in the parser. Since TCSL is line-oriented, each line is one complete statement. There is no need for statement terminators (like ; in C), no need for line-continuation characters (like \ in Python), and no risk of the parser misinterpreting a continuation line as a new statement. For LLM generation, this constraint is particularly valuable: every code example in the training data has an unambiguous structure that can be reproduced reliably.

18.4.Why Four Fixed Zones?#

The four-zone structure (INPUT → LET → GEOMETRY → EXPORT) mirrors the mental model of a furniture maker: first establish the parameters, then compute derived dimensions, then build the parts, then name them for the bill of materials. The fixed ordering also enables the Viewer to differentiate sliders (input), computed values (let), and renderable bodies (geometry) without semantic analysis — the parser can switch modes based on keyword context alone.

19.Changelog#

v1.5 (2026-03-18) — Current

Version 1.5 introduced several refinements to the language.

The plate constructor was added as an alias for box, improving readability when modeling flat panels. The subtract alias was added for cut to align with standard CSG terminology. Warning TCSL_W004 was added for dimensions exceeding 10,000 mm. The specification document was restructured into the current 19-section format with formal EBNF grammar, arithmetic compatibility tables, and normative code examples.

v1.4 (2026-03-17)

Version 1.4 added support for the shell modifier with inward and outward direction enumerations. The select_faces_by_axis selector was extended with the keep parameter (previously it always selected the max face). Error codes were renumbered to the current TCSL_Xnnn format. The circular_pattern function was added.

v1.3 (2026-02-28)

Version 1.3 introduced the tag separator $ for grouping related geometry in the Viewer’s scene tree (e.g., box$shelf_bracket). The linear_pattern function was added. Error code TCSL_E013 (forward reference) was added. The fillet and chamfer modifiers were added with the requirement that they be applied after boolean operations.

v1.2 (2026-01-15)

Version 1.2 added the scale transform, sphere constructor, and intersect boolean operation. The rad unit suffix was introduced for angles in radians. Unit conversion factors were formalized in the specification.

v1.1 (2025-12-01)

Version 1.1 introduced the let zone (previously, all variables were declared with input). This separation enabled the Viewer to generate sliders only for base parameters while derived values recompute automatically. The rotate function was extended from 4 arguments (angle + axis) to 7 (angle + axis + center point).

v1.0 (2025-10-15) — Initial Release

Initial public release of the TCSL specification. Defined the four-zone structure, box and cylinder constructors, translate and rotate transforms, union and cut boolean operations, the pipeline operator |>, and the mandatory-units type system. Five length units (mm, cm, m, in, ft) and one angle unit (deg) were supported.