-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcore.scm
183 lines (139 loc) · 4.71 KB
/
core.scm
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
;; core.scm
(use srfi-13)
;;; --- special variables ---
(define *debug* #f)
(define *current-line* #f) ;; current line being processed
(define *fields* '()) ;; fields in the current line
(define fs #/[ \n\t\r]+/) ;; field separator; if undefined, use whitespace
(define ls "\n") ;; line separator; if undefined, use newline
(define ofs " ") ;; output file separator
(define ols "\n") ;; output line separator
(define nf 0) ;; number of fields; set for each line
(define nl 0) ;; number of lines in file; set when file is read
(define ln 0) ;; number of the current line (overall)
;;; --- handling Scheme expressions ---
(define default-expr
'(format #t "~a~%" *current-line*))
;; FIXME: Should use ols?
(define (is-before-expr? expr)
(and (list? expr)
(not (null? expr))
(member (car expr) '(b before B BEFORE))))
(define (is-after-expr? expr)
(and (list? expr)
(not (null? expr))
(member (car expr) '(a after A AFTER))))
(define (normalize-before/after-expr expr)
;; replace (BEFORE ...) with (begin ...), etc.
(cons 'begin (cdr expr)))
(define (parse-exprs sexprs)
(let loop ((sexprs sexprs) (before-exprs '()) (after-exprs '()) (exprs '()))
(cond
((null? sexprs) ;; done?
(values (reverse (map normalize-before/after-expr before-exprs))
(reverse (map normalize-before/after-expr after-exprs))
(reverse (map replace-$-syntax exprs))))
((is-before-expr? (car sexprs))
(loop (cdr sexprs)
(cons (car sexprs) before-exprs)
after-exprs
exprs))
((is-after-expr? (car sexprs))
(loop (cdr sexprs)
before-exprs
(cons (car sexprs) after-exprs)
exprs))
(else (loop (cdr sexprs)
before-exprs after-exprs (cons (car sexprs) exprs))))))
;; FIXME: stops when encountering characters like 0xFA
(define (read-data port)
;; read all data from the port, and return it as a string.
(with-input-from-port port
(lambda () (port->string port))))
(define (read-exprs-from-string s)
(with-input-from-string s
(lambda () (port->sexp-list (current-input-port)))))
(define (read-exprs-from-file filename)
(with-input-from-file filename
(lambda () (port->sexpr-list (current-input-port)))))
(define (process-exprs exprs)
;; evaluate a list of expressions.
(for-each (cut eval <> #f) exprs))
(define (process-line line exprs)
(set! *current-line* line)
(set! *fields* (string-split line fs))
(set! nf (length *fields*))
(inc! ln)
(when *debug*
(format (standard-error-port) "~s~%" *fields*))
;; also set *fields*, etc
(process-exprs exprs))
(define (process-file port exprs)
(let* ((data (read-all-from-port port))
(lines (string-split data ls)))
(set! nl (length lines))
(for-each (cut process-line <> exprs) lines)))
;;; --- $ shorthand ---
;; In Chicken I could define a reader macro to have $N mean (field N);
;; this does not seem to work in Gauche, so we replace the appropriate
;; symbols in expressions by hand. Kludgy, but it'll do for now.
;; Because of this restriction, however, we only support ${number} and $nf.
(define (replace-$-syntax expr)
(search-and-replace expr matches-$-syntax? convert-$-syntax))
(define (matches-$-syntax? sym)
(if (symbol? sym)
(let ((s (symbol->string sym)))
(string-prefix? "$" s))
#f))
(define (convert-$-syntax sym)
(let ((s (symbol->string sym)))
(if (equal? s "$nf")
'(field nf)
(list 'field (string->number (string-copy s 1))))))
;;; --- fields ---
(define (safe-field-get n)
(if (> n (length *fields*))
""
(list-ref *fields* (- n 1))))
(define (field n)
(cond
((= n 0) *current-line*)
((> n 0) (safe-field-get n))
(else (safe-field-get (+ (+ (length *fields*) 1) n)))))
;;; --- macros ---
(define-syntax def
(syntax-rules ()
((def) #f)
((def (name value) rest ...)
(begin
(define name value)
(def rest ...)))
((def name rest ...)
(begin
(define name 0)
(def rest ...)))))
(define (?false? x)
(not (not (member x '(0 "" () 0.0 #f)))))
(define-syntax ?
(syntax-rules ()
((? cond true-expr false-expr)
(if (?false? cond)
false-expr
true-expr))
((? cond true-expr)
(? cond true-expr #f))))
(define (?out x)
(? x (out x)))
;;; --- regular expressions ---
(define (~ regex :optional (s #f))
(rxmatch regex (or s *current-line*)))
(define (!~ regex :optional (s #f))
(not (~ regex s)))
;;; --- output ---
(define (out . args)
(print-out ofs ols args))
(define (print-out ofs ols args)
(let* ((args-as-strings (map ->string args))
(s (string-join args-as-strings ofs)))
(display s)
(display ols)))