seq-compiler 7.0.0

Compiler for the Seq programming language
Documentation
# Integer Math Standard Library for Seq
#
# Common mathematical operations for integer arithmetic.
#
# ## Usage
#
#   include std:imath
#
#   : main ( -- Int )
#     -5 abs int->string io.write-line
#     0
#   ;
#
# ## Available Functions
#
# - abs: ( Int -- Int ) - Absolute value
# - max: ( Int Int -- Int ) - Maximum of two values
# - min: ( Int Int -- Int ) - Minimum of two values
# - gcd: ( Int Int -- Int ) - Greatest common divisor (Euclidean algorithm)
# - pow: ( Int Int -- Int ) - Power (base^exp) - WARNING: Not tail-recursive
# - sign: ( Int -- Int ) - Sign function: returns -1, 0, or 1
# - square: ( Int -- Int ) - Square a number
# - clamp: ( Int min max -- Int ) - Clamp value between min and max
# - int->bits: ( Int -- String ) - Binary string, minimum width, no leading zeros
# - int->bits-padded: ( Int Int -- String ) - Binary string padded/truncated to N bits
# - int->bits-fmt-8: ( Int -- String ) - 8-bit binary, nibbles separated by space
# - int->bits-fmt-16: ( Int -- String ) - 16-bit binary, nibbles separated by space
#
# ## Notes
#
# - All operations use wrapping integer arithmetic (i64)
# - Recursive functions (gcd, pow) may stack overflow for very large inputs
# - Division by zero will panic at runtime
# - int->bits and int->bits-padded are defined for non-negative ints only.
#   Negative inputs terminate (the runtime's `shr` clamps to 0 once the
#   intermediate value escapes the 63-bit Int range), but the resulting
#   bit string is lossy — typically a single leading "1" bit — because
#   the upper bits of the i64 sign-extension fall off in the first shr.
#   See the Bitwise Operations section of docs/language-guide.md for the
#   63-bit Int contract.
# - int->bits-padded truncates to the low `width` bits when width is
#   smaller than the value's bit length
#
# ## Examples
#
#   48 18 gcd                   # Returns 6
#   2 10 pow                    # Returns 1024
#   15 0 100 clamp              # Returns 15 (within range)
#   -5 abs                      # Returns 5
#   10 int->bits                # Returns "1010"
#   10 8 int->bits-padded       # Returns "00001010"
#   255 4 int->bits-padded      # Returns "1111" (low 4 bits)
#   40 int->bits-fmt-8          # Returns "0010 1000"
#   40 int->bits-fmt-16         # Returns "0000 0000 0010 1000"
#

# Absolute value
# Both branches must have identical stack effects for type checker
: abs ( Int -- Int )
  dup dup 0 i.< [
    # Stack: n n, n < 0, so compute 0 - n
    nip 0 swap i.subtract
  ] [
    # Stack: n n, n >= 0, keep original
    drop
  ] if
;

# Maximum of two values
: max ( Int Int -- Int )
  2dup i.> [
    drop
  ] [
    nip
  ] if
;

# Minimum of two values
: min ( Int Int -- Int )
  2dup i.< [
    drop
  ] [
    nip
  ] if
;

# Greatest common divisor (Euclidean algorithm)
# Calls i.modulo directly. The 0-divisor case is screened by the base
# case above, so the success Bool can be dropped.
# seq:allow(unchecked-modulo)
: gcd ( Int Int -- Int )
  dup 0 i.= [
    drop
  ] [
    2dup i.modulo      # ( a b r success )
    drop               # drop success flag (divisor non-zero, see base case)
    rot drop           # ( b r )
    gcd
  ] if
;

# Power (base^exp using recursion)
: pow ( Int Int -- Int )
  dup 0 i.= [
    drop drop 1
  ] [
    dup 1 i.= [
      drop
    ] [
      over
      swap 1 i.subtract
      pow
      i.multiply
    ] if
  ] if
;

# Sign function: returns -1, 0, or 1
: sign ( Int -- Int )
  dup 0 i.= [
    drop 0
  ] [
    dup 0 i.< [
      drop 0 1 i.subtract
    ] [
      drop 1
    ] if
  ] if
;

# Square
: square ( Int -- Int )
  dup i.multiply
;

# Clamp value between min and max
# Stack: ( value min max -- clamped )
# Returns: min if value < min, max if value > max, else value
: clamp ( Int Int Int -- Int )
  # Stack: value min max
  rot rot
  # Stack: max value min
  2dup i.< [
    # value < min, return min
    nip nip
  ] [
    # value >= min
    drop
    # Stack: max value
    2dup i.> [
      # value > max, return max
      nip
    ] [
      # min <= value <= max, return value
      drop
    ] if
  ] if
;

# Internal: build bits from least-significant to most, prepending each
# bit-char so the accumulator reads MSB-first. Tail-recursive (TCO).
# Stack: ( n acc -- String )
: int->bits-loop ( Int String -- String )
  over 0 i.= [
    nip
  ] [
    over 1 band int->string swap string.concat
    swap 1 shr swap
    int->bits-loop
  ] if
;

# Convert an Int to its binary-string representation (no leading zeros).
# Defined for non-negative inputs; negatives terminate but produce
# lossy output (see header note + docs/language-guide.md Bitwise
# Operations).
: int->bits ( Int -- String )
  dup 0 i.= [
    drop "0"
  ] [
    "" int->bits-loop
  ] if
;

# Internal: same shape as int->bits-loop but driven by a width counter
# instead of n reaching zero. Truncates to the low `width` bits.
# Stack: ( n width acc -- String )
: int->bits-padded-loop ( Int Int String -- String )
  over 0 i.<= [
    nip nip
  ] [
    2 pick 1 band int->string swap string.concat
    swap 1 i.- swap
    rot 1 shr rot rot
    int->bits-padded-loop
  ] if
;

# Convert an Int to a binary string padded/truncated to exactly `width` bits.
# width <= 0 yields the empty string.
: int->bits-padded ( Int Int -- String )
  "" int->bits-padded-loop
;

# 8-bit binary representation, with the two nibbles separated by a single
# space — convenient for reading bit patterns at a glance in the REPL.
: int->bits-fmt-8 ( Int -- String )
  8 int->bits-padded                  # ( s )
  dup 0 4 string.substring            # ( s "hhhh" )
  " " string.concat                   # ( s "hhhh " )
  swap 4 4 string.substring           # ( "hhhh " "llll" )
  string.concat                       # ( "hhhh llll" )
;

# 16-bit binary representation, with each nibble separated by a single
# space (four nibbles, three spaces).
: int->bits-fmt-16 ( Int -- String )
  16 int->bits-padded                 # ( s )
  dup 0 4 string.substring            # ( s n0 )
  " " string.concat                   # ( s "n0 " )
  over 4 4 string.substring           # ( s "n0 " n1 )
  string.concat                       # ( s "n0 n1" )
  " " string.concat                   # ( s "n0 n1 " )
  over 8 4 string.substring           # ( s "n0 n1 " n2 )
  string.concat                       # ( s "n0 n1 n2" )
  " " string.concat                   # ( s "n0 n1 n2 " )
  swap 12 4 string.substring          # ( "n0 n1 n2 " n3 )
  string.concat                       # ( "n0 n1 n2 n3" )
;