Preserve Typed Jerboa module source

ober

08c973c7570c785f353d3cb82ef5f64f6822c28c

diff --git a/docs/typed-jerboa.md b/docs/typed-jerboa.md
index 8100296..2b77d06 100644
--- a/docs/typed-jerboa.md
+++ b/docs/typed-jerboa.md
@@ -840,7 +840,10 @@ Minimum excluded features:
 
 - Read typed module forms. Initial datum parser landed as
   `(jerboa typed parser)`.
-- Preserve source spans. Not yet landed; the first parser validates datums only.
+- Preserve source spans. Initial module-level source preservation has landed:
+  `typed-module-source` stores the reader source location for annotated
+  `typed-library` forms. Declaration and expression spans are still future
+  work.
 - Parse type expressions. Initial symbolic and compound type parsing landed.
 - Parse records, variants, and function definitions. Initial AST records landed.
 - Reject unsupported forms clearly.
diff --git a/lib/jerboa/typed/parser.ss b/lib/jerboa/typed/parser.ss
index c8a8c38..297c13b 100644
--- a/lib/jerboa/typed/parser.ss
+++ b/lib/jerboa/typed/parser.ss
@@ -15,6 +15,7 @@
 
     typed-module? make-typed-module
     typed-module-name typed-module-exports typed-module-declarations
+    typed-module-source
 
     typed-type-decl? make-typed-type-decl
     typed-type-decl-name
@@ -45,7 +46,7 @@
           (only (jerboa core) def defstruct)
           (jerboa reader))
 
-  (defstruct typed-module (name exports declarations))
+  (defstruct typed-module (name exports declarations source))
   (defstruct typed-type-decl (name))
   (defstruct typed-field (name type mutable?))
   (defstruct typed-record (name fields))
@@ -67,6 +68,10 @@
          (map strip-source-annotations (vector->list datum)))]
       [else datum]))
 
+  (def (datum-source datum)
+    (and (annotated-datum? datum)
+         (annotated-datum-source datum)))
+
   (def (proper-list? x)
     (cond
       [(null? x) #t]
@@ -119,7 +124,8 @@
            (eq? (car form) 'typed-library))))
 
   (def (parse-typed-library form)
-    (let ([form (strip-source-annotations form)])
+    (let ([source (datum-source form)]
+          [form (strip-source-annotations form)])
       (expect-proper-list 'parse-typed-library form)
       (unless (typed-library-form? form)
         (error 'parse-typed-library "expected typed-library form" form))
@@ -130,7 +136,7 @@
       (let ([name (parse-module-name (cadr form))]
             [exports (parse-export-form (caddr form))]
             [declarations (map parse-typed-declaration (cdddr form))])
-        (make-typed-module name exports declarations))))
+        (make-typed-module name exports declarations source))))
 
   (def (parse-typed-type type)
     (let ([type (strip-source-annotations type)])
diff --git a/tests/test-typed-parser.ss b/tests/test-typed-parser.ss
index 83e3654..b3f25dc 100644
--- a/tests/test-typed-parser.ss
+++ b/tests/test-typed-parser.ss
@@ -88,6 +88,15 @@
   (typed-module-name (parse-typed-library annotated-sample))
   '(sample typed rust-basic))
 
+(test "unannotated typed library source is false"
+  (typed-module-source parsed)
+  #f)
+
+(test "parse reader-annotated typed library source path"
+  (source-location-path
+    (typed-module-source (parse-typed-library annotated-sample)))
+  "tests/fixtures/typed/rust-basic.ss")
+
 (test "module name"
   (typed-module-name parsed)
   '(sample typed split-tree))