YREA-SLS/stack_lang_spec_v04.md

21 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: ::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

2.3 Literals

Integer Literals

42          // i32 (default)
42 i64      // Annotate as i64
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 \\ \""

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

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:

  • trait operator parses the TokenString as a trait definition
  • fn operator parses the TokenString as a function definition
  • impl operator parses the TokenString as a trait implementation
  • eval operator parses and executes the TokenString immediately

Within TokenStrings, the :: prefix may be used for clarity when referring to traits, though it's not strictly required since the context determines how identifiers are interpreted.

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)

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 both types and traits
  • Every operator must be backed by a trait

3.3 All Constructs are Generic

Every function, struct, and union is implicitly generic. Constraints are specified via traits:

// Function generic over any type T
(T -- T) { dup * } square fn

// Function requiring T to implement Addable trait
(T T -- T) Addable { + } add_two fn

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

// Union generic over variant types
(T --) { Some(T) None } Option union

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 -- 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
{ (i32 -- Self) pick: } Pickable trait
{ (i32 i32 -- ) roll: } Rollable trait
{ (-- i32) depth: } Inspectable trait

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) log: } Logarithmic trait    // log base 10

{ (Self -- Self) ln: } NaturalLogarithmic trait    // natural log

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

Logical Operations Traits

{ (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 i32 -- Self) shl: (Self i32 -- Self) shr: } Bitwise trait

Container Traits

{ (-- Self) create: (Self i32 -- T) at: (Self -- i32) length: } ArrayOf trait

{ (Self Self -- Self) concat: } Concatenable trait

{ (Self i32 i32 -- Self) slice: } Sliceable trait

String Traits

{ (Self Self -- Self) concat: (Self -- i32) length: (Self i32 i32 -- Self) substr: (Self Self -- ArrayOf[Self]) split: } Stringable trait

Conversion Traits

{ (Self T -- U) as: } Convertible trait

{ (Self -- String) str: } Stringifiable trait

{ (String -- Self) parse: } 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 NaturalLogarithmic ] Number inher

Meta-Traits

Traits for defining and working with traits themselves:

{ (-- TokenString) name: } Identifier trait

{ (-- Self) push: } Pushable trait

{ (TokenString Identifier --) trait: (Identifier TokenString Identifier --) impl: (ArrayOf[Identifier] Identifier --) inher: } Implementable trait

4.2 Trait Definition

Syntax: { function_signatures } identifier trait

The identifier can be provided as an identifier literal (::Name) or as a regular identifier on the stack.

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

4.3 Trait Implementation

Syntax: identifier { method_implementations } identifier impl

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

// Implement Drawable for Rectangle
::Rectangle {
    (Rectangle -- ) {
        "Drawing rectangle" print
        dup width get print
        height get print
    } draw:
} ::Drawable impl

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

4.4 Trait Inheritance

Syntax: [ identifier_list ] identifier inher

// Number inherits from multiple arithmetic traits
[ ::Addable ::Multiplyable ] ::BasicNumber inher

// Full Number inherits everything numeric
[ ::Addable ::Multiplyable ::Exponentiable ::Comparable ::Logarithmic ::NaturalLogarithmic ] ::Number inher

// Complex inheritance
[ ::Drawable ::Transformable ::Collidable ] ::GameObject inher

4.5 Using Traits in Functions

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

// Multiple trait requirements
(T -- T) Number Copyable {
    dup abs swap dup * +
} complex_calc fn

// Using identifier literals
(T T -- T) ::Addable {
    +
} add_values 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 [NaturalLogarithmic]

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
(i32 -- i32) { dup * } square fn

// Use it
5 square      // 25

// Multiple inputs and outputs
(i32 i32 -- i32 i32) {
    over over / swap %
} divmod fn

10 3 divmod   // 3 1 (quotient remainder)

7.2 Generic Functions with Trait Constraints

Syntax: (type_sig) trait_constraints { body } name fn

// Generic identity - works with any type
(T -- T) {} identity fn

// Requires T to be Addable
(T T -- T) Addable {
    +
} add_values fn

// Multiple trait constraints
(T U -- U T) Copyable Swappable {
    swap
} swap_generic fn

// Multiple type parameters with different traits
(T U -- T) Addable Number {
    dup U as +
} add_converted fn

// Using identifier literals for clarity
(T -- T) ::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 max set

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

// Use with specific types
3.0 4.0 Point       // Creates Point with f64 fields
3 4 Point           // Creates Point with i32 fields

// More complex struct
(T U V --) { 
    width: 
    height: 
    depth: 
} Box3D 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 * 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 Some             // Creates Option::Some(42)
None                // Creates Option::None
"success" Ok        // Creates Result::Ok("success")
"error" Err         // Creates Result::Err("error")

9.4 Enum Definition

Syntax: () { variants } name enum

() { 
    Pending 
    Active 
    Complete 
} Status enum

// Usage
Pending             // Creates Status::Pending
Active              // Creates Status::Active

10. Memory Management (Postfix)

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

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

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

42 f64 as               // Convert i32 to f64
"123" i32 parse         // Parse string to i32
3.14 str as             // Convert to string

14. Complete Examples

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.3
Status: Draft Specification - Trait-Backed Operations with Identifier Literals