diff --git a/iniquity-plus/ast.rkt b/iniquity-plus/ast.rkt new file mode 100644 index 0000000..bc239bf --- /dev/null +++ b/iniquity-plus/ast.rkt @@ -0,0 +1,65 @@ +#lang racket +(provide Lit Prim0 Prim1 Prim2 Prim3 If Eof Begin + Let Var Prog Defn App + Apply FunPlain FunRest FunCase) + +;; type Prog = (Prog (Listof Defn) Expr) +(struct Prog (ds e) #:prefab) + +;; type Defn = (Defn Id Fun) +(struct Defn (f fun) #:prefab) + +;; type Fun = (FunPlain [Listof Id] Expr) +;; | (FunRest [Listof Id] Id Expr) +;; | (FunCase [Listof FunCaseClause]) +;; type FunCaseClause = (FunPlain [Listof Id] Expr) +;; | (FunRest [Listof Id] Id Expr) +(struct FunPlain (xs e) #:prefab) +(struct FunRest (xs x e) #:prefab) +(struct FunCase (cs) #:prefab) +;; type Expr = (Lit Datum) +;; | (Eof) +;; | (Prim0 Op0) +;; | (Prim1 Op1 Expr) +;; | (Prim2 Op2 Expr Expr) +;; | (Prim3 Op3 Expr Expr Expr) +;; | (If Expr Expr Expr) +;; | (Begin Expr Expr) +;; | (Let Id Expr Expr) +;; | (Var Id) +;; | (App Id (Listof Expr)) +;; | (Apply Id (Listof Expr) Expr) + +;; type Id = Symbol +;; type Datum = Integer +;; | Boolean +;; | Character +;; | String +;; type Op0 = 'read-byte | 'peek-byte | 'void +;; type Op1 = 'add1 | 'sub1 +;; | 'zero? +;; | 'char? | 'integer->char | 'char->integer +;; | 'write-byte | 'eof-object? +;; | 'box | 'car | 'cdr | 'unbox +;; | 'empty? | 'cons? | 'box? +;; | 'vector? | 'vector-length +;; | 'string? | 'string-length +;; type Op2 = '+ | '- | '< | '= +;; | 'eq? | 'cons +;; | 'make-vector | 'vector-ref +;; | 'make-string | 'string-ref +;; type Op3 = 'vector-set! + +(struct Eof () #:prefab) +(struct Lit (d) #:prefab) +(struct Prim0 (p) #:prefab) +(struct Prim1 (p e) #:prefab) +(struct Prim2 (p e1 e2) #:prefab) +(struct Prim3 (p e1 e2 e3) #:prefab) +(struct If (e1 e2 e3) #:prefab) +(struct Begin (e1 e2) #:prefab) +(struct Let (x e1 e2) #:prefab) +(struct Var (x) #:prefab) +(struct App (f es) #:prefab) +(struct Apply (f es e) #:prefab) + diff --git a/iniquity-plus/build-runtime.rkt b/iniquity-plus/build-runtime.rkt new file mode 100644 index 0000000..7278350 --- /dev/null +++ b/iniquity-plus/build-runtime.rkt @@ -0,0 +1,14 @@ +#lang racket +(provide runtime-path) + +(require racket/runtime-path) +(define-runtime-path here ".") + +(unless (system (string-append "make -C '" + (path->string (normalize-path here)) + "' -s runtime.o")) + (error 'build-runtime "could not build runtime")) + +(define runtime-path + (normalize-path (build-path here "runtime.o"))) + diff --git a/iniquity-plus/compile-ops.rkt b/iniquity-plus/compile-ops.rkt new file mode 100644 index 0000000..a0ae02e --- /dev/null +++ b/iniquity-plus/compile-ops.rkt @@ -0,0 +1,350 @@ +#lang racket +(provide compile-op0 compile-op1 compile-op2 compile-op3 pad-stack) +(require "ast.rkt") +(require "types.rkt") +(require a86/ast) + +(define rax 'rax) +(define eax 'eax) ; 32-bit load/store +(define rbx 'rbx) ; heap +(define rdi 'rdi) ; arg +(define r8 'r8) ; scratch in op2 +(define r9 'r9) ; scratch +(define r10 'r10) ; scratch + +(define r15 'r15) ; stack pad (non-volatile) +(define rsp 'rsp) ; stack + +;; Op0 -> Asm +(define (compile-op0 p) + (match p + ['void (seq (Mov rax (value->bits (void))))] + ['read-byte (seq pad-stack (Call 'read_byte) unpad-stack)] + ['peek-byte (seq pad-stack (Call 'peek_byte) unpad-stack)])) + +;; Op1 -> Asm +(define (compile-op1 p) + (match p + ['add1 + (seq (assert-integer rax) + (Add rax (value->bits 1)))] + ['sub1 + (seq (assert-integer rax) + (Sub rax (value->bits 1)))] + ['zero? + (seq (assert-integer rax) + (Cmp rax 0) + if-equal)] + ['char? + (seq (And rax mask-char) + (Cmp rax type-char) + if-equal)] + ['char->integer + (seq (assert-char rax) + (Sar rax char-shift) + (Sal rax int-shift))] + ['integer->char + (seq (assert-codepoint) + (Sar rax int-shift) + (Sal rax char-shift) + (Xor rax type-char))] + ['eof-object? + (seq (Cmp rax (value->bits eof)) + if-equal)] + ['write-byte + (seq assert-byte + pad-stack + (Mov rdi rax) + (Call 'write_byte) + unpad-stack)] + ['box + (seq (Mov (Offset rbx 0) rax) ; memory write + (Mov rax rbx) ; put box in rax + (Xor rax type-box) ; tag as a box + (Add rbx 8))] + ['unbox + (seq (assert-box rax) + (Xor rax type-box) + (Mov rax (Offset rax 0)))] + ['car + (seq (assert-cons rax) + (Xor rax type-cons) + (Mov rax (Offset rax 8)))] + ['cdr + (seq (assert-cons rax) + (Xor rax type-cons) + (Mov rax (Offset rax 0)))] + + ['empty? (seq (Cmp rax (value->bits '())) if-equal)] + ['cons? (type-pred ptr-mask type-cons)] + ['box? (type-pred ptr-mask type-box)] + ['vector? (type-pred ptr-mask type-vect)] + ['string? (type-pred ptr-mask type-str)] + ['vector-length + (let ((zero (gensym)) + (done (gensym))) + (seq (assert-vector rax) + (Xor rax type-vect) + (Cmp rax 0) + (Je zero) + (Mov rax (Offset rax 0)) + (Sal rax int-shift) + (Jmp done) + (Label zero) + (Mov rax 0) + (Label done)))] + ['string-length + (let ((zero (gensym)) + (done (gensym))) + (seq (assert-string rax) + (Xor rax type-str) + (Cmp rax 0) + (Je zero) + (Mov rax (Offset rax 0)) + (Sal rax int-shift) + (Jmp done) + (Label zero) + (Mov rax 0) + (Label done)))])) + + +;; Op2 -> Asm +(define (compile-op2 p) + (match p + ['+ + (seq (Pop r8) + (assert-integer r8) + (assert-integer rax) + (Add rax r8))] + ['- + (seq (Pop r8) + (assert-integer r8) + (assert-integer rax) + (Sub r8 rax) + (Mov rax r8))] + ['< + (seq (Pop r8) + (assert-integer r8) + (assert-integer rax) + (Cmp r8 rax) + if-lt)] + ['= + (seq (Pop r8) + (assert-integer r8) + (assert-integer rax) + (Cmp r8 rax) + if-equal)] + ['cons + (seq (Mov (Offset rbx 0) rax) + (Pop rax) + (Mov (Offset rbx 8) rax) + (Mov rax rbx) + (Xor rax type-cons) + (Add rbx 16))] + ['eq? + (seq (Pop r8) + (Cmp rax r8) + if-equal)] + ['make-vector ;; size value + (let ((loop (gensym)) + (done (gensym)) + (empty (gensym))) + (seq (Pop r8) ;; r8 = size + (assert-natural r8) + (Cmp r8 0) ; special case empty vector + (Je empty) + + (Mov r9 rbx) + (Xor r9 type-vect) + + (Sar r8 int-shift) + (Mov (Offset rbx 0) r8) + (Add rbx 8) + + (Label loop) + (Mov (Offset rbx 0) rax) + (Add rbx 8) + (Sub r8 1) + (Cmp r8 0) + (Jne loop) + + (Mov rax r9) + (Jmp done) + + (Label empty) + (Mov rax type-vect) + (Label done)))] + ['vector-ref ; vector index + (seq (Pop r8) + (assert-vector r8) + (assert-integer rax) + (Cmp r8 type-vect) + (Je 'err) ; special case for empty vector + (Cmp rax 0) + (Jl 'err) + (Xor r8 type-vect) ; r8 = ptr + (Mov r9 (Offset r8 0)) ; r9 = len + (Sar rax int-shift) ; rax = index + (Sub r9 1) + (Cmp r9 rax) + (Jl 'err) + (Sal rax 3) + (Add r8 rax) + (Mov rax (Offset r8 8)))] + ['make-string + (let ((loop (gensym)) + (done (gensym)) + (empty (gensym))) + (seq (Pop r8) + (assert-natural r8) + (assert-char rax) + (Cmp r8 0) ; special case empty string + (Je empty) + + (Mov r9 rbx) + (Xor r9 type-str) + + (Sar r8 int-shift) + (Mov (Offset rbx 0) r8) + (Add rbx 8) + + (Sar rax char-shift) + + (Add r8 1) ; adds 1 + (Sar r8 1) ; when + (Sal r8 1) ; len is odd + + (Label loop) + (Mov (Offset rbx 0) eax) + (Add rbx 4) + (Sub r8 1) + (Cmp r8 0) + (Jne loop) + + (Mov rax r9) + (Jmp done) + + (Label empty) + (Mov rax type-str) + (Label done)))] + ['string-ref + (seq (Pop r8) + (assert-string r8) + (assert-integer rax) + (Cmp r8 type-str) + (Je 'err) ; special case for empty string + (Cmp rax 0) + (Jl 'err) + (Xor r8 type-str) ; r8 = ptr + (Mov r9 (Offset r8 0)) ; r9 = len + (Sar rax int-shift) ; rax = index + (Sub r9 1) + (Cmp r9 rax) + (Jl 'err) + (Sal rax 2) + (Add r8 rax) + (Mov 'eax (Offset r8 8)) + (Sal rax char-shift) + (Xor rax type-char))])) + +;; Op3 -> Asm +(define (compile-op3 p) + (match p + ['vector-set! + (seq (Pop r10) + (Pop r8) + (assert-vector r8) + (assert-integer r10) + (Cmp r8 type-vect) + (Je 'err) + (Cmp r10 0) + (Jl 'err) + (Xor r8 type-vect) ; r8 = ptr + (Mov r9 (Offset r8 0)) ; r9 = len + (Sar r10 int-shift) ; r10 = index + (Sub r9 1) + (Cmp r9 r10) + (Jl 'err) + (Sal r10 3) + (Add r8 r10) + (Mov (Offset r8 8) rax) + (Mov rax (value->bits (void))))])) + + +;; -> Asm +;; set rax to #t or #f if comparison flag is equal +(define if-equal + (seq (Mov rax (value->bits #f)) + (Mov r9 (value->bits #t)) + (Cmove rax r9))) + +;; -> Asm +;; set rax to #t or #f if comparison flag is less than +(define if-lt + (seq (Mov rax (value->bits #f)) + (Mov r9 (value->bits #t)) + (Cmovl rax r9))) + +(define (assert-type mask type) + (λ (arg) + (seq (Mov r9 arg) + (And r9 mask) + (Cmp r9 type) + (Jne 'err)))) + +(define (type-pred mask type) + (seq (And rax mask) + (Cmp rax type) + if-equal)) + +(define assert-integer + (assert-type mask-int type-int)) +(define assert-char + (assert-type mask-char type-char)) +(define assert-box + (assert-type ptr-mask type-box)) +(define assert-cons + (assert-type ptr-mask type-cons)) +(define assert-vector + (assert-type ptr-mask type-vect)) +(define assert-string + (assert-type ptr-mask type-str)) + +(define (assert-codepoint) + (let ((ok (gensym))) + (seq (assert-integer rax) + (Cmp rax (value->bits 0)) + (Jl 'err) + (Cmp rax (value->bits 1114111)) + (Jg 'err) + (Cmp rax (value->bits 55295)) + (Jl ok) + (Cmp rax (value->bits 57344)) + (Jg ok) + (Jmp 'err) + (Label ok)))) + +(define assert-byte + (seq (assert-integer rax) + (Cmp rax (value->bits 0)) + (Jl 'err) + (Cmp rax (value->bits 255)) + (Jg 'err))) + +(define (assert-natural r) + (seq (assert-integer r) + (Cmp r (value->bits 0)) + (Jl 'err))) + +;; Asm +;; Dynamically pad the stack to be aligned for a call +(define pad-stack + (seq (Mov r15 rsp) + (And r15 #b1000) + (Sub rsp r15))) + +;; Asm +;; Undo the stack alignment after a call +(define unpad-stack + (seq (Add rsp r15))) + diff --git a/iniquity-plus/compile.rkt b/iniquity-plus/compile.rkt new file mode 100644 index 0000000..69b0e26 --- /dev/null +++ b/iniquity-plus/compile.rkt @@ -0,0 +1,200 @@ +#lang racket +(provide (all-defined-out)) +(require "ast.rkt") +(require "compile-ops.rkt") +(require "types.rkt") +(require a86/ast) + +(define rax 'rax) +(define rbx 'rbx) ; heap +(define rsp 'rsp) ; stack +(define rdi 'rdi) ; arg +(define r15 'r15) ; stack pad (non-volatile) + +;; Prog -> Asm +(define (compile p) + (match p + [(Prog ds e) + (prog (Global 'entry) + (Extern 'peek_byte) + (Extern 'read_byte) + (Extern 'write_byte) + (Extern 'raise_error) + (Label 'entry) + (Push rbx) ; save callee-saved register + (Push r15) + (Mov rbx rdi) ; recv heap pointer + + (compile-e e '()) + (Pop r15) ; restore callee-save register + (Pop rbx) + (Ret) + (compile-defines ds) + (Label 'err) + pad-stack + (Call 'raise_error))])) + +;; [Listof Defn] -> Asm +(define (compile-defines ds) + (match ds + ['() (seq)] + [(cons d ds) + (seq (compile-define d) + (compile-defines ds))])) + +;; Defn -> Asm +(define (compile-define d) + (match d + [(Defn f fun) + (compile-fun f fun)])) + +;; Id Fun -> Asm +(define (compile-fun f fun) + (match fun + [(FunPlain xs e) + (seq (Label (symbol->label f)) + ;; TODO: check arity + (compile-e e (reverse xs)) + (Add rsp (* 8 (length xs))) + (Ret))] + [_ ;; TODO: handle other kinds of functions + (seq (seq (Label (symbol->label f)) + ;; unimplemented + (Jmp 'err)))])) + +;; type CEnv = (Listof [Maybe Id]) +;; Expr CEnv -> Asm +(define (compile-e e c) + (match e + [(Lit d) (compile-value d)] + [(Eof) (compile-value eof)] + [(Var x) (compile-variable x c)] + [(Prim0 p) (compile-prim0 p)] + [(Prim1 p e) (compile-prim1 p e c)] + [(Prim2 p e1 e2) (compile-prim2 p e1 e2 c)] + [(Prim3 p e1 e2 e3) (compile-prim3 p e1 e2 e3 c)] + [(If e1 e2 e3) + (compile-if e1 e2 e3 c)] + [(Begin e1 e2) + (compile-begin e1 e2 c)] + [(Let x e1 e2) + (compile-let x e1 e2 c)] + [(App f es) + (compile-app f es c)] + [(Apply f es e) + (compile-apply f es e c)])) + +;; Value -> Asm +(define (compile-value v) + (cond [(string? v) (compile-string v)] + [else (Mov rax (value->bits v))])) + +;; Id CEnv -> Asm +(define (compile-variable x c) + (let ((i (lookup x c))) + (seq (Mov rax (Offset rsp i))))) + +;; String -> Asm +(define (compile-string s) + (let ((len (string-length s))) + (if (zero? len) + (seq (Mov rax type-str)) + (seq (Mov rax len) + (Mov (Offset rbx 0) rax) + (compile-string-chars (string->list s) 8) + (Mov rax rbx) + (Xor rax type-str) + (Add rbx + (+ 8 (* 4 (if (odd? len) (add1 len) len)))))))) + +;; [Listof Char] Integer -> Asm +(define (compile-string-chars cs i) + (match cs + ['() (seq)] + [(cons c cs) + (seq (Mov rax (char->integer c)) + (Mov (Offset rbx i) 'eax) + (compile-string-chars cs (+ 4 i)))])) + +;; Op0 -> Asm +(define (compile-prim0 p) + (compile-op0 p)) + +;; Op1 Expr CEnv -> Asm +(define (compile-prim1 p e c) + (seq (compile-e e c) + (compile-op1 p))) + +;; Op2 Expr Expr CEnv -> Asm +(define (compile-prim2 p e1 e2 c) + (seq (compile-e e1 c) + (Push rax) + (compile-e e2 (cons #f c)) + (compile-op2 p))) + +;; Op3 Expr Expr Expr CEnv -> Asm +(define (compile-prim3 p e1 e2 e3 c) + (seq (compile-e e1 c) + (Push rax) + (compile-e e2 (cons #f c)) + (Push rax) + (compile-e e3 (cons #f (cons #f c))) + (compile-op3 p))) +;; Expr Expr Expr CEnv -> Asm +(define (compile-if e1 e2 e3 c) + (let ((l1 (gensym 'if)) + (l2 (gensym 'if))) + (seq (compile-e e1 c) + (Cmp rax (value->bits #f)) + (Je l1) + (compile-e e2 c) + (Jmp l2) + (Label l1) + (compile-e e3 c) + (Label l2)))) +;; Expr Expr CEnv -> Asm +(define (compile-begin e1 e2 c) + (seq (compile-e e1 c) + (compile-e e2 c))) + +;; Id Expr Expr CEnv -> Asm +(define (compile-let x e1 e2 c) + (seq (compile-e e1 c) + (Push rax) + (compile-e e2 (cons x c)) + (Add rsp 8))) + +;; Id [Listof Expr] CEnv -> Asm +;; The return address is placed above the arguments, so callee pops +;; arguments and return address is next frame +(define (compile-app f es c) + (let ((r (gensym 'ret))) + (seq (Lea rax r) + (Push rax) + (compile-es es (cons #f c)) + (Jmp (symbol->label f)) + (Label r)))) + +;; Id [Listof Expr] Expr CEnv -> Asm +(define (compile-apply f es e c) + ;; TODO: implement apply + (seq)) + +;; [Listof Expr] CEnv -> Asm +(define (compile-es es c) + (match es + ['() '()] + [(cons e es) + (seq (compile-e e c) + (Push rax) + (compile-es es (cons #f c)))])) + +;; Id CEnv -> Integer +(define (lookup x cenv) + (match cenv + ['() (error "undefined variable:" x)] + [(cons y rest) + (match (eq? x y) + [#t 0] + [#f (+ 8 (lookup x rest))])])) + diff --git a/iniquity-plus/interp-io.rkt b/iniquity-plus/interp-io.rkt new file mode 100644 index 0000000..f763754 --- /dev/null +++ b/iniquity-plus/interp-io.rkt @@ -0,0 +1,12 @@ +#lang racket +(provide interp/io) +(require "interp.rkt") +;; String Prog -> (Cons Value String) +;; Interpret p with given string as input, +;; return value and collected output as string +(define (interp/io p input) + (parameterize ((current-output-port (open-output-string)) + (current-input-port (open-input-string input))) + (cons (interp p) + (get-output-string (current-output-port))))) + diff --git a/iniquity-plus/interp-prim.rkt b/iniquity-plus/interp-prim.rkt new file mode 100644 index 0000000..aa73685 --- /dev/null +++ b/iniquity-plus/interp-prim.rkt @@ -0,0 +1,76 @@ +#lang racket +(provide interp-prim0 interp-prim1 interp-prim2 interp-prim3) + +;; Op0 -> Value +(define (interp-prim0 op) + (match op + ['read-byte (read-byte)] + ['peek-byte (peek-byte)] + ['void (void)])) + +;; Op1 Value -> Answer +(define (interp-prim1 op v) + (match (list op v) + [(list 'add1 (? integer?)) (add1 v)] + [(list 'sub1 (? integer?)) (sub1 v)] + [(list 'zero? (? integer?)) (zero? v)] + [(list 'char? v) (char? v)] + [(list 'integer->char (? codepoint?)) (integer->char v)] + [(list 'char->integer (? char?)) (char->integer v)] + [(list 'write-byte (? byte?)) (write-byte v)] + [(list 'eof-object? v) (eof-object? v)] + [(list 'box v) (box v)] + [(list 'unbox (? box?)) (unbox v)] + [(list 'car (? pair?)) (car v)] + [(list 'cdr (? pair?)) (cdr v)] + [(list 'empty? v) (empty? v)] + [(list 'cons? v) (cons? v)] + [(list 'box? v) (box? v)] + [(list 'vector? v) (vector? v)] + [(list 'vector-length (? vector?)) (vector-length v)] + [(list 'string? v) (string? v)] + [(list 'string-length (? string?)) (string-length v)] + [_ 'err])) + +;; Op2 Value Value -> Answer +(define (interp-prim2 op v1 v2) + (match (list op v1 v2) + [(list '+ (? integer?) (? integer?)) (+ v1 v2)] + [(list '- (? integer?) (? integer?)) (- v1 v2)] + [(list '< (? integer?) (? integer?)) (< v1 v2)] + [(list '= (? integer?) (? integer?)) (= v1 v2)] + [(list 'eq? v1 v2) (eq? v1 v2)] + [(list 'cons v1 v2) (cons v1 v2)] + [(list 'make-vector (? integer?) _) + (if (<= 0 v1) + (make-vector v1 v2) + 'err)] + [(list 'vector-ref (? vector?) (? integer?)) + (if (<= 0 v2 (sub1 (vector-length v1))) + (vector-ref v1 v2) + 'err)] + [(list 'make-string (? integer?) (? char?)) + (if (<= 0 v1) + (make-string v1 v2) + 'err)] + [(list 'string-ref (? string?) (? integer?)) + (if (<= 0 v2 (sub1 (string-length v1))) + (string-ref v1 v2) + 'err)] + [_ 'err])) + +;; Op3 Value Value Value -> Answer +(define (interp-prim3 p v1 v2 v3) + (match (list p v1 v2 v3) + [(list 'vector-set! (? vector?) (? integer?) _) + (if (<= 0 v2 (sub1 (vector-length v1))) + (vector-set! v1 v2 v3) + 'err)] + [_ 'err])) + +;; Any -> Boolean +(define (codepoint? v) + (and (integer? v) + (or (<= 0 v 55295) + (<= 57344 v 1114111)))) + diff --git a/iniquity-plus/interp.rkt b/iniquity-plus/interp.rkt new file mode 100644 index 0000000..3603ddd --- /dev/null +++ b/iniquity-plus/interp.rkt @@ -0,0 +1,154 @@ +#lang racket +(provide interp) +(provide interp-env) +(require "ast.rkt") +(require "interp-prim.rkt") + +;; type Value = +;; | Integer +;; | Boolean +;; | Character +;; | Eof +;; | Void +;; | '() +;; | (cons Value Value) +;; | (box Value) +;; | (string Character ...) +;; | (vector Value ...) + +;; type Env = (Listof (List Id Value)) +;; Prog -> Answer +(define (interp p) + (match p + [(Prog ds e) + (interp-env e '() ds)])) +;; Expr Env Defns -> Answer +(define (interp-env e r ds) + (match e + [(Lit d) d] + [(Eof) eof] + [(Var x) (lookup r x)] + [(Prim0 p) (interp-prim0 p)] + [(Prim1 p e) + (match (interp-env e r ds) + ['err 'err] + [v (interp-prim1 p v)])] + [(Prim2 p e1 e2) + (match (interp-env e1 r ds) + ['err 'err] + [v1 (match (interp-env e2 r ds) + ['err 'err] + [v2 (interp-prim2 p v1 v2)])])] + [(Prim3 p e1 e2 e3) + (match (interp-env e1 r ds) + ['err 'err] + [v1 (match (interp-env e2 r ds) + ['err 'err] + [v2 (match (interp-env e3 r ds) + ['err 'err] + [v3 (interp-prim3 p v1 v2 v3)])])])] + [(If e0 e1 e2) + (match (interp-env e0 r ds) + ['err 'err] + [v + (if v + (interp-env e1 r ds) + (interp-env e2 r ds))])] + [(Begin e1 e2) + (match (interp-env e1 r ds) + ['err 'err] + [v (interp-env e2 r ds)])] + [(Let x e1 e2) + (match (interp-env e1 r ds) + ['err 'err] + [v (interp-env e2 (ext r x v) ds)])] + [(App f es) + (match (interp-env* es r ds) + ['err 'err] + [vs + (match (defns-lookup ds f) + [(Defn _ fun) + (apply-fun fun vs ds)])])] + [(Apply f es e) + (match (interp-env* es r ds) + ['err 'err] + [vs + (match (interp-env e r ds) + ['err 'err] + [ws + (if (list? ws) + (match (defns-lookup ds f) + [(Defn _ fun) + (apply-fun fun (append vs ws) ds)]) + 'err)])])])) + +;; (Listof Expr) REnv Defns -> (Listof Value) | 'err +(define (interp-env* es r ds) + (match es + ['() '()] + [(cons e es) + (match (interp-env e r ds) + ['err 'err] + [v (match (interp-env* es r ds) + ['err 'err] + [vs (cons v vs)])])])) + +;; Fun [Listof Values] Defns -> Answer +(define (apply-fun f vs ds) + (match f + [(FunPlain xs e) + ; check arity matches-arity-exactly? + (if (= (length xs) (length vs)) + (interp-env e (zip xs vs) ds) + 'err)] + [(FunRest xs x e) + ; check arity is acceptable + (if (< (length vs) (length xs)) + 'err + (interp-env e + (zip (cons x xs) + (cons (drop vs (length xs)) + (take vs (length xs)))) + ds))] + [(FunCase cs) + (match (select-case-lambda cs (length vs)) + ['err 'err] + [f (apply-fun f vs ds)])])) + +;; [Listof FunCaseClause] Nat -> Fun | 'err +(define (select-case-lambda cs n) + (match cs + ['() 'err] + [(cons (and (FunPlain xs e) f) cs) + (if (= (length xs) n) + f + (select-case-lambda cs n))] + [(cons (and (FunRest xs x e) f) cs) + (if (<= (length xs) n) + f + (select-case-lambda cs n))])) + +;; Defns Symbol -> Defn +(define (defns-lookup ds f) + (findf (match-lambda [(Defn g _) (eq? f g)]) + ds)) + +(define (zip xs ys) + (match* (xs ys) + [('() '()) '()] + [((cons x xs) (cons y ys)) + (cons (list x y) + (zip xs ys))])) + +;; Env Id -> Value +(define (lookup r x) + (match r + [(cons (list y val) r) + (if (symbol=? x y) + val + (lookup r x))])) + +;; Env Id Value -> Env +(define (ext r x v) + (cons (list x v) r)) + diff --git a/iniquity-plus/main.rkt b/iniquity-plus/main.rkt new file mode 100644 index 0000000..e0e3892 --- /dev/null +++ b/iniquity-plus/main.rkt @@ -0,0 +1,13 @@ +#lang racket +(require "ast.rkt") +(require "parse.rkt") +(require "interp.rkt") +(require "compile.rkt") +(require "run.rkt") +(provide (all-from-out "ast.rkt")) +(provide (all-from-out "parse.rkt")) +(provide (all-from-out "interp.rkt")) +(provide (all-from-out "compile.rkt")) +(provide (all-from-out "run.rkt")) + + diff --git a/iniquity-plus/parse.rkt b/iniquity-plus/parse.rkt new file mode 100644 index 0000000..430ea3b --- /dev/null +++ b/iniquity-plus/parse.rkt @@ -0,0 +1,123 @@ +#lang racket +(provide parse parse-e parse-define) +(require "ast.rkt") + +;; S-Expr ... -> Prog +(define (parse . s) + (match s + [(cons (and (cons 'define _) d) s) + (match (apply parse s) + [(Prog ds e) + (Prog (cons (parse-define d) ds) e)])] + [(cons e '()) (Prog '() (parse-e e))] + [_ (error "program parse error")])) + +;; S-Expr -> Defn +(define (parse-define s) + (match s + [(list 'define (? symbol? f) + (cons 'case-lambda cs)) + (Defn f (FunCase (parse-case-lambda-clauses cs)))] + [(list 'define (cons (? symbol? f) xs) e) + (if (all symbol? xs) + (Defn f (parse-param-list xs e)) + (error "parse definition error"))] + [_ (error "Parse defn error" s)])) + +;; like andmap, but work on improper lists too +(define (all p? xs) + (match xs + ['() #t] + [(cons x xs) (and (p? x) (all p? xs))] + [x (p? x)])) + +;; S-Expr -> [Listof FunCaseClause] +(define (parse-case-lambda-clauses cs) + (match cs + ['() '()] + [(cons c cs) + (cons (parse-case-lambda-clause c) + (parse-case-lambda-clauses cs))] + [_ + (error "parse case-lambda error")])) + +;; S-Expr -> FunRest +(define (parse-case-lambda-clause c) + (match c + [(list (? symbol? x) e) + (FunRest '() x (parse-e e))] + [(list xs e) + (parse-param-list xs e)])) + +;; S-Expr S-Expr -> FunPlain or FunRest +(define (parse-param-list xs e) + (match xs + ['() (FunPlain '() (parse-e e))] + [(cons x xs) + (match (parse-param-list xs e) + [(FunPlain xs e) (FunPlain (cons x xs) e)] + [(FunRest xs y e) (FunRest (cons x xs) y e)])] + [(? symbol? xs) + (FunRest '() xs (parse-e e))] + [_ + (error "parse parameter list error")])) + +;; S-Expr -> Expr +(define (parse-e s) + (match s + [(? datum?) (Lit s)] + ['eof (Eof)] + [(? symbol?) (Var s)] + [(list 'quote (list)) (Lit '())] + [(list (? op0? p0)) (Prim0 p0)] + [(list (? op1? p1) e) (Prim1 p1 (parse-e e))] + [(list (? op2? p2) e1 e2) (Prim2 p2 (parse-e e1) (parse-e e2))] + [(list (? op3? p3) e1 e2 e3) + (Prim3 p3 (parse-e e1) (parse-e e2) (parse-e e3))] + [(list 'begin e1 e2) + (Begin (parse-e e1) (parse-e e2))] + [(list 'if e1 e2 e3) + (If (parse-e e1) (parse-e e2) (parse-e e3))] + [(list 'let (list (list (? symbol? x) e1)) e2) + (Let x (parse-e e1) (parse-e e2))] + [(cons 'apply (cons (? symbol? f) es)) + (parse-apply f es)] + [(cons (? symbol? f) es) + (App f (map parse-e es))] + [_ (error "Parse error" s)])) + +;; Id S-Expr -> Expr +(define (parse-apply f es) + (match es + [(list e) (Apply f '() (parse-e e))] + [(cons e es) + (match (parse-apply f es) + [(Apply f es e0) + (Apply f (cons (parse-e e) es) e0)])] + [_ (error "parse apply error")])) + + +;; Any -> Boolean +(define (datum? x) + (or (exact-integer? x) + (boolean? x) + (char? x) + (string? x))) + +;; Any -> Boolean +(define (op0? x) + (memq x '(read-byte peek-byte void))) + +(define (op1? x) + (memq x '(add1 sub1 zero? char? integer->char char->integer + write-byte eof-object? + box unbox empty? cons? box? car cdr + vector? vector-length string? string-length))) + +(define (op2? x) + (memq x '(+ - < = eq? cons + make-vector vector-ref make-string string-ref))) + +(define (op3? x) + (memq x '(vector-set!))) + diff --git a/iniquity-plus/run.rkt b/iniquity-plus/run.rkt new file mode 100644 index 0000000..3448bba --- /dev/null +++ b/iniquity-plus/run.rkt @@ -0,0 +1,19 @@ +#lang racket +(require a86/interp) +(require "types.rkt") +(require "build-runtime.rkt") +(provide run run/io) +;; Asm -> Answer +(define (run is) + (parameterize ((current-objs (list (path->string runtime-path)))) + (match (asm-interp is) + ['err 'err] + [b (bits->value b)]))) +;; Asm String -> (cons Answer String) +(define (run/io is in) + (parameterize ((current-objs (list (path->string runtime-path)))) + (match (asm-interp/io is in) + [(cons 'err out) (cons 'err out)] + [(cons b out) + (cons (bits->value b) out)]))) + diff --git a/iniquity-plus/test/compile.rkt b/iniquity-plus/test/compile.rkt new file mode 100644 index 0000000..48ea7ad --- /dev/null +++ b/iniquity-plus/test/compile.rkt @@ -0,0 +1,8 @@ +#lang racket +(require "../compile.rkt") +(require "../parse.rkt") +(require "../run.rkt") +(require "test-runner.rkt") +(test (λ p (run (compile (apply parse p))))) +(test/io (λ (in . p) (run/io (compile (apply parse p)) in))) + diff --git a/iniquity-plus/test/interp.rkt b/iniquity-plus/test/interp.rkt new file mode 100644 index 0000000..c6b3641 --- /dev/null +++ b/iniquity-plus/test/interp.rkt @@ -0,0 +1,8 @@ +#lang racket +(require "../interp.rkt") +(require "../interp-io.rkt") +(require "../parse.rkt") +(require "test-runner.rkt") +(test (λ p (interp (apply parse p)))) +(test/io (λ (in . p) (interp/io (apply parse p) in))) + diff --git a/iniquity-plus/test/test-runner.rkt b/iniquity-plus/test/test-runner.rkt new file mode 100644 index 0000000..c486ede --- /dev/null +++ b/iniquity-plus/test/test-runner.rkt @@ -0,0 +1,491 @@ +#lang racket +(provide test test/io) +(require rackunit) + +(define (test run) + (begin ;; Abscond + (check-equal? (run 7) 7) + (check-equal? (run -8) -8)) + + (begin ;; Blackmail + (check-equal? (run '(add1 (add1 7))) 9) + (check-equal? (run '(add1 (sub1 7))) 7)) + + (begin ;; Con + (check-equal? (run '(if (zero? 0) 1 2)) 1) + (check-equal? (run '(if (zero? 1) 1 2)) 2) + (check-equal? (run '(if (zero? -7) 1 2)) 2) + (check-equal? (run '(if (zero? 0) + (if (zero? 1) 1 2) + 7)) + 2) + (check-equal? (run '(if (zero? (if (zero? 0) 1 0)) + (if (zero? 1) 1 2) + 7)) + 7)) + + (begin ;; Dupe + (check-equal? (run #t) #t) + (check-equal? (run #f) #f) + (check-equal? (run '(if #t 1 2)) 1) + (check-equal? (run '(if #f 1 2)) 2) + (check-equal? (run '(if 0 1 2)) 1) + (check-equal? (run '(if #t 3 4)) 3) + (check-equal? (run '(if #f 3 4)) 4) + (check-equal? (run '(if 0 3 4)) 3) + (check-equal? (run '(zero? 4)) #f) + (check-equal? (run '(zero? 0)) #t)) + + (begin ;; Dodger + (check-equal? (run #\a) #\a) + (check-equal? (run #\b) #\b) + (check-equal? (run '(char? #\a)) #t) + (check-equal? (run '(char? #t)) #f) + (check-equal? (run '(char? 8)) #f) + (check-equal? (run '(char->integer #\a)) (char->integer #\a)) + (check-equal? (run '(integer->char 955)) #\λ)) + + (begin ;; Evildoer + (check-equal? (run '(void)) (void)) + (check-equal? (run '(begin 1 2)) 2) + (check-equal? (run '(eof-object? (void))) #f)) + + (begin ;; Extort + (check-equal? (run '(add1 #f)) 'err) + (check-equal? (run '(sub1 #f)) 'err) + (check-equal? (run '(zero? #f)) 'err) + (check-equal? (run '(char->integer #f)) 'err) + (check-equal? (run '(integer->char #f)) 'err) + (check-equal? (run '(integer->char -1)) 'err) + (check-equal? (run '(write-byte #f)) 'err) + (check-equal? (run '(write-byte -1)) 'err) + (check-equal? (run '(write-byte 256)) 'err) + (check-equal? (run '(begin (integer->char 97) + (integer->char 98))) + #\b)) + + (begin ;; Fraud + (check-equal? (run '(let ((x 7)) x)) 7) + (check-equal? (run '(let ((x 7)) 2)) 2) + (check-equal? (run '(let ((x 7)) (add1 x))) 8) + (check-equal? (run '(let ((x (add1 7))) x)) 8) + (check-equal? (run '(let ((x 7)) (let ((y 2)) x))) 7) + (check-equal? (run '(let ((x 7)) (let ((x 2)) x))) 2) + (check-equal? (run '(let ((x 7)) (let ((x (add1 x))) x))) 8) + + (check-equal? (run '(let ((x 0)) + (if (zero? x) 7 8))) + 7) + (check-equal? (run '(let ((x 1)) + (add1 (if (zero? x) 7 8)))) + 9) + (check-equal? (run '(+ 3 4)) 7) + (check-equal? (run '(- 3 4)) -1) + (check-equal? (run '(+ (+ 2 1) 4)) 7) + (check-equal? (run '(+ (+ 2 1) (+ 2 2))) 7) + (check-equal? (run '(let ((x (+ 1 2))) + (let ((z (- 4 x))) + (+ (+ x x) z)))) + 7) + + (check-equal? (run '(= 5 5)) #t) + (check-equal? (run '(= 4 5)) #f) + (check-equal? (run '(= (add1 4) 5)) #t) + (check-equal? (run '(< 5 5)) #f) + (check-equal? (run '(< 4 5)) #t) + (check-equal? (run '(< (add1 4) 5)) #f)) + + (begin ;; Hustle + (check-equal? (run ''()) '()) + (check-equal? (run '(empty? '())) #t) + (check-equal? (run '(empty? 3)) #f) + (check-equal? (run '(empty? (cons 1 2))) #f) + (check-equal? (run '(box 1)) (box 1)) + (check-equal? (run '(box -1)) (box -1)) + (check-equal? (run '(cons 1 2)) (cons 1 2)) + (check-equal? (run '(unbox (box 1))) 1) + (check-equal? (run '(car (cons 1 2))) 1) + (check-equal? (run '(cdr (cons 1 2))) 2) + (check-equal? (run '(cons 1 '())) (list 1)) + (check-equal? (run '(let ((x (cons 1 2))) + (begin (cdr x) + (car x)))) + 1) + (check-equal? (run '(let ((x (cons 1 2))) + (let ((y (box 3))) + (unbox y)))) + 3) + (check-equal? (run '(eq? 1 1)) #t) + (check-equal? (run '(eq? 1 2)) #f) + (check-equal? (run '(eq? (cons 1 2) (cons 1 2))) #f) + (check-equal? (run '(let ((x (cons 1 2))) (eq? x x))) #t)) + + (begin ;; Hoax + (check-equal? (run '(make-vector 0 0)) #()) + (check-equal? (run '(make-vector 1 0)) #(0)) + (check-equal? (run '(make-vector 3 0)) #(0 0 0)) + (check-equal? (run '(make-vector 3 5)) #(5 5 5)) + (check-equal? (run '(vector? (make-vector 0 0))) #t) + (check-equal? (run '(vector? (cons 0 0))) #f) + (check-equal? (run '(vector-ref (make-vector 0 #f) 0)) 'err) + (check-equal? (run '(vector-ref (make-vector 3 5) -1)) 'err) + (check-equal? (run '(vector-ref (make-vector 3 5) 0)) 5) + (check-equal? (run '(vector-ref (make-vector 3 5) 1)) 5) + (check-equal? (run '(vector-ref (make-vector 3 5) 2)) 5) + (check-equal? (run '(vector-ref (make-vector 3 5) 3)) 'err) + (check-equal? (run '(let ((x (make-vector 3 5))) + (begin (vector-set! x 0 4) + x))) + #(4 5 5)) + (check-equal? (run '(let ((x (make-vector 3 5))) + (begin (vector-set! x 1 4) + x))) + #(5 4 5)) + (check-equal? (run '(vector-length (make-vector 3 #f))) 3) + (check-equal? (run '(vector-length (make-vector 0 #f))) 0) + (check-equal? (run '"") "") + (check-equal? (run '"fred") "fred") + (check-equal? (run '"wilma") "wilma") + (check-equal? (run '(make-string 0 #\f)) "") + (check-equal? (run '(make-string 3 #\f)) "fff") + (check-equal? (run '(make-string 3 #\g)) "ggg") + (check-equal? (run '(string-length "")) 0) + (check-equal? (run '(string-length "fred")) 4) + (check-equal? (run '(string-ref "" 0)) 'err) + (check-equal? (run '(string-ref (make-string 0 #\a) 0)) 'err) + (check-equal? (run '(string-ref "fred" 0)) #\f) + (check-equal? (run '(string-ref "fred" 1)) #\r) + (check-equal? (run '(string-ref "fred" 2)) #\e) + (check-equal? (run '(string-ref "fred" 4)) 'err) + (check-equal? (run '(string? "fred")) #t) + (check-equal? (run '(string? (cons 1 2))) #f) + (check-equal? (run '(begin (make-string 3 #\f) + (make-string 3 #\f))) + "fff") + (check-equal? (run '(vector-set! (make-vector 0 #f) 0 #t)) 'err)) + + (begin ;; Iniquity + (check-equal? (run + '(define (f x) x) + '(f 5)) + 5) + (check-equal? (run + '(define (tri x) + (if (zero? x) + 0 + (+ x (tri (sub1 x))))) + '(tri 9)) + 45) + + (check-equal? (run + '(define (even? x) + (if (zero? x) + #t + (odd? (sub1 x)))) + '(define (odd? x) + (if (zero? x) + #f + (even? (sub1 x)))) + '(even? 101)) + #f) + + (check-equal? (run + '(define (map-add1 xs) + (if (empty? xs) + '() + (cons (add1 (car xs)) + (map-add1 (cdr xs))))) + '(map-add1 (cons 1 (cons 2 (cons 3 '()))))) + '(2 3 4)) + (check-equal? (run '(define (f x y) y) + '(f 1 (add1 #f))) + 'err)) + + (begin ;; Iniquity+ + (check-equal? (run '(define (f x) x) + '(f)) + 'err) + (check-equal? (run '(define (f) 1) + '(f 2)) + 'err) + (check-equal? (run '(define (f x) x) + '(let ((y 2)) + (f 1 y))) + 'err) + (check-equal? (run '(define (f . xs) + (if (empty? xs) + #t + (f))) + '(f 1 2 3)) + #t) + (check-equal? (run '(define (list . xs) xs) + '(list (list) (list 1 2 3) (list #t) (list 3 4 5))) + '(() (1 2 3) (#t) (3 4 5))) + (check-equal? (run '(define (f x y . z) (cons x (cons y z))) + '(cons (f 1 2) (cons (f 8 9 10) '()))) + '((1 2) (8 9 10))) + (check-equal? (run '(define (f x . xs) x) + '(f 1)) + 1) + (check-equal? (run '(define (f x . xs) xs) + '(f 1)) + '()) + (check-equal? (run '(define (f x . xs) xs) + '(f)) + 'err) + (check-equal? (run '(define (f x . xs) xs) + '(let ((x 3)) + (f 1 x))) + '(3)) + (check-equal? (run '(define f + (case-lambda)) + '(f)) + 'err) + (check-equal? (run '(define f + (case-lambda)) + '(add1 8)) + 9) + (check-equal? (run '(define f + (case-lambda + [(x) x])) + '(f 1)) + 1) + (check-equal? (run '(define f + (case-lambda + [x #t] + [(x) x])) + '(f 1)) + #t) + (check-equal? (run '(define f + (case-lambda + [(x y) #f] + [(x) x])) + '(cons (f 1) (cons (f 1 2) '()))) + '(1 #f)) + (check-equal? (run '(define f + (case-lambda + [x #f] + [y #t])) + '(cons (f 1) (cons (f 1 2) '()))) + '(#f #f)) + (check-equal? (run '(define f + (case-lambda + [(x y . z) z] + [(x) (+ x x)] + [z 2])) + '(cons (f 1 2) + (cons (f 1) + (cons (f 1 2 3) + '())))) + '(() 2 (3))) + + (check-equal? (run '(define (f) 1) + '(apply f '())) + 1) + (check-equal? (run '(define (f . xs) 1) + '(apply f '())) + 1) + (check-equal? (run '(define (f . xs) xs) + '(apply f '())) + '()) + (check-equal? (run '(define (f . xs) xs) + '(apply f (cons 1 (cons 2 (cons 3 '()))))) + '(1 2 3)) + (check-equal? (run '(define (f . xs) xs) + '(apply f 1 2 (cons 3 '()))) + '(1 2 3)) + (check-equal? (run '(define (append . xss) + (if (empty? xss) + '() + (if (empty? (car xss)) + (apply append (cdr xss)) + (cons (car (car xss)) + (apply append (cdr (car xss)) (cdr xss)))))) + '(define (list . xs) xs) + '(define (flatten xs) + (apply append xs)) + '(flatten (list (append) (append (list 1 2 3) (list 4 5) (list 6)) (list 7)))) + '(1 2 3 4 5 6 7)) + + ;; Extra tests + (check-equal? (run '(define f (case-lambda)) + '(if #f (f) 1)) + 1) + (check-equal? (run '(define (f . xs) xs) + '(let ((x (f 1 2 3))) + (f x))) + (list (list 1 2 3))) + (check-equal? (run '(define (f x . xs) xs) + '(let ((x (f 1 2 3))) + (f x))) + '()) + (check-equal? (run '(define (f x . xs) + (let ((ys xs)) + (if (empty? xs) + x + (apply f ys)))) + '(let ((z 1)) + (f 1 2 3))) + 3) + (check-equal? (run '(define (f x . xs) + (let ((ys xs)) + (if (empty? xs) + x + (apply f ys)))) + '(let ((z 1)) + (f (f 1 2 3)))) + 3) + (check-equal? (run '(define f + (case-lambda + [(x . xs) + (let ((ys xs)) + (if (empty? xs) + x + (apply f xs)))])) + '(let ((z 1)) + (f (f 1 2 3)))) + 3) + (check-equal? (run '(define f + (case-lambda + [(x) x] + [(x . xs) + (apply f xs)])) + '(f 1 2 3)) + 3) + (check-equal? (run '(define f + (case-lambda + [(x) x] + [(x . xs) + (apply f xs)])) + '(f)) + 'err) + (check-equal? (run '(define f + (case-lambda + [(x y) x] + [(x y . xs) + (apply f xs)])) + '(f 1 2 3)) + 'err) + (check-equal? (run '(define f + (case-lambda + [(x y) x] + [(x y . xs) + (apply f xs)])) + '(f 1 2 (cons 3 (cons 4 '())))) + 'err) + (check-equal? (run '(define f + (case-lambda + [(x) (char->integer (car x))] + [(x y . xs) + (apply f xs)])) + '(f 1 2 (cons #\A 4))) + 65) + (check-equal? (run '(define f + (case-lambda + [(x y) x] + [(x y . xs) + (char->integer y)] + [(x y z . xs) + (char->integer z)])) + '(f 1 #\a 3)) + 97) + (check-equal? (run '(define plus + (case-lambda + [() 0] + [(n . ns) (+ n (apply plus ns))])) + '(define (cars xss) + (if (empty? xss) + '() + (cons (car (car xss)) (cars (cdr xss))))) + '(define (cdrs xss) + (if (empty? xss) + '() + (cons (cdr (car xss)) (cdrs (cdr xss))))) + '(define (mapplus ns . nss) + (if (cons? ns) + (cons (apply plus (car ns) (cars nss)) + (apply mapplus (cdr ns) (cdrs nss))) + '())) + '(mapplus (cons 1 (cons 2 '())) + (cons 3 (cons 4 '())) + (cons 5 (cons 6 '())))) + '(9 12)))) + +(define (test/io run) + (begin ;; Evildoer + (check-equal? (run "" 7) (cons 7 "")) + (check-equal? (run "" '(write-byte 97)) (cons (void) "a")) + (check-equal? (run "a" '(read-byte)) (cons 97 "")) + (check-equal? (run "b" '(begin (write-byte 97) (read-byte))) + (cons 98 "a")) + (check-equal? (run "" '(read-byte)) (cons eof "")) + (check-equal? (run "" '(eof-object? (read-byte))) (cons #t "")) + (check-equal? (run "a" '(eof-object? (read-byte))) (cons #f "")) + (check-equal? (run "" '(begin (write-byte 97) (write-byte 98))) + (cons (void) "ab")) + + (check-equal? (run "ab" '(peek-byte)) (cons 97 "")) + (check-equal? (run "ab" '(begin (peek-byte) (read-byte))) (cons 97 "")) + (check-equal? (run "†" '(read-byte)) (cons 226 "")) + (check-equal? (run "†" '(peek-byte)) (cons 226 ""))) + + (begin ;; Extort + (check-equal? (run "" '(write-byte #t)) (cons 'err ""))) + + (begin ;; Fraud + (check-equal? (run "" '(let ((x 97)) (write-byte x))) (cons (void) "a")) + (check-equal? (run "" + '(let ((x 97)) + (begin (write-byte x) + x))) + (cons 97 "a")) + (check-equal? (run "b" '(let ((x 97)) (begin (read-byte) x))) + (cons 97 "")) + (check-equal? (run "b" '(let ((x 97)) (begin (peek-byte) x))) + (cons 97 ""))) + + (begin ;; Iniquity + (check-equal? (run "" + '(define (print-alphabet i) + (if (zero? i) + (void) + (begin (write-byte (- 123 i)) + (print-alphabet (sub1 i))))) + '(print-alphabet 26)) + (cons (void) "abcdefghijklmnopqrstuvwxyz")) + + (check-equal? (run "" + '(define (f x) + (write-byte x)) + '(f 97)) + (cons (void) "a")) + (check-equal? (run "" + '(define (f x y) + (write-byte x)) + '(f 97 98)) + (cons (void) "a")) + (check-equal? (run "" + '(define (f x) + (let ((y x)) + (write-byte y))) + '(f 97)) + (cons (void) "a")) + (check-equal? (run "" + '(define (f x y) + (let ((y x)) + (write-byte y))) + '(f 97 98)) + (cons (void) "a")) + (check-equal? (run "" + '(define (f x) + (write-byte x)) + '(let ((z 97)) + (f z))) + (cons (void) "a")) + (check-equal? (run "" + '(define (f x y) + (write-byte x)) + '(let ((z 97)) + (f z 98))) + (cons (void) "a")))) + diff --git a/iniquity-plus/types.rkt b/iniquity-plus/types.rkt new file mode 100644 index 0000000..e0da50d --- /dev/null +++ b/iniquity-plus/types.rkt @@ -0,0 +1,90 @@ +#lang racket +(provide (all-defined-out)) +(require ffi/unsafe) + +(define imm-shift 3) +(define imm-mask #b111) +(define ptr-mask #b111) +(define type-box #b001) +(define type-cons #b010) +(define type-vect #b011) +(define type-str #b100) +(define int-shift (+ 1 imm-shift)) +(define mask-int #b1111) +(define char-shift (+ 2 imm-shift)) +(define type-int #b0000) +(define type-char #b01000) +(define mask-char #b11111) + +(define (bits->value b) + (cond [(= b (value->bits #t)) #t] + [(= b (value->bits #f)) #f] + [(= b (value->bits eof)) eof] + [(= b (value->bits (void))) (void)] + [(= b (value->bits '())) '()] + [(int-bits? b) + (arithmetic-shift b (- int-shift))] + [(char-bits? b) + (integer->char (arithmetic-shift b (- char-shift)))] + [(box-bits? b) + (box (bits->value (heap-ref b)))] + [(cons-bits? b) + (cons (bits->value (heap-ref (+ b 8))) + (bits->value (heap-ref b)))] + [(vect-bits? b) + (if (zero? (untag b)) + (vector) + (build-vector (heap-ref b) + (lambda (j) + (bits->value (heap-ref (+ b (* 8 (add1 j))))))))] + [(str-bits? b) + (if (zero? (untag b)) + (string) + (build-string (heap-ref b) + (lambda (j) + (char-ref (+ b 8) j))))] + [else (error "invalid bits")])) + +(define (value->bits v) + (cond [(eq? v #t) #b00011000] + [(eq? v #f) #b00111000] + [(integer? v) (arithmetic-shift v int-shift)] + [(eof-object? v) #b01011000] + [(void? v) #b01111000] + [(empty? v) #b10011000] + [(char? v) + (bitwise-ior type-char + (arithmetic-shift (char->integer v) char-shift))] + [else (error "not an immediate value" v)])) + +(define (int-bits? v) + (= type-int (bitwise-and v mask-int))) + +(define (char-bits? v) + (= type-char (bitwise-and v mask-char))) + +(define (imm-bits? v) + (zero? (bitwise-and v imm-mask))) + +(define (cons-bits? v) + (= type-cons (bitwise-and v imm-mask))) + +(define (box-bits? v) + (= type-box (bitwise-and v imm-mask))) + +(define (vect-bits? v) + (= type-vect (bitwise-and v imm-mask))) + +(define (str-bits? v) + (= type-str (bitwise-and v imm-mask))) + +(define (untag i) + (arithmetic-shift (arithmetic-shift i (- (integer-length ptr-mask))) + (integer-length ptr-mask))) + +(define (heap-ref i) + (ptr-ref (cast (untag i) _int64 _pointer) _int64)) + +(define (char-ref i j) + (integer->char (ptr-ref (cast (untag i) _int64 _pointer) _uint32 j))) + diff --git a/knock-plus/interp.rkt b/knock-plus/interp.rkt index 62e643d..93c1023 100644 --- a/knock-plus/interp.rkt +++ b/knock-plus/interp.rkt @@ -77,12 +77,12 @@ ; check arity matches (if (= (length xs) (length vs)) (interp-env e (zip xs vs) ds) - 'err)])])] + 'err)])])] [(Match e ps es) (match (interp-env e r ds) ['err 'err] [v - (interp-match v ps es r ds)])])) + (interp-match v ps es r ds)])])) ;; (Listof Expr) REnv Defns -> (Listof Value) | 'err (define (interp-env* es r ds) @@ -137,7 +137,7 @@ ['err 'err] [#f #f] [v r])] - [_ 'err])] + [_ 'err])] [(Vect ps) (and (vector? v) (interp-match-pat-list ps (vector->list v) r ds))]))