Skip to content

Instantly share code, notes, and snippets.

@owainlewis
Created July 24, 2016 13:08
Show Gist options
  • Save owainlewis/ab7d54104a9183685897ce41b34811d6 to your computer and use it in GitHub Desktop.
Save owainlewis/ab7d54104a9183685897ce41b34811d6 to your computer and use it in GitHub Desktop.
(use extras)
;;;; Joy in Scheme, version 0.5.2
;;; System-dependent but essential code (currently for Chicken)
(display "sysdep...")
;; SYSTEM-DEPENDENT: look up Joy symbol, return () if not known
(define (joy-get s)
(let ((r (get s 'joy)))
(if r r '())))
;; SYSTEM-DEPENDENT: set value of Joy symbol
(define (joy-set! s v) (put! s 'joy v))
;; SYSTEM-DEPENDENT: report error (accepts multiple arguments)
(define (joy-error . s) (error "joy code" (apply string-append s)))
;; SYSTEM-DEPENDENT: return an unused (or at least unusual) symbol
(define (joy-gensym) (gensym))
;;; The user interface
(display "ui...")
;; User interface for running Joy code (supports autoput)
(define-syntax joy (syntax-rules ()
((joy . code) (joy-exec-autoput 'code))))
;; User interface for defining Joy symbols
(define-syntax joy-define (syntax-rules ()
((joy-define name . code) (joy-set! 'name 'code))))
;; User interface for defining Joy modules
(define-syntax joy-module (syntax-rules ()
((joy-module name . members) (joy-module-set! 'name 'members))))
;;; The main Joy interpreter
(display "main...")
;; The Joy stack
(define joy-stack '())
;; Write a list without its outer parentheses
(define (joy-write-list q)
(cond
((null? q) (if #f #f))
((null? (cdr q)) (write (car q)) (newline))
((pair? q)
(write (car q))
(display #\space)
(joy-write-list (cdr q)))
(else (display ". ") (write q))))
;; The Joy undefined-error flag
(define joy-undeferror #t)
;; The Joy autoput code (1 = put, 2 = put stack, else nothing)
(define joy-autoput 2)
;; Push an object on the Joy stack
(define (joy-push! x) (set! joy-stack (cons x joy-stack)))
;; Pop an object from the Joy stack
(define (joy-pop!)
(if (null? joy-stack) (joy-error "Stack underflow"))
(let ((x (car joy-stack)))
(set! joy-stack (cdr joy-stack))
x))
;; Push a list (which must be freshly consed) onto the Joy stack
(define (joy-push-list! x)
(set! joy-stack
(append (reverse x) joy-stack)))
;; Joy predicate for truth
(define (joy-true? x)
(cond
((number? x) (not (zero? x)))
((eq? x #f) #f)
(else #t)))
;; Execute a list as Joy code
(define (joy-exec c) (for-each joy-exec-one c))
(define (joy-exec-autoput c)
(joy-exec c)
(cond
((eqv? joy-autoput 1)
(write (car joy-stack)) (newline))
((eqv? joy-autoput 2)
(joy-write-list joy-stack))))
;; Lookup Joy symbol
(define (joy-lookup i)
(let ((p (joy-get i)))
(and
(null? p)
joy-undeferror
(joy-error "Undefined symbol " (symbol->string i)))
p))
;; Invoke a symbol or push a datum
(define (joy-exec-one i)
(if (symbol? i)
(joy-invoke (joy-lookup i))
(joy-push! i)))
;; Execute a Joy quotation or call a Scheme procedure
(define (joy-invoke p)
(cond
((procedure? p) (p))
((pair? p) (joy-exec p))
((null? p) #f)
(else (joy-error "Attempt to invoke non-procedure"))))
;;; Module definition
(display "modules...")
;; A-list for alphatizing definition names
(define joy-alpha '())
(define joy-modstring "unknown:")
;; Alphatize a symbol
(define (joy-alphatize mode s) ; convert symbol s depending on mode
(case mode
((private) (gensym))
((public) (string->symbol (string-append
joy-modstring
(symbol->string s))))
((exported) s)
(else (joy-error (symbol-string mode) " mode unknown"))))
;; Add a redefinition to joy-alpha
(define (joy-redef! username truename)
(set! joy-alpha (cons (cons username truename) joy-alpha)))
;; Analyze the definitions and build up joy-alpha
(define (joy-analyze! mode defs)
(cond
((null? defs) (if #f #f))
((symbol? (car defs)) (joy-analyze! (car defs) (cdr defs)))
(else (joy-redef! (cadar defs) (joy-alphatize mode (cadar defs)))
(joy-analyze! mode (cdr defs)))))
;; Substitute based on joy-alpha
(define (joy-subst t)
(let ((a (assq t joy-alpha)))
(if a (cdr a)
(if (pair? t)
(cons (joy-subst (car t)) (joy-subst (cdr t)))
t))))
;; Install amodule definition
(define (joy-install! def)
(if (pair? def) (joy-set! (cadr def) (cddr def))))
;; Install module
(define (joy-module-set! name members)
(set! joy-alpha '())
(set! joy-modstring (string-append (symbol->string name) ":"))
(joy-analyze! 'public members)
(for-each joy-install! (joy-subst members)))
;;; Macros for defining Joy primitives
(display "macros...")
;; Push one result
(define-syntax joy-prim (syntax-rules ()
((joy-prim (name . vars) . code)
(joy-set! 'name (lambda ()
(joy-let vars (joy-push! (begin . code))))))))
;; Push a freshly consed list of results
(define-syntax joy-prim-list (syntax-rules ()
((joy-prim-list (name . vars) . code)
(joy-set! 'name (lambda ()
(joy-let vars (joy-push-list! (begin . code))))))))
;; Push nothing
(define-syntax joy-prim-void (syntax-rules ()
((joy-prim-void (name . vars) . code)
(joy-set! 'name (lambda ()
(joy-let vars (begin . code)))))))
;; Set up appropriate pops
(define-syntax joy-let (syntax-rules ()
((joy-let () . body)
(begin . body))
((joy-let (x1 x2 ...) . body)
(joy-let (x2 ...) (let ((x1 (joy-pop!))) . body)))))
;;; Joy non-combinator primitives
(display "prims...")
;; Simple niladic primitives
(joy-prim (false) #f)
(joy-prim (true) #t)
(joy-prim (maxint) #f)
(joy-prim (setsize) #f)
(joy-prim (stack) joy-stack)
(joy-prim (autoput) joy-autoput)
(joy-prim (undeferror) (if joy-undeferror 1 0))
(joy-prim (stdin) (current-input-port))
(joy-prim (stdout) (current-output-port))
;; Simple operators
(joy-prim-void (id) #f)
(joy-prim-list (dup x) (list x x))
(joy-prim-list (swap x y) (list y x))
(joy-prim-list (rollup x y z) (list z x y))
(joy-prim-list (rolldown x y z) (list y z x))
(joy-prim-list (rotate x y z) (list z y x))
(joy-prim (popd y z) z)
(joy-prim-list (dupd y z) (list y y z))
(joy-prim-list (swapd x y z) (list y x z))
(joy-prim-list (rollupd x y z w) (list z x y w))
(joy-prim-list (rolldownd x y z w) (list y z x w))
(joy-prim-list (rotated x y z w) (list z y x w))
(joy-prim-void (pop x) #f)
(joy-prim (choice b t f) (if b t f))
;; Logical primitives (FIXME: don't handle sets yet)
(joy-prim (or x y) (or x y))
(joy-prim (xor x y) (eq? x (not y)))
(joy-prim (and x y) (and x y))
(joy-prim (not x) (not x))
;; Arithmetic primitives
(joy-prim (+ i j) (+ i j))
(joy-prim (- i j) (- i j))
(joy-prim (* i j) (* i j))
(joy-prim (/ i j) (/ i j))
(joy-prim (rem i j) (remainder i j))
(joy-prim (div i j) (list (trunc (/ i j)) (remainder i j)))
(joy-prim (sign i) (if (negative? i) -1 (if (zero? i) 0 1)))
(joy-prim (neg i) (- i))
(joy-prim (ord c) (char->integer c))
(joy-prim (chr i) (integer->char i))
(joy-prim (abs n) (abs n))
(joy-prim (pred n) (- n 1))
(joy-prim (succ n) (+ n 1))
(joy-prim (max m n) (max m n))
(joy-prim (min m n) (max m n))
;; Transcendental primitives
(joy-prim (acos f) (acos f))
(joy-prim (asin f) (asin f))
(joy-prim (atan f) (atan f))
(joy-prim (atan2 f g) (atan f g))
(joy-prim (ceil f) (ceiling f))
(joy-prim (cos f) (cos f))
(joy-prim (cosh f) (cosh f)) ; SYSTEM-DEPENDENT
(joy-prim (exp f) (exp f))
(joy-prim (floor f) (floor f))
(joy-prim (log f) (log f))
(joy-prim (log10 f) (/ (log f) (log 10)))
(joy-prim (pow f g) (expt f g))
(joy-prim (sin f) (sin f))
(joy-prim (sinh f) (sinh f)) ; SYSTEM-DEPENDENT
(joy-prim (sqrt f) (sqrt f))
(joy-prim (tan f) (tan f))
(joy-prim (tanh f) (tanh f)) ; SYSTEM-DEPENDENT
(joy-prim (trunc f) (truncate f))
;; Date primitives are system-dependent and not implemented
;; String/numeric conversion primitives
(joy-prim (strtol s i) (string->number s i))
(joy-prim (strtod s) (string-number s))
(joy-prim (format n i) (number->string n i)) ; different from C-Joy
; formatf not implemented
;; Random number primitives are system-dependent and not implemented
;; Simple I/O primitives
(joy-prim (get) (read))
(joy-prim-void (put x) (write x))
(joy-prim-void (putchar c) (display (integer->char c)))
(joy-prim-void (putchars x) (display x))
(joy-prim-void (include s) (load (string-append s ".ss"))) ; SYSTEM-DEPENDENT
;; Stream primitives
(joy-prim-void (fclose f)
(if (input-port? f) (close-input-port f) (close-output-port f)))
(joy-prim (eof x) (eof-object? x)) ; different from C-Joy
(joy-prim (fgetch) (read-char (car joy-stack)))
(joy-prim (fopen p m)
(cond
((string-equal m "r") (open-input-port p))
((string-equal m "w") (open-output-port p))
(else (joy-error "Invalid fopen mode " m))))
(joy-prim (fput x) (write x (car joy-stack)))
(joy-prim (fputch c) (display c (car joy-stack)))
(joy-prim (fputchars s) (display s (car joy-stack)))
;; Replace the stack with its topmost member
(joy-prim-void (unstack x) (set! joy-stack x))
;; Cons element onto aggregate
(joy-prim (cons x a)
(if (string? a)
(string-append (string x) a)
(cons x a)))
;; Swapped cons
(joy-prim (swons a x)
(if (string? a)
(string-append (string x) a)
(cons x a)))
;; Get first element
(joy-prim (first a)
(if (string? a)
(string-ref a 0)
(car a)))
;; Get remaining elements
(joy-prim (rest a)
(if (string? a)
(substring a 1 (string-length a))
(cdr a)))
;; FIXME: compare not implemented
;; Element of aggregate at location (zero-based)
(joy-prim (at a i)
(if (string? a)
(string-ref a i)
(list-ref a i)))
;; Inverse of at
(joy-prim (of i a)
(if (string? a)
(string-ref a i)
(list-ref a i)))
;; Size of aggregate
(joy-prim (size a)
(if (string? a)
(string-length a)
(list-length a)))
;; FIXME: opcase not implemented
;; FIXME: case not implemented
;; Uncons an aggregate
(joy-prim-list (uncons a)
(if (string? a)
(list (string-ref a 0) (substring a 0 (string-length a)))
(list (car a) (cdr a))))
;; Uncons an aggregate and swap
(joy-prim-list (unswons a)
(if (string? a)
(list (substring a 0 (string-length a)) (string-ref a 0))
(list (cdr a) (car a))))
;; Drop first n elements of an aggregate
(joy-prim (drop a n)
(if (string? a)
(substring a n (string-length a))
(list-tail a n)))
;; Take first n elements of aggregate
(joy-prim (take a n)
(if (string? a)
(substring a 0 n)
(reverse (joy-reversed-head a n))))
(define (joy-reversed-head a n)
(if (or (zero? n) (null? a))
'()
(cons (car a) (reversed-head (cdr a) (- n 1)))))
;; Concatenate aggregates
(joy-prim (concat s t)
(if (string? s) (string-append s t) (append s t)))
;; Concatenate aggregates with an element in the middle
(joy-prim (enconcat x s t)
(if (string? s)
(string-append s (string x) t)
(append s (list x) t)))
;; Symbol/string conversion
(joy-prim (name s) (symbol->string s))
(joy-prim (intern s) (string->symbol s))
(joy-prim (body u) (joy-get u))
;; Null aggregate or zero number
(joy-prim (null x)
(cond
((string? x) (zero? (string-length x)))
((null? x) #t)
(else (zero? x))))
;; Small aggregate or zero or one number
(joy-prim (small x)
(cond
((string? x) (<= (string-length x) 1))
((null? x) #t)
((pair? x) (null? (cdr x)))
(else (<= 0 x 1))))
;; Relational operators
(joy-prim (= x y)
(cond
((symbol? x)
(string=? (symbol->string x) (symbol->string y)))
((string? x)
(string=? x y))
(else (= x y))))
(joy-prim (!= x y)
(cond
((symbol? x)
(not (string=? (symbol->string x) (symbol->string y))))
((string? x)
(not (string=? x y)))
(else (not (= x y)))))
(joy-prim (< x y)
(cond
((symbol? x)
(string<? (symbol->string x) (symbol->string y)))
((string? x)
(string<? x y))
(else (= x y))))
(joy-prim (> x y)
(cond
((symbol? x)
(string>? (symbol->string x) (symbol->string y)))
((string? x)
(string>? x y))
(else (> x y))))
(joy-prim (<= x y)
(cond
((symbol? x)
(string<=? (symbol->string x) (symbol->string y)))
((string? x)
(string<=? x y))
(else (<= x y))))
(joy-prim (>= x y)
(cond
((symbol? x)
(string>=? (symbol->string x) (symbol->string y)))
((string? x)
(string>=? x y))
(else (>= x y))))
;; Tree equality
(joy-prim (equal t u) (equal? t u))
;; Membership
(joy-prim (has a x) (if (string? a) (joy-stringmem a x) (memq a x)))
(joy-prim (in x a) (if (string? a) (joy-stringmem a x) (memq a x)))
(define (joy-stringmem s c)
(define (try i r)
(cond
((zero? r) #f)
((eqv? c (string-ref s i)) #t)
(else (stringmem (+ i 1) (- r 1)))))
(try 0 (string-length s)))
;; Type predicates
(joy-prim (integer x) (integer? x))
(joy-prim (char x) (character? x))
(joy-prim (logical x) (boolean? x))
(joy-prim (set x) (list? x))
(joy-prim (string x) (string? x))
(joy-prim (list x) (or (pair? x) (null? x)))
(joy-prim (leaf x) (not (or (pair? x) (null? x))))
(joy-prim (float x) (real? x))
(joy-prim (user x) (and (symbol? x) (pair? (joy-get x))))
(joy-prim (file x) (or (input-port? x) (output-port? x)))
;; Environment manipulation
(joy-prim-void (setundeferror n) (set! joy-undeferror (joy-true? n)))
(joy-prim-void (setautoput n) (set! joy-autoput n))
;;; Joy combinator primitives
(display "combs...")
;; Evaluate thunk on a stabilized stack
(define (joy-stable p)
(let*
((s joy-stack)
(r (p)))
(set! joy-stack s)
r))
;; Execute Joy quotation stably, return top of stack
(define (joy-stable-exec p) (joy-stable (lambda () (joy-exec p) (joy-pop!))))
;; Return truth value of stabilized execution
(define (joy-yields-true? p) (joy-true? (joy-stable-exec p)))
;; Simple combinators
(joy-prim-void (i x) (joy-exec x))
(joy-prim-void (x) (joy-exec (car joy-stack)))
(joy-prim (dip x p) (joy-exec p) x)
;; app1 app11 app12
;; Construct combinator
(joy-prim-void (construct p1 p2)
(joy-stable (lambda ()
(joy-exec p1)
(for-each (lambda (q) (joy-exec q) (joy-pop!)) p2))))
;; N-ary combinators
(joy-prim (nullary p) (let ((r (joy-stable-exec p))) r))
(joy-prim (unary p) (let ((r (joy-stable-exec p))) (joy-pop!) r))
(joy-prim (binary p) (let ((r (joy-stable-exec p))) (joy-pop!) (joy-pop!) r))
(joy-prim (ternary p) (let ((r (joy-stable-exec p))) (joy-pop!) (joy-pop!) (joy-pop!) r))
;; Execute unary combinator twice
(joy-prim-list (unary2 x1 x2 p)
(let*
((r1 (begin (joy-push! x1) (joy-exec p) (joy-pop!)))
(r2 (begin (joy-push! x2) (joy-exec p) (joy-pop!))))
(list r1 r2)))
(joy-set! 'app2 (joy-get 'unary2))
;; Execute unary combinator three times
(joy-prim (unary3 x1 x2 x3 p)
(let*
((r1 (begin (joy-push! x1) (joy-exec p) (joy-pop!)))
(r2 (begin (joy-push! x2) (joy-exec p) (joy-pop!)))
(r3 (begin (joy-push! x3) (joy-exec p) (joy-pop!))))
(list r1 r2 r3)))
(joy-set! 'app3 (joy-get 'unary3))
;; Execute unary combinator four times
(joy-prim (unary4 x1 x2 x3 x4 p)
(let*
((r1 (begin (joy-push! x1) (joy-exec p) (joy-pop!)))
(r2 (begin (joy-push! x2) (joy-exec p) (joy-pop!)))
(r3 (begin (joy-push! x3) (joy-exec p) (joy-pop!)))
(r4 (begin (joy-push! x4) (joy-exec p) (joy-pop!))))
(list r1 r2 r3 r4)))
(joy-set! 'app4 (joy-get 'unary4))
;; Cleave combinator
(joy-prim-list (cleave p1 p2)
(let*
((r1 (joy-stable-exec p1))
(r2 (joy-stable-exec p2)))
(joy-pop!)
(list r1 r2)))
;; Conditional combinators
(joy-prim-void (branch p t e)
(if (joy-true? p) (joy-exec t) (joy-exec e)))
(joy-prim-void (ifte p t e)
(if (joy-yields-true? p) (joy-exec t) (joy-exec e)))
(joy-prim-void (ifinteger x t e) (if (integer? x) (joy-exec t) (joy-exec e)))
(joy-prim-void (ifchar x t e) (if (character? x) (joy-exec t) (joy-exec e)))
(joy-prim-void (iflogical x t e) (if (boolean? x) (joy-exec t) (joy-exec e)))
(joy-prim-void (ifset x t e) (if (list? x) (joy-exec t) (joy-exec e)))
(joy-prim-void (ifstring x t e) (if (string? x) (joy-exec t) (joy-exec e)))
(joy-prim-void (iflist x t e)
(if (or (pair? x) (null? x)) (joy-exec t) (joy-exec e)))
(joy-prim-void (iffloat x t e) (if (real? x) (joy-exec t) (joy-exec e)))
(joy-prim-void (iffile x t e)
(if (or (input-port? x) (output-port? x)) (joy-exec t) (joy-exec e)))
;; Joy's version of cond
(joy-prim-void (cond p) (joy-cond p))
(define (joy-cond p)
(cond
((null? p) #f)
((null? (cdr p)) (joy-exec (car p)))
((joy-yields-true? (caar p)) (joy-exec (cdar p)))
(else (joy-cond (cdr p)))))
;; While-do combinator
(joy-prim-void (while p q) (joy-while p q))
(define (joy-while p q)
(when (joy-yields-true? p)
(joy-exec q)
(joy-while p q)))
;; Linear recursion combinator
(joy-prim-void (linrec p t r1 r2) (joy-linrec p t r1 r2))
(define (joy-linrec p t r1 r2)
(cond
((joy-yields-true? p) (joy-exec t))
(else (joy-exec r1) (joy-linrec p t r1 r2) (joy-exec r2))))
;; Tail recursion combinator
(joy-prim-void (tailrec p t r) (joy-tailrec p t r))
(define (joy-tailrec p t r)
(cond
((joy-yields-true? p) (joy-exec t))
(else (joy-exec r) (joy-tailrec p t r))))
;; Binary recursion combinator
(joy-prim-void (binrec p t r1 r2) (joy-binrec p t r1 r2))
(define (joy-binrec p t r1 r2)
(cond
((joy-yields-true? p) (joy-exec t))
(else (joy-exec r1)
(let* ((n2 (joy-pop!)) (n1 (joy-pop!)))
(joy-push! n1)
(joy-binrec p t r1 r2)
(joy-push! n2)
(joy-binrec p t r1 r2)
(joy-exec r2)))))
;; General recursion combinator
(joy-prim-void (genrec p t r1 r2)
(cond
((joy-yields-true? p) (joy-exec t))
(else
(joy-exec r1)
(joy-push! (list p t r1 r2 'genrec))
(joy-exec r2))))
;; FIXME: condlinrec
(joy-prim-void (step a p)
(if (string? a)
(joy-step-string a p 0 (string-length a))
(joy-step-list a p)))
(define (joy-step-string s p i n)
(cond
((zero? n) #f)
(else
(joy-stable (lambda ()
(joy-push! (string-ref s i))
(joy-exec p)))
(joy-step-string s p (+ i 1) (- n 1)))))
(define (joy-step-list a p)
(for-each (lambda (e)
(joy-stable (lambda ()
(joy-push! e)
(joy-exec p)))) a))
;; FIXME: fold
;; Map aggregate through quotation
(define joy-map-result '())
(joy-prim-void (map a p)
(cond
((string? a)
(let ((len (string-length a)))
(set! joy-map-result (make-string len))
(joy-map-string! a p 0 len)))
(else
(set! joy-map-result '())
(joy-map-list! a p)))
(joy-push! joy-map-result))
(define (joy-map-string! s p i n)
(cond
((zero? n) #f)
(else
(string-set! joy-map-result i
(joy-stable (lambda ()
(joy-push! (string-ref s i))
(joy-exec p))))
(joy-map-string! s p (+ i 1) (- n 1)))))
(define (joy-map-list! a p)
(cond
((null? a) #f)
(else
(set! joy-map-result (cons (joy-stable (lambda ()
(joy-push! (car a))
(joy-exec p))) joy-map-result))
(joy-map-list! (cdr a) p))))
;; Execute N times combinator
(joy-prim-void (times n p) (joy-times n p))
(define (joy-times n p)
(cond
((zero? n) #f)
(else (joy-exec p) (joy-times (- n 1) p))))
;; Infra-stack combinator
(joy-prim (infra l p)
(let ((s joy-stack))
(set! joy-stack l)
(joy-exec p)
(let ((r joy-stack))
(set! joy-stack s)
r)))
;; FIXME: filter, split, some, all
;; FIXME: treestep, treerec, treegenrec
;; FIXME: need to do something for manual (doc strings?)
;; System access is system-dependent and not included here.
;;; The Joy integrator
(display "integrator...")
(joy-prim-void (integrate words)
(for-each (lambda (w)
(joy-set! w (joy-integrate w '()))) words))
(define (joy-integrate word parents)
(cond
((memq word parents) word)
((symbol? word) (joy-integrate-sym word parents))
((pair? word) (cons
(joy-integrate (car word) parents)
(joy-integrate (cdr word) parents)))
(else word)))
(define (joy-integrate-sym s parents)
(let ((v (joy-get s)))
(if (pair? v)
(joy-integrate v (cons s parents))
s)))
;;; REPL
(display "repl...")
;; Read-exec-print loop
(define (joy-repl)
(joy-exec-autoput (read))
(joy-repl))
;;; Done
(display "done
")
(joy-repl)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment