procgen  Check-in [e856b9f3a8]

Overview
Comment:add structure macro
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e856b9f3a88b93753e314f71e1bf98126cff05ec15e410fe8406e53f5792e4a6
User & Date: lexi on 2019-07-27 06:29:59
Other Links: manifest | tags
Context
2019-07-27
06:30
fix typo check-in: ec168497f7 user: lexi tags: trunk
06:29
add structure macro check-in: e856b9f3a8 user: lexi tags: trunk
2019-06-24
09:24
updates check-in: d425e8a7c7 user: lexi tags: trunk
Changes

Added lib/fn-struct.scm version [70386af811].



























































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
; [ʞ] fn-struct.scm
;  ~ lexi hale <lexi@hale.su>
;  © affero general public license
;  > (load "lib/struct.scm")
;
; generates immutable, relatively efficient structs. declare
; a struct type x with
;	(define x (struct 'field1 'field2) 
; create a record y of that type with
;	(define y (x 123 456))
; access field1 of record y with
;	(y 'field1)
; update field2 of record y with
;	(y 'field2 123) → new record (field1 = 123; field2 = 123)
;
; this  unit also  includes a  few utility  function that
; chicken scheme  conveniently "forgot." i  apologize for
; the implementation of (list-head). i was very tired.

; return a sub-list of lst up to ct (inverse of list-tail)
(define (list-head lst ct)
  (let* ([reverse-lst (reverse lst)]
		 [idx (- (length lst) (+ 1 ct))])
	(reverse (list-tail reverse-lst idx))))
	; i'm not proud of this

; search for the first occurence of `term` in list lst.
; equal? is used by default; an alternate predicate may
; be passed after `term`
(define (member-index lst term . opt) 
  (let ([predicate (if (eq? '() opt) equal?
					   (car opt))])
	(define (search idx l)
	  (if (eq? l '()) #f
		  (if (predicate (car l) term) idx
			  (search (+ 1 idx) (cdr l)))))
	  (search 0 lst)))

; generate a new struct type and return it as a function
; that may be called to instantiate that type.
(define (struct . keys)
  (define (generate-record . vals)
  ; the function returned by a (struct) call and called
  ; to generate a new struct by functional-update syntax
	(define (struct-ref key)
	; searches the struct for the named key and if found
	; return its index, otherwise return #f
	  (let ([query-idx (member-index keys key)])
		(if (eq? #f query-idx) #f
			(list-ref vals query-idx))))
	(define (update-record key val)
	; return a new version of this construct with the same
	; field names and values except for { key = val }
		(let* ([field-idx (member-index keys key)]
			   [new-tail (list-tail vals (+ 1 field-idx))]
			   [new-head (list-head vals (- field-idx 1))])
		  (apply generate-record (append new-head (list val) new-tail))))

	; are values specified for every field and no more?
	; TODO: consider loosening the restriction - could
	; there be some use to allowing the used to add fields
	; only accessible through the record->list facility?
	(if (not (= (length vals) (length keys))) #f
		(lambda access
		  ; returned by generate-record, this function is called
		  ; whenever a struct is accessed
		  (case (length access)
			;determine operation to perform
			((0) vals) ; return list of values
			((1) (struct-ref (car access))); search for value
			((2) (update-record (car access) (cadr access)))
			(else #f))))) ; no other functions currently available

	; keys are now stored in the closure; return the
	; (generate-record) function to the user so she can
	; call it to instantiate the defined struct.
	generate-record)

Added lib/select.scm version [ce284edc9d].



































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
; [ʞ] select.scm
;  ~ lexi hale <lexi@hale.su>
;  © 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))))

Modified lib/struct.scm from [06f94eb978] to [7cd42aa5f2].

1
2

3
4
5
6
7
8
9
10
11
12
13
14




15
16
17
18

19
20
21
22
23
24

25

26
27
28
29
30
31
32

33
34
35
36

37
38
39
40
41
42


43
44
45
46
47
48
49
50
51
52
53
54

55
56
57

58
59
60
61
62
63
64
65
66
67
68
69

70
71
72
73

74
75
76
77























; [ʞ] struct.scm
;  ~ lexi hale <lexi@hale.su>

;  © affero general public license
;  > (load "lib/struct.scm")
;
; generates immutable, relatively efficient structs. declare
; a struct type x with
;	(define x (struct 'field1 'field2) 
; create a record y of that type with
;	(define y (x 123 456))
; access field1 of record y with
;	(y 'field1)
; update field2 of record y with
;	(y 'field2 123) → new record (field1 = 123; field2 = 123)




;
; this  unit also  includes a  few utility  function that
; chicken scheme  conveniently "forgot." i  apologize for
; the implementation of (list-head). i was very tired.


; return a sub-list of lst up to ct (inverse of list-tail)
(define (list-head lst ct)
  (let* ([reverse-lst (reverse lst)]
		 [idx (- (length lst) (+ 1 ct))])
	(reverse (list-tail reverse-lst idx))))

	; i'm not proud of this


; search for the first occurence of `term` in list lst.
; equal? is used by default; an alternate predicate may
; be passed after `term`
(define (member-index lst term . opt) 
  (let ([predicate (if (eq? '() opt) equal?
					   (car opt))])

	(define (search idx l)
	  (if (eq? l '()) #f
		  (if (predicate (car l) term) idx
			  (search (+ 1 idx) (cdr l)))))

	  (search 0 lst)))

; generate a new struct type and return it as a function
; that may be called to instantiate that type.
(define (struct . keys)
  (define (generate-record . vals)


  ; the function returned by a (struct) call and called
  ; to generate a new struct by functional-update syntax
	(define (struct-ref key)
	; searches the struct for the named key and if found
	; return its index, otherwise return #f
	  (let ([query-idx (member-index keys key)])
		(if (eq? #f query-idx) #f
			(list-ref vals query-idx))))
	(define (update-record key val)
	; return a new version of this construct with the same
	; field names and values except for { key = val }
		(let* ([field-idx (member-index keys key)]

			   [new-tail (list-tail vals (+ 1 field-idx))]
			   [new-head (list-head vals (- field-idx 1))])
		  (apply generate-record (append new-head (list val) new-tail))))


	; are values specified for every field and no more?
	; TODO: consider loosening the restriction - could
	; there be some use to allowing the used to add fields
	; only accessible through the record->list facility?
	(if (not (= (length vals) (length keys))) #f
		(lambda access
		  ; returned by generate-record, this function is called
		  ; whenever a struct is accessed
		  (case (length access)
			;determine operation to perform
			((0) vals) ; return list of values

			((1) (struct-ref (car access))); search for value
			((2) (update-record (car access) (cadr access)))
			(else #f))))) ; no other functions currently available


	; keys are now stored in the closure; return the
	; (generate-record) function to the user so she can
	; call it to instantiate the defined struct.
	generate-record)

























>
|
<
|
|
|
|
|
|
|
|
|
|
>
>
>
>
|
<
<
<
>

<
<
|
|
<
>
|
>

<
|
|
|
|
|
>
|
<
|
|
>
|

<
<
|
<
>
>
|
|
|
|
|
|
|
|
|
|
|
<
>
|
|
|
>

<
|
|
|
|
|
|
|
|
|
<
>
|
|
<

>
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19



20
21


22
23

24
25
26
27

28
29
30
31
32
33
34

35
36
37
38
39


40

41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59

60
61
62
63
64
65
66
67
68

69
70
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
; [ʞ] struct.scm
;  ~ lexi hale <lexi@hale.su>
;  $ csc -X fail.scm …
;  © affero gpl v3

;  > (include "lisp-macro.scm")
;  ? a typed struct mechanism for scheme, because the
;    srfi ones suck ass and don't support FRU syntax.
;  X (structure user (name . string)
;                    (age . integer)
;                    (pw . string)
;                    gecos)
;    (define stacy (user name: "stacy"
;                        age: 26
;                        pw: "swordfish"))
;    (if (user? stacy) (user.name stacy))
;    (define oldstacy (user stacy age: 69))
;    (user.name oldstacy)
;    (user.age oldstacy)




(include "lisp-macro.scm")



(import (chicken pretty-print)
		(chicken keyword))


(begin-for-syntax (import (chicken keyword)))
	; i cannot believe this worked


(define-macro (structure . body)
  (define (uniq lst)
	(define (in? x l)
	  (if (null? l) #f
		  (if (eqv? x (car l)) #t
			  (in? x (cdr l)))))
	(define (loop acc hd)

	  (if (null? hd) acc
		  (if (in? (car hd) acc) (loop acc (cdr hd))
			  (loop (cons (car hd) acc) (cdr hd)))))
	(loop '() lst))



  (define (symsuf sym . suffix)

	(string->symbol (apply string-append (cons (symbol->string sym) suffix))))

  (define (emit-field sname idx f)
	(let* ([prop (cond ((symbol? f) (cons f 'any))
					   ((pair? f) f))]
		   [name (car prop)]
		   [type (cdr prop)]
		   [fn (symsuf sname "." (symbol->string name))])
	  `(begin 
		 (: ,fn ((pair symbol vector) --> ,type))
		 (define (,fn struct)
		 (if (,(symsuf sname "?") struct) (vector-ref (cdr struct) ,idx) 
			 (error ,(string-append "argument is not of type " (symbol->string sname))))))))


  (define (reduce-fields sname idx fields)
	(if (null? fields) '()
		(cons (emit-field    sname      idx  (car fields))
			  (reduce-fields sname (+ 1 idx) (cdr fields)))))


  (define (emit-pred name typesym) 
	(let ([fn (symsuf name "?")])
	  `(begin (: ,fn (any --> boolean))
			  (define (,fn candidate) (and (pair? candidate)
										   (eq? (quote ,typesym) (car candidate))
										   (vector? (cdr candidate)))))))
  (define (extract-type field)
	(cond ((symbol? field) 'any)
		  ((pair? field) (cdr field))))


  (define (emit-def name types)
	`((define-type ,name (pair symbol (vector ,@types)))))


  (define (emit-gen name types typesym fields)
	(define (fld-get idx fld)
	  (let ([name (if (symbol? fld) fld (car fld))])
		`(get-keyword ,(string->keyword (symbol->string name)) spec
					  (lambda() (safe-vector-ref basis ,idx)))))
	(define (reduce-flds idx flds)
	  (if (null? flds) '()
		  (cons (fld-get idx (car flds))
				(reduce-flds (+ 1 idx) (cdr flds)))))
	`((: ,name (#!rest (or symbol ,@(uniq types)) --> ,name ))
	  (define (,name . args)
		(define (safe-vector-ref v i) (if (null? v) (error "all struct fields need to be specified unless functional record update syntax is in use")
										  (vector-ref v i)))
		(let* ([basis (if (,(symsuf name "?") (car args)) (cdar args) '())]
			   [spec (if (null? basis) args (cdr args))])
		  (cons (quote ,typesym)
				(vector ,@(reduce-flds 0 fields)

				  ))))))

  (let* ([name (car body)]
		 [fields (cdr body)]
		 [typesym (gensym)]
		 [types (map extract-type fields)])
	(append (emit-pred name typesym)
			(emit-def  name types)
			(emit-gen  name types typesym fields)
			(reduce-fields name 0 fields))))