YREA-SLS/stack_lang_spec.md

50 KiB

Stack Language Specification

Version: 0.6.1
Status: Draft Specification
Changes:

  • 0.5 (AI)
    1. Escape sequences - Added a complete list of escape sequences for string literals
    2. Type Tuples - Defined them as function signatures representing stack effects
    3. Control flow TokenStrings - Clarified that if, while, match, etc. use TokenStrings
    4. Generic constructs - Clarified that generics need trait constraints when operations are performed
    5. Stack manipulation traits - Combined into a single Stackable trait
    6. Size trait - Added proper definition inheriting from Addable, Comparable, and Convertible
    7. Empty traits - Clarified that traits can be empty (especially when used with inheritance)
    8. Trait inheritance - Fixed syntax to require both inher and trait declarations
    9. Array combinators - Clarified that the functions inside parse their TokenStrings
    10. :: in TokenStrings - Clarified it should NOT be used in trait/impl contexts
  • 0.5.1 (Human)
    1. Created TODOs
    2. Made various corrections (not everything was corrected)
    3. Reviewed and corrected some code blocks
    4. Made a few adjustments manually
  • 0.6 (AI)
    1. Operator descriptions - Added natural language descriptions for all built-in operators
    2. Operators vs Functions - Clarified distinction and naming rules
    3. Generic trait syntax - Documented <> syntax for generic traits
    4. Generic trait inheritance - Clarified rules for inheriting from generic traits
    5. Code block types - Distinguished actual language definitions from examples
    6. Trait appendix - Created comprehensive trait definitions appendix
    7. TokenString contexts - Listed all control flow operators and their parsing behavior
    8. Lambda operator - Added specification for lambda operator
    9. Type parameter enforcement - Documented current parsing behavior
    10. Identifier literal requirements - Specified :: rules for different contexts
    11. Trait implementation rules - Clarified implementation semantics
    12. Type conversions - Changed to explicit conversion functions
    13. Memory management - Moved to appendix as future feature
    14. Module system - Added appendix entry for future imports
    15. Examples section - Moved to appendix
    16. Syntax summary - Moved to appendix and expanded
  • 0.6.1 (AI)
    1. Variadic arguments - Decision: Use array syntax for variable arguments (no special variadic syntax yet)
    2. Inheritance before trait - Decision: Yes, inheritance declarations must come before trait definition
    3. inher operator - Decision: No, merge inheritance into trait operator (removed inher)
    4. Separate inher vs impl - Decision: Keep them separate (impl is for implementations only)
  • 0.6.2 (Human) - In progress

INSTRUCTIONS FOR AI: AI agents are not allowed to change human reviewed md code blocks. If an AI reviewer thinks a change needs to be made to one of these blocks, the AI model can add a human todo explaining a suggested change or problem. It is totally possible for a md code block that was human reviewed and be correct for the current specification and new TODOs would change it, AIs are still not allowed to change it, only add a human todo. Reviewed md code blocks could also implement new TODOs already. TODO: (FOR HUMAN) These specifications may need to be reorganized. ie. do we need both "Stack Manipulation Trait" in 4.1 and "5. Stack Operations", etc. Should some of these be in the appendix?


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 (future feature)
  • Types define what things are, traits define how things act
  • All constructs are implicitly generic
  • Every operator is defined by a trait

Operators vs Functions

TODO: Operators are not necessarily primitive operations, they can be human or library defined (still by implementing a trait).

Operators are primitive operations that implement trait methods. They:

  • Must be backed by a trait
  • Cannot share names with functions
  • Are the fundamental building blocks of the language
  • Examples: +, -, dup, swap, if, while

Functions are user-defined or library-defined procedures. They:

  • Do not implement traits directly (though they may use operators that do)
  • Cannot share names with operators
  • Are defined using the fn operator
  • Examples: factorial, fizzbuzz, square

Naming Rule: Once a name is used for either an operator or a function, it cannot be reused for the other category.

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: ::name pushes the identifier name as a value
  • Example: ::Addable pushes the identifier Addable onto the stack
  • Example: ::Point pushes the identifier Point onto the stack

Identifier Literal Rules by Context:

  • Function bodies and eval: :: is required for identifier literals
  • Trait definitions: :: is not allowed (identifiers are method names)
  • Trait implementations: :: is not allowed (identifiers are method names)

2.3 Literals

Integer Literals

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

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 \\ \""

Escape Sequences

  • \n - Newline (LF)
  • \r - Carriage return (CR)
  • \t - Tab
  • \\ - Backslash
  • \" - Double quote
  • \' - Single quote
  • \0 - Null character
  • \xNN - Hexadecimal byte (e.g., \x41 for 'A')
  • \u{NNNN} - Unicode code point (e.g., \u{1F600} for 😀)

Boolean Literals

true
false

Array Literals

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

[1 2 3 4 5]              // array of i32
[1.0 2.0 3.0]            // array of f64
[[1 2] [3 4]]            // 2D array

Type Tuples

Type tuples represent stack effects and are used in function signatures to specify what a function consumes from and produces to the stack.

(T T -- T)               // Function signature: two inputs of type T, one output of type T
(i32 f64 -- String)      // Takes i32 and f64, returns String
(-- bool)                // No inputs, returns bool
(Point --)               // Takes Point, no outputs
(---)                    // No inputs, no outputs (side effects only)

Variable Arguments: For operations that need variable numbers of arguments, use array syntax:

For AI Reviewers @ 0.6: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

(-- Size) depth:                        // Zero arguments, returns stack depth
(String Stringifiable[] --) printf:     // String plus array of printable values
([T] -- T) sum:                         // Array of values, returns sum

This approach:

  • Keeps the type system simple (no special variadic syntax)
  • Makes it clear that arguments are grouped together
  • Works naturally with the existing array literal syntax

Usage Example:

"x=%d, y=%d, z=%d" [x y z] printf    // Format string with array of values
[1 2 3 4 5] sum                       // Sum array of numbers
depth print                           // No arguments needed

Token Strings

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

{ 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 parsed. Different operators parse TokenStrings in different ways:

Operators that parse TokenStrings:

  • trait - Parses as trait definition (method signatures)
  • impl - Parses as trait implementation (method definitions)
  • fn - Parses as function body (code block)
  • eval - Parses and executes as code block immediately
  • lambda - Parses as code block, pushes the block as a callable value
  • if - Parses both TokenStrings as code blocks (then/else branches)
  • while - Parses both TokenStrings as code blocks (condition/body)
  • for - Parses TokenString as code block (loop body)
  • match - Parses TokenString as pattern matching arms
  • map - Parses TokenString as code block (transformation function)
  • filter - Parses TokenString as code block (predicate function)
  • reduce - Parses TokenString as code block (accumulator function)
  • each - Parses TokenString as code block (iteration function)

Lambda Operator

The lambda operator converts a TokenString into a callable code block that can be stored and passed around:

{ dup * } lambda        // Creates a callable code block
::square_fn swap        // Store reference to the lambda
// Later use:
5 square_fn eval        // Calls the lambda: pushes 25

Lambdas are useful for:

  • Creating first-class functions
  • Passing code blocks as values
  • Dynamic dispatch
  • Higher-order array operations

3. Type System

3.1 Primitive Types

  • i8, i16, i32, i64 - Signed integers
  • u8, u16, u32, u64 - Unsigned integers
  • f32, f64 - Floating point
  • bool - Boolean
  • char - Single character (UTF-8)
  • ptr - Raw pointer (generic over pointed type) - Future feature, see Appendix

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 Generic Constructs

Functions, structs, and unions can be generic over type parameters using the <> syntax. Type parameters must be constrained by traits when operations are performed on them.

Note on Type Parameters: Type parameters are currently only suggestions when parsing code blocks. The compiler does not yet enforce that type parameters actually constrain how operators and functions act at parse time. This is documented as a possible future enhancement in the Appendix.

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

// Function generic over any type with Multiplyable constraint
(T -- T) { dup * } ::square<T:Multiplyable> fn

// Struct generic over field type T
(T T --) { x: y: } ::Point<T> struct

// Union generic over variant type T
(T --) { Some(T) None } ::Option<T> union

Generic Trait Syntax

Traits can be generic using the <> syntax:

// Generic trait over type T
{ (Self T -- Self) append: } ::Container<T> trait

// Generic trait with multiple type parameters
{ (Self K -- V) get: (Self K V -- Self) insert: } ::Map<K V> trait

Generic Trait Inheritance

When inheriting from a generic trait, you must either:

  1. Make the inheriting trait similarly generic with the same parameters
  2. Specify concrete types for the generic parameters
// Inheriting trait is also generic
[ ::Container<T> ] ::Stack<T> inher
{ (Self -- T) pop: } ::Stack<T> trait

// Inheriting trait specifies concrete type
[ ::Container<i32> ] ::IntStack inher
{ (Self -- i32) pop: } ::IntStack trait

Important: Unconstrained generic functions (those that don't perform operations on their type parameters) can omit trait constraints.

// Generic identity - works with any type (no operations performed)
(---) { } ::identity fn

4. Trait System

4.1 Standard Traits

Traits define behavioral contracts. Every operator in the language is backed by one or more traits. See Appendix A for complete trait definitions.

Stack Manipulation Trait

The Stackable trait provides fundamental stack manipulation operations:

{
    (Self -- Self Self) dup:      // Duplicate the top item
    (Self -- ) drop:              // Remove the top item
    (Self Self -- Self Self) swap: // Swap the top two items
    (Self Self -- Self Self Self) over: // Copy second item to top
    (Self Self Self -- Self Self Self) rot: // Rotate top three items
    (Size -- Self) pick:          // Copy nth item to top (0 = top, the item before the size is 1)
    (Size Size -- ) roll:         // Rotate n items, times times
    (-- Size) depth:              // Push current stack depth
} ::Stackable trait

Size Trait

The Size trait represents types suitable for indexing and sizing operations:

[ ::Addable ::Comparable ::Convertible<i64> ] { } ::Size trait

Types implementing Size can be used as indices, loop bounds, and array sizes. Standard implementations include all integer types (i8, i16, i32, i64, u8, u16, u32, u64).

Arithmetic Traits

// Addition and subtraction operations
{ 
    (Self Self -- Self) +:   // Add two values
    (Self Self -- Self) -:   // Subtract two values
} ::Addable trait

// Multiplication, division, and modulo operations
{ 
    (Self Self -- Self) *:   // Multiply two values
    (Self Self -- Self) /:   // Divide two values
    (Self Self -- Self) %:   // Modulo operation
} ::Multiplyable trait

// Exponentiation operations
{ 
    (Self Self -- Self) ^:   // Raise to power
} ::Exponentiable trait

// Logarithmic operations
{ 
    (Self Self -- Self) logb: // Logarithm with custom base
    (Self -- Self) log:       // Logarithm base 10
    (Self -- Self) ln:        // Natural logarithm
} ::Logarithmic trait

Comparison Traits

// Ordering operations
{ 
    (Self Self -- bool) >:   // Greater than
    (Self Self -- bool) >=:  // Greater than or equal
    (Self Self -- bool) <:   // Less than
    (Self Self -- bool) <=:  // Less than or equal
} ::Orderable trait

// Equality operations
{ 
    (Self Self -- bool) ==:  // Equal to
    (Self Self -- bool) !=:  // Not equal to
} ::Equatable trait

// Comparable combines ordering and equality
[ ::Orderable ::Equatable ] ::Comparable inher
{ } ::Comparable trait

Logical Operations Traits

// Logical operations
{ 
    (Self -- bool) truthy:      // Convert to boolean (truthiness)
    (Self Self -- Self) and:    // Logical AND
    (Self Self -- Self) or:     // Logical OR
    (Self -- Self) not:         // Logical NOT
} ::Logical trait

Bitwise Operations Traits

// Bitwise operations
{ 
    (Self Self -- Self) bitand:  // Bitwise AND
    (Self Self -- Self) bitor:   // Bitwise OR
    (Self Self -- Self) bitxor:  // Bitwise XOR
    (Self -- Self) bitnot:       // Bitwise NOT
    (Self Size -- Self) shl:     // Left shift
    (Self Size -- Self) shr:     // Right shift
} ::Bitwise trait

Container Traits

// Size information
{ 
    (Self -- i64) length:   // Get length/size of container
} ::Sized trait

// Element selection
{ 
    (Self Size -- T) at:    // Access element at index
} ::Selectable<T> trait

// Concatenation
{ 
    (Self Self -- Self) concat:  // Concatenate two containers
} ::Concatenable trait

// Slicing
{ 
    (Self Size Size -- Self) slice:  // Extract slice from start to end
} ::Sliceable trait

// ArrayOf combines container operations
[ ::Sized ::Selectable<T> ::Sliceable ] ::ArrayOf<T> inher
{ } ::ArrayOf<T> trait

String Traits

// String operations
[ ::Concatenable ] ::String inher
{ 
    (Self Size Size -- Self) substr:       // Extract substring
    (Self Self -- ArrayOf<Self>) split:    // Split by delimiter
} ::String trait

Conversion Traits

// Type conversion (uses explicit conversion functions)
{ 
    (Self -- i8) to_i8:
    (Self -- i16) to_i16:
    (Self -- i32) to_i32:
    (Self -- i64) to_i64:
    (Self -- u8) to_u8:
    (Self -- u16) to_u16:
    (Self -- u32) to_u32:
    (Self -- u64) to_u64:
    (Self -- f32) to_f32:
    (Self -- f64) to_f64:
} ::Convertible trait

// String conversion
{ 
    (Self -- String) to_str:  // Convert to string representation
} ::Stringifiable trait

// Parse from string
{ 
    (String -- Self) parse:   // Parse from string
} ::Parseable trait

Numeric Composite Trait

The Number trait represents the full suite of numeric operations by inheriting from multiple traits:

[ ::Addable ::Multiplyable ::Exponentiable ::Comparable ::Logarithmic ] { } ::Number trait

Meta-Traits

Traits for defining and working with traits themselves:

// Marks identifiers
{ } ::Identifier trait

// Operations for trait manipulation
{ 
    (TokenString Identifier --) trait:              // Define a trait
    (Identifier TokenString Identifier --) impl:    // Implement a trait
    (ArrayOf<Identifier> Identifier --) inher:      // Declare trait inheritance
} ::Implementable trait

4.2 Trait Definition

Syntax: inheritance_array? { function_signatures } ::identifier<type_params>? trait

Traits can be defined with or without method signatures. Empty traits are valid and are typically used when inheriting from other traits to create composite traits (marker traits).

Inheritance: Traits can inherit from other traits by specifying an array of trait identifiers before the trait body. The inheritance array is optional.

Example - Actual Language Definitions:

// Trait with methods (no inheritance)
{
    (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<T> trait

// Empty trait (marker trait, no inheritance)
{ } ::Serializable trait

// Trait with inheritance (no additional methods)
[ ::Orderable ::Equatable ] { } ::Comparable trait

// Trait with inheritance and additional methods
[ ::Drawable ::Transformable ] {
    (Self -- ) update:
    (Self -- ) destroy:
} ::GameObject trait

Within the TokenString (the { } block), identifiers like Self, add:, draw: are part of the trait definition syntax and should not use the :: prefix.

4.3 Trait Implementation

Syntax: ::trait_identifier { method_implementations } ::type_or_trait_identifier impl

Implementation Rules:

  1. Implementing a trait for itself (::Trait { ... } ::Trait impl) implements the trait for all types as a default implementation
  2. A trait must be defined before it can be implemented for types or traits
  3. It is not required for all operators of a trait to be in the same implementation block - implementations can be split across multiple blocks
  4. Later implementations can override earlier ones for the same type

Example:

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

// 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

TODO: Yes the following are actual language definitions, but they are used here as an example.

Example - Actual Language Definitions (Human Verified):

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

// Implement Addable for Point
::Addable {
    (Self<T> Self<T> -- Self<T>) {
        over ::x get over ::x get +
        3 pick ::y get 3 pick ::y get +
        Point
    } +:

    (Self<T> Addable -- Self<T>) {
        over ::x get over +
        3 pick ::y get 3 pick +
        Point
    } +:

    (Addable Self<T> -- Self<T>) {
        over over ::x get +
        3 pick 3 pick ::y get +
        Point
    } +:

    (Self<T> Self<T> -- Self<T>) {
        over ::x get over ::x get -
        3 pick ::y get 3 pick ::y get -
        Point
    } -:

    (Self<T> Addable -- Self<T>) {
        over ::x get over -
        3 pick ::y get 3 pick -
        Point
    } -:
} ::Point<T> impl

// Implement Logical for everything (default implementation)
::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<T> -- bool) { { Some(_) => { true } None => { false } } match } truthy:
} ::Option<T> impl

// Overload Logical for Result
::Logical {
    (Self<T U> -- bool) { { Ok(_) => { true } Err(_) => { false } } match } truthy:
} ::Result<T U> impl

4.4 Trait Inheritance

Syntax: [ identifier_list ] { methods } ::identifier<type_params>? trait

Trait inheritance is specified by providing an array of trait identifiers before the trait body in the trait definition. The trait body may be empty if the trait only serves to combine inherited traits.

Inheritance Declaration: The array of inherited traits must appear directly before the trait body TokenString. No separate inher operator is needed.

TODO: Are these actual language definitions just used as examples or actually specification definitions?

Example - Actual Language Definitions:

// Combine multiple arithmetic traits
[ ::Addable ::Multiplyable ] { } ::BasicNumber trait

// Full Number inherits everything numeric
[ ::Addable ::Multiplyable ::Exponentiable ::Comparable ::Logarithmic ] { } ::Number trait

// Inheritance with additional methods
[ ::Drawable ::Transformable ::Collidable ] {
    (Self -- ) update:
    (Self -- ) destroy:
} ::GameObject trait

// Size trait with composite behavior
[ ::Addable ::Comparable ::Convertible<i64> ] { } ::Size trait

// String trait inheriting and adding methods
[ ::Concatenable ] {
    (Self Size Size -- Self) substr:
    (Self Self -- ArrayOf<Self>) split:
} ::String trait

Generic Trait Inheritance: When inheriting from generic traits, you must either make the inheriting trait similarly generic or specify concrete types:

// Inheriting trait is also generic (generic parameter matches)
[ ::Container<T> ] {
    (Self -- T) pop:
} ::Stack<T> trait

// Inheriting trait specifies concrete type
[ ::Container<i32> ] {
    (Self -- i32) pop:
} ::IntStack trait

// Multiple generic inheritance
[ ::Selectable<T> ::Sized ::Sliceable ] { } ::ArrayOf<T> trait

4.5 Using Traits in Functions

TODO: These are not actual language definitions, they are just examples.

Example - Using Actual Definitions:

// Function requiring Drawable trait
(Drawable -- ) {
    draw
} ::draw_twice fn

// Function requiring multiple trait bounds
(Number Number -- Number) {
    dup * swap dup * +  // Pythagorean: a² + b²
} ::sum_of_squares fn

5. Stack Operations

5.1 Stack Manipulation

All stack operations are backed by the Stackable trait.

dup         // ( a -- a a ) Duplicate top item
drop        // ( a -- ) Remove top item
swap        // ( a b -- b a ) Swap top two items
over        // ( a b -- a b a ) Copy second item to top
rot         // ( a b c -- b c a ) Rotate top three items

5.2 Stack Inspection

depth       // ( -- n ) Push current stack depth
pick        // ( n -- x ) Copy nth item to top (0 = top)
roll        // ( n times -- ) Rotate n items, times times

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 - add two numbers
10 3 -      // ( a b -- result ) Subtraction - subtract b from a
5 6 *       // ( a b -- result ) Multiplication - multiply two numbers
20 4 /      // ( a b -- result ) Division - divide a by b
17 5 %      // ( a b -- result ) Modulo - remainder of a divided by b
2 8 ^       // ( a b -- result ) Exponentiation - raise a to power b
100 log     // ( a -- result ) Logarithm base 10 of a
2.718 ln    // ( a -- result ) Natural logarithm of a

6.2 Comparison

5 3 >       // ( a b -- bool ) Greater than - true if a > b
5 3 >=      // ( a b -- bool ) Greater or equal - true if a >= b
5 3 <       // ( a b -- bool ) Less than - true if a < b
5 3 <=      // ( a b -- bool ) Less or equal - true if a <= b
5 5 ==      // ( a b -- bool ) Equal - true if a == b
5 3 !=      // ( a b -- bool ) Not equal - true if a != b

6.3 Logical

true false and    // ( a b -- result ) Logical AND - true if both are truthy
true false or     // ( a b -- result ) Logical OR - true if either is truthy
true not          // ( a -- result ) Logical NOT - inverts truthiness

6.4 Bitwise

0xFF 0x0F bitand     // ( a b -- result ) Bitwise AND - AND each bit
0xFF 0x0F bitor      // ( a b -- result ) Bitwise OR - OR each bit
0xFF 0x0F bitxor     // ( a b -- result ) Bitwise XOR - XOR each bit
0xFF bitnot          // ( a -- result ) Bitwise NOT - invert all bits
8 2 shl              // ( a n -- result ) Left shift - shift a left by n bits
8 2 shr              // ( a n -- result ) Right shift - shift a right by n bits

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

Example - Using Actual Definitions:

// Define a square function (requires Multiplyable)
(T -- T) { dup * } ::square<T:Multiplyable> 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<type_params> fn

Example - Using Actual Definitions:

// Generic identity - works with any type (no operations, no constraint needed)
(T -- T) { } ::identity<T> fn

// Requires Addable
(T T -- T) {
    +
} ::add_values<T:Addable> fn

// Requires Number, alternate syntax
(Number -- Number) {
    dup 0 > { } { 0 swap - } if
} ::abs fn

8. Control Flow (Postfix)

8.1 Conditionals

Syntax: condition { then-block } { else-block } if

The if operator parses both TokenStrings as code blocks (then/else branches).

Example - Using Actual Definitions:

// 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

The while operator parses both TokenStrings as code blocks (condition/body).

Example - Using Actual Definitions:

// 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 for operator parses the TokenString as a code block (loop body).

Example - Using Actual Definitions:

// 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 the current loop immediately
continue    // Skip to the next iteration of the current loop

8.4 Match/Pattern Matching

Syntax: value { pattern => block ... } match

The match operator parses the TokenString as pattern matching arms.

Example - Using Actual Definitions:

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<type_params> struct

Example - Actual Language Definitions:

For AI Reviewers @ 0.5: The following block has been human verified to be syntactically and logically correct. NO AI IS ALLOWED TO CHANGE THIS. If there is a discrepancy between this block and others, follow the syntax in this block.

// 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

Example - Using Actual Definitions:

point dup ::x get     // Get x field
point 15.0 ::x set    // Set x field to 15.0

// Chaining
point dup ::x get 2 * over ::y get +     // (point.x * 2) + point.y

9.3 Union Definition

Syntax: (variant_types -- ) { variants } ::name<type_params> union

Example - Actual Language Definitions:

// Option type - generic over T
(T --) { 
    Some(T) 
    None 
} ::Option<T> union

// Result type - generic over T and E
(T E --) { 
    Ok(T) 
    Err(E) 
} ::Result<T E> 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

Example - Actual Language Definitions:

{ 
    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. Array Operations (Postfix)

10.1 Basic Array Operations

Example - Using Actual Definitions:

// Creation
1 10 range              // Create range array [1..10]

// Shape operations
arr shape               // Get shape of array
arr 2 3 reshape         // Reshape to 2x3

// Element access
arr 2 at                // Access element at index 2
arr 1 3 slice           // Slice from index 1 to 3

10.2 Array Combinators

Array combinators take TokenString arguments containing the function bodies to apply. The if, while, and other control structures inside these function bodies parse their own TokenString arguments.

Example - Using Actual Definitions:

// 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

10.3 Array Arithmetic

Example - Using Actual Definitions:

[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]

10.4 Array Manipulation

Example - Using Actual Definitions:

[1 2 3] [4 5 6] concat  // Concatenate: [1 2 3 4 5 6]
[1 2 3] reverse         // Reverse: [3 2 1]
[[1 2] [3 4]] transpose // Transpose: [[1 3] [2 4]]
[1 2 3 4] 2 window      // Sliding window: [[1 2] [2 3] [3 4]]

11. Eval Operator (Postfix)

Execute code dynamically at runtime. The eval operator parses and executes its TokenString argument immediately.

Example - Using Actual Definitions:

// Evaluate string as code
"2 3 +" eval            // Pushes 5

// Build and execute code
"(T -- T) { dup * } ::square<T:Multiplyable> fn" eval
5 square                // 25

// Dynamic dispatch
operation_name " get" concat eval

12. Standard Library Concepts

All standard library functions and traits are automatically in scope (no imports needed in current version). See Appendix C for future module system design.

12.1 I/O

Example - Using Actual Definitions:

"Hello" print           // Print string to stdout
"Enter name: " input    // Read line from stdin
"file.txt" read         // Read file contents as string
"data" "file.txt" write // Write string to file

12.2 String Operations

Example - Using Actual Definitions:

"hello" " world" concat     // Concatenate: "hello world"
"hello" length              // Get length: 5
"hello" 1 3 substr          // Substring: "el"
"a,b,c" "," split           // Split: ["a" "b" "c"]
["a" "b"] "," join          // Join: "a,b"

12.3 Type Conversion

Type conversion uses explicit conversion functions:

Example - Using Actual Definitions:

42 to_f64               // Convert i32 to f64: 42.0
3.14 to_i32             // Convert f64 to i32: 3 (truncates)
"123" parse             // Parse string to inferred type
42 to_str               // Convert to string: "42"

Appendix A: Complete Trait Definitions

This appendix contains all built-in trait definitions with complete documentation.

A.1 Stackable Trait

Provides fundamental stack manipulation operations.

{
    // Duplicate the top item on the stack
    // Example: 5 dup => 5 5
    (Self -- Self Self) dup:
    
    // Remove and discard the top item
    // Example: 5 10 drop => 5
    (Self -- ) drop:
    
    // Swap the top two items
    // Example: 5 10 swap => 10 5
    (Self Self -- Self Self) swap:
    
    // Copy the second item to the top
    // Example: 5 10 over => 5 10 5
    (Self Self -- Self Self Self) over:
    
    // Rotate the top three items
    // Example: 1 2 3 rot => 2 3 1
    (Self Self Self -- Self Self Self) rot:
    
    // Copy the nth item to top (0 = top)
    // Example: 1 2 3 4 2 pick => 1 2 3 4 2
    (Size -- Self) pick:
    
    // Rotate n items, times times
    // Example: 1 2 3 4 3 1 roll => 1 3 4 2
    (Size Size -- ) roll:
    
    // Push the current stack depth
    // Example: 1 2 3 depth => 1 2 3 3
    (-- Size) depth:
} ::Stackable trait

A.2 Arithmetic Traits

// Addition and subtraction
{
    // Add two values
    // Example: 3 4 + => 7
    (Self Self -- Self) +:
    
    // Subtract second from first
    // Example: 10 3 - => 7
    (Self Self -- Self) -:
} ::Addable trait

// Multiplication, division, and modulo
{
    // Multiply two values
    // Example: 5 6 * => 30
    (Self Self -- Self) *:
    
    // Divide first by second
    // Example: 20 4 / => 5
    (Self Self -- Self) /:
    
    // Remainder after division
    // Example: 17 5 % => 2
    (Self Self -- Self) %:
} ::Multiplyable trait

// Exponentiation
{
    // Raise first to power of second
    // Example: 2 8 ^ => 256
    (Self Self -- Self) ^:
} ::Exponentiable trait

// Logarithmic operations
{
    // Logarithm with custom base
    // Example: 8 2 logb => 3.0
    (Self Self -- Self) logb:
    
    // Logarithm base 10
    // Example: 100 log => 2.0
    (Self -- Self) log:
    
    // Natural logarithm (base e)
    // Example: 2.718 ln => 1.0
    (Self -- Self) ln:
} ::Logarithmic trait

A.3 Comparison Traits

// Ordering operations
{
    // True if first > second
    (Self Self -- bool) >:
    
    // True if first >= second
    (Self Self -- bool) >=:
    
    // True if first < second
    (Self Self -- bool) <:
    
    // True if first <= second
    (Self Self -- bool) <=:
} ::Orderable trait

// Equality operations
{
    // True if values are equal
    (Self Self -- bool) ==:
    
    // True if values are not equal
    (Self Self -- bool) !=:
} ::Equatable trait

// Combined comparison (inherits both)
[ ::Orderable ::Equatable ] { } ::Comparable trait

A.4 Logical Operations

{
    // Convert to boolean (truthiness check)
    // Default: all values are truthy
    // Numbers: 0 is falsy, others truthy
    // Option: Some is truthy, None is falsy
    (Self -- bool) truthy:
    
    // Logical AND - returns first if falsy, else second
    (Self Self -- Self) and:
    
    // Logical OR - returns first if truthy, else second
    (Self Self -- Self) or:
    
    // Logical NOT - inverts truthiness
    (Self -- Self) not:
} ::Logical trait

A.5 Bitwise Operations

{
    // Bitwise AND of two values
    (Self Self -- Self) bitand:
    
    // Bitwise OR of two values
    (Self Self -- Self) bitor:
    
    // Bitwise XOR of two values
    (Self Self -- Self) bitxor:
    
    // Bitwise NOT (complement)
    (Self -- Self) bitnot:
    
    // Shift left by n bits
    (Self Size -- Self) shl:
    
    // Shift right by n bits
    (Self Size -- Self) shr:
} ::Bitwise trait

A.6 Container Traits

// Size information
{
    // Get the number of elements
    (Self -- i64) length:
} ::Sized trait

// Element access
{
    // Get element at index
    (Self Size -- T) at:
} ::Selectable<T> trait

// Concatenation
{
    // Join two containers
    (Self Self -- Self) concat:
} ::Concatenable trait

// Slicing
{
    // Extract elements from start to end index
    (Self Size Size -- Self) slice:
} ::Sliceable trait

// Complete array operations (inherits all above)
[ ::Sized ::Selectable<T> ::Sliceable ] { } ::ArrayOf<T> trait

A.7 Conversion Traits

// Type conversions
{
    (Self -- i8) to_i8:
    (Self -- i16) to_i16:
    (Self -- i32) to_i32:
    (Self -- i64) to_i64:
    (Self -- u8) to_u8:
    (Self -- u16) to_u16:
    (Self -- u32) to_u32:
    (Self -- u64) to_u64:
    (Self -- f32) to_f32:
    (Self -- f64) to_f64:
} ::Convertible trait

// String conversion
{
    // Convert value to string representation
    (Self -- String) to_str:
} ::Stringifiable trait

// Parse from string
{
    // Parse string to value (may fail)
    (String -- Self) parse:
} ::Parseable trait

A.8 Composite Traits

// Size for indexing
[ ::Addable ::Comparable ::Convertible<i64> ] { } ::Size trait

// Full numeric operations
[ ::Addable ::Multiplyable ::Exponentiable ::Comparable ::Logarithmic ] { } ::Number trait

// String operations
[ ::Concatenable ] {
    // Extract substring from start to end
    (Self Size Size -- Self) substr:
    
    // Split by delimiter
    (Self Self -- ArrayOf<Self>) split:
} ::String trait

Appendix B: Future Features

B.1 Memory Management

The language specification currently does not include heap memory management. This appendix documents potential future approaches.

Current State: All values are stack-allocated or embedded in data structures.

Potential Approaches:

Option A: Manual Management

// Allocate on heap
3.0 4.0 Point alloc     // ( Point -- ptr<Point> )

// Dereference
ptr deref               // ( ptr<T> -- T )

// Store through pointer
new_value ptr store     // ( T ptr<T> -- )

// Free memory
ptr free                // ( ptr<T> -- )

Pros: Full control, predictable, zero overhead Cons: Error-prone, requires discipline, potential memory leaks

Option B: Reference Counting

// Create reference-counted value
3.0 4.0 Point rc        // ( Point -- rc<Point> )

// Automatic reference counting
value dup               // Increments count
drop                    // Decrements count, frees if zero

Pros: Automatic cleanup, relatively simple Cons: Runtime overhead, cannot handle cycles, larger memory footprint

Option C: Ownership System (Rust-like)

// Linear types - each value has one owner
value                   // Move semantics by default
value dup               // Error: cannot copy owned value
value ::clone call      // Explicit clone required

Pros: Memory safe, zero overhead, prevents leaks Cons: Complex type system, restricts stack operations, steep learning curve

Option D: Arena/Region-Based

// Create arena
::arena new             // ( -- arena )

// Allocate in arena
arena 3.0 4.0 Point alloc_in  // ( arena Point -- ptr<Point> )

// Free entire arena
arena free_arena        // ( arena -- )

Pros: Fast allocation, simple bulk deallocation Cons: Less granular control, memory held until arena freed

Recommendation: Start without heap allocation (current approach). When needed, implement Option A (manual) for simplicity, with Option D (arenas) added later for performance-critical code. The stack-based nature makes ownership tracking (Option C) particularly challenging.

B.2 Type Parameter Enforcement

Current State: Type parameters in generic functions are currently suggestions and are not enforced at parse time.

Example:

(T -- T) { dup * } ::square<T> fn  // Currently no error even without Multiplyable constraint

Future Enhancement: The compiler could enforce that type parameters actually constrain how operators and functions act, validated at parse time:

(T -- T) { dup * } ::square<T:Multiplyable> fn  // Would require explicit constraint

This would provide stronger type safety but add complexity to the type checker.


Appendix C: Module System (Future)

Current State: All standard library functions and traits are automatically in scope.

Future Design: A module system for organizing code and managing namespaces.

TODO: I want the syntax to be ::std::math use. TODO: What options for aliases are there in addition to { ::std::io::File ::F as } use and ::std::io::File ::F use_as?

Proposed Syntax:

// Import entire module
"std::math" use

// Import specific items
"std::collections::HashMap" use

// Import with alias
"std::io::File" ::F use

// Export from current module
::Point "geometry" export
::distance "geometry" export

// Module declaration
"my_module" module {
    // Module contents
}

Module Resolution:

  • Standard library: std::<module>::<item>
  • User modules: Relative to current file
  • Third-party: Package manager integration (future)

Benefits:

  • Clean namespaces
  • Explicit dependencies
  • Code organization
  • Faster compilation (selective imports)

Appendix D: Examples

D.1 Trait Implementation Example

Example - Actual Language Definitions:

// Define the Addable trait
{
    (Self Self -- Self) +:
    (Self Self -- Self) -:
} ::Addable trait

// Implement for i32
::Addable {
    (Self Self -- Self) {
        // Native addition
    } +:
    
    (Self Self -- Self) {
        // Native subtraction
    } -:
} ::i32 impl

// Implement for Point
::Addable {
    (Self Self -- Self) {
        over ::x get over ::x get +
        swap ::y get swap ::y get +
        Point
    } +:
    
    (Self Self -- Self) {
        over ::x get over ::x get -
        swap ::y get swap ::y get -
        Point
    } -:
} ::Point impl

D.2 Trait Inheritance Example

Example - Actual Language Definitions:

// 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 -- Self) logb: (Self -- Self) log: (Self -- Self) ln: } ::Logarithmic trait

// Number inherits from multiple traits
[ ::Addable ::Multiplyable ::Exponentiable ::Comparable ::Logarithmic ] { } ::Number trait

D.3 Logarithm Usage

Example - Using Actual Definitions:

// 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)

D.4 Factorial

Example - Using Actual Definitions:

(T -- T) {
    dup 1
        { drop 1 }
        { dup 1 - factorial * }
    <= if
} ::factorial<T:Number> fn

5 factorial print       // 120

D.5 FizzBuzz

Example - Using Actual Definitions:

(T -- ) {
    dup 15 % 0 ==
        { drop "FizzBuzz" print }
        {
            dup 3 % 0 ==
                { drop "Fizz" print }
                {
                    dup 5 % 0 ==
                        { drop "Buzz" print }
                        { print }
                    if
                }
            if
        }
    if
} ::fizzbuzz<T:Number> fn

1 100 { fizzbuzz } for

D.6 Using Roll

Example - Using Actual Definitions:

// 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

D.7 Array Processing

Example - Using Actual Definitions:

// 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

D.8 Identifier Literals in Practice

Example - Using Actual Definitions:

// 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<T> struct

// Dynamic trait implementation
::MyType { ... } ::MyTrait impl

Appendix E: Grammar Summary

E.1 Lexical Elements

// Comments
line_comment ::= "//" <any characters until newline>

// Identifiers
identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
identifier_literal ::= "::" identifier

// Integer literals
integer ::= decimal | hexadecimal | binary
decimal ::= [0-9]+ (":" type_name)?
hexadecimal ::= "0x" [0-9a-fA-F]+
binary ::= "0b" [01]+

// Floating point literals
float ::= [0-9]+ "." [0-9]+ (":" type_name)?

// String literals
string ::= '"' (character | escape_sequence)* '"'
escape_sequence ::= "\n" | "\r" | "\t" | "\\" | '\"' | "\'" | "\0" | "\x" hex_digit hex_digit | "\u{" hex_digit+ "}"

// Boolean literals
boolean ::= "true" | "false"

// Array literals
array ::= "[" (expression)* "]"

// Token strings
token_string ::= "{" (token)* "}"

E.2 Type Expressions

// Type tuples (stack signatures)
type_tuple ::= "(" type_list "--" type_list ")"
type_list ::= (type)*

// Generic types
generic_type ::= identifier "<" type_param_list ">"
type_param_list ::= type_param ("," type_param)*
type_param ::= identifier (":" trait_constraint)?

E.3 Definitions

// Function definition
function_def ::= type_tuple (trait_constraint)? token_string identifier_literal "fn"

// Struct definition
struct_def ::= type_tuple token_string identifier_literal generic_params? "struct"

// Union definition
union_def ::= type_tuple token_string identifier_literal generic_params? "union"

// Enum definition
enum_def ::= token_string identifier_literal "enum"

// Trait definition
trait_def ::= inheritance? token_string identifier_literal generic_params? "trait"

// Trait implementation
trait_impl ::= identifier_literal token_string identifier_literal generic_params? "impl"

// Trait inheritance (now part of trait definition)
inheritance ::= "[" identifier_list "]"

E.4 Control Flow

// Conditional
if_expr ::= expression token_string token_string "if"

// While loop
while_expr ::= token_string token_string "while"

// For loop
for_expr ::= expression expression token_string "for"

// Match expression
match_expr ::= expression token_string "match"

E.5 Complete Grammar Patterns

Functions: (inputs -- outputs) trait_constraint? { body } ::name<type_params> fn

Structs: (field_types --) { field_names: } ::name<type_params> struct

Unions: (variant_types --) { Variant(T) ... } ::name<type_params> union

Enums: { Variant value?: ... } ::name enum

Traits: inheritance? { (sig) method: ... } ::identifier<type_params>? trait

Trait Impl: ::trait_id { (sig) { body } method: ... } ::type_id<type_params>? impl

Trait Inheritance: [ ::trait1 ::trait2 ... ] { methods } ::identifier<type_params>? trait

If: condition { then } { else } if

While: { condition } { body } while

For: start end { body } for

Match: value { pattern => block ... } match

Lambda: { code } lambda

Identifier Literal: ::name