23 KiB
Stack Language Specification v0.4
1. Overview
A statically-typed, stack-based language with pure postfix notation combining the execution model of HP's RPL, the type system of C and Rust, and modern array operations from Uiua.
Design Principles
- Everything is postfix - no exceptions
- Stack-based execution (no local variables)
- Static typing with type inference
- Manual heap memory management
- Types define what things are, traits define how things act
- All constructs are implicitly generic
- Every operator is defined by a trait
2. Lexical Structure
2.1 Comments
// Single-line comment
2.2 Identifiers
Regular Identifiers
- Start with letter or underscore:
[a-zA-Z_][a-zA-Z0-9_]* - Case-sensitive
- When encountered, identifiers are executed immediately
Identifier Literals
- Prefix with
::to push the identifier itself onto the stack instead of executing it - Syntax:
::namepushes the identifiernameas a value - Example:
::Addablepushes the identifierAddableonto the stack - Example:
::Pointpushes the identifierPointonto the stack
2.3 Literals
Integer Literals
42 // i64 (default)
42:i32 // Annotate as i32
0xFF // hexadecimal
0b1010 // binary
Floating Point Literals
3.14 // f64 (default)
3.14:f32 // Annotate as f32
String Literals
"hello world"
"escape sequences: \n \t \\ \""
TODO: List all escape sequences.
Boolean Literals
true
false
Array Literals
[1 2 3 4 5] // array of i32
[1.0 2.0 3.0] // array of f64
[[1 2] [3 4]] // 2D array
TODO: Define Type Tuples:
(T T -- T).
Token Strings
{ code here } // TokenString - lexed but not parsed/executed
Token strings contain lexed tokens that are not parsed or executed until an operator causes them to be:
traitoperator parses the TokenString as a trait definitionfnoperator parses the TokenString as a function definitionimploperator parses the TokenString as a trait implementationevaloperator parses and executes the TokenString immediately
Within TokenStrings, the :: prefix may be used, though it's not strictly required since the context (trait definition or implementation, or function definition or eval) determines how identifiers are interpreted.
TODO:
::should not be allowed for traits and implementations.
TODO:
if,while,match, etc. also use Token Strings
TODO: (FOR HUMAN) Should
::be required for identifier literals in functions?
3. Type System
3.1 Primitive Types
i8,i16,i32,i64- Signed integersu8,u16,u32,u64- Unsigned integersf32,f64- Floating pointbool- Booleanchar- Single character (UTF-8)ptr- Raw pointer (generic over pointed type)
3.2 Types vs Traits
Types define the concrete structure and memory layout:
Point struct // Point is a type
Rectangle struct // Rectangle is a type
Traits define behavioral contracts - how things act:
{ ... } ::Addable trait // Addable is a trait
{ ... } ::Drawable trait // Drawable is a trait
Key Distinction:
- A value has a type (what it is structurally)
- A value implements a trait (how it behaves)
- Types are concrete; traits are interfaces
- Functions can be generic over traits
- Functions can have types and traits defined as return types
- Every operator must be backed by a trait
3.3 All Constructs are Generic
TODO: Constructs are not all Generic.
Every function, struct, and union is implicitly generic. Constraints are specified via traits:
// Function generic over any type T
(T -- T) { dup * } ::square<T> fn
// Struct generic over field types
(T T --) { x: y: } ::Point<T> struct
// Union generic over variant types
(T --) { Some(T) None } ::Option<T> union
TODO:
(T -- T) { dup * } ::square<T> fnwould be illegal as the use of*requiresMultiplyable.
4. Trait System
4.1 Standard Traits
Traits define behavioral contracts. Every operator in the language is backed by one or more traits.
Stack Manipulation Traits
{ (-- Self) push: } ::Pushable trait
{ (Self -- Self Self) dup: } ::Duplicable trait
{ (Self -- ) drop: } ::Droppable trait
{ (Self Self -- Self Self) swap: } ::Swappable trait
{ (Self Self -- Self Self Self) over: } ::Overable trait
{ (Self Self Self -- Self Self Self) rot: } ::Rotatable trait
{ (Size -- Self) pick: } ::Pickable trait
{ (Size Size -- ) roll: } ::Rollable trait
{ (-- i64) depth: } ::Inspectable trait
TODO: (FOR HUMAN) Can a type tuple have no parameters?
TODO: Stack manipulation should use one trait.
TODO: Add a
Sizetrait that inherits fromAddable,Comparable, andConvertible<i64>.
Arithmetic Traits
{ (Self Self -- Self) +: (Self Self -- Self) -: } ::Addable trait
{ (Self Self -- Self) *: (Self Self -- Self) /: (Self Self -- Self) %: } ::Multiplyable trait
{ (Self Self -- Self) ^: } ::Exponentiable trait
{ (Self Self -- Self) logb: (Self -- Self) log: (Self -- Self) ln: } ::Logarithmic trait
Comparison Traits
{ (Self Self -- bool) >: (Self Self -- bool) >=: (Self Self -- bool) <: (Self Self -- bool) <=: } ::Orderable trait
{ (Self Self -- bool) ==: (Self Self -- bool) !=: } ::Equatable trait
// Comparable combines ordering and equality
[ ::Orderable ::Equatable ] ::Comparable inher
{ } ::Comparable trait
Logical Operations Traits
{ (Self -- bool) truthy: (Self Self -- Self) and: (Self Self -- Self) or: (Self -- Self) not: } ::Logical trait
Bitwise Operations Traits
{ (Self Self -- Self) bitand: (Self Self -- Self) bitor: (Self Self -- Self) bitxor: (Self -- Self) bitnot: (Self Size -- Self) shl: (Self Size -- Self) shr: } ::Bitwise trait
Container Traits
{ (Self -- i64) length: } ::Sized trait
{ (Self Size -- T) at: } ::Selectable<T> trait
{ (Self Self -- Self) concat: } ::Concatenable trait
{ (Self Size Size -- Self) slice: } ::Sliceable trait
[ ::Sized ::Selectable<T> ::Sliceable ] ::ArrayOf<T> inher
{ } ::ArrayOf<T> trait
String Traits
[ ::Concatenable ] ::String inher
{ (Self Size Size -- Self) substr: (Self Self -- ArrayOf<Self>) split: } ::String trait
Conversion Traits
{ (Self Type -- T) as: } ::Convertible<T> trait
{ (Self -- String) str: } ::Stringifiable trait
{ (String -- Self) parse: } ::Parseable trait
TODO: (FOR HUMAN) Type conversion may need to work a different way?
Numeric Composite Trait
The Number trait represents the full suite of numeric operations by inheriting from multiple traits:
[ Addable Multiplyable Exponentiable Comparable Logarithmic ] ::Number inher
{ } ::Number inher
Meta-Traits
Traits for defining and working with traits themselves:
{ } ::Identifier trait
{ (TokenString Identifier --) trait: (Identifier TokenString Identifier --) impl: (ArrayOf<Identifier> Identifier --) inher: } ::Implementable trait
TODO: (FOR HUMAN) Can traits be completely empty (no inher and empty trait)?
4.2 Trait Definition
Syntax: { function_signatures } ::identifier trait
// Using identifier literal
{
(Self -- ) draw:
} ::Drawable trait
// Trait with multiple methods
{
(Self Self -- Self) add:
(Self Self -- Self) sub:
(Self -- Self) neg:
} ::Numeric trait
// Generic trait
{
(Self T -- Self) append:
(Self -- T) pop:
} ::Container trait
Within the TokenString (the { } block), identifiers like Self, add:, draw: are part of the trait definition syntax. When referencing existing traits within the definition, you may use ::TraitName for clarity, though the context makes it clear they are trait references.
TODO: Again,
::should not be allowed in the Token String for traits and implementations.
4.3 Trait Implementation
Syntax: identifier { method_implementations } ::identifier impl
// Implement Addable for i32
::Addable {
(Self Self -- Self) {
// Native addition implementation
} +:
(Self Self -- Self) {
// Native subtraction implementation
} -:
} ::i32 impl
// Implement Drawable for Rectangle
::Drawable {
(Self -- ) {
"Drawing rectangle" print
dup ::width get print
::height get print
} draw:
} ::Rectangle impl
Note: The following block has been human verified to be syntactically and logically correct.
// Implement Addable for Point
::Addable {
(Self Self -- Self) {
over ::x get over ::x get +
3 pick ::y get 3 pick ::y get +
Point
} +:
(Self Addable -- Self) {
over ::x get over +
3 pick ::y get 3 pick +
Point
} +:
(Addable Self -- Self) {
over over ::x get +
3 pick 3 pick ::y get +
Point
} +:
(Self Self -- Self) {
over ::x get over ::x get -
3 pick ::y get 3 pick ::y get -
Point
} -:
(Self Addable -- Self) {
over ::x get over -
3 pick ::y get 3 pick -
Point
} -:
} ::Point impl
// Implement Logical for everything
::Logical {
(Self -- bool) { true } truthy:
(Self Self -- Self) {
over truthy { } { swap } if drop
} and:
(Self Self -- Self) {
over truthy { swap } { } if drop
} or:
} ::Logical impl
// Overload Logical for bool
::Logical {
(Self -- Self) { } truthy:
} ::bool impl
// Overload Logical for Numeric
::Logical {
(Self -- bool) { 0 != } truthy:
} ::Number impl
// Overload Logical for Option
::Logical {
(Self -- bool) { { Some(_) => { true } None => { false } } match } truthy:
} ::Option impl
// Overload Logical for Result
::Logical {
(Self -- bool) { { Ok(_) => { true } Err(_) => { false } } match } truthy:
} ::Result impl
4.4 Trait Inheritance
Syntax: [ identifier_list ] identifier inher
TODO:
inhermust be beforetraitand must have atrait.
// Number inherits from multiple arithmetic traits
[ ::Addable ::Multiplyable ] ::BasicNumber inher
// Full Number inherits everything numeric
[ ::Addable ::Multiplyable ::Exponentiable ::Comparable ::Logarithmic ] ::Number inher
// Complex inheritance
[ ::Drawable ::Transformable ::Collidable ] ::GameObject inher
4.5 Using Traits in Functions
// Function requiring Drawable trait
(Drawable -- ) {
draw
} ::draw_twice fn
5. Stack Operations
5.1 Stack Manipulation
All stack operations are backed by traits.
dup // ( a -- a a ) Duplicate top [Duplicable]
drop // ( a -- ) Remove top [Droppable]
swap // ( a b -- b a ) Swap top two [Swappable]
over // ( a b -- a b a ) Copy second to top [Overable]
rot // ( a b c -- b c a ) Rotate three items [Rotatable]
5.2 Stack Inspection
depth // ( -- n ) Push stack depth [Inspectable]
pick // ( n -- x ) Copy nth item to top (0 = top) [Pickable]
roll // ( n times -- ) Rotate n items, times times [Rollable]
Roll Examples:
// Stack: a b c d e
3 1 roll // Rotate top 3 items once: a b d e c
3 2 roll // Rotate top 3 items twice: a b e c d
5 1 roll // Rotate all 5 items once: b c d e a
4 3 roll // Rotate top 4 items three times: a d e c b
6. Operators (Postfix)
Every operator is backed by a trait and must be implemented for types that use it.
6.1 Arithmetic
3 4 + // ( a b -- result ) Addition [Addable]
10 3 - // Subtraction [Addable]
5 6 * // Multiplication [Multiplyable]
20 4 / // Division [Multiplyable]
17 5 % // Modulo [Multiplyable]
2 8 ^ // Exponentiation [Exponentiable]
100 log // Log base 10 [Logarithmic]
2.718 ln // Natural logarithm [Logarithmic]
6.2 Comparison
5 3 > // Greater than [Orderable]
5 3 >= // Greater or equal [Orderable]
5 3 < // Less than [Orderable]
5 3 <= // Less or equal [Orderable]
5 5 == // Equal [Equatable]
5 3 != // Not equal [Equatable]
6.3 Logical
true false and // Logical AND [Logical]
true false or // Logical OR [Logical]
true not // Logical NOT [Logical]
6.4 Bitwise
0xFF 0x0F bitand // Bitwise AND [Bitwise]
0xFF 0x0F bitor // Bitwise OR [Bitwise]
0xFF 0x0F bitxor // Bitwise XOR [Bitwise]
0xFF bitnot // Bitwise NOT [Bitwise]
8 2 shl // Left shift [Bitwise]
8 2 shr // Right shift [Bitwise]
7. Functions (Postfix Definition)
Functions are defined in postfix notation. The signature and body come before the name.
7.1 Basic Function Definition
Syntax: (inputs -- outputs) { body } name fn
// Define a square function
(Number -- Number) { dup * } ::square fn
// Use it
5 square // 25
// Multiple inputs and outputs
(Number Number -- Number Number) {
over over / swap %
} ::divmod fn
10 3 divmod // 3 1 (quotient remainder)
7.2 Generic Functions with Trait Constraints
Syntax: (type_sig) { body } name fn
// Generic identity - works with any type
(T -- T) { } ::identity fn
// Requires Addable
(Addable Addable -- Addable) {
+
} ::add_values fn
(Number -- Number) {
dup 0 > { } { 0 T as - } if
} ::abs fn
8. Control Flow (Postfix)
8.1 Conditionals
Syntax: condition { then-block } { else-block } if
// if-then (else block is empty)
x 0 > { "positive" print } {} if
// if-then-else
x 0 >
{ "positive" print }
{ "non-positive" print }
if
// The condition comes first, then both blocks, then 'if'
a b >
{ a }
{ b }
if
// Nested
x 0 >
{
y 0 >
{ "both positive" print }
{ "x positive, y not" print }
if
}
{ "x not positive" print }
if
8.2 Loops
While Loop
Syntax: { condition-block } { body-block } while
// Sum 1 to 10
0 1 // sum counter
{ dup 10 <= } // condition block
{ // body block
over over + // Add counter to sum
swap 1 + swap // Increment counter
}
while
drop // Drop counter, leave sum
// Infinite loop with break
{ true } {
// body
condition { break } {} if
} while
For Loop
Syntax: start end { body-with-counter } for
// The loop variable is implicitly pushed to stack in each iteration
1 10 {
// Stack has loop counter on top
dup print
} for
// More complex
1 100 {
dup fizzbuzz
} for
8.3 Loop Control
break // Exit loop
continue // Skip to next iteration
8.4 Match/Pattern Matching
Syntax: value { pattern => block ... } match
value {
Some(x) => { x print }
None => { "Nothing" print }
} match
// With multiple patterns
status {
Pending => { "Waiting" print }
Active => { "Running" print }
Complete => { "Done" print }
} match
9. Data Structures (Postfix)
9.1 Struct Definition
Syntax: (field_types -- ) { field_names } name struct
// Define Point struct - generic over coordinate types
(T T --) { x: y: } ::Point<T> struct
// Use with specific types
3.0 4.0 Point // Creates Point with f64 fields
3 4 Point // Creates Point with i64 fields
// More complex struct
(T U V --) {
width:
height:
depth:
} ::Box3D<T U V> struct
10.0 20.0 30.0 Box3D
9.2 Struct Field Access
Syntax (postfix): struct field get or struct value field set
point ::x get // Get x field
point 15.0 ::x set // Set x field to 15.0
// Chaining
point ::x get 2 * over ::y get + // (point.x * 2) + point.y
9.3 Union Definition
Syntax: (variant_types -- ) { variants } name union
// Option type - generic over T
(T --) {
Some(T)
None
} ::Option union
// Result type - generic over T and E
(T E --) {
Ok(T)
Err(E)
} ::Result union
// Create union values
42 Option::Some // Creates Option::Some(42)
Option::None // Creates Option::None
"success" Result::Ok // Creates Result::Ok("success")
"error" Result::Err // Creates Result::Err("error")
9.4 Enum Definition
Syntax: { variants } name enum
{
Pending 1: // Normally starts at 0
Active: // Defaults to 2 (one plus the last)
Complete 0:
} ::Status enum
// Usage
Status::Pending // Creates Status::Pending
Status::Active // Creates Status::Active
10. Memory Management (Postfix)
TODO: (FOR HUMAN) Leave out or redo how memory management is done?
10.1 Heap Operations
// Allocate
3.0 4.0 Point alloc // ( Point -- ptr<Point> )
// Dereference
ptr deref // ( ptr<T> -- T )
// Store (dereference and update)
new_value ptr store // ( T ptr<T> -- )
// Free
ptr free // ( ptr<T> -- )
10.2 Example
// Create heap-allocated point
3.0 4.0 Point alloc // Returns ptr<Point>
dup x get print // Dereference and print x
free // Clean up
11. Array Operations (Postfix)
11.1 Basic Array Operations
// Creation
1 10 range // Create range array [1..10]
// Shape operations
arr shape // Get shape
arr 2 3 reshape // Reshape to 2x3
// Element access
arr 2 at // Index access
arr 1 3 slice // Slice array
11.2 Array Combinators
TODO: Funcs don't parse, the
if,while, etc. inside do.
// Map - apply function to each element
[1 2 3 4] { 2 * } map // [2 4 6 8]
// Filter - keep elements matching predicate
[1 2 3 4 5] { 2 % 0 == } filter // [2 4]
// Reduce - fold with function
[1 2 3 4] 0 { + } reduce // 10
// Each - apply to each element (side effects)
[[1 2] [3 4]] { sum print } each
11.3 Array Arithmetic
[1 2 3] [4 5 6] +. // Element-wise add: [5 7 9]
[1 2 3] [4 5 6] *. // Element-wise multiply: [4 10 18]
[1 2 3] 2 *. // Scalar multiply: [2 4 6]
11.4 Array Manipulation
[1 2 3] [4 5 6] concat // Concatenate: [1 2 3 4 5 6]
[1 2 3] reverse // [3 2 1]
[[1 2] [3 4]] transpose // [[1 3] [2 4]]
[1 2 3 4] 2 window // [[1 2] [2 3] [3 4]]
12. Eval Operator (Postfix)
Execute code dynamically at runtime.
// Evaluate string as code
"2 3 +" eval // Pushes 5
// Build and execute code
"(i32 -- i32) { dup * } square fn" eval
5 square // 25
// Dynamic dispatch
operation_name " get" concat eval
13. Standard Library Concepts
TODO: (FOR HUMAN) How are imports done? Is everything automatically in scope?
13.1 I/O
"Hello" print // Print to stdout
"Enter name: " input // Read from stdin
"file.txt" read // Read file contents
"data" "file.txt" write // Write to file
13.2 String Operations
"hello" " world" concat // Concatenate: "hello world"
"hello" length // 5
"hello" 1 3 substr // "el"
"a,b,c" "," split // ["a" "b" "c"]
["a" "b"] "," join // "a,b"
13.3 Type Conversion
TODO: Again, conversions need to be re-done.
42 f64 as // Convert i32 to f64
"123" i32 parse // Parse string to i32
3.14 str as // Convert to string
14. Complete Examples
TODO: These need to be reviewed for correctness.
14.1 Trait Implementation Example
// Define the Addable trait using identifier literal
{
(Self Self -- Self) +:
(Self Self -- Self) -:
} ::Addable trait
// Implement for i32
::i32 {
(Self Self -- Self) {
// Native addition
} +:
(Self Self -- Self) {
// Native subtraction
} -:
} ::Addable impl
// Implement for Point
::Point {
(Point Point -- Point) {
over x get over x get +
swap y get swap y get +
Point
} +:
(Point Point -- Point) {
over x get over x get -
swap y get swap y get -
Point
} -:
} ::Addable impl
14.2 Trait Inheritance Example
// Define base traits
{ (Self Self -- Self) +: (Self Self -- Self) -: } ::Addable trait
{ (Self Self -- Self) *: (Self Self -- Self) /: } ::Multiplyable trait
{ (Self Self -- Self) ^: } ::Exponentiable trait
{ (Self -- Self) log: } ::Logarithmic trait
{ (Self -- Self) ln: } ::NaturalLogarithmic trait
// Number inherits from multiple traits
[ ::Addable ::Multiplyable ::Exponentiable ::Logarithmic ::NaturalLogarithmic ] ::Number inher
14.3 Logarithm Usage
// Calculate log base 10
100 log print // 2.0
1000 log print // 3.0
// Calculate natural logarithm
2.718 ln print // ~1.0
7.389 ln print // ~2.0
// Combine with other operations
10 3 ^ log print // 3.0 (log of 1000)
14.4 Factorial
(i32 -- i32) {
dup 1
{ drop 1 }
{ dup 1 - factorial * }
<= if
} factorial fn
5 factorial print // 120
14.5 FizzBuzz
(i32 -- ) {
dup 15 % 0 ==
{ drop "FizzBuzz" print }
{
dup 3 % 0 ==
{ drop "Fizz" print }
{
dup 5 % 0 ==
{ drop "Buzz" print }
{ print }
if
}
if
}
if
} fizzbuzz fn
1 100 { fizzbuzz } for
14.6 Using Roll
// Stack: 1 2 3 4 5
3 1 roll // Rotate top 3 once: 1 2 4 5 3
3 2 roll // Rotate top 3 twice: 1 2 5 3 4
// More complex example
10 20 30 40 50
4 2 roll // Rotate top 4, twice: 10 30 40 50 20
14.7 Array Processing
// Sum of squares of even numbers from 1 to 10
[1 2 3 4 5 6 7 8 9 10]
{ 2 % 0 == } filter // Keep even numbers
{ dup * } map // Square each
0 { + } reduce // Sum
print // 220
14.8 Identifier Literals in Practice
// Push identifier literal to stack
::Point // Pushes identifier "Point"
// Use with trait definition
{ (Self -- ) draw: } ::Drawable trait
// Use with struct definition
(T T --) { x: y: } ::Point struct
// Dynamic trait implementation
::MyType { ... } ::MyTrait impl
15. Syntax Summary
Complete Grammar Patterns
Functions: (in -- out) trait_constraints { body } name fn
Structs: (types -- ) { fields: } name struct
Unions: (types -- ) { Variant(T) ... } name union
Enums: () { Variant ... } name enum
Traits: { (sig) method: ... } identifier trait
Trait Impl: identifier { (sig) { body } method: ... } identifier impl
Trait Inheritance: [ identifier_list ] identifier inher
If: condition { then } { else } if
While: { condition } { body } while
For: start end { body } for
Match: value { pattern => block ... } match
Identifier Literal: ::name pushes identifier instead of executing
Version: 0.4
Status: Draft Specification