123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- ;;; parser.scm -- parse tokens for Joy.
- ;;; Copyright © 2016 Eric Bavier <bavier@member.fsf.org>
- ;;;
- ;;; Joy 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.
- ;;;
- ;;; Joy 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 Joy. If not, see <http://www.gnu.org/licenses/>.
- (define-module (language joy parser)
- #:use-module (language joy lexer)
- #:use-module (ice-9 format)
- #:use-module (ice-9 match)
- #:export (parse-joy parse-joy*))
- (define* (parse-error token msg . args)
- (apply error
- (format #f "~@[~a:~]~d:~d: ~a"
- (source-property token 'filename)
- (source-property token 'line)
- (source-property token 'column)
- msg)
- args))
- (define (return result token)
- (if (pair? result)
- (set-source-properties!
- result
- (source-properties token)))
- result)
- (define (lex-buffer lexer)
- "Given a lexer LEXER that returns tokens when invoked, return a new
- lexer. This lexer also returns tokens when invoked with no arguments
- but can also \"unread\" tokens when invoked with arguments, saving
- those tokens for later retrieval.
- E.g.
- (define lex (lex-buffer (get-lexer)))
- (lex)
- -> 'foo
- (lex 'foo)
- (lex)
- -> 'foo
- (lex)
- -> 'bar
- "
- (let ((buffer '()))
- (lambda tokens
- (match tokens
- (() (match buffer
- (() (lexer))
- ((token rest ...)
- (set! buffer rest)
- token)))
- (tokens
- (set! buffer (append tokens buffer)))))))
- (define (get-quote lex)
- (let* ((token (lex))
- (type (car token)))
- (case type
- ((eof)
- (parse-error token "unexpected end of file in quote"))
- ((square-open)
- (let* ((term (get-term lex #t))
- (token (lex))
- (type (car token)))
- (case type
- ((square-close)
- term)
- (else
- (parse-error
- token "expecting closing ']' in quote, got" token))))))))
- (define* (get-definition-sequence lex #:optional (consume-newline? #f))
- "Parse a DEFINE block and return a sequence of '<name> <term> def'"
- (define (consume-newlines)
- (match (lex)
- (('newline . _) (consume-newlines))
- (other (lex other))))
- (define (get-definition)
- (match (lex)
- ((and token' ('symbol . name))
- (consume-newlines)
- (match (lex)
- (('== . _)
- (consume-newlines)
- (let ((term (get-term lex)))
- (match (lex)
- (('semicolon . _)
- (values (list name) term))
- (token
- (parse-error token
- "expecting ';' at end of definition, got" token)))))
- (token
- (lex token' token)
- (values #f #f))))
- (token'
- (lex token')
- (values #f #f))))
- (match (lex)
- ((and token ((or 'define 'public 'private) . _))
- (consume-newlines)
- (let iterate ((definitions '()))
- (when consume-newline?
- (consume-newlines))
- (call-with-values (lambda () (get-definition))
- (lambda (name term)
- (if (and name term)
- (iterate (cons* 'def term name definitions))
- (begin
- (match (lex)
- (('end . _) #t) ;consume trailing 'END'
- (other (lex other)))
- (return (reverse definitions) token)))))))
- (token
- (parse-error token "expecting definition block, got" token))))
- (define* (get-term lex #:optional (consume-newline? #t))
- (let iterate ((items '()))
- (let* ((token (lex))
- (type (car token)))
- (case type
- ((eof semicolon end square-close define public private)
- (lex token) ;do not consume
- (return (reverse items) token))
- ((==)
- (parse-error token "'==' outside definition"))
- ((square-open)
- (lex token)
- (iterate (cons (get-quote lex) items)))
- ((newline)
- (if consume-newline?
- (iterate items) ;ignore newline
- (begin
- (lex token) ;replace newline
- (return (reverse items) token))))
- (else
- (iterate (cons (cdr token) items)))))))
- (define* (get-expression lex #:optional (consume-newline? #t))
- (let iterate ((items '()))
- (let* ((token (lex))
- (type (car token)))
- (case type
- ((eof)
- (match items
- (() (cdr token))
- (else (apply append (reverse items)))))
- ((== end)
- (parse-error token (string-append "'" (cdr token)
- "' outside definition")))
- ((paren-open bracket-open)
- (parse-error token "joy sets not implemented"))
- ((paren-close brack-blose square-close semicolon)
- (parse-error token "unexpected" (cdr token)))
- ((public private define)
- (lex token)
- (let ((defs (get-definition-sequence lex consume-newline?)))
- (iterate (cons defs items))))
- ((newline)
- (if consume-newline?
- (iterate items)
- (apply append (reverse items))))
- (else
- (lex token) ;put token back
- (iterate (cons (get-term lex consume-newline?)
- items)))))))
- (define* (parse-joy* port #:optional (consume-newline? #t))
- (let ((lexer (lex-buffer (get-lexer port))))
- (get-expression lexer consume-newline?)))
- (define (parse-joy port)
- (parse-joy* port))
|