Language Reference
Practical reference for the current tgFortran implementation.
Overview
- Case-sensitive
- Free-form source
- Curly-brace blocks
- Zero-indexed arrays and strings
- Function-scoped locals
- Statement separators are newline or
;
Top-level declarations
Supported top-level declarations:
programfunctionmoduleenumudtextern functionextern udt
module linalg {
function twice(int64 x) -> int64 {
return x * 2
}
}
enum mode {
fast = 1,
accurate
}
udt Point {
real64 x
real64 y
}
Types
Built-in scalar types:
int32int64real32real64complex64complex128boolstring
Structured and user types:
dict[K, V]set[T]- user-defined
enum - user-defined
udt
FFI-only boundary types:
c_ptrandc_ptr<T>c_ptr_ptrc_stringc_enum<Name>c_fn<Return, Args...>
Declarations may be grouped:
real64 x, y, z
real64 px[n], py[n], vx[n], vy[n]
Arrays
Arrays are zero-based and row-major.
real64 a[8]
real64 m[4, 4]
print(a[0])
print(m[1, 2])
print(a[2:6])
print(m[:, 1])
Important current semantics:
- assignment performs a deep copy
- slice assignment is overlap-safe
- scalar broadcast to arrays is supported
- general NumPy-style rank broadcasting is not
Containers
Dict
dict[string, int64] counts
counts["a"] = 1
counts["b"] = 2
print(len(counts))
print(contains(counts, "a"))
remove(counts, "b")
Typed literals are supported:
dict[string, int64]{"x": 1, "y": 2}
Set
set[int64] active
insert(active, 4)
insert(active, 7)
print(contains(active, 7))
Container iteration order is unspecified.
Enum
enum boundary_kind {
dirichlet = 10,
neumann,
periodic
}
boundary_kind bc = boundary_kind.dirichlet
if (bc == boundary_kind.neumann) {
print("flux")
}
Enums are distinct semantic types even though they are integer-backed internally.
UDT
UDTs are value-semantics records.
udt Vec2 {
real64 x
real64 y
}
udt Particle {
Vec2 pos
real64[:] rho
}
Vec2 a = Vec2{1.0, 2.0}
Vec2 b = Vec2{x: 3.0, y: 4.0}
Supported now:
- positional literals
- named literals
- nested UDTs
- arrays of UDT
- container fields
- field-subscript assignment like
p.rho[i] = ... - UDT as
dictkey orsetelement when recursively hashable
Control flow
if (x > 0) {
print("positive")
} else {
print("non-positive")
}
do i = 0, 10, 2 {
print(i)
}
while (done == false) {
break
}
do bounds are inclusive. Negative steps are supported. Zero step is a runtime error.
Parallel loops
do parallel i = 0, n - 1 {
out[i] = a[i] + b[i]
}
do parallel distributes iterations across CPU threads via OpenMP.
Each thread receives a contiguous chunk of iterations and loops internally,
preserving LLVM auto-vectorization of inner loops.
- use it for independent numeric loops
- nested serial loops inside a parallel loop are supported
- nested
do parallelloops are supported - heavier kernels benefit the most
break,continue, andreturnare not allowed insidedo parallel
GPU syntax exists separately, but it is experimental and gated behind --experimental-gpu. The stable documented path here is CPU do parallel.
Atomic compound assignment
Use atomic to safely update shared array elements from multiple threads:
do parallel i = 0, n - 1 {
atomic counts[bucket[i]] += 1
}
- supported operators:
+=and-=on integer and real types - only valid on array element assignments inside
do parallel - maps directly to hardware atomic instructions
Explicit iteration
do key in keys(counts) {
print(key)
}
do key, value in items(counts) {
print(key, value)
}
Modules
module stats {
function twice(int64 x) -> int64 {
return x * 2
}
}
program main {
use stats: twice
print(twice(21))
}
use mod and use mod: name are supported. Cross-file module resolution works.
Functions
function factorial(int64 n) -> int64 {
if (n <= 1) {
return 1
}
return n * factorial(n - 1)
}
Current semantics:
- non-void functions must return on all control paths
- void functions omit the return type
- module-owned functions and extern declarations are supported
C ABI and FFI
The current interop model is C ABI oriented. It is for consuming external C libraries, not for treating native tgFortran runtime types as C-compatible by accident.
extern udt Vec2 {
real64 x
real64 y
}
enum file_mode {
read = 0,
write = 1
}
extern function vec2_scale(c_ptr<Vec2> value, real64 scale)
extern function set_mode(c_enum<file_mode> mode) -> c_enum<file_mode>
extern function register_progress(c_fn<void, c_string, real64> cb)
extern udthas foreign C-layout storage, distinct from nativeudt.ffi.address_of,ffi.null_ptr,ffi.is_null,ffi.load_library,ffi.lookup_symbol,ffi.bind_symbol, andffi.close_libraryare module-owned helpers.ffi.bind_symbol(handle, "name")binds a loaded symbol to a declaredextern function.c_fn<...>is currently for extern callback parameters, not general first-class function values.
Numeric modules
linalg provides dense helpers such as dot, matvec, matmul, transpose, and trace.
lapack currently provides:
lapack.solve(A, b)for squarereal32andreal64systemslapack.cholesky(A)returning the lower-triangular factor of a positive-definite matrixlapack.lu(A)returning the standard packed LU factor matrix
program main {
use lapack
real64 A[2, 2] = [[3.0, 1.0], [1.0, 2.0]]
real64 b[2] = [9.0, 8.0]
real64 x[2]
x = lapack.solve(A, b)
print(x[0], x[1])
}
Process and CLI modules
Command-line programs should use the current module surface:
program main {
use os
use cli
string[:] args
args = os.argv()
if (cli.has_flag(args, "--verbose")) {
print("verbose")
}
print(cli.option_or(args, "--config", "default.json"))
}
os.argc()os.argv(i)os.argv()returningstring[:]cli.has_flag,cli.option,cli.option_or