# core.cem - Essential Combinators for Cem
#
# These are the foundational building blocks for stack-based programming.
# They are always available and form the basis for all other combinators.
# NOTE: Basic stack operations (dup, drop, swap, over, rot) are built into
# the compiler and don't need definitions here. This file documents their
# signatures and provides higher-level combinators built on top of them.
# ============================================================================
# BASIC STACK OPERATIONS (built-in)
# ============================================================================
# These are provided by the compiler but documented here for reference:
# : dup ( A -- A A )
# Duplicate the top element of the stack
# : drop ( A -- )
# Remove the top element of the stack
# : swap ( A B -- B A )
# Swap the top two elements
# : over ( A B -- A B A )
# Copy the second element to the top
# : rot ( A B C -- B C A )
# Rotate the top three elements
# ============================================================================
# FUNDAMENTAL COMBINATORS
# ============================================================================
: dip ( rest A [rest -- rest'] -- rest' A )
# Execute quotation "under" the top element
# The quotation sees the stack without the top element
# After execution, the original top element is restored
#
# Example:
# 10 20 30 [ + ] dip # Stack: 10 50
# # Adds 10 + 20, then puts 30 back on top
swap call swap ;
: keep ( rest A [rest A -- rest'] -- rest' A )
# Execute quotation while preserving the top element
# The quotation receives a copy of the top element
#
# Example:
# 5 [ dup * ] keep # Stack: 25 5
# # Computes 5 * 5 = 25, keeps 5 on stack
over [ dip ] dip ;
: 2dip ( rest A B [rest -- rest'] -- rest' A B )
# Execute quotation "under" the top two elements
# Like dip but preserves two stack items
swap [ dip ] dip ;
: 2keep ( rest A [rest A A -- rest'] -- rest' A )
# Execute quotation while preserving top element (passed twice)
[ [ dup ] dip ] dip ;
: 3dip ( rest A B C [rest -- rest'] -- rest' A B C )
# Execute quotation "under" the top three elements
swap [ 2dip ] dip ;
# ============================================================================
# CLEAVE COMBINATORS (apply multiple quotations to same value)
# ============================================================================
: bi ( A [A -- B] [A -- C] -- B C )
# Apply two quotations to the same value
# "Bi" = binary = two quotations
#
# Example:
# 5 [ dup * ] [ 2 * ] bi # Stack: 25 10
# # Computes both 5² and 5*2
[ keep ] dip call ;
: tri ( A [A -- B] [A -- C] [A -- D] -- B C D )
# Apply three quotations to the same value
#
# Example:
# 5 [ dup * ] [ 2 * ] [ 1 + ] tri # Stack: 25 10 6
[ [ keep ] dip keep ] dip call ;
: 2bi ( A B [A B -- C] [A B -- D] -- C D )
# Apply two quotations to the same two values
[ 2keep ] dip call ;
# ============================================================================
# SPREAD COMBINATORS (apply different quotations to different values)
# ============================================================================
: bi* ( A B [A -- C] [B -- D] -- C D )
# Apply first quotation to first value, second to second value
# "Bi-star" = spread across values
#
# Example:
# 5 10 [ dup * ] [ 2 * ] bi* # Stack: 25 20
# # Computes 5² and 10*2
[ dip ] dip call ;
: tri* ( A B C [A -- D] [B -- E] [C -- F] -- D E F )
# Apply three quotations to three values
[ [ 2dip ] dip dip ] dip call ;
: 2bi* ( A B C D [A B -- E] [C D -- F] -- E F )
# Apply two quotations to two pairs
[ 2dip ] dip call ;
# ============================================================================
# APPLY COMBINATORS (quotation manipulation)
# ============================================================================
: bi@ ( A B [A -- C] -- C C )
# Apply same quotation to two values
# "Bi-at" = apply at two places
#
# Example:
# 5 10 [ dup * ] bi@ # Stack: 25 100
dup bi* ;
: tri@ ( A B C [A -- D] -- D D D )
# Apply same quotation to three values
dup dup tri* ;
# ============================================================================
# CONDITIONAL COMBINATORS
# ============================================================================
: when ( A Bool [A -- A] -- A )
# Execute quotation only if condition is true
# Otherwise, leave value unchanged
#
# Example:
# -5 [ 0 < ] [ 0 swap - ] when # Stack: 5 (made positive)
# 5 [ 0 < ] [ 0 swap - ] when # Stack: 5 (unchanged)
swap [ call ] [ drop ] if ;
: unless ( A Bool [A -- A] -- A )
# Execute quotation only if condition is false
# Opposite of when
swap [ drop ] [ call ] if ;
: if* ( A Bool [A -- B] [A -- C] -- B|C )
# Like if but both branches receive the value
rot [ call ] [ swap call ] if ;
# ============================================================================
# LOOP COMBINATORS
# ============================================================================
: while ( [-- Bool] [-- ] -- )
# Execute body while condition is true
# Both quotations see the same stack state
#
# NOTE: Requires tail-call optimization for unbounded loops
# Without TCO, this will stack overflow on long iterations
#
# Example:
# 10 [ dup 0 > ] [ dup print 1 - ] while drop
# # Prints: 10 9 8 7 6 5 4 3 2 1
swap [ dup call ] dip
swap [ while ] [ drop drop ] if ;
: until ( [-- Bool] [-- ] -- )
# Execute body until condition is true
# Opposite of while
# TODO: Requires concat implementation
# [ not ] concat swap while ;
; # Placeholder until concat is implemented
: times ( Int [-- ] -- )
# Execute quotation N times
#
# Example:
# 5 [ "Hello" println ] times
# # Prints "Hello" five times
[ dup 0 > ]
[ swap dup call swap 1 - ]
while drop drop ;
# ============================================================================
# DATA FLOW COMBINATORS
# ============================================================================
: nip ( A B -- B )
# Remove second element
swap drop ;
: tuck ( A B -- B A B )
# Copy top element under second
swap over ;
: 2dup ( A B -- A B A B )
# Duplicate top two elements
over over ;
: 2drop ( A B -- )
# Drop top two elements
drop drop ;
: 2swap ( A B C D -- C D A B )
# Swap top two pairs
rot [ rot ] dip ;
: 2over ( A B C D -- A B C D A B )
# Copy second pair to top
[ over ] dip over ;
: 2nip ( A B C D -- C D )
# Remove second pair
2swap 2drop ;
: dupd ( A B -- A A B )
# Duplicate second element
[ dup ] dip ;
: swapd ( A B C -- B A C )
# Swap second and third elements
[ swap ] dip ;
: overd ( A B C -- A B A C )
# Copy third element
[ over ] dip ;
: rotd ( A B C D -- A C D B )
# Rotate under top element
[ rot ] dip ;
# ============================================================================
# QUOTATION COMBINATORS
# ============================================================================
: call ( [-- A] -- A )
# Execute quotation
# NOTE: This is built-in, documented for completeness
call ;
# NOTE: Advanced quotation combinators (concat, curry, compose) are defined
# in stdlib/unimplemented.cem as they require compiler support not yet available
# ============================================================================
# UTILITY COMBINATORS
# ============================================================================
: identity ( A -- A )
# Do nothing (useful for higher-order functions)
;
# NOTE: const and flip require closure/quotation manipulation support
# See stdlib/unimplemented.cem for their definitions
# ============================================================================
# BOOLEAN COMBINATORS
# ============================================================================
: not ( Bool -- Bool )
# Logical negation
true false if ;
: and ( Bool Bool -- Bool )
# Logical AND
# Short-circuits: doesn't evaluate second if first is false
[ ] [ drop false ] if ;
: or ( Bool Bool -- Bool )
# Logical OR
# Short-circuits: doesn't evaluate second if first is true
[ drop true ] [ ] if ;
: xor ( Bool Bool -- Bool )
# Logical XOR (exclusive or)
swap over or rot rot and not and ;
: both ( A [A -- Bool] [A -- Bool] -- Bool )
# Test if value satisfies both predicates
bi and ;
: either ( A [A -- Bool] [A -- Bool] -- Bool )
# Test if value satisfies at least one predicate
bi or ;
# ============================================================================
# COMPARISON COMBINATORS
# ============================================================================
# NOTE: Basic comparisons (<, >, =, etc.) are built-in primitives
: != ( A A -- Bool )
# Not equal
= not ;
: <= ( Int Int -- Bool )
# Less than or equal
2dup < rot rot = or ;
: >= ( Int Int -- Bool )
# Greater than or equal
2dup > rot rot = or ;
: between ( Int Int Int -- Bool )
# Check if value is between min and max (inclusive)
# Stack: value min max
[ >= ] dip <= and ;
: min ( Int Int -- Int )
# Return minimum of two values
2dup < [ drop ] [ swap drop ] if ;
: max ( Int Int -- Int )
# Return maximum of two values
2dup > [ drop ] [ swap drop ] if ;
: clamp ( Int Int Int -- Int )
# Clamp value between min and max
# Stack: value min max
[ max ] dip min ;
# ============================================================================
# NOTES ON IMPLEMENTATION
# ============================================================================
# All combinators in this file are fully implemented and ready to use.
#
# Advanced combinators requiring compiler support (concat, curry, compose,
# const, flip, until) have been moved to stdlib/unimplemented.cem for
# documentation purposes. They will be added when the compiler supports:
# 1. Quotation concatenation (for concat, compose, until)
# 2. Closure capture (for curry, const)
# 3. Quotation rewriting (for flip)
# ============================================================================
# EXAMPLES
# ============================================================================
# Example 1: Using bi to compute multiple properties
# : describe-number ( Int -- String String )
# [ "Even" "Odd" if ]
# [ "Positive" "Negative" "Zero" if if ]
# bi ;
#
# 42 describe-number # Stack: "Even" "Positive"
# Example 2: Using keep to preserve a value while operating on it
# : square-and-original ( Int -- Int Int )
# [ dup * ] keep ;
#
# 5 square-and-original # Stack: 25 5
# Example 3: Using dip to apply operation below top element
# : add-to-second ( Int Int Int -- Int Int )
# [ + ] dip ;
#
# 10 20 30 add-to-second # Stack: 30 30 (10 + 20, then 30)
# Example 4: Using when for conditional transformation
# : make-positive ( Int -- Int )
# dup 0 < [ 0 swap - ] when ;
#
# -5 make-positive # Stack: 5
# 7 make-positive # Stack: 7
# End of core.cem