diff options
author | Daniel Kraft <d@domob.eu> | 2009-08-26 21:36:37 +0200 |
---|---|---|
committer | Daniel Kraft <d@domob.eu> | 2009-08-26 21:36:37 +0200 |
commit | e840cc654032c60e43aec0f868d67905a3bf5523 (patch) | |
tree | f8acb132df51b45ba909b553b9828503c3f72a23 | |
parent | ddb4364b1a1635fce0b1d2eaa7d50a951c6c4dfc (diff) |
Parser for elisp and use it as reader.
* module/language/elisp/parser.scm: New parser file.
* module/language/elisp/lexer.scm: Fix lexer/1 and add unquote-splicing support.
* module/language/elisp/spec.scm: Use new elisp-reader.
* module/language/elisp/README: Document we've got a reader now.
* test-suite/tests/elisp-reader.test: Test the parser.
-rw-r--r-- | module/language/elisp/README | 2 | ||||
-rw-r--r-- | module/language/elisp/lexer.scm | 20 | ||||
-rw-r--r-- | module/language/elisp/parser.scm | 76 | ||||
-rw-r--r-- | module/language/elisp/spec.scm | 6 | ||||
-rw-r--r-- | test-suite/tests/elisp-reader.test | 44 |
5 files changed, 136 insertions, 12 deletions
diff --git a/module/language/elisp/README b/module/language/elisp/README index be625ffd3..4f33711de 100644 --- a/module/language/elisp/README +++ b/module/language/elisp/README @@ -21,9 +21,9 @@ Already implemented: * defconst, defvar, defun * macros * quotation and backquotation with unquote/unquote-splicing + * specific elisp reader Especially still missing: - * real elisp reader instead of Scheme's * more general built-ins * advice? * defsubst and inlining diff --git a/module/language/elisp/lexer.scm b/module/language/elisp/lexer.scm index 05df03d89..0a981ca24 100644 --- a/module/language/elisp/lexer.scm +++ b/module/language/elisp/lexer.scm @@ -297,7 +297,14 @@ ((#\]) (return 'square-close #f)) ((#\') (return 'quote #f)) ((#\`) (return 'backquote #f)) - ((#\,) (return 'unquote #f)) + + ; Unquote and unquote-splicing. + ((#\,) + (if (is-char? (peek-char port) #\@) + (if (not (char=? (read-char port) #\@)) + (error "expected @ in unquote-splicing") + (return 'unquote-splicing #f)) + (return 'unquote #f))) ; Remaining are numbers and symbols. Process input until next ; whitespace is found, and see if it looks like a number @@ -338,6 +345,8 @@ ; Build a special lexer that will only read enough for one expression and then ; always return end-of-input. +; If we find one of the quotation stuff, one more expression is needed in any +; case. (define (get-lexer/1 port) (let ((lex (get-lexer port)) @@ -346,12 +355,15 @@ (lambda () (if finished '*eoi* - (let ((next (lex))) + (let ((next (lex)) + (quotation #f)) (case (car next) ((paren-open square-open) (set! paren-level (1+ paren-level))) ((paren-close square-close) - (set! paren-level (1- paren-level)))) - (if (<= paren-level 0) + (set! paren-level (1- paren-level))) + ((quote backquote unquote unquote-splicing) + (set! quotation #t))) + (if (and (not quotation) (<= paren-level 0)) (set! finished #t)) next))))) diff --git a/module/language/elisp/parser.scm b/module/language/elisp/parser.scm new file mode 100644 index 000000000..431eba3d0 --- /dev/null +++ b/module/language/elisp/parser.scm @@ -0,0 +1,76 @@ +;;; Guile Emac Lisp + +;; Copyright (C) 2001 Free Software Foundation, Inc. + +;; This program 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 2, or (at your option) +;; any later version. +;; +;; This program 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; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Code: + +(define-module (language elisp parser) + #:use-module (language elisp lexer) + #:use-module (language ecmascript parse-lalr) + #:export (read-elisp)) + +; The parser (reader) for elisp expressions. It is implemented using the +; (text parse-lalr) parser generator and uses my hand-written lexer as +; the tokenizer. + + +; Build the parser itself using parse-lalr. + +(define elisp-parser + (lalr-parser (integer float symbol character string + paren-open paren-close square-open square-close + dot quote backquote unquote unquote-splicing) + + ; Expressions are our main interest. + ; It seems the symbol we're interested for return from the parser must + ; come very first, so here it is. + (expression (integer) -> $1 + (float) -> $1 + (symbol) -> $1 + (character) -> $1 + (string) -> $1 + (list) -> $1 + (quotation) -> $1 + (vector) -> $1) + + ; Pairs, lists and dotted lists. + (partial-list (expression) -> (list $1) + (expression dot expression) -> (cons $1 $3) + (expression partial-list) -> (cons $1 $2)) + (list (paren-open paren-close) -> '() + (paren-open dot expression paren-close) -> $3 + (paren-open partial-list paren-close) -> $2) + + ; Quotation and unquotation expressions. + (quotation (quote expression) -> `(quote ,$2) + (backquote expression) -> `(\` ,$2) + (unquote expression) -> `(\, ,$2) + (unquote-splicing expression) -> `(\,@ ,$2)) + + ; Vectors. + (vector-elements (expression) -> (list $1) + (expression vector-elements) -> (cons $1 $2)) + (vector (square-open square-close) -> (make-vector 0) + (square-open vector-elements square-close) -> (list->vector $2)))) + + +; Use the parser to define the elisp reader function. +; We only want to read a single expression at a time, so use get-lexer/1. + +(define (read-elisp port) + (elisp-parser (get-lexer/1 port) error)) diff --git a/module/language/elisp/spec.scm b/module/language/elisp/spec.scm index 061bcb830..f89e0c1d4 100644 --- a/module/language/elisp/spec.scm +++ b/module/language/elisp/spec.scm @@ -20,13 +20,13 @@ (define-module (language elisp spec) #:use-module (language elisp compile-tree-il) + #:use-module (language elisp parser) #:use-module (system base language) #:export (elisp)) (define-language elisp #:title "Emacs Lisp" #:version "0.0" - #:reader read + #:reader (lambda () (read-elisp (current-input-port))) #:printer write - #:compilers `((tree-il . ,compile-tree-il)) - ) + #:compilers `((tree-il . ,compile-tree-il))) diff --git a/test-suite/tests/elisp-reader.test b/test-suite/tests/elisp-reader.test index 365f578ce..ab91792c8 100644 --- a/test-suite/tests/elisp-reader.test +++ b/test-suite/tests/elisp-reader.test @@ -19,7 +19,8 @@ (define-module (test-elisp-reader) :use-module (test-suite lib) - :use-module (language elisp lexer)) + :use-module (language elisp lexer) + :use-module (language elisp parser)) ; ============================================================================== @@ -50,10 +51,11 @@ (eq? (lexer) '*eoi*)))) (pass-if "single character tokens" - (equal? (lex-string "()[]'`, . ") + (equal? (lex-string "()[]'`,,@ . ") '((paren-open . #f) (paren-close . #f) (square-open . #f) (square-close . #f) - (quote . #f) (backquote . #f) (unquote . #f) (dot . #f)))) + (quote . #f) (backquote . #f) + (unquote . #f) (unquote-splicing . #f) (dot . #f)))) (pass-if "whitespace and comments" (equal? (lex-string " (\n\t) ; this is a comment\n. ; until eof") @@ -117,10 +119,44 @@ test\"ab\"\\ abcd ,(- (char->integer #\X) (char->integer #\@)) ,(+ (expt 2 22) (expt 2 23) (expt 2 24) 32)))) - (let* ((lex1-string "((1 2) [2 [3]] 5)") + (let* ((lex1-string "'((1 2) [2 [3]] 5)") (lexer (call-with-input-string (string-append lex1-string " 1 2") get-lexer/1))) (pass-if "lexer/1" (and (equal? (lex-all lexer) (lex-string lex1-string)) (eq? (lexer) '*eoi*) (eq? (lexer) '*eoi*))))) + + +; ============================================================================== +; Test the parser. + +(define (parse-str str) + (call-with-input-string str read-elisp)) + +(with-test-prefix "Parser" + + (pass-if "only next expression" + (equal? (parse-str "1 2 3") 1)) + + (pass-if "constants" + (and (equal? (parse-str "-12") -12) + (equal? (parse-str ".123") 0.123) + (equal? (parse-str "foobar") 'foobar) + (equal? (parse-str "\"abc\"") "abc") + (equal? (parse-str "?A") 65) + (equal? (parse-str "?\\C-@") 0))) + + (pass-if "quotation" + (and (equal? (parse-str "'(1 2 3 '4)") + '(quote (1 2 3 (quote 4)))) + (equal? (parse-str "`(1 2 ,3 ,@a)") + '(\` (1 2 (\, 3) (\,@ a)))))) + + (pass-if "lists" + (equal? (parse-str "(1 2 (3) () 4 (. 5) (1 2 . (3 4)) (1 . 2) . 42)") + '(1 2 (3) () 4 5 (1 2 3 4) (1 . 2) . 42))) + + (pass-if "vectors" + (equal? (parse-str "[1 2 [] (3 4) \"abc\" d]") + #(1 2 #() (3 4) "abc" d)))) |