-
Notifications
You must be signed in to change notification settings - Fork 2
/
basic-reader.rkt
164 lines (148 loc) · 4.56 KB
/
basic-reader.rkt
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
#lang racket
(provide (rename-out (basic-read-syntax read-syntax)))
(require syntax/readerr)
(define (basic-read-syntax src in)
(datum->syntax
#f
`(module basic racket
(require "basic.rkt")
(basic
,@(parse-program src in)))))
(define (parse-program src in)
(define line (parse-line src in))
(if (eof-object? line)
'()
(cons line (parse-program src in))))
(define (parse-line src in)
(regexp-try-match #px"^\\s+" in)
(if (eof-object? (peek-char in))
eof
(let ()
(define line-number (get-line-number src in))
(define command (parse-command src in))
`(,line-number ,command))))
(define (next-token src in (peek? #f))
(skip-whitespace in)
(define match (if peek? regexp-match-peek regexp-try-match))
(cond
((match #rx"^(PRINT|GOTO|GOSUB|RETURN|IF|THEN|ELSE|\\*|\\+|-|/|=|<=|>=|<|>)" in)
=> (lambda (match)
(string->symbol (bytes->string/utf-8 (car match)))))
((match #rx"^\\(" in)
'open-paren)
((match #rx"^\\)" in)
'closed-paren)
((match #rx"^," in)
'comma)
((match #rx"^[0-9]+" in)
=> (lambda (match)
(string->number (bytes->string/utf-8 (car match)))))
((match #rx"^[a-zA-Z]+$?" in)
=> (lambda (match)
(string->symbol (bytes->string/utf-8 (car match)))))
((match #rx"\"([^\"]+)\"" in)
=> (lambda (match)
(bytes->string/utf-8 (cadr match))))
((eof-object? (peek-char in))
eof)
((equal? #\newline (peek-char in))
(read-char in)
eof)
((match "^$" in)
eof)
(else
(complain src in "unknown lexeme"))))
(define (tokenize src in)
(define token (next-token src in))
(if (eof-object? token)
'()
(cons token (tokenize src in))))
(define (get-line-number src in)
(regexp-try-match #px"^\\s+" in)
(cond
((regexp-try-match #rx"^[0-9]+" in)
=> (lambda (match)
(string->number (bytes->string/utf-8 (car match)))))
(else
(complain src in "no line number"))))
(define (complain src in msg)
(define-values (line col pos) (port-next-location in))
(raise-read-error msg src line col pos 1))
(define (parse-command src in)
(define first-token (next-token src in))
(when (eof-object? first-token)
(error "no command after line number"))
(cond
((eq? 'PRINT first-token)
`(print ,@(parse-arguments src in)))
((eq? 'GOTO first-token) `(goto ,(get-line-number src in)))
((eq? 'GOSUB first-token) `(gosub ,(get-line-number src in)))
((eq? 'RETURN first-token) '(return))
((eq? 'IF first-token)
(define test (parse-expr src in))
(unless (eq? 'THEN (next-token src in))
(complain src in "missing THEN in IF"))
(define then (parse-command src in))
(unless (eq? 'ELSE (next-token src in))
(complain src in "missing ELSE in IF"))
(define else (parse-command src in))
`(if ,test ,then ,else))
((symbol? first-token)
(unless (eq? '= (next-token src in))
(complain src in "incomplete assignment"))
(define expr (parse-expr src in))
`(:= ,first-token ,expr))))
(define (parse-arguments src in)
(define first (parse-expr src in))
(if (eq? 'comma (next-token src in 'peek))
(begin
(next-token src in)
(cons first (parse-arguments src in)))
(list first)))
(define (skip-whitespace in)
(regexp-try-match #px"^[ \t]+" in))
(define (parse-expr src in)
(define left (parse-expr-1 src in))
(define next (next-token src in 'peek))
(cond
((eof-object? next) left)
((memq next '(= < > <= >=))
(next-token src in)
(define right (parse-expr src in))
`(,next ,left ,right))
(else
left)))
(define (parse-expr-1 src in)
(define left (parse-expr-2 src in))
(define next (next-token src in 'peek))
(cond
((eof-object? next) left)
((memq next '(+ -))
(next-token src in)
(define right (parse-expr-1 src in))
`(,next ,left ,right))
(else
left)))
(define (parse-expr-2 src in)
(define left (parse-expr-3 src in))
(define next (next-token src in 'peek))
(cond
((eof-object? next) left)
((memq next '(* /))
(next-token src in)
(define right (parse-expr-2 src in))
`(,next ,left ,right))
(else
left)))
(define (parse-expr-3 src in)
(define next (next-token src in))
(cond
((eof-object? next)
(complain src in "premature end of input"))
((eq? next 'open-paren)
(define expr (parse-expr src in))
(define after (next-token src in))
(unless (eq? 'closed-paren after)
(complain src in "no closing parenthesis"))
expr)
(else next)))