Index: clib/compose.c ================================================================== --- clib/compose.c +++ clib/compose.c @@ -27,43 +27,79 @@ typedef struct safestr { union { const char* ptr; char* mutptr; }; // im sorry okay - bool heap; +# ifndef k_static + bool heap; +# endif size_t len; } safestr; -void delstr(safestr s) fn ({ - if (s.heap) { free(s.mutptr); } -}); +#ifndef k_static + void delstr(safestr s) fn ({ + if (s.heap) { free(s.mutptr); } + }); +#endif void clrstr(safestr* s) fn ({ - delstr(*s); - s->heap = false; +# ifndef k_static + delstr(*s); + s->heap = false; +# endif s->ptr = NULL; s->len = 0; }) -char* compose(pstr* lst,size_t ct, size_t* strsz) fn({ +size_t pstrsum(pstr* lst,size_t ct) fn({ size_t len = 0; for (size_t i = 0; i < ct; ++i) { if (lst[i].len == 0) { if (lst[i].ptr == NULL) continue; lst[i].len = strlen(lst[i].ptr); } len += lst[i].len; } - if (strsz != NULL) *strsz = len; - if (len == 0) return NULL; - - char* str = malloc(len + 1); - char* ptr = str; +}) + +char* pstrcoll(pstr* lst, size_t ct, char* ptr) fn({ for (size_t i = 0; i < ct; ++i) { if (lst[i].len == 0) continue; strncpy(ptr,lst[i].ptr,lst[i].len); ptr += lst[i].len; } + return ptr; +}) + +#ifndef k_static +char* compose(pstr* lst,size_t ct, size_t* strsz) fn({ + size_t len = pstrsum(lst,ct) + if (strsz != NULL) *strsz = len; + if (len == 0) return NULL; + + char* str = malloc(len + 1); + char* ptr = pstrcoll(lst, ct, ptr); *ptr = 0; return str; }); +#endif + +char* impose(pstr* lst,size_t ct, size_t* strsz, char* buf) fn({ + size_t len = pstrsum(lst,ct); + if (strsz != NULL) *strsz = len; + if (len == 0) return NULL; + + char* ptr = pstrcoll(lst, ct, buf); + *ptr = 0; + return ptr; +}); +char* imprint(pstr lst, size_t* strsz, char* buf) fn({ + size_t len = pstrsum(&lst,1); + if (strsz != NULL) *strsz = len; + if (len == 0) return NULL; + + char* ptr = pstrcoll(&lst,1,buf); + *ptr = 0; + return ptr; +}); +#undef fn Index: clipserv/clip.b ================================================================== --- clipserv/clip.b +++ clipserv/clip.b @@ -189,6 +189,10 @@ ] true [ usage ] ] ] -quit/return main args no +success: main args no +if eq? success true [ + quit/return 0 +] +quit/return 1 ADDED scmlib/lisp-macro.scm Index: scmlib/lisp-macro.scm ================================================================== --- scmlib/lisp-macro.scm +++ scmlib/lisp-macro.scm @@ -0,0 +1,16 @@ +; [ʞ] lisp-macro +; ~ lexi hale +; © affero general public license +; > (load "lib/lisp-macro.scm") + +; enable use of the define-macro syntax in chicken scheme +; ex: (define-macro (if-or-f . body) +; `(if ,(car body) ,(cadr body) #f)) + +(define-syntax define-macro + (er-macro-transformer (lambda (exp r c) + `(define-syntax ,(caadr exp) + (er-macro-transformer + (lambda (,(cdadr exp) id-rename id-compare) + (let ((,(cdadr exp) (cdr ,(cdadr exp)))) + ,@(cddr exp)))))))) ADDED scmlib/select.scm Index: scmlib/select.scm ================================================================== --- scmlib/select.scm +++ scmlib/select.scm @@ -0,0 +1,33 @@ +; [ʞ] select.scm +; ~ lexi hale +; © CC0 / public domain +; > (include "lisp-macro.scm") +; (include "select.scm") +; ? a reimplementation of (case) that lets you +; specify your own evaluator function. same syntax, +; except the expression to evaluate is preceeded by +; a predicate function +; = (select equal? "weed" +; ("weed" 420) +; ("sex" 69) +; (else 42069) + +(define-macro (select . body) + (let* ([eqfn (list-ref body 0)] + [expr (list-ref body 1)] + [cases (cddr body)] + [result (gensym)]) + (define (make-cond-entry case) + (let ([case-expr (car case)] + [func (cdr case)]) + (if (eqv? case-expr 'else) (cons #t func) + (cons (list eqfn result case-expr) func)))) + (define cond-entries (map make-cond-entry cases)) + `(let ([,result ,expr]) (cond ,@cond-entries)))) + +(define-macro (maybe . body) + (let (( result (gensym) )) + `(let (( ,result ,(cadr body) )) + (if ,result (let (( ,(car body) ,result )) + ,(cddr body)) + #f)))) ADDED sexpc/example.cse Index: sexpc/example.cse ================================================================== --- sexpc/example.cse +++ sexpc/example.cse @@ -0,0 +1,50 @@ +; [ʞ] example.cse +; ~ lexi hale +; - vim: ft=scheme +; © CCO / public domain +; $ ./sexpc example.cse +; ? this file is an example program that can be +; transpiled to valid C with sexpc.scm. +(use sys "unistd.h") +(use sys "stddef.h") +(use sys "stdint.h") +(use sys "string.h") + +(@ (: sayl (string -> list)) + ; (@ … ) introduces a block of Scheme code that + ; is evaluated in the program's environment, but + ; whose value is silently ignored. this makes it + ; good for defining globals you intend to use + ; later in the program with (* … ) or (= … ) + ; blocks - see the paragraph below + (define (sayl str) + `(add-> total (call write 1 + ; the return value of a scheme expression will + ; be evaluated just like any other expression + ; and transformed into C code, so we can rely + ; on existing syntax instead of haven't to + ; generate raw strings of C + ,(string-append str "\\n") + ,(+ 1 (string-length str)))))) + +(decl sayhello ((ptr char) → int)) +(def (total size_t) 0) + +(def (main int (argc int) (argv (array (ptr char)))) + (ret (call sayhello "dumbass"))) + +(def (sayhello int (name (ptr char))) + (* sayl "hello…") + ; a list prefixed with * indicates that it + ; should be evaluated as a scheme expression + ; and its result then processed, instead of + ; being processed directly like (call) or + ; (def). scheme expressions may be inserted + ; anywhere a normal sexpc expression can. or + ; we could instead write (= (sayl "hello…")) + ; to accomplish the same effect; (= … ) is + ; really just shorthand for (* begin … ) + + (call write 1 name (call strlen name)) + (ret total) +) ADDED sexpc/sexpc.scm Index: sexpc/sexpc.scm ================================================================== --- sexpc/sexpc.scm +++ sexpc/sexpc.scm @@ -0,0 +1,288 @@ +; [ʞ] sexpc.scm - sexp→c converter +; ~ lexi hale +; © GNU Affero GPL v3 +; $ chicken-csc -O5 sexpc.scm; strip sexpc +; > ./sexpc +; ? a tool to convert sheets of s-expressions into +; valid C, enabling the use of Scheme as a +; preprocessor language. it also generates C type +; definitions from a human-readable syntax, +; removing the difficulties introduced by C's +; legendarily inscrutable compound type syntax: +; e.g. (def r (array (const (ptr (char u128 -> +; (ptr (array 5 bool))))))), +; which represents an array of const pointers to +; functions taking a char and a 128-bit integer, +; and returning a pointer to an array of five bools. +; this emits the C type: +; _Bool (((*(*const (((v)[])))(char, unsigned +; __int128_t)))[5]) +; (as i'm sure you immediately guessed) +; +; → no dependencies besides the lisp-macro.scm file +; bundled with it, and the chicken stdlib. + +(import (chicken process-context)) +(import (chicken condition)) +(import (chicken pretty-print)) +(import (chicken io)) +(include "../scmlib/lisp-macro.scm") + +(: *code-depth* integer) +(define *code-depth* 0) ; im sry + +(: indent (string -> string)) +(define (indent str) + (define (iterate n) + (if (eq? n 0) str + (string-append "\t" (iterate (- n 1))))) + (iterate *code-depth*)) +(define (first fst . lst) fst) + +(: string-reduce (list --> string)) +(: words->string (list --> string)) +(: separate (string list --> string)) +(define (string-reduce lst) (foldl string-append "" lst)) +(define (words->string lst) (foldl (lambda (a b) (string-append a " " b)) "" lst)) +(define (separate delim lst) (foldl (lambda (a b) + (cond ((equal? a "") b) + ((equal? b "") a) + (else (string-append a delim b)))) "" lst)) + +(: ensure-string ((or string symbol) -> string)) +(define (ensure-string s) (if (string? s) s (if (symbol? s) (symbol->string s) + (abort '(exn bad-string-or-symbol))))) +(define (handle fn) + (define (err text) (string-append "\x1b[1;31m(fatal) \x1b[m" text)) + (condition-case (fn) + [(exn file) (display (err "could not open file"))] + [var () (display (err "an unknown error occurred"))])) + +(define (make-evaluator) + (let ((*eval-env* (interaction-environment))) + (lambda (lst) (eval lst *eval-env*)))) + +(define evaluate-list (make-evaluator)) + +(define (c-def-function body) "") +(define (c-declare body) + (cond ((>= (length body) 3) + (let ([fn-name (list-ref body 0)] + [fn-return (list-ref body 1)] + [fn-args (list-tail body 2)]) + (c-function-decl fn-return fn-name fn-args))) + ((eqv? (length body) 2) ((eval-type (cadr body)) (car body))) + (else (abort '(exn syntax declare))))) + +(define (c-define body) + (cond + ((and (list? (car body)) (>= (length body) 2)) + (let ([contents (words->string (map (lambda(x) (eval-code #f x)) (cdr body)))]) + (string-append (c-declare (car body)) + (if (>= (length (car body)) 3) (string-append " {\n" contents "\n}") + (string-append " = " contents))))) + (else (abort '(exn syntax define))))) + +(: c-call-function (list -> string)) +(define (c-call-function body) + (unless (> (length body) 0) (abort '(exn parse call))) + (string-append + (eval-code #t (car body)) "(" (separate "," (map (lambda(x) (eval-code #t x)) (cdr body))) ")")) + +(define (c-cpp-include body) + (case (length body) + ((1) (if (string? (car body)) (string-append "#include \"" (car body) "\"\n") + (abort '(exn syntax include)))) + ((2) (if (and (eqv? (car body) 'sys) + (string? (cadr body))) (string-append "#include <" (cadr body) ">\n") + (abort '(exn syntax include sys)))) + (else (abort '(exn syntax include))))) + +(define (and* . body) + (if (null? body) #t + (if (eq? #f (car body)) #f + (apply and* (cdr body))))) + +(: c-function-decl (symbol + symbol + (list-of (or null + string + (pair (or symbol + (list-of symbol)) symbol))) -> string)) +(define (c-function-decl ret name args) + (define (parse-arg a) + (cond ((null? a) "void") + ((string? a) a) + ((symbol? a) ((eval-type a)"")) + ((and (list? a) (= (length a) 2)) ((eval-type (cadr a)) (car a))) + ((list? a) ((eval-type a)"")) + (else (abort '(exn syntax function-declare argument))))) + (let ([arg-str (separate ", " (map parse-arg args))] + [ret-fn (eval-type ret)]) + (ret-fn (string-append (ensure-string name) "(" arg-str ")")))) + +(define-macro (with-deeper-context . body) + `(begin + (set! *code-depth* (+ 1 *code-depth*)) + (first + ,@body + (set! *code-depth* (- *code-depth* 1)) + ))) + +(: c-statement (string -> string)) +(define (c-statement s) (indent (with-deeper-context (string-append s ";\n")))) + +(: eval-type ((or null symbol list) -> ((or symbol string) -> string))) +(define (eval-type t) + ; a type object is a function that is called with a name to + ; generate a definition. this is necessary for the case of + ; arrays and function pointers, which require special syntax + (: simple ((or string symbol) --> string)) + (define (simple x) (lambda (n) (if (equal? "" n) x (string-append x " " (ensure-string n))))) + (define (wrap ty name inner) (lambda (n) + (string-append (car ty) ((eval-type inner) + (string-append (cdr ty) (car name) (ensure-string n) (cdr name)))))) + + (define (parse-function-def t) + (if (>= (length t) 3) ; function def?) + (let ([arrow (list-ref t (- (length t) 2))]) + (if (or (eqv? arrow '->) (eqv? arrow '→) (eqv? arrow '=>)) ; function def! + (let* ([args (butlast (butlast t))] ; rrrrrrgh + [ret (list-ref t (- (length t) 1))] + [arg-map (map (lambda (x) ((eval-type x)"")) args)]) + (lambda (n) (c-function-decl ret n arg-map))) + #f)) + #f)) + (define (parse-type t) + (or (parse-function-def t) + ; not a function + (case (car t) + ('ptr (wrap '["".""] '["(*".")"] (cadr t))) + ('ref (wrap '["".""] '["(&".")"] (cadr t))) ; for C++ support + ('const (wrap '["".""] '["const (".")"] (cadr t))) + ('struct (wrap '["struct ".""] '["".""] (cadr t))) + ('enum (wrap '["enum ".""] '["".""] (cadr t))) + ('volatile (wrap '["".""] '["volatile(".")"] (cadr t))) + ('onlyptr (wrap '["".""] '["(*restrict ".")"] (cadr t))) + ('static (wrap '["".""] '["static ".""] (cadr t))) + ('array (if (null? (cddr t)) (wrap '["".""] '["((".")[])"] + (cadr t)) + (wrap '["".""] [cons "((" (string-append ")[" (eval-code #t (cadr t)) "])" )] + (caddr t)))) + (else (abort '(exn syntax)))))) + + (cond + ((null? t) (simple "void")) + ((symbol? t) (case t + ('bool (simple "_Bool")) + ('uchar (simple "unsigned char")) + ('schar (simple "signed char")) + ('ushort (simple "unsigned short")) + ('uint (simple "unsigned int")) + ('ulong (simple "unsigned long")) + ('ullong (simple "unsigned long long")) + ('llong (simple "long long")) + ('ldouble (simple "long double")) + ('u128 (simple "unsigned __int128_t")) + ('s128 (simple "__int128_t")) + (else (simple (symbol->string t))))) + ((list? t) (parse-type t)) + ((string? t) (simple t)) + (else (abort '(exn syntax))))) + +(define (c-construct type name body) + (string-append type " " name " {\n" + (string-reduce (map (lambda(x) (indent (eval-code #f x))) body)) + "\n}")) + +(define (c-fn-return body) + (string-append "return " (string-reduce (map (lambda(x) (eval-code #t x)) body)))) + +(define (c-math-op op body) + (unless (= 2 (length body)) (abort '(exn syntax math))) + (string-append "((" (eval-code #t (car body)) ")" op "(" (eval-code #t (cadr body)) "))")) + +(define (inter s x) (print* "-- " s ": ") (pretty-print x) x) +(: eval-code (boolean list -> string)) +(define (eval-code inexpr data) + (define (ctx x) inexpr (if (eq? inexpr #t) x (c-statement x))) + (define (deepen n) (set! *code-depth* (+ *code-depth* n))) + (: dispatch (list -> string)) + (define (dispatch form body) + (case form + ('* (eval-code inexpr (evaluate-list body))) + ('= (eval-code inexpr (evaluate-list (cons 'begin body)))) + ('@ (eval (cons 'begin body)) "") + ('use (c-cpp-include body)) + ('def (first (c-statement (with-deeper-context (c-define body))) )) + ('decl (first (c-statement (with-deeper-context (c-declare body))) )) + ('call (ctx (c-call-function body))) + ('list (first (string-append "{" (separate "," (map (lambda(x) (eval-code #t x)) body)) "}") )) + ('struct (first (ctx (c-construct "struct" (car form) (cdr form))) )) + ('enum (first (ctx (c-construct "enum" (car form) (cdr form))) )) + ('ret (c-statement (c-fn-return body))) + ('add (c-math-op "+" body)) + ('add-> (ctx (c-math-op "+=" body))) + ('sub (c-math-op "-" body)) + ('sub-> (ctx (c-math-op "-=" body))) + ('mul (c-math-op "*" body)) + ('mul-> (ctx (c-math-op "*=" body))) + ('div (c-math-op "/" body)) + ('div-> (ctx (c-math-op "/=" body))) + ('== (c-math-op "==" body)) + ('-> (ctx (c-math-op "=" body))) + ('→ (ctx (c-math-op "=" body))) + ('; ";") + (else (string-append "string form) ">")) + ))) + (cond + ((null? data) "") + ((symbol? data) (symbol->string data)) + ((string? data) (string-append "\"" data "\"")) + ((char? data) (apply string (list #\' data #\'))) + ((number? data) (number->string data)) + ((list? data) (let ([form (car data)] + [body (cdr data)]) + (dispatch form body))) + (else (abort '(exn syntax))))) + +(define (parse-code file) (map (lambda(x) (eval-code #f x)) file)) + +(define (compile filename) + (let ([file (if (equal? filename "-") (read-list) + (handle (lambda() + (call-with-input-file filename read-list))))]) + (string-reduce (parse-code file)))) + +(: usage (--> string)) +(define (usage) "usage: sexpc ") + +(define (cull-args args switches params) + (define (parse-switch switch) + (cond + ((equal? switch "-") "-") + ((eqv? (string-ref switch 1) #\-) (substring switch 2)) + (else (map string (string->list (substring switch 1)))))) + (define (is-switch arg) + (eqv? (string-ref arg 0) #\-)) + (define (split p) + (cons (flatten (map parse-switch switches)) p)) + (let* ([hd (if (null? args) '() (car args))] + [tl (if (null? hd) '() (cdr args))]) + (cond + ((null? args) (split params)) + ((equal? "--" hd) (split (append params tl))) + ((is-switch hd) (cull-args tl (cons hd switches) params)) + (else (cull-args tl switches + (cons hd params)))))) + +(define (main args) + (let* ([switches-and-params (cull-args args '() '())] + [switches (car switches-and-params)] + [params (cdr switches-and-params)]) + (cond + ((null? params) (usage)) + ((eqv? (length params) 1) (compile (car params))) + (else (usage))))) + +(cond-expand (csi) (else (display (main (command-line-arguments))))) ADDED sexpc/type-tests.scm Index: sexpc/type-tests.scm ================================================================== --- sexpc/type-tests.scm +++ sexpc/type-tests.scm @@ -0,0 +1,30 @@ +(include "sexpc.scm") +((eval-type '()) 'v); + +(define (test form) + (print ((eval-type form) 'v))) + +(define tests '( + int char float uint + (struct chungus_t) + (ptr int) (array char) + (array 5 (const int)) (const int) + (const (ptr (array 5 (const int)))) + (const (ptr int)) + (array 5 (ptr int)) + (ref (array 5 int)) + (ptr (array (volatile char))) + (const (ptr float)) + (ptr int) + (ullong schar -> (ptr (bool u128 -> ldouble))) + (int -> chungus_t) + (ptr (int -> chungus_t)) + (array 5 (ptr (int -> chungus_t))) + ((array 5 (ptr int)) -> (const (ptr (array 3 int)))) + ; int (((*const (v(int (*(())[5])))))[3]) + ; int (* const v(int *[5]))[3] +(array (const (ptr (char u128 -> (ptr (array 5 bool)))))) + + (array 5 (const (ref char))))) + +(map test tests)