1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-11 00:52:44 +03:00
mal/guile/reader.scm
2017-09-28 07:40:47 -05:00

132 lines
4.3 KiB
Scheme

;; Copyright (C) 2015
;; "Mu Lei" known as "NalaGinrut" <NalaGinrut@gmail.com>
;; This file is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(library (reader)
(export read_str)
(import (guile) (pcre) (ice-9 match) (srfi srfi-1)
(ice-9 regex) (types) (ice-9 format)))
(define (make-Reader tokens)
(lambda (cmd)
(case cmd
((next)
(if (null? tokens)
'()
(let ((r (car tokens))) (set! tokens (cdr tokens)) r)))
((peek) (if (null? tokens) '() (car tokens)))
(else (error "Reader: Invalid cmd!" cmd)))))
(define *token-re*
(new-pcre "[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;[^\n]*|[^\\s\\[\\]{}('\"`,;)]*)"))
(define (tokenizer str)
(filter (lambda (s) (and (not (string-null? s)) (not (string=? (substring s 0 1) ";"))))
(pcre-search *token-re* str)))
(define (delim-read reader delim)
(let lp((next (reader 'peek)) (ret '()))
(cond
((null? next) (throw 'mal-error (format #f "expected '~a'" delim)))
((string=? next delim) (reader 'next) (reverse ret))
(else
(let* ((cur (read_form reader))
(n (reader 'peek)))
(lp n (cons cur ret)))))))
(define (read_list reader)
(cond
((string=? ")" (reader 'peek))
(reader 'next)
'())
(else (delim-read reader ")"))))
(define (read_vector reader)
(cond
((string=? "]" (reader 'peek))
(reader 'next)
#())
(else (list->vector (delim-read reader "]")))))
(define (read_hashmap reader)
(define ht (make-hash-table))
(define lst (delim-read reader "}"))
(cond
((null? lst) ht)
(else
(let lp((next lst))
(cond
((null? next) ht)
(else
(when (null? (cdr next))
(throw 'mal-error
(format #f "read_hashmap: '~a' lack of value" (car next))))
(let ((k (car next))
(v (cadr next)))
(hash-set! ht k v)
(lp (cddr next)))))))))
(define (read_atom reader)
(let ((token (reader 'next)))
(cond
((string-match "^-?[0-9][0-9.]*$" token)
=> (lambda (m) (string->number (match:substring m 0))))
((eqv? (string-ref token 0) #\")
(if (eqv? (string-ref token (- (string-length token) 1)) #\")
(with-input-from-string token read)
(throw 'mal-error "expected '\"'")))
((string-match "^:(.*)" token)
=> (lambda (m) (string->keyword (match:substring m 1))))
((string=? "nil" token) nil)
((string=? "true" token) #t)
((string=? "false" token) #f)
(else (string->symbol token)))))
(define (read_form reader)
(define (clean x)
(if (string? x)
(string-trim-both
x
(lambda (c) (char-set-contains? char-set:whitespace c)))
x))
(define (next) (reader 'next))
(define (more) (read_form reader))
(match (clean (reader 'peek))
(() (throw 'mal-error "blank line")) ; FIXME: what should be returned?
("'" (next) (list 'quote (more)))
("`" (next) (list 'quasiquote (more)))
("~" (next) (list 'unquote (more)))
("~@" (next) (list 'splice-unquote (more)))
("^" (next) (let ((meta (more))) `(with-meta ,(more) ,meta)))
("@" (next) `(deref ,(more)))
(")" (next) (throw 'mal-error "unexpected ')'"))
("(" (next) (read_list reader))
("]" (throw 'mal-error "unexpected ']'"))
("[" (next) (read_vector reader))
("}" (throw 'mal-error "unexpected '}'"))
("{" (next) (read_hashmap reader))
("" (next) (read_form reader))
(else (read_atom reader))))
(define (read_str str)
(if (eof-object? str)
str
(let* ((tokens (tokenizer str))
(t (if (null? tokens)
(if (char=? (string-ref str 0) #\;)
'()
(list str))
tokens)))
(read_form (make-Reader t)))))