+;;; -*-lisp-*-
+;;;
+;;; Top-level parser for module syntax
+;;;
+;;; (c) 2010 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This file is part of the Sensble Object Design, an object system for C.
+;;;
+;;; SOD 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 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; SOD 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 SOD; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(in-package #:sod)
+
+;;;--------------------------------------------------------------------------
+;;; Toplevel syntax.
+
+(export 'module)
+
+;;; Type names.
+
+(define-pluggable-parser module typename (scanner)
+ ;; `typename' ID ( `,' ID )* `;'
+
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (and "typename"
+ (skip-many (:min 1)
+ (seq ((id :id))
+ (if (gethash id *module-type-map*)
+ (cerror* "Type `~A' already defined" id)
+ (add-to-module *module*
+ (make-instance 'type-item
+ :name id))))
+ #\,)
+ #\;))))
+
+;;; Fragments.
+
+(define-pluggable-parser module code (scanner)
+ ;; `code' ID `:' ID [ CONSTRAINTS ] `{' C-FRAGMENT `}'
+
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (seq ("code"
+ (reason :id)
+ #\:
+ (name :id)
+ (constraints (? (seq (#\[
+ (constraints (list (:min 1)
+ (list (:min 1) :id)
+ #\,))
+ #\])
+ constraints)))
+ (fragment (parse-delimited-fragment scanner #\{ #\})))
+ (add-to-module *module* (make-instance 'code-fragment-item
+ :fragment fragment
+ :constraints constraints
+ :reason reason
+ :name name))))))
+
+;;; External files.
+
+(defun read-module (pathname &key (truename (truename pathname)) location)
+ "Parse the file at PATHNAME as a module, returning it.
+
+ This is the main entry point for parsing module files. You may well know
+ the file's TRUENAME already (e.g., because `probe-file' dropped it into
+ your lap) so you can avoid repeating the search by providing it.
+
+ The LOCATION is the thing which wanted the module imported -- usually a
+ `file-location' object, though it might be anything other than `t' which
+ can be printed in the event of circular imports."
+
+ (define-module (pathname :location location :truename truename)
+ (with-open-file (f-stream pathname :direction :input)
+ (let* ((*readtable* (copy-readtable))
+ (char-scanner (make-instance 'charbuf-scanner
+ :stream f-stream))
+ (scanner (make-instance 'sod-token-scanner
+ :char-scanner char-scanner)))
+ (with-default-error-location (scanner)
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (skip-many () (plug module scanner)))))))))
+
+(define-pluggable-parser module test (scanner)
+ ;; `demo' STRING `;'
+
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (seq ("demo" (string :string) #\;)
+ (format t ";; DEMO ~S~%" string)))))
+
+(define-pluggable-parser module file (scanner)
+ ;; `import' STRING `;'
+ ;; `load' STRING `;'
+
+ (flet ((common (name type what thunk)
+ (find-file scanner
+ (merge-pathnames name
+ (make-pathname :type type
+ :case :common))
+ what
+ thunk)))
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (or (seq ("import" (name :string) #\;)
+ (common name "SOD" "module"
+ (lambda (path true)
+ (handler-case
+ (let ((module (read-module path
+ :truename true)))
+ (when module
+ (module-import module)
+ (pushnew module
+ (module-dependencies
+ *module*))))
+ (file-error (error)
+ (cerror* "Error reading module ~S: ~A"
+ path error))))))
+ (seq ("load" (name :string) #\;)
+ (common name "LISP" "Lisp file"
+ (lambda (path true)
+ (handler-case
+ (load true :verbose nil :print nil)
+ (error (error)
+ (cerror* "Error loading Lisp file ~S: ~A"
+ path error)))))))))))
+
+;;; Lisp escape.
+
+(define-pluggable-parser module lisp (scanner)
+ ;; `lisp' s-expression `;'
+
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (seq ((sexp (if (and (eql (token-type scanner) :id)
+ (string= (token-value scanner) "lisp"))
+ (let* ((stream (make-scanner-stream scanner))
+ (sexp (read stream t)))
+ (scanner-step scanner)
+ (values sexp t t))
+ (values '((:id "lisp")) nil nil)))
+ #\;)
+ (eval sexp)))))
+
+;;;--------------------------------------------------------------------------
+;;; Class declarations.
+
+(define-pluggable-parser module class (scanner)
+ ;; `class' id [`:' id-list] `{' class-item* `}'
+
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (seq ("class"
+ (name :id)
+ (supers (? (seq (#\: (supers (list (:min 1) :id #\,)))
+ supers)))
+ #\{
+
+
+;;;----- That's all, folks --------------------------------------------------