summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Kraft <d@domob.eu>2009-08-26 21:36:37 +0200
committerDaniel Kraft <d@domob.eu>2009-08-26 21:36:37 +0200
commite840cc654032c60e43aec0f868d67905a3bf5523 (patch)
treef8acb132df51b45ba909b553b9828503c3f72a23
parentddb4364b1a1635fce0b1d2eaa7d50a951c6c4dfc (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/README2
-rw-r--r--module/language/elisp/lexer.scm20
-rw-r--r--module/language/elisp/parser.scm76
-rw-r--r--module/language/elisp/spec.scm6
-rw-r--r--test-suite/tests/elisp-reader.test44
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))))