git.fiddlerwoaroof.com
Raw Blame History
;;; File: module-parser         Author: John

;;; This is for using the parser to parse strings.

(define (parse-from-string string parse-proc filename)
 (dynamic-let ((*current-file* filename))
  (call-with-input-string string
    (lambda (port)
      (let ((tokens (lex-port port '#f)))
	(init-token-stream tokens)
	(let ((res (funcall parse-proc)))
	  (if (not (eq-token? 'eof))
	      (signal-leftover-tokens)
	      res)))))))

(define (signal-leftover-tokens)
  (fatal-error 'leftover-tokens
	       "Leftover tokens after parsing."))


;;; This file deals with the basic structure of a module.  It also adds
;;; the `module Main where' required by abbreviated modules.

(define (parse-tokens tokens)
  (init-token-stream tokens)
  (let ((mod (token-case
	      (|module| (parse-module))
	      (else (parse-modules/named '|Main| '())))))
    (cons mod (parse-module-list))))

(define (parse-module)
  (token-case
   (modid (let* ((mod-name (token->symbol))
		 (exports (parse-exports)))
	    (require-token
	      |where|
	      (signal-missing-token "`where'" "module definition"))
	    (parse-modules/named mod-name exports)))
   (else (signal-missing-token "<modid>" "module definition"))))

(define (parse-module-list)
  (token-case
   (|module|
    (let ((mod (parse-module)))
      (cons mod (parse-module-list))))
   (eof '())
   (else (signal-missing-module))))

(define (signal-missing-module)
  (parser-error 'missing-module
		"Missing `module', or leftover junk after module definition."))

(define (parse-exports)
  (token-case
   (\( (parse-export-list))
   (else '())))

(define (parse-export-list)
  (let ((entity (parse-entity 'export)))
    (token-case
     (\) (list entity))
     (\, (cons entity (parse-export-list)))
     (else (signal-missing-token "`)' or ','" "export list")))))

(define (parse-modules/named mod-name exports)
  (trace-parser module
    (let ((mod-ast (make module
		     (name mod-name)
		     (type 'standard)
		     (exports exports)
		     (default *standard-module-default*))))
      (start-layout (lambda (in-layout?)
		      (parse-module-decls mod-ast in-layout? 'import))))))

;;; The mod-ast fields are kept in non-reversed order by appending
;;; each decl to the end of the appropriate list.  This loses for
;;; value decls, so these are in reversed order!!

(define (parse-module-decls mod-ast in-layout? state)
  (token-case
   (|import| (let ((import (parse-import)))
	       (if (eq? state 'import)
		   (push-decl-list import (module-imports mod-ast))
		   (signal-misplaced-import)))
	     (terminate-topdecl mod-ast in-layout? state))
   (|infix| (terminate-topdecl mod-ast in-layout?
			       (parse-fixity 'n mod-ast state)))
   (|infixl| (terminate-topdecl mod-ast in-layout?
				(parse-fixity 'l mod-ast state)))
   (|infixr| (terminate-topdecl mod-ast in-layout?
				(parse-fixity 'r mod-ast state)))
   (|data| (let ((data-decl (parse-type-decl '#f)))
	     (push-decl-list data-decl (module-algdatas mod-ast)))
	   (terminate-topdecl mod-ast in-layout? 'topdecl))
   (|type| (let ((synonym-decl (parse-synonym-decl)))
	     (push-decl-list synonym-decl (module-synonyms mod-ast)))
	   (terminate-topdecl mod-ast in-layout? 'topdecl))
   (|class| (let ((class-decl (parse-class-decl)))
	      (push-decl-list class-decl (module-classes mod-ast)))
	    (terminate-topdecl mod-ast in-layout? 'topdecl))
   (|instance| (let ((instance-decl (parse-instance-decl '#f)))
		 (push-decl-list instance-decl (module-instances mod-ast)))
	       (terminate-topdecl mod-ast in-layout? 'topdecl))
   (|default| (let ((types 
		     (token-case
		      (\( (token-case (\) '())
				      (else (parse-type-list))))
		      (else (list (parse-type))))))
		(if (eq? (module-default mod-ast) *standard-module-default*)
		    (setf (module-default mod-ast)
			  (make default-decl (types types)))
		    (signal-multiple-defaults)))
    (terminate-topdecl mod-ast in-layout? 'topdecl))
   ((begin-annotation no-advance)
    (let ((annotations (parse-annotations)))
      (setf (module-annotations mod-ast)
	    (append (module-annotations mod-ast) annotations)))
    (terminate-topdecl mod-ast in-layout? state))
   (pat-start (let ((decl (parse-decl)))
		(setf (module-decls mod-ast)
		      (decl-push decl (module-decls mod-ast))))
	      (terminate-topdecl mod-ast in-layout? 'topdecl))
   (else
    (maybe-end-module mod-ast in-layout? state))))

(define (signal-misplaced-import)
  (parser-error 'misplaced-import
		"The import declaration is misplaced."))

(define (signal-multiple-defaults)
  (parser-error 'multiple-defaults
		"There are multiple default declarations."))

(define (terminate-topdecl mod-ast in-layout? state)
  (token-case
   (\; (parse-module-decls mod-ast in-layout? state))
   (else (maybe-end-module mod-ast in-layout? state))))

(define (maybe-end-module mod-ast in-layout? state)
  (declare (ignore state))
  (cond ((or (eq-token? '|module|) (eq-token? 'eof) (eq-token? '\})
	     (eq-token? '$\}))
	 (close-layout in-layout?)
	 (wrapup-module mod-ast)
	 mod-ast)
	(else
	 (signal-invalid-syntax "a topdecl"))))

(define (wrapup-module mod-ast)
  (setf (module-decls mod-ast)
	(nreverse (module-decls mod-ast)))
  (when (and (null? (module-imports mod-ast))
	     (null? (module-decls mod-ast))
	     (null? (module-algdatas mod-ast))
	     (null? (module-synonyms mod-ast))
	     (null? (module-instances mod-ast))
	     (null? (module-classes mod-ast)))
    (signal-empty-module)))

(define (signal-empty-module)
  (parser-error 'empty-module "Module definition is empty."))

(define (parse-import)
 (save-parser-context
  (token-case
   (modid (let ((mod (token->symbol))
		(mode 'all)
		(specs '()))
	    (token-case
	     (\( (setf mode 'by-name)
		 (token-case
		  (\) (setf specs '()))
		  (else (setf specs (parse-import-list)))))
	     (|hiding| (require-token
			 \(
			 (signal-missing-token "`('" "hiding clause"))
		       (setf specs (parse-import-list)))
	     (else '()))
	    (let ((renamings (token-case (|renaming|
					   (require-token
					     \(
					     (signal-missing-token
					       "`('" "renaming clause"))
					   (parse-renamings))
					 (else '()))))
	      (make import-decl (module-name mod) (mode mode) (specs specs)
		                (renamings renamings)))))
   (else
    (signal-missing-token "<modid>" "import declaration")))))

(define (parse-import-list)
  (let ((import (parse-entity 'import)))
    (token-case
     (\, (cons import (parse-import-list)))
     (\) (list import))
     (else (signal-missing-token "`)' or `,'" "import list")))))

(define (parse-renamings)
 (let ((renaming
	(save-parser-context
	 (token-case
	  (var (let ((name1 (var->symbol)))
		 (require-token
		   |to|
		   (signal-missing-token "`to'" "import renaming clause"))
		 (token-case
		  (var (let ((name2 (var->symbol)))
			 (make renaming (from name1) (to name2)
			       (referenced? '#f))))
		  (else (signal-invalid-syntax "import renaming clause")))))
	  (con (let ((name1 (con->symbol)))
		 (require-token
 		   |to| 
		   (signal-missing-token "`to'" "import renaming clause"))
		 (token-case
		  (con (let ((name2 (con->symbol)))
			 (make renaming (from name1) (to name2)
			       (referenced? '#f))))
		  (else (signal-invalid-syntax "import renaming clause")))))
	  (else (signal-invalid-syntax "import renaming clause"))))))
    (token-case (\, (cons renaming (parse-renamings)))
		(\) (list renaming)))))

(define (parse-fixity associativity mod-ast state)
  (let ((fixity-decl
	 (save-parser-context
	  (let* ((prec (token-case
			(k (let ((p (token->integer)))
			     (cond ((<= p 9)
				    p)
				   (else
				    (signal-bad-fixity)
				    9))))
			(else 9)))
		 (ops (parse-op-list))
		 (fixity (make fixity (associativity associativity)
		       (precedence prec))))
	    (make fixity-decl (fixity fixity) (names ops))))))
    (push-decl-list fixity-decl (module-fixities mod-ast))
    (cond ((or (eq? state 'import)
	       (eq? state 'fixity))
	   'fixity)
	  (else
	   (signal-misplaced-fixity)
	   state))))


(define (signal-bad-fixity)
  (parser-error 'bad-fixity
		"Expecting fixity value of 0 - 9."))

(define (signal-misplaced-fixity)
  (parser-error 'misplaced-fixity "The fixity declaration is misplaced."))

(define (parse-op-list)
  (let ((name (token-case
	       (op (op->symbol))
	       (else (signal-missing-token "<op>" "fixity declaration")))))
    (token-case
     (\, (cons name (parse-op-list)))
     (else (list name)))))

(define (parse-entity context)
 (trace-parser entity
  (save-parser-context
   (token-case
    (var (var->entity))
    (tycon
     (let ((name (token->symbol)))
       (token-case
	(\( (token-case
	     (\.\. (require-token
		     '\)
		     (signal-missing-token "`)'" "class or datatype entity"))
		   (make entity-abbreviated (name name)))
	     (var (parse-entity-class name))
	     (con (parse-entity-datatype name))
	     (\) (make entity-class (name name) (methods '())))
	     (else (signal-invalid-syntax "an entity"))))
	(\.\. (if (eq? context 'export)
		  (make entity-module (name name))
		  (signal-invalid-syntax "an entity")))
	(else
	 (make entity-con (name name))))))
    (else (signal-invalid-syntax "an entity"))))))

(define (parse-entity-class class-name)
  (let ((vars (parse-var-list)))
    (make entity-class (name class-name) (methods vars))))

(define (parse-entity-datatype type-name)
  (let ((constrs (parse-con-list)))
    (make entity-datatype (name type-name) (constructors constrs))))

(define (parse-var-list)
  (token-case
   (var (let ((name (var->symbol)))
	  (token-case
	   (\) (list name))
	   (\, (cons name (parse-var-list)))
	   (else
	    (signal-missing-token "`)' or `,'" "class entity")))))
   (else (signal-missing-token "<var>" "class entity"))))

(define (parse-con-list)
  (token-case
   (con (let ((name (con->symbol)))
	  (token-case
	   (\) (list name))
	   (\, (cons name (parse-con-list)))
	   (else (signal-missing-token "`)' or `,'" "datatype entity")))))
   (else (signal-missing-token "<con>" "datatype entity"))))