| 
									
										
										
										
											2025-10-23 16:12:29 +02:00
										 |  |  | (import (rnrs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (define get-word | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |   (lambda (file) | 
					
						
							| 
									
										
										
										
											2025-10-23 16:12:29 +02:00
										 |  |  |     (let ([c (lookahead-char file)]) | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |       (cond | 
					
						
							| 
									
										
										
										
											2025-10-25 20:42:36 +02:00
										 |  |  |         [(eof-object? c) ""] | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |         [(char-alphabetic? c) | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  |          (get-char file) | 
					
						
							|  |  |  |          (string-append (string c) (get-word file))] | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |         [else ""])))) | 
					
						
							| 
									
										
										
										
											2025-10-23 16:12:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  | (define char->digit | 
					
						
							|  |  |  |   (lambda (c) | 
					
						
							|  |  |  |     (- (char->integer c) (char->integer #\0)))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ; get-number supports only nonnegative integers. | 
					
						
							|  |  |  | (define get-number | 
					
						
							|  |  |  |   (lambda (file) | 
					
						
							|  |  |  |     (let f ([total 0]) | 
					
						
							|  |  |  |       (let ([c (lookahead-char file)]) | 
					
						
							|  |  |  |         (if (char-numeric? c) | 
					
						
							|  |  |  |           (begin | 
					
						
							|  |  |  |             (get-char file) | 
					
						
							|  |  |  |             (f (+ (* total 10) (char->digit c)))) | 
					
						
							|  |  |  |           total))))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (define get-token | 
					
						
							| 
									
										
										
										
											2025-10-23 16:12:29 +02:00
										 |  |  |   (lambda (file) | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |     (let ([c (lookahead-char file)]) | 
					
						
							|  |  |  |       (cond | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  |         [(eof-object? c) 'end-of-file] | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |         [(char-whitespace? c) | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  |          (get-char file) | 
					
						
							|  |  |  |          (get-token file)] | 
					
						
							| 
									
										
										
										
											2025-10-23 16:36:18 +02:00
										 |  |  |         [(char-alphabetic? c) | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  |          (get-word file)] | 
					
						
							|  |  |  |         [(char-numeric? c) | 
					
						
							|  |  |  |          (get-number file)] | 
					
						
							|  |  |  |         [(eq? c #\+) (get-char file) '+] | 
					
						
							|  |  |  |         [(eq? c #\-) (get-char file) '-] | 
					
						
							|  |  |  |         [(eq? c #\*) (get-char file) '*] | 
					
						
							|  |  |  |         [(eq? c #\/) (get-char file) '/] | 
					
						
							|  |  |  |         [(eq? c #\=) (get-char file) #\=] | 
					
						
							|  |  |  |         [else (error 'get-token "Bad character" c)])))) | 
					
						
							| 
									
										
										
										
											2025-10-23 16:12:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  | (define get-tokens | 
					
						
							|  |  |  |   (lambda (file) | 
					
						
							|  |  |  |     (let ([token (get-token file)]) | 
					
						
							|  |  |  |       (if (eq? token 'end-of-file) | 
					
						
							|  |  |  |           '() | 
					
						
							|  |  |  |           (cons token (get-tokens file)))))) | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-10-23 16:12:29 +02:00
										 |  |  | (define tokenize | 
					
						
							|  |  |  |   (lambda (filename) | 
					
						
							|  |  |  |     (let ([file (open-input-file filename)]) | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  |       (get-tokens file)))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-25 20:42:36 +02:00
										 |  |  | (define test-lexer-fail | 
					
						
							|  |  |  |   (lambda (input want got) | 
					
						
							|  |  |  |     (display "text-lexer-fail: input=") | 
					
						
							|  |  |  |     (write input) | 
					
						
							|  |  |  |     (display "; want: ") | 
					
						
							|  |  |  |     (write want) | 
					
						
							|  |  |  |     (display "; got: ") | 
					
						
							|  |  |  |     (write got) | 
					
						
							|  |  |  |     (display ".") | 
					
						
							|  |  |  |     (newline))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (define test-lexer-success | 
					
						
							|  |  |  |   (lambda (input got) | 
					
						
							|  |  |  |     (display "text-lexer-success: input=") | 
					
						
							|  |  |  |     (write input) | 
					
						
							|  |  |  |     (display "; got: ") | 
					
						
							|  |  |  |     (write got) | 
					
						
							|  |  |  |     (display ".") | 
					
						
							|  |  |  |     (newline))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  | (define test-lexer | 
					
						
							|  |  |  |   (lambda (input want) | 
					
						
							|  |  |  |     (let ([port (open-string-input-port input)]) | 
					
						
							| 
									
										
										
										
											2025-10-25 20:42:36 +02:00
										 |  |  |       (call/cc | 
					
						
							|  |  |  |         (lambda (i-got-exception) | 
					
						
							|  |  |  |           (with-exception-handler | 
					
						
							|  |  |  |             (lambda (x) | 
					
						
							|  |  |  |               (display "test-lexer-fail: input ") | 
					
						
							|  |  |  |               (write input) | 
					
						
							|  |  |  |               (display " throws ") | 
					
						
							|  |  |  |               (write x) | 
					
						
							|  |  |  |               (display ".") | 
					
						
							|  |  |  |               (newline) | 
					
						
							|  |  |  |               (i-got-exception x)) | 
					
						
							|  |  |  |             (lambda () | 
					
						
							|  |  |  |               (let ([got (get-tokens port)]) | 
					
						
							|  |  |  |                 (if (equal? got want) | 
					
						
							|  |  |  |                 (test-lexer-success input got) | 
					
						
							|  |  |  |                 (test-lexer-fail input want got)))))))))) | 
					
						
							| 
									
										
										
										
											2025-10-25 19:49:41 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | (test-lexer "hello world" '("hello" "world")) | 
					
						
							| 
									
										
										
										
											2025-10-25 20:42:36 +02:00
										 |  |  | (test-lexer "+ * / -" '(+ * / -)) | 
					
						
							|  |  |  | (test-lexer "identifier = 14" '("identifier" = 14)) |