readtable: case folding

This commit is contained in:
Peter McGoron 2024-09-07 21:10:40 -04:00
parent 3f56b03f1a
commit 6945841d1c
2 changed files with 62 additions and 37 deletions

View File

@ -19,12 +19,12 @@ designed to be used by a severely limited Scheme interpreter, which
* has fixnums only * has fixnums only
* only uses immutable strings * only uses immutable strings
* does not use "load" recursively * does not use "load" recursively
* uses R3RS essental procedures only (with some exceptions) * uses R3RS essential procedures only (with some exceptions)
The goal is to have the compiler run under the MiniScheme in ``miniscm`` The goal is to have the compiler run under the MiniScheme in ``miniscm``
in DOS, and then run in GLLV to compile itself. in DOS, and then run in GLLV to compile itself.
A proper implementation must have A proper implementation must have, in addition to R3RS,
* ``open-input-port``, ``read-char`` * ``open-input-port``, ``read-char``
* ``open-output-port``, ``write-char`` * ``open-output-port``, ``write-char``
@ -32,7 +32,6 @@ A proper implementation must have
* ``and``, ``or`` * ``and``, ``or``
* ``error`` * ``error``
If your system supports any macro system at all, ``cond-expand``, Hopefully your implementation has input and output ports (there's no other
``and``, and ``or`` can be implemented. ``error`` can be implemented way to write a compiler). Everything else can be expressed in terms of
as in SRFI-23 : https://srfi.schemers.org/srfi-23/srfi-23.html . macros or R3RS essential procedures.

View File

@ -26,6 +26,7 @@
;;; ;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;;;;;;;;;;;;;;;;;;;;;;
;;; Port reader wrapper ;;; Port reader wrapper
;;; ;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;;;;;;;;;;;;;;;;;;;;;;
(define port->read-function (define port->read-function
@ -39,38 +40,50 @@
;;; (READ): Read the next character in the stream. Returns #F on EOF. ;;; (READ): Read the next character in the stream. Returns #F on EOF.
;;; (PUSH CHAR): Push CHAR such that it will be the next character read ;;; (PUSH CHAR): Push CHAR such that it will be the next character read
;;; when (READ) is called. ;;; when (READ) is called.
;;; (FOLD-CASE?): Returns a boolean if case folding is enabled.
;;; (FOLD-CASE! BOOL): Sets the case folding to BOOL.
(define port->read (define port->read
(lambda (read-function filename) (lambda (read-function filename)
(let ((line-number 1) (let ((line-number 1)
(offset 0) (offset 0)
(pushback-buffer '())) (pushback-buffer '())
(let ((update-position (fold-case? #f))
(lambda (ch) (letrec ((update-position!
(cond (lambda (ch)
((or (eof-object? ch) (not ch)) (set! ch #f)) (cond
((eqv? ch #\newline) ((eqv? ch #\newline)
(set! line-number (+ 1 line-number)) (set! offset 0)) (set! line-number (+ 1 line-number)) (set! offset 0))
(offset (set! offset (+ 1 offset)))) (offset (set! offset (+ 1 offset))))))
ch))) (process
(lambda (op . args) (lambda (ch)
(cond (update-position! ch)
((eq? op 'pos) (list filename line-number offset)) (cond
((eq? op 'read) ((or (eof-object? ch) (not ch)) ch)
(update-position (fold-case? (char-downcase ch))
(if (null? pushback-buffer) (else ch))))
(read-function) (port
(let ((ch (car pushback-buffer))) (lambda (op . args)
(set! pushback-buffer (cdr pushback-buffer)) (cond
ch)))) ((eq? op 'pos) (list filename line-number offset))
((eq? op 'push) ((eq? op 'read)
(let ((ch (car args))) (process
(if (eqv? ch #\newline) (if (null? pushback-buffer)
(begin (read-function)
(set! line-number (- line-number 1)) (let ((ch (car pushback-buffer)))
(set! offset #f)) (set! pushback-buffer (cdr pushback-buffer))
(set! offset (- offset 1))) ch))))
(set! pushback-buffer (cons ch pushback-buffer)))) ((eq? op 'push)
(else (error "read->port: invalid" (cons op args))))))))) (let ((ch (car args)))
(if (eqv? ch #\newline)
(begin
(set! line-number (- line-number 1))
(set! offset #f))
(set! offset (- offset 1)))
(set! pushback-buffer (cons ch pushback-buffer))))
((eq? op 'fold-case?) fold-case?)
((eq? op 'fold-case!) (set! fold-case? (car args)))
(else (error "read->port: invalid" (cons op args)))))))
port))))
;;; ;;;;;;;;;;;;;; ;;; ;;;;;;;;;;;;;;
;;; Character maps ;;; Character maps
@ -315,10 +328,19 @@
(define readtable:top (define readtable:top
(readtable:update-many-sequence (readtable:update-many-sequence
readtable:empty readtable:empty
(cons "#!fold-case"
(readtable:exec:new
(lambda (table char acc port)
(port 'fold-case! #t)
'())))
(cons "#!no-fold-case"
(readtable:exec:new
(lambda (table char acc port)
(port 'fold-case! #f)
'())))
(cons "#t" (readtable:return-value #t)) (cons "#t" (readtable:return-value #t))
(cons "#f" (readtable:return-value #f)) (cons "#f" (readtable:return-value #f))
(cons "#true" (readtable:return-value #t)) (cons "#true" (readtable:return-value #t))))
(cons "#trouble" (readtable:return-value 15))))
;;; ;;;;;;;;;;; ;;; ;;;;;;;;;;;
;;; Test reader ;;; Test reader
@ -334,8 +356,12 @@
ch))) ch)))
"test"))) "test")))
(let ((true-reader (%list->read (string->list "#t#false#true")))) (let ((true-reader (%list->read (string->list "#!fold-case#TRUE#!no-fold-case#TRUE"))))
(display (list "first:" (readtable:top (true-reader 'read) #f true-reader))) (display (list "first:" (readtable:top (true-reader 'read) #f true-reader)))
(newline) (newline)
(display (list "second: " (readtable:top (true-reader 'read) #f true-reader))) (display (list "second: " (readtable:top (true-reader 'read) #f true-reader)))
(newline)
(display (list "third: " (readtable:top (true-reader 'read) #f true-reader)))
(newline)
(display (list "fourth: " (readtable:top (true-reader 'read) #f true-reader)))
(newline)) (newline))