FCS Language Basics
Migration / Verification Status: Foundational reference for expressions, variables, control flow, math, strings, arrays/higher-order functions, units, lambdas, and related constructs. Content reviewed against the corresponding pages in the HiStructOrg femcad-doc wiki (especially the detailed Array-functions catalog). All major areas are backed by tested gists and early Golem test cases. See
project/migration/full-wiki-to-dzone-mapping.mdfor full traceability to the live wiki pages.
Wiki Sources for Language Fundamentals (from live crawl of https://github.com/HiStructOrg/femcad-doc/wiki)
The following wiki pages were the primary sources for the depth in this document (and the low-numbered gists). They have been cross-checked, with their richer examples and edge cases folded into the tested D-Zone version where they added value beyond the existing gists.
| Wiki Page (live) | Lines | Key Topics Covered | Primary D-Zone Home | Related Verified Gists | Verification Notes |
|---|---|---|---|---|---|
| Array-functions.md | 620 | Select, SelectIterate, SelectMany, Where, Find, Take/Skip, Aggregate, OrderBy (multi-column), Zip, ColectBy (group), Sum/CumulativeSums, All/Any, Spacings, array ops/merging | This page + gist-006 | gist-006-lists-linq, gist-009 | Rich catalog; core methods already exercised in Golem tests 004 (array sum) |
| Math-operations.md | 164 | Arithmetic, functions, operators, Math namespace | This page | gist-001-arithmetic, gist-007-math | Strong coverage |
| Strings.md | 165 | String ops, formatting, interpolation | This page | gist-003-strings | Good |
| Units.md | 85 | Unit constants, conversion, Unit. namespace |
This page + 08 | gist-008-units | Core |
| Intervals.md | 61 | Range, interval syntax and ops | This page | gist-009 (parametric) | - |
| Ternary-operator.md | 59 | cond ? a : b and variants |
This page | gist-004-conditionals | - |
| Lambda-function.md | 23 | x => expr, higher-order usage |
This page | gist-005-lambdas | - |
| Enumerable-range.md | 22 | Range generation | This page | gist-009 | - |
| Map-(-[key,-value]-).md | 48 | Dictionary / map type | This page | - | Needs dedicated gist? |
| IndexPath.md | 29 | Path indexing into nested structures | This page + 19/27 | - | - |
| Native-Entities.md | 153 | Built-in geometry primitives (moved to 02) | 02-GEOMETRY-PRIMITIVES | gist-100-vertex and friends | Geometry foundation |
| gblock-distribution.md | 69 | GBlock + distribution basics | 04-CLASSES-DISTRIBUTIONS | gist-300+, gist-010 | See 04 |
Note on Array-functions.md: This 620-line wiki page is the most detailed single source for FCS array power. The current "Lists (arrays)" section in this document covers the most common methods. Advanced methods (SelectMany, multi-column OrderBy, ColectBy, CumulativeSums, Spacings, etc.) are referenced via the gists and will receive expanded dedicated examples in future passes of this wave.
1. File Structure
FCS scripts are plain text files with .fcs extension. A script consists of:
- Comments
- Variable assignments
- Data item definitions (geometry, materials, etc.)
- Import statements
# This is a comment
## Multi-line comments use multiple #
# Variable definitions
height := 10.5
width := 20
# Computed values
area := height * width
# Data items (geometry, mesh, analysis)
vertex {v1} xyz 0 0 0
2. Comments
# Single line comment
## Another comment style
# Comments can appear at end of line
x := 10 # inline comment
3. Variable Assignment
FCS has two binding operators, := and =. Both are legal at the top level and inside updaters (Class{ ... }, parameters { ... }). FCS is pure and lazy (see How FCS Evaluates): nothing runs top to bottom, and a definition is evaluated only when something asks for it. Neither operator means "assign now, mutate later". There is no mutable state.
What the operators decide is whether a field is an input that callers may override, and when an updater entry is evaluated.
Top-level fields: := is an input, = is internal
a := 1 # input: lazy, cached per instance, overridable
b = 2 # internal: may be inlined/optimised by the engine
s = a + b
Engine (.fcsconfig.json) |
Class{a = 10}.s |
Class{b = 20}.s |
|---|---|---|
e3.emulation (no config file) |
12 | 21 |
e4.trans |
12 | 21 |
e4.pure |
12 | error: Unable to update readonly field 'b' ... define it with ':=' |
Practical rule: declare every value that a caller, a parent gblock or user input (PVC / configurator) may change with :=. Use = for derived or internal values. Code written this way behaves the same under every engine.
Inside updaters: = passes a value, := passes a lazy expression
gclass {gc} filename "child.fcs"
x = gc{ a = someValue }.t
y = gc{ a := someValue }.t
p := exprkeeps the expression (with the caller's scope) and evaluates it only when the child readsp.p = exprpasses a value. Undere4.transande4.pure, an updater whose entries are all=is evaluated eagerly. A failingexprthen fails the whole expression even if the child never readsp. Undere3.emulationupdaters are lazy either way.
For constants and pass-through values (parameters {w = w}) both forms give the same result.
All tables in this section were verified with fli.exe on 2026-10-12.
4. Data Types
Primitives
| Type | Examples | Description |
|---|---|---|
| Integer | 10, -5, 0 |
Whole numbers |
| Double | 3.14, -0.5, 1e6 |
Floating point |
| Boolean | True, False |
Logical values |
| String | "hello", 'world' |
Text |
Compound Types
# Lists (arrays)
numbers := [1, 2, 3, 4, 5]
mixed := [1, "two", 3.0]
empty := []
# Tuples
point := (1.0, 2.0, 3.0)
# Dynamic objects (anonymous)
config := { width := 10, height := 20, name := "box" }
Accessing Properties
# Object property access
obj := { x := 1, y := 2 }
xVal := obj.x # dot notation
# List indexing (0-based)
arr := [10, 20, 30]
first := arr[0] # 10
last := arr[-1] # 30 (negative = from end)
# Slicing
subset := arr[1:3] # [20, 30]
5. Operators
Arithmetic
a := 10 + 3 # Addition: 13
b := 10 - 3 # Subtraction: 7
c := 10 * 3 # Multiplication: 30
d := 10 / 3 # Division: 3.333...
e := 10 % 3 # Modulo: 1
f := 2 ** 3 # Power: 8 (or use Pow(2,3))
Comparison
a := 5 == 5 # Equal: True
b := 5 != 3 # Not equal: True
c := 5 > 3 # Greater: True
d := 5 < 3 # Less: False
e := 5 >= 5 # Greater or equal: True
f := 5 <= 3 # Less or equal: False
Logical
a := True and False # False
b := True or False # True
c := not True # False
String Concatenation
greeting := "Hello" + " " + "World" # "Hello World"
6. Control Flow
Ternary Expression
# condition ? if_true : if_false
max := a > b ? a : b
status := count > 0 ? "has items" : "empty"
FCS has no if statement or if ... then ... else expression. Writing one is a syntax error that stops the whole file from parsing (verified with fli.exe, all engines). Use the ternary operator above.
7. Functions (Lambdas)
Single-argument Lambda
square := x => x * x
result := square(5) # 25
Multi-argument Lambda
add := (a, b) => a + b
multiply := a, b => a * b
sum := add(3, 4) # 7
prod := multiply(3, 4) # 12
Lambda with Body
# For complex functions, use block syntax
compute := (x, y) => {
temp := x * 2
result := temp + y
result # last expression is return value
}
8. Class Instantiation
Classes (defined elsewhere) are instantiated with curly braces:
# Basic instantiation
myBeam := BeamClass{}
# With property overrides
myBeam := BeamClass{ length = 10, width = 0.5 }
# Nested
config := OuterClass{
inner = InnerClass{ value = 100 }
}
Property Override (Updater Syntax)
# Clone with modified properties
original := MyClass{ a = 1, b = 2 }
modified := original{ a = 10 } # b remains 2
9. Built-in Functions
Math Functions
# Trigonometry (angles in radians)
s := Sin(0.5)
c := Cos(0.5)
t := Tan(0.5)
a := Asin(0.5)
a := Acos(0.5)
a := Atan(0.5)
a := Atan2(y, x)
# Other math
sq := Sqrt(16) # 4
pw := Pow(2, 3) # 8
ab := Abs(-5) # 5
mn := Min(3, 7) # 3
mx := Max(3, 7) # 7
rd := Round(3.7) # 4
fl := Floor(3.7) # 3
cl := Ceil(3.2) # 4
# Constants
pi := PI # 3.14159...
e := E # 2.71828...
Unit Conversions
# Common unit conversions
angle_rad := 45 * Unit.deg # degrees to radians
angle_deg := 0.785 / Unit.deg # radians to degrees
Type Conversions
intVal := Math.ToInteger(3.7) # 3
dblVal := Math.ToDouble(5) # 5.0
strVal := x.ToString() # "..."
List Operations
arr := [1, 2, 3, 4, 5]
count := arr.Count # 5
first := arr.First() # 1
last := arr.Last() # 5
sum := arr.Sum() # 15
avg := arr.Average() # 3.0
min := arr.Min() # 1
max := arr.Max() # 5
# Functional operations
doubled := arr.Select(x => x * 2) # [2, 4, 6, 8, 10]
filtered := arr.Where(x => x > 2) # [3, 4, 5]
any := arr.Any(x => x > 4) # True
all := arr.All(x => x > 0) # True
10. Import Statements
# Import another FCS file
import "path/to/file.fcs"
# All definitions from imported file become available
11. String Formatting
name := "beam"
length := 10.5
# String concatenation
msg := "The " + name + " is " + length.ToString() + " meters"
# Formatted strings (if supported)
formatted := $"Length: {length:F2} m"
12. Special Identifiers
| Identifier | Description |
|---|---|
this |
Reference to current class instance |
GCS |
Global Coordinate System |
Unit |
Unit conversion namespace |
Fcs |
FCS namespace for built-in types |
13. Syntax Rules Summary
- Case Sensitive:
myVar≠MyVar - Whitespace: Significant for line breaks, not indentation
- Semicolons: Not required (line breaks separate statements)
- Identifiers: Start with letter/underscore, contain letters/digits/underscores
- Keywords: Cannot be used as identifiers
14. Common Patterns
Parametric Design
# Parameters
L := 10 # Length
W := L / 2 # Width depends on L
H := 3 # Height
# Geometry uses parameters
vertex {v1} xyz 0 0 0
vertex {v2} xyz L 0 0
vertex {v3} xyz L W 0
vertex {v4} xyz 0 W 0
Conditional Geometry
includeSupport := True
supportHeight := includeSupport ? 1.0 : 0.0
Iteration via Select
# Create list of points
points := Fcs.Converters.EnumerableRange(10).Select(i => {
x := i * 0.1
y := Sin(x)
result := { X := x, Y := y }
})