Overview
Dryft features:- Stack-based concatenative programming model
- Pure and impure function distinction (functions vs actions)
- Linear types and stack-based resource management
- Full optional type inference
- Single-pass compilation
- Pluggable backend system
Build Graph
The compilation process follows this pipeline:Pipeline Stages
- Source Files -
.drysource files and standard library - Frontend - Single-pass parser and semantic analyzer
- Backend - Code generator for target platform
- IR - Intermediate representation (C code, assembly, etc.)
- Assembler - Platform assembler/compiler (GCC, NASM, etc.)
- Objects - Compiled object files from Dryft code and native bindings
- Linker - Links all objects into final executable
- Interpreter - Executes the resulting program
Frontend Architecture
The frontend is implemented insrc/frontend.rs and provides two main functions:
compile()
The core compilation function (frontend.rs:27) that performs single-pass compilation:
- Character-by-character processing - The source code is drained one character at a time (line 45)
- Token accumulation - Characters are accumulated into words until whitespace is encountered
- Immediate processing - Each token is processed immediately via
handle_token() - State tracking - The
CompileStatestruct maintains all compilation state
- Comment handling -
#comment#syntax (lines 65-69) - String literals -
"text"syntax (lines 70-79) - Annotations -
(annotation)syntax for metadata (lines 80-86) - File inclusion - Tracks file stack for
includekeyword (lines 94-104) - Line tracking - Maintains line and file information for error reporting (lines 54-62)
compile_full()
A convenience wrapper (frontend.rs:112) that compiles and finalizes the output:
compile() and then invokes the backend’s complete() method to add runtime boilerplate.
Token Handling
Thehandle_token() function (frontend.rs:119) is the heart of the compiler. It:
- Recognizes token types - Keywords, numbers, variables, function calls, etc.
- Manages definition contexts - Tracks whether we’re inside a function, loop, etc.
- Performs type checking - Validates stack types match operation requirements
- Emits backend code - Calls appropriate backend methods to generate output
Definition Stack
Thedefnstack tracks nested contexts:
Function- Pure functions (declared withfun:)Action- Impure actions (declared withact:)Then- Conditional blocksElect- When/elect blocks (switch-like)Loop- Infinite loopsVariable- Variable declarationsLinkin- External function bindingsInclude- File inclusionModule- Module definitions
Token Matching
The massivematch statement (line 225) handles all token types:
Keywords for definitions:
fun:/:fun- Define functionsact:/:act- Define actionsthen:/:then- Conditional blockselect:/:electorwhen:/:when- Switch-like constructsloop:/:looporcycle:/:cycle- Infinite loopsvar:- Variable declaration
break- Exit loopsreturn- Return from functions;orend- End current block
- Integer regex match (line 473)
- String literals (handled separately)
true/false- Boolean literals
$varname- Read variable (line 463)varname!- Write variable (line 478)
- Arithmetic:
+,-,*,/,mod - Stack ops:
^(copy),v(drop),swap - Comparison:
=?,!=?,>?,<?,>=?,=<? - Logic:
not,both?,either?,xor
linkin- Link external functions (line 329)include- Include other files (line 334)- Method calls - Any previously defined function/action name
Compile State
TheCompileState struct (src/state.rs) maintains all compiler state:
Core Fields
out: Option<String>- Final compiled outputmethods: HashMap<String, Method>- All defined functions and actionsword: String- Current token being accumulated
Stacks
defnstack- Definition context stack (what kind of block we’re in)metastack- Metadata for current definition (e.g., function names)bodystack- Code bodies for nested definitionsvarscopes- Variable scopes (nested HashMap stack)typestack- Type state for type checkingvoidstack- Input types for functions/actions
State Flags
iscomment/isstring/isannotation- Parser mode flagslinenumber/tokenumber- Position trackingcurrent_file/file_stack- File inclusion tracking
Backend Interface
The backend system (src/backends.rs) defines the Backend trait that all code generators implement. The frontend calls backend methods to emit target-specific code.
Key backend methods:
- Code generation:
create_function(),create_loop_block(), etc. - Stack operations:
push_integer(),fun_add(), etc. - Finalization:
complete()wraps code with runtime
Backend Selection
Backends are selected via theselect() function (backends.rs:90):
Type System
Dryft has a simple type system defined instate.rs:
Value Types
Method Classes
Functions are classified as:Type Checking
The compiler performs stack-based type checking:- Each operation declares expected input types via
cs.expect_types() - Operations push their output types via
cs.push_type() - Type mismatches cause compilation errors
- Actions are tracked to ensure they’re not called in pure contexts
Error Handling
Errors are reported with file and line information:cs.token_lineandcs.token_filetrack where each token originatescs.throw_error()reports errors with context- The file stack enables accurate error reporting even in included files
Single-Pass Compilation
Dryft uses true single-pass compilation (see comment on line 44 offrontend.rs):
- Each token is processed immediately as it’s encountered
- No AST is built
- Backend code is generated during parsing
- This keeps the compiler extremely simple but limits certain optimizations
Key Design Decisions
Stack-Based Concatenation
Dryft is concatenative - programs are built by composing functions that transform the stack. The compiler maintains type information about the stack state.Linear Types
The type system enforces linear resource usage - values must be consumed exactly once (though this is still under development).Macros
The frontend uses Rust macros heavily for code generation patterns:new_definition!- Start new definition contextsadd_method!- Finalize method definitionsadd_builtin!- Emit builtin operationscheck_terminator!- Validate block endings
Standard Library
The standard library is written in.dry files and compiled alongside user code. The build profile specifies how to compile and link it (see src/targets/*.toml).