A defn- is private to its module, and the module's own macros may call it

This commit is contained in:
Joseph Ferano 2026-09-25 08:57:20 +07:00
commit 11dc230974
33 changed files with 433 additions and 108 deletions

View File

@ -531,11 +531,22 @@ routes must arrive under one set of names or the checker sees every declaration
twice. The price is that the same directory under two aliases is refused, naming twice. The price is that the same directory under two aliases is refused, naming
both. both.
** TODO A package cannot mark a name private ** DONE A package cannot mark a name private
=rl/get-color-raw= is callable from outside its package. The refusal machinery CLOSED: [2026-09-25]
takes a second rule in one line; the blocker is that there is no way for a package =defn-= declares a function private to its module; it is a =defn= otherwise.
to *say* a name is private, and adding one is a parser change the author has to The module is the import boundary: every file of a directory package, or the
choose a spelling for. Every lane has skipped it for that reason. one file of a package imported by naming the file. A use from outside — a call
or the name as a value — is refused in =Check=, so it holds under C-c C-c too.
Code the package's own macro wrote counts as inside (SBCL's rule, not
Clojure's); what the importer wrote, passed through or from its own macro, does
not. Functions only. The edn and json internals are now =defn-=;
=rl/get-color-raw= no longer exists. =docs/BUILT.md= has the placement.
** WAIT A private scoped to one file of a directory package
Deferred until a need appears: Odin's =@(private="file")=, a function visible
to its own file only when the package is a directory. =defn-= covers the
package; a one-file package already gets file scope because the file is the
module.
** CANCELLED A struct version word, so a redefined layout keeps working ** CANCELLED A struct version word, so a redefined layout keeps working
CLOSED: [2026-09-20] CLOSED: [2026-09-20]

View File

@ -794,10 +794,29 @@ takes a macro-stamped location — and `Loc.from_macro` sets a name and leaves f
the frame the break loop reports is still the line the reader is looking at. `temps` is not reset on this path, unlike `decl`'s — a declaration is the frame the break loop reports is still the line the reader is looking at. `temps` is not reset on this path, unlike `decl`'s — a declaration is
a fresh top level, an expression is evaluated into a session that has been handing out temporaries all along. a fresh top level, an expression is evaluated into a session that has been handing out temporaries all along.
Still missing: a package-private marker for anything other than `main`, which is why `rl/get-color-raw` is callable. A function can be private to its module: `defn-`, Clojure's spelling, is a `defn` whose uses outside the module that
The gap is surface syntax and not `load.ml` — `exported` is one predicate and the refusal machinery that points at the declares it are refused. It is not done in `load.ml`, although `main`'s refusal is. `Load.refuse_hidden` walks every
line which tried already exists, so a second rule is a line. What does not exist is any way for a package to *mark* a declaration after the rename, the package's own included, and by then the package's calls to its helper read
name private, and inventing one is a reader and parser change. `alias/helper` like anyone else's; and a C-c C-c goes through `Load.program` with one form and no imports in sight. So
the flag rides on `Ast.fn`, `Check` records every `defn-` in `privates`, and `Check.private_ref` compares the file the
use is written in with the file the definition is — the same shape as `shadows_builtin`'s file test. Being in `Check`
is what makes it hold in the session too: every evaluation re-checks the whole declaration list. Only a qualified name
is asked about, because an unqualified one is the program's own.
The module is what `Load` imports as one unit. For a directory package that is every `.flan` file in the directory, so
the test is the two files' directories. For a package imported by naming a single file it is that file, and its
siblings in the directory are not part of it; the parser cannot tell which kind it is reading, so `Load.file_scoped`
narrows the flag to `Private_to_file` at import, and `Session.eval` does the same for a C-c C-c in such a file. A
file-only private inside a directory package, Odin's `@(private="file")`, is not provided.
Code a package's own macro wrote counts as inside the package wherever it is expanded, which is SBCL's rule: an
expansion there refers to the package's internal symbols freely. Clojure refuses the same thing. `Expand.unmarshal`
already separates the two kinds of node an expansion holds. A node the macro invented carries the call site's
location, tagged with the macro's name and no separate call site; a form the author wrote and the macro passed
through keeps its own position, with the call recorded in `msite`. So an invented node whose macro has the function's
qualifier is accepted, and an author's form is judged by the file it sits in. The macro name is the outermost one,
because the tag is outermost-wins: a call written by a package macro that was itself expanded out of an importer's
macro is judged as the importer's.
## The link follows the program ## The link follows the program

View File

@ -119,7 +119,7 @@
;; patched only when somebody notices a gap is the list that is always a ;; patched only when somebody notices a gap is the list that is always a
;; release behind the parser. ;; release behind the parser.
(defconst flan--definers (defconst flan--definers
'("defn" "defmacro" "def" "defonce" "defconst" "defstruct" "defdata" "defunion" '("defn" "defn-" "defmacro" "def" "defonce" "defconst" "defstruct" "defdata" "defunion"
"defenum" "defalias" "defenum" "defalias"
;; The object and dispatch heads (lib/parse.ml:1277-1334). ;; The object and dispatch heads (lib/parse.ml:1277-1334).
"defclass" "defgeneric" "defmulti" "defmethod" "defclass" "defgeneric" "defmulti" "defmethod"
@ -278,7 +278,7 @@ below — and not again here.")
;; it implements, which is the name in the same place, so a file of several ;; it implements, which is the name in the same place, so a file of several
;; methods shows that name several times — the honest answer, and better ;; methods shows that name several times — the honest answer, and better
;; than listing none of them as it did. ;; than listing none of them as it did.
`(("Functions" ,(concat "^(def\\(?:n\\|generic\\|multi\\|method\\)\\s-+" `(("Functions" ,(concat "^(def\\(?:n-?\\|generic\\|multi\\|method\\)\\s-+"
flan--name-re) flan--name-re)
1) 1)
;; Its own heading rather than a second `Functions' entry: a macro runs at ;; Its own heading rather than a second `Functions' entry: a macro runs at
@ -575,6 +575,7 @@ For `syntax-propertize-function'."
;; the parameters and the body is optional; a count would have to know ;; the parameters and the body is optional; a count would have to know
;; whether one is there, and `:defn' does not care. ;; whether one is there, and `:defn' does not care.
("defn" . :defn) ("defn" . :defn)
("defn-" . :defn)
;; `(declare-c NAME [params] RET "CSymbol")'. The name is the one special ;; `(declare-c NAME [params] RET "CSymbol")'. The name is the one special
;; argument; everything after it is written down the page in one column. ;; argument; everything after it is written down the page in one column.
("declare" . 1) ("declare" . 1)

View File

@ -2576,7 +2576,7 @@ daemon reads that as stopping on entry instead."
;; and `package' is a single named exception rather than the edge of a subtler ;; and `package' is a single named exception rather than the edge of a subtler
;; rule that was never quite true. ;; rule that was never quite true.
(defconst flan--declaration-heads (defconst flan--declaration-heads
'("defmacro" "defn" "def" "defonce" "defconst" '("defmacro" "defn" "defn-" "def" "defonce" "defconst"
"defstruct" "defdata" "defunion" "defenum" "defalias" "defstruct" "defdata" "defunion" "defenum" "defalias"
;; The object and dispatch heads (lib/parse.ml:1277-1334), which are ;; The object and dispatch heads (lib/parse.ml:1277-1334), which are
;; declarations in exactly the way `defn' is: each introduces a top-level ;; declarations in exactly the way `defn' is: each introduces a top-level

View File

@ -388,6 +388,12 @@
"and the name it introduces") "and the name it introduces")
("(defonce seed i64 1)" "defonce" font-lock-keyword-face ("(defonce seed i64 1)" "defonce" font-lock-keyword-face
"defonce") "defonce")
;; A private defn: the head as a whole, not `defn' and a
;; stray `-', and the name after it.
("(defn- mix [a i32] i32 a)" "defn-" font-lock-keyword-face
"defn-'s head")
("(defn- mix [a i32] i32 a)" "mix"
font-lock-function-name-face "and the private function's name")
("(defclass point [x y])" "defclass" font-lock-keyword-face ("(defclass point [x y])" "defclass" font-lock-keyword-face
"defclass") "defclass")
("(defgeneric area [self] dyn)" "defgeneric" ("(defgeneric area [self] dyn)" "defgeneric"

View File

@ -730,7 +730,7 @@ already rely on it — so nothing here is a stand-in for the real thing."
;; back. Read off `Parse.decl', so the check is the derivation. ;; back. Read off `Parse.decl', so the check is the derivation.
(with-temp-buffer (with-temp-buffer
(flan-mode) (flan-mode)
(dolist (head '("defmacro" "defn" "def" "defonce" "defconst" "defstruct" (dolist (head '("defmacro" "defn" "defn-" "def" "defonce" "defconst" "defstruct"
"defdata" "defunion" "defenum" "defalias" "defdata" "defunion" "defenum" "defalias"
"defclass" "defgeneric" "defmulti" "defmethod" "defclass" "defgeneric" "defmulti" "defmethod"
"import" "declare" "declare-c")) "import" "declare" "declare-c"))

View File

@ -215,6 +215,12 @@ and pattern =
makes each instantiation check the concrete type answers yes. *) makes each instantiation check the concrete type answers yes. *)
type pred = { pname : string; pvar : string; ploc : Loc.t } type pred = { pname : string; pvar : string; ploc : Loc.t }
(* Who may use a function. [defn-] parses as [Private_to_package]; [Load]
narrows it to [Private_to_file] when the package is a single file named
outright, because that file is the whole module and its siblings in the
directory are not part of it. [Check.private_ref] is what enforces both. *)
type privacy = Exported | Private_to_package | Private_to_file
type fn = { type fn = {
name : string; name : string;
params : field list; params : field list;
@ -232,6 +238,7 @@ type fn = {
fwhere : pred list; fwhere : pred list;
fbody : expr list; fbody : expr list;
nloc : Loc.t; nloc : Loc.t;
fprivate : privacy;
} }
type decl = { d : decl_kind; dloc : Loc.t } type decl = { d : decl_kind; dloc : Loc.t }

View File

@ -114,6 +114,9 @@ type env = {
function can show it. Kept apart from [fparams] because a foreign function can show it. Kept apart from [fparams] because a foreign
[declare] has a location and no parameter vector worth showing. *) [declare] has a location and no parameter vector worth showing. *)
fn_locs : (string, Loc.t) Hashtbl.t; fn_locs : (string, Loc.t) Hashtbl.t;
(* Every [defn-], by name, with where it was written and how far it is
visible. See [private_ref]. *)
privates : (string, Loc.t * Ast.privacy) Hashtbl.t;
globals : (string, Types.t * bool) Hashtbl.t; (* type, is a constant *) globals : (string, Types.t * bool) Hashtbl.t; (* type, is a constant *)
(* Where each global was declared, so a refusal about one can show it. A (* Where each global was declared, so a refusal about one can show it. A
second table rather than a third field, because every other reader of second table rather than a third field, because every other reader of
@ -194,6 +197,7 @@ let new_env () = {
fns = Hashtbl.create 32; fns = Hashtbl.create 32;
fparams = Hashtbl.create 32; fparams = Hashtbl.create 32;
fn_locs = Hashtbl.create 32; fn_locs = Hashtbl.create 32;
privates = Hashtbl.create 8;
globals = Hashtbl.create 16; globals = Hashtbl.create 16;
global_locs = Hashtbl.create 16; global_locs = Hashtbl.create 16;
lifted = []; lifted = [];
@ -3912,6 +3916,7 @@ and var ctx ?(qualified = false) loc ~want name =
a question this language does not ask. *) a question this language does not ask. *)
(match Hashtbl.find_opt ctx.env.fns name with (match Hashtbl.find_opt ctx.env.fns name with
| Some (params, ret) -> | Some (params, ret) ->
private_ref ctx loc name;
(* A foreign function is in [fns] too, and its emitted signature (* A foreign function is in [fns] too, and its emitted signature
is C's: no transfer channel, and an aggregate flattened by the is C's: no transfer channel, and an aggregate flattened by the
shim. Nothing could call the resulting pointer correctly, so it shim. Nothing could call the resulting pointer correctly, so it
@ -9004,11 +9009,13 @@ and ordinary_call ctx ~want loc name args =
| Some b -> call_value ctx ~want loc (mk loc b.bty (Tast.Local b.slot)) args | Some b -> call_value ctx ~want loc (mk loc b.bty (Tast.Local b.slot)) args
| None -> assert false) | None -> assert false)
| _ when Hashtbl.mem ctx.env.gsigs name -> | _ when Hashtbl.mem ctx.env.gsigs name ->
private_ref ctx loc name;
let vars, params, ret = Hashtbl.find ctx.env.gsigs name in let vars, params, ret = Hashtbl.find ctx.env.gsigs name in
generic_call ctx ~want loc name vars params ret args generic_call ctx ~want loc name vars params ret args
| _ -> | _ ->
match Hashtbl.find_opt ctx.env.fns name with match Hashtbl.find_opt ctx.env.fns name with
| Some (params, ret) -> | Some (params, ret) ->
private_ref ctx loc name;
if List.length args <> List.length params then if List.length args <> List.length params then
fail loc "%s takes %d argument%s, given %d" name fail loc "%s takes %d argument%s, given %d" name
(List.length params) (List.length params)
@ -9170,6 +9177,63 @@ and ordinary_call ctx ~want loc name args =
is not the file the defn was written in, so it reaches the builtin. That is not the file the defn was written in, so it reaches the builtin. That
is the conservative direction, and C-c C-c — which sends the buffer's own is the conservative direction, and C-c C-c — which sends the buffer's own
path — is not affected. *) path — is not affected. *)
(* A [defn-] is its module's own. A use of one — a call, or the name taken
as a value — is refused unless it is written inside the module that
declares it. A directory package is every file in its directory, so the
test there is the two files' directories; a single file imported outright
is that file alone, which [Load] records by narrowing the flag to
[Private_to_file]. Realpath'd, because one side is usually the path the
importer was given on the command line and the other the one [Load]
resolved.
Code the package's own macro wrote counts as inside, wherever it was
expanded: SBCL's rule, where a macro's expansion refers to its package's
internal symbols freely. What the importer wrote itself does not, even when
it is an argument the macro passed through. [Expand.unmarshal] tells the two
apart already: a node the macro invented carries the call site's location
with the macro's name on it and no separate call site, and a form the author
wrote keeps its own position with the call recorded beside it. The macro's
package is its qualifier, which is the function's when both come from the
same module, because an import qualifies every name a directory declares
under the one alias it is read as.
Only a qualified name is asked about. An unqualified one belongs to the
program being built, and nothing outside a program can name it. *)
and private_ref ctx loc name =
let qualifier n =
match String.rindex_opt n '/' with
| Some i -> Some (String.sub n 0 i)
| None -> None
in
let written_by_own_macro () =
match loc.Loc.macro, loc.Loc.msite with
| Some m, None -> qualifier m <> None && qualifier m = qualifier name
| _ -> false
in
match Hashtbl.find_opt ctx.env.privates name with
| Some (at, scope) when String.contains name '/' ->
let real file = try Unix.realpath file with Unix.Unix_error _ -> file in
let inside =
match scope with
| Ast.Private_to_file -> String.equal (real at.Loc.file) (real loc.Loc.file)
| _ ->
String.equal (Filename.dirname (real at.Loc.file))
(Filename.dirname (real loc.Loc.file))
in
if not inside && not (written_by_own_macro ()) then begin
let where =
match scope with
| Ast.Private_to_file -> real at.Loc.file
| _ -> "the files in " ^ Filename.dirname (real at.Loc.file)
in
Loc.failk "check/private" loc
~notes:[ Loc.note at (Printf.sprintf "%s is declared here" name) ]
"%s is private to its package: it is declared with defn-, so only %s \
can use it. Declaring it with defn instead makes it usable from here"
name where
end
| _ -> ()
and shadows_builtin ctx loc name = and shadows_builtin ctx loc name =
(* Where the definition was written, if this name has one. A generic is in (* Where the definition was written, if this name has one. A generic is in
[generics] and nowhere near [fn_locs], so both tables are asked. *) [generics] and nowhere near [fn_locs], so both tables are asked. *)
@ -10611,6 +10675,9 @@ let collect env (decls : Ast.decl list) =
in in
env.tyvars <- []; env.tyvars <- [];
env.tvpreds <- []; env.tvpreds <- [];
if fn.Ast.fprivate <> Ast.Exported then
Hashtbl.replace env.privates fn.Ast.name
(fn.Ast.nloc, fn.Ast.fprivate);
if vars = [] then begin if vars = [] then begin
Hashtbl.replace env.fns fn.Ast.name (params, ret); Hashtbl.replace env.fns fn.Ast.name (params, ret);
Hashtbl.replace env.fparams fn.Ast.name fn.Ast.params; Hashtbl.replace env.fparams fn.Ast.name fn.Ast.params;

View File

@ -838,7 +838,7 @@ let of_dump ~env ~taken ~bound_syms ~config (d : dump) : imported =
decls := decls :=
{ Ast.d = { Ast.d =
Ast.DeclareC Ast.DeclareC
({ Ast.name = flan; params; praw = None; ret; fwhere = []; fbody = []; nloc = f.cloc }, ({ Ast.name = flan; params; praw = None; ret; fwhere = []; fbody = []; nloc = f.cloc; fprivate = Ast.Exported },
f.csym); f.csym);
dloc = f.cloc } dloc = f.cloc }
:: !decls) :: !decls)

View File

@ -175,7 +175,7 @@ let constructor n slots loc : Ast.decl =
Ast.Defn Ast.Defn
{ Ast.name = n; params; praw = None; ret = Some (dyn_at loc); { Ast.name = n; params; praw = None; ret = Some (dyn_at loc);
fwhere = []; fbody = [ ex loc (Ast.MapLit (Some n, pairs)) ]; fwhere = []; fbody = [ ex loc (Ast.MapLit (Some n, pairs)) ];
nloc = loc }; nloc = loc; fprivate = Ast.Exported };
dloc = loc } dloc = loc }
(* The dispatch value a method answers for, as an expression to compare (* The dispatch value a method answers for, as an expression to compare

View File

@ -22,12 +22,13 @@
of one directory load it once, keyed by its real path; the same directory of one directory load it once, keyed by its real path; the same directory
under two different aliases is refused, and so is a cycle. under two different aliases is refused, and so is a cycle.
Visibility is one rule so far: [main] is not exported. A package carrying Visibility is two rules. [main] is not exported: a package carrying one
one would collide with the importer's, and worse, would keep everything it would collide with the importer's, and worse, would keep everything it
calls reachable (see [Reach]) — which for a raylib front-end is the whole calls reachable (see [Reach]) — which for a raylib front-end is the whole
library, on the target that cannot link it. Package-private markers for library, on the target that cannot link it. And a [defn-] is exported like
anything else are still missing, which is why [rl/get-color-raw] is any other name but refused at a use outside its package, which
callable. is [Check.private_ref]'s and not this module's: the rename has to qualify
the package's own calls to it exactly as it qualifies everything else.
A package may also carry the C it binds to. Every [.c] file in the A package may also carry the C it binds to. Every [.c] file in the
directory is compiled into the build, and a file named [link] lists extra directory is compiled into the build, and a file named [link] lists extra
@ -726,9 +727,8 @@ let imports_of (forms : Form.t list) =
exists to prevent. exists to prevent.
So the package's [main] is dropped rather than qualified, and [alias/main] So the package's [main] is dropped rather than qualified, and [alias/main]
is not a name. Everything else is still exported; package-private markers is not a name. Everything else is exported, a [defn-] included — see
are a separate gap (TODO.org, "A package cannot mark a name private" — [Check.private_ref] for where that one is refused. *)
[rl/get-color-raw] should not be callable either). *)
let exported n = not (String.equal n "main") let exported n = not (String.equal n "main")
(* Where a name is *used*, which is what a refusal has to point at. A rename (* Where a name is *used*, which is what a refusal has to point at. A rename
@ -905,6 +905,20 @@ let refuse_hidden hidden ds =
(* Every top-level name the package declares — types and values alike, since a (* Every top-level name the package declares — types and values alike, since a
use site is rewritten by name and the two never collide in one namespace. *) use site is rewritten by name and the two never collide in one namespace. *)
(* A [defn-] in a package that is one file named outright is private to that
file: the file is the whole module, and whatever else sits in its directory
is not part of it. The parser cannot know which kind of package it is
reading, so the flag is narrowed here and in [Session.eval], the two places
that do. *)
let file_scoped (ds : Ast.decl list) =
List.map
(fun (d : Ast.decl) ->
match d.Ast.d with
| Ast.Defn ({ Ast.fprivate = Ast.Private_to_package; _ } as fn) ->
{ d with Ast.d = Ast.Defn { fn with Ast.fprivate = Ast.Private_to_file } }
| _ -> d)
ds
let owned_names (ds : Ast.decl list) = let owned_names (ds : Ast.decl list) =
List.filter_map Ast.declared_name ds List.filter_map Ast.declared_name ds
@ -1415,6 +1429,7 @@ let rec import ~seen ~open_ ~loc alias dir =
in in
let owned = List.filter exported (owned_names ds) in let owned = List.filter exported (owned_names ds) in
let decls = List.map (qualify_decl owned alias) own in let decls = List.map (qualify_decl owned alias) own in
let decls = if one_file then file_scoped decls else decls in
let lflags = if one_file then [] else link_flags dir in let lflags = if one_file then [] else link_flags dir in
let csrcs = if one_file then [] else entries dir ".c" in let csrcs = if one_file then [] else entries dir ".c" in
let phidden = let phidden =

View File

@ -168,7 +168,7 @@ let reduce (forms : Form.t list) : Form.t list =
List.iter List.iter
(fun f -> (fun f ->
match head_name f with match head_name f with
| Some (("defn" | "defmacro"), n) when not (List.mem n !out) -> | Some (("defn" | "defn-" | "defmacro"), n) when not (List.mem n !out) ->
if names_macro !out f then begin out := n :: !out; changed := true end if names_macro !out f then begin out := n :: !out; changed := true end
| _ -> ()) | _ -> ())
forms forms
@ -188,7 +188,7 @@ let reduce (forms : Form.t list) : Form.t list =
List.filter List.filter
(fun f -> (fun f ->
match head_name f with match head_name f with
| Some ("defn", n) -> not (List.mem n !out) | Some (("defn" | "defn-"), n) -> not (List.mem n !out)
| _ -> true) | _ -> true)
forms forms

View File

@ -710,7 +710,7 @@ and form f mk (head : Form.t) (args : Form.t list) : Ast.expr =
a head, which is the same property that makes a quasiquoted macro call a head, which is the same property that makes a quasiquoted macro call
output rather than a dependency. Building a declaration as a value is what output rather than a dependency. Building a declaration as a value is what
a macro is for. *) a macro is for. *)
| Sym ("defmacro" | "defn" | "def" | "defonce" | "defconst" | "defstruct" | Sym ("defmacro" | "defn" | "defn-" | "def" | "defonce" | "defconst" | "defstruct"
| "defdata" | "defunion" | "defclass" | "defgeneric" | "defmulti" | "defdata" | "defunion" | "defclass" | "defgeneric" | "defmulti"
| "defmethod" | "defenum" | "defalias" | "import" as name) -> | "defmethod" | "defenum" | "defalias" | "import" as name) ->
fail f fail f
@ -1400,7 +1400,12 @@ let rec decl (f : Form.t) : Ast.decl =
rule's and not this file's, and [Session.compatible] is where it is felt -- rule's and not this file's, and [Session.compatible] is where it is felt --
a redefinition that changes a signature is refused there, and this is a a redefinition that changes a signature is refused there, and this is a
way for a signature to change with nothing redefined. *) way for a signature to change with nothing redefined. *)
| List ({ v = Sym "defn"; _ } :: args) -> (* [defn-] is a [defn] in every respect but one: [Check.private_ref] refuses
a use of it from outside the package that declares it. *)
| List ({ v = Sym ("defn" | "defn-" as head); _ } :: args) ->
let fprivate =
if String.equal head "defn-" then Ast.Private_to_package else Ast.Exported
in
(match args with (match args with
| n :: { v = Vec ps; _ } :: ret :: body -> | n :: { v = Vec ps; _ } :: ret :: body ->
(* The slot's own failure, because the thing found there is almost (* The slot's own failure, because the thing found there is almost
@ -1434,11 +1439,12 @@ let rec decl (f : Form.t) : Ast.decl =
let fwhere, body = constraints body in let fwhere, body = constraints body in
mk (Ast.Defn { Ast.name = sym n; params = []; praw = Some (pitems ps); mk (Ast.Defn { Ast.name = sym n; params = []; praw = Some (pitems ps);
ret = Some rty; fwhere; fbody = body_of body; ret = Some rty; fwhere; fbody = body_of body;
nloc = n.loc }) nloc = n.loc; fprivate })
| _ -> | _ ->
fail f fail f
"defn is (defn name [param Type ...] ReturnType body ...). The return \ "%s is (%s name [param Type ...] ReturnType body ...). The return \
type is not optional; a function that returns nothing writes ()") type is not optional; a function that returns nothing writes ()"
head head)
(* ── The dyn side's classes and generic functions ────────────────── (* ── The dyn side's classes and generic functions ──────────────────
Four forms, all of them shorthand: nothing below [Classes.expand] knows Four forms, all of them shorthand: nothing below [Classes.expand] knows
@ -1489,7 +1495,7 @@ let rec decl (f : Form.t) : Ast.decl =
else fun fn -> Ast.Defmulti fn) else fun fn -> Ast.Defmulti fn)
{ Ast.name = sym n; params = dyn_params which ps; praw = None; { Ast.name = sym n; params = dyn_params which ps; praw = None;
ret = Some (texpr ret); fwhere = []; fbody = body_of body; ret = Some (texpr ret); fwhere = []; fbody = body_of body;
nloc = n.loc }) nloc = n.loc; fprivate = Ast.Exported })
| _ -> fail f "%s" usage) | _ -> fail f "%s" usage)
| List ({ v = Sym "defmethod"; _ } :: args) -> | List ({ v = Sym "defmethod"; _ } :: args) ->
@ -1503,7 +1509,7 @@ let rec decl (f : Form.t) : Ast.decl =
mfn = { Ast.name = gen ^ "@" ^ Ast.dispatch_text k; mfn = { Ast.name = gen ^ "@" ^ Ast.dispatch_text k;
params = dyn_params "defmethod" ps; praw = None; params = dyn_params "defmethod" ps; praw = None;
ret = None; fwhere = []; fbody = body_of body; ret = None; fwhere = []; fbody = body_of body;
nloc = n.loc } }) nloc = n.loc; fprivate = Ast.Exported } })
| _ -> | _ ->
fail f fail f
"defmethod is (defmethod generic dispatch [param ...] body ...). \ "defmethod is (defmethod generic dispatch [param ...] body ...). \
@ -1534,11 +1540,12 @@ let rec decl (f : Form.t) : Ast.decl =
(match List.rev rest with (match List.rev rest with
| [ n; { v = Form.Vec ps; _ } ] -> | [ n; { v = Form.Vec ps; _ } ] ->
mk (mkd { Ast.name = sym n; params = fields f ps; praw = None; mk (mkd { Ast.name = sym n; params = fields f ps; praw = None;
ret = None; fwhere = []; fbody = []; nloc = n.loc } csym) ret = None; fwhere = []; fbody = []; nloc = n.loc;
fprivate = Ast.Exported } csym)
| [ n; { v = Form.Vec ps; _ }; r ] -> | [ n; { v = Form.Vec ps; _ }; r ] ->
mk (mkd { Ast.name = sym n; params = fields f ps; praw = None; mk (mkd { Ast.name = sym n; params = fields f ps; praw = None;
ret = Some (texpr r); fwhere = []; fbody = []; ret = Some (texpr r); fwhere = []; fbody = [];
nloc = n.loc } csym) nloc = n.loc; fprivate = Ast.Exported } csym)
| _ -> fail f "%s" usage) | _ -> fail f "%s" usage)
| _ -> fail f "%s" usage) | _ -> fail f "%s" usage)
@ -1770,7 +1777,7 @@ let rec decl (f : Form.t) : Ast.decl =
leave off. *) leave off. *)
praw = None; praw = None;
ret = Some form_t; fwhere = []; fbody = macro_body sg body; ret = Some form_t; fwhere = []; fbody = macro_body sg body;
nloc = n.loc }) nloc = n.loc; fprivate = Ast.Exported })
| _ -> | _ ->
fail f "defmacro is (defmacro name [param ...] body ...)") fail f "defmacro is (defmacro name [param ...] body ...)")

View File

@ -644,7 +644,8 @@ let eval ?(origin = "<eval>") ?pause t src : change =
p.Load.owns p.Load.owns
@ List.filter_map Ast.declared_name ds @ List.filter_map Ast.declared_name ds
in in
List.map (Load.qualify_decl owns p.Load.alias) ds let ds = List.map (Load.qualify_decl owns p.Load.alias) ds in
if Load.is_package_file p.Load.dir then Load.file_scoped ds else ds
in in
let loc = let loc =
match incoming with d :: _ -> d.Ast.dloc | [] -> Loc.unknown match incoming with d :: _ -> d.Ast.dloc | [] -> Loc.unknown

View File

@ -0,0 +1,7 @@
;;;; A package that is one file, imported by naming the file. Its defn- is
;;;; private to this file: the files beside it in the directory are not part
;;;; of it.
(defn- inner [a i32] i32 (* a 3))
(defn outer [a i32] i32 (inner a))

View File

@ -0,0 +1,8 @@
;;;; Sits beside lib.flan and imports it by name. lib's defn- is private to
;;;; lib.flan, so the shared directory does not make it callable here.
(import lib "lib.flan")
(defn main [] i32
(print (lib/inner 5))
0)

View File

@ -0,0 +1,7 @@
;;;; Imports lib.flan by name and calls its public function.
(import lib "lib.flan")
(defn main [] i32
(print (lib/outer 5)) (println "")
0)

View File

@ -0,0 +1,7 @@
;;;; A call to a package's private function, from outside the package.
(import secret "pkgs/secret")
(defn main [] i32
(print (secret/mix 1 2))
0)

View File

@ -0,0 +1,8 @@
;;;; A package that calls another package's private function. The refusal is
;;;; at nosy's call, in nosy's file.
(import nosy "pkgs/nosy")
(defn main [] i32
(print (nosy/peek 1))
0)

View File

@ -0,0 +1,10 @@
;;;; A call to a package's private function written by a macro of this file,
;;;; not of the package.
(import secret "pkgs/secret")
(defmacro sneak [& args] `(do (secret/mix 1 2)))
(defn main [] i32
(print (sneak))
0)

View File

@ -0,0 +1,9 @@
;;;; A call to a package's private function written here and handed to the
;;;; package's own macro. The macro passes it through; the call is still this
;;;; file's.
(import secret "pkgs/secret")
(defn main [] i32
(print (secret/pass (secret/mix 1 2)))
0)

View File

@ -0,0 +1,7 @@
;;;; A package's private function, taken as a value from outside the package.
(import secret "pkgs/secret")
(defn main [] i32
(print (secret/apply2 secret/mix 1 2))
0)

View File

@ -0,0 +1,13 @@
;;;; A package's private function, used from inside the package: from the
;;;; file that declares it, from the package's other file, as a value, and
;;;; through code the package's own macros write here.
(import secret "pkgs/secret")
(defn main [] i32
(print (secret/combine 1 2)) (println "")
(print (secret/twice 3)) (println "")
(print (secret/via-value 4 5)) (println "")
(print (secret/mixed 6 7)) (println "")
(print (secret/mixed-in-do 8 9)) (println "")
0)

View File

@ -0,0 +1,6 @@
;;;; A package that imports secret and reaches for its private function under
;;;; the qualified name it arrives as.
(import secret "../secret")
(defn peek [a i32] i32 (secret/mix a 1))

View File

@ -0,0 +1,8 @@
;;;; The package's second file. It is in the same directory, so mix is its
;;;; own to call.
(defn twice [a i32] i32 (mix a a))
(defn apply2 [f (Fn [i32 i32] i32) a i32 b i32] i32 (f a b))
(defn via-value [a i32 b i32] i32 (apply2 mix a b))

View File

@ -0,0 +1,18 @@
;;;; A package with a private function. mix is declared with defn-, so only
;;;; the files in this directory may use it: combine calls it here, and the
;;;; package's other file calls it and hands it out as a value.
(defn- mix [a i32 b i32] i32 (+ (* a 10) b))
(defn combine [a i32 b i32] i32 (mix a b))
;; Code these macros write is the package's own, wherever it is expanded: the
;; call to mix they answer with is accepted in the importer's file, on its own
;; and nested inside a do.
(defmacro mixed [& args] `(mix ~(at args 0) ~(at args 1)))
(defmacro mixed-in-do [& args] `(do (mix ~(at args 0) ~(at args 1))))
;; What the importer writes and this macro only passes through is still the
;; importer's.
(defmacro pass [& args] (at args 0))

View File

@ -2830,6 +2830,16 @@ let () =
shape/Box and not area/shape/Box. *) shape/Box and not area/shape/Box. *)
outputs "a diamond, with a type crossing it" "programs/pkg-diamond.flan" outputs "a diamond, with a type crossing it" "programs/pkg-diamond.flan"
"3\n6\n20\n"; "3\n6\n20\n";
(* A [defn-] is callable from anywhere in its own package: the file that
declares it, the package's other file, and as a value handed out from
there. The refusals are with the others, further down. *)
outputs "a defn- used inside its package" "programs/pkg-private.flan"
"12\n33\n45\n67\n89\n";
(* A single-file package's defn- is callable from that file, and the file
beside it that imports it reaches the public function. The refusal for
the same sibling calling the defn- directly is with the others. *)
outputs "a single-file package calls its own defn-"
"programs/loose/use.flan" "15\n";
(* A defn named after a builtin, and the boundary the shadow stops at. (* A defn named after a builtin, and the boundary the shadow stops at.
The numbers are the whole claim and none of them could be printed by The numbers are the whole claim and none of them could be printed by
the other reading: 7 is the program's own one-argument (get p), which the other reading: 7 is the program's own one-argument (get p), which
@ -3105,6 +3115,33 @@ let () =
if sand_checks then if sand_checks then
refuses "a package's main is not visible" "programs/pkg-hidden-main.flan" refuses "a package's main is not visible" "programs/pkg-hidden-main.flan"
"sand/main is not a name"; "sand/main is not a name";
(* [defn-]: a package's own function, refused at every use from outside
its directory — a call, the name taken as a value, and a call from a
second package that imports it, where the name arrives qualified under
the alias that package chose. The working half is
[outputs "a defn- used inside its package"]. *)
refuses "a defn- called from outside its package"
"programs/pkg-private-call.flan" "secret/mix is private to its package";
refuses "a defn- taken as a value from outside its package"
"programs/pkg-private-value.flan" "secret/mix is private to its package";
refuses "a defn- called from another package"
"programs/pkg-private-nested.flan" "secret/mix is private to its package";
refuses "and the refusal names the package's directory"
"programs/pkg-private-call.flan" "pkgs/secret";
refuses "and names the fix"
"programs/pkg-private-call.flan" "Declaring it with defn instead";
(* The package's own macro may write a call to its defn- (that half is in
pkg-private.flan); what the importer writes is the importer's, whether
the package's macro passes it through or the importer's macro wrote it. *)
refuses "a defn- call the importer wrote, passed through the package's macro"
"programs/pkg-private-passed.flan" "secret/mix is private to its package";
refuses "a defn- call written by the importer's own macro"
"programs/pkg-private-own-macro.flan"
"secret/mix is private to its package";
(* A single file named outright is the whole module, so its defn- is not
visible to the file beside it in the directory. *)
refuses "a single-file package's defn-, from the file beside it"
"programs/loose/peek.flan" "lib/inner is private to its package";
refuses "one directory under two aliases" "programs/pkg-two-aliases.flan" refuses "one directory under two aliases" "programs/pkg-two-aliases.flan"
"one directory takes one alias"; "one directory takes one alias";
(* A ring is refused and the ring is named. The needle is the chain, not (* A ring is refused and the ring is named. The needle is the chain, not

View File

@ -590,6 +590,46 @@ let () =
(fun d -> try Unix.rmdir d with Unix.Unix_error _ -> ()) (fun d -> try Unix.rmdir d with Unix.Unix_error _ -> ())
[ pkg; tmp ]; [ pkg; tmp ];
(* ── A defn- through the dev loop ──────────────────────────────────
C-c C-c on a [defn-] in its package's own file redefines it under the
qualified name and the session keeps it private: the package's callers
still reach the new body, and a form evaluated in the importer's buffer
is still refused. A [defn-] in the program's own file is the program's
and is callable from the rest of it. *)
let tp, _ = Session.create ~file:"programs/pkg-private.flan" () in
(match Session.eval ~origin:"programs/pkgs/secret/secret.flan" tp
"(defn- mix [a i32 b i32] i32 (+ (* a 100) b))" with
| c ->
if not (has c.Session.ir "mul i32") then
fail "a defn- redefined in its package's file installed no new body"
| exception Loc.Error { Loc.dmsg = m; _ } ->
fail "redefining a defn- in its package's file: %s" m);
(match Session.eval ~origin:"programs/pkg-private.flan" tp
"(defn peek [] i32 (secret/mix 1 2))" with
| _ -> fail "a redefined defn- became callable from outside its package"
| exception Loc.Error { Loc.dmsg = m; _ } ->
if not (has m "secret/mix is private to its package") then
fail "a call to a redefined defn- was refused for another reason: %s" m);
(match Session.eval ~origin:"programs/pkg-private.flan" tp
"(defn- helper [] i32 7)\n(defn use-helper [] i32 (helper))" with
| _ -> ()
| exception Loc.Error { Loc.dmsg = m; _ } ->
fail "a defn- in the program's own file: %s" m);
(* And a single-file package: redefined from its own file, it stays private
to that file, so the file beside it that imports it is still refused. *)
let tl, _ = Session.create ~file:"programs/loose/use.flan" () in
(match Session.eval ~origin:"programs/loose/lib.flan" tl
"(defn- inner [a i32] i32 (* a 4))" with
| _ -> ()
| exception Loc.Error { Loc.dmsg = m; _ } ->
fail "redefining a single-file package's defn-: %s" m);
(match Session.eval ~origin:"programs/loose/use.flan" tl
"(defn peek [] i32 (lib/inner 1))" with
| _ -> fail "a redefined single-file defn- became callable beside its file"
| exception Loc.Error { Loc.dmsg = m; _ } ->
if not (has m "lib/inner is private to its package") then
fail "a call to a single-file defn- was refused for another reason: %s" m);
(* ── C-x C-e expands, which it never used to ──────────────────────── (* ── C-x C-e expands, which it never used to ────────────────────────
[Parse.expr] did not call the expander at all, so an expression typed at [Parse.expr] did not call the expander at all, so an expression typed at
the REPL saw no macros — not a package's and not the prelude's, which is the REPL saw no macros — not a package's and not the prelude's, which is

28
vendor/edn/edn.flan vendored
View File

@ -228,18 +228,18 @@
;; A comma is whitespace in EDN, which is the rule most hand-written readers ;; A comma is whitespace in EDN, which is the rule most hand-written readers
;; get wrong: {:a 1, :b 2} is one map and the comma is not a token. ;; get wrong: {:a 1, :b 2} is one map and the comma is not a token.
(defn ws? [b u8] bool (defn- ws? [b u8] bool
(or (space? b) (= b \,))) (or (space? b) (= b \,)))
;; Everything that ends an unquoted token. Note `;` is here: `[1;c` has the ;; Everything that ends an unquoted token. Note `;` is here: `[1;c` has the
;; comment start immediately after the 1, with no space, and a scanner that ;; comment start immediately after the 1, with no space, and a scanner that
;; only stopped on whitespace and brackets would read "1;c" as one number. ;; only stopped on whitespace and brackets would read "1;c" as one number.
(defn delim? [b u8] bool (defn- delim? [b u8] bool
(or (ws? b) (or (ws? b)
(= b \() (= b \)) (= b \[) (= b \]) (= b \{) (= b \}) (= b \() (= b \)) (= b \[) (= b \]) (= b \{) (= b \})
(= b \") (= b \;))) (= b \") (= b \;)))
(defn alpha? [b u8] bool (defn- alpha? [b u8] bool
(or (and (>= b \a) (<= b \z)) (or (and (>= b \a) (<= b \z))
(and (>= b \A) (<= b \Z)))) (and (>= b \A) (<= b \Z))))
@ -247,7 +247,7 @@
;; "anything that is not a delimiter": without it every stray byte becomes a ;; "anything that is not a delimiter": without it every stray byte becomes a
;; one-character symbol, and `@` or a backtick — a Clojure reader macro, not ;; one-character symbol, and `@` or a backtick — a Clojure reader macro, not
;; EDN — reads as a name instead of being reported at the byte it is on. ;; EDN — reads as a name instead of being reported at the byte it is on.
(defn sym-start? [b u8] bool (defn- sym-start? [b u8] bool
(or (alpha? b) (or (alpha? b)
(= b \.) (= b \*) (= b \+) (= b \!) (= b \-) (= b \_) (= b \.) (= b \*) (= b \+) (= b \!) (= b \-) (= b \_)
(= b \?) (= b \$) (= b \%) (= b \&) (= b \=) (= b \<) (= b \>) (= b \?) (= b \$) (= b \%) (= b \&) (= b \=) (= b \<) (= b \>)
@ -259,26 +259,26 @@
;; yet, so edn/scan-atom and edn/push-open are as callable as edn/next is. ;; yet, so edn/scan-atom and edn/push-open are as callable as edn/next is.
;; Nothing below is part of the API and none of it will keep its shape. ;; Nothing below is part of the API and none of it will keep its shape.
(defn at-end? [c (Ptr Cursor)] bool (defn- at-end? [c (Ptr Cursor)] bool
(>= (.pos c) (length (.src c)))) (>= (.pos c) (length (.src c))))
;; An empty slice of src, positioned at p. Used for the tokens that have no ;; An empty slice of src, positioned at p. Used for the tokens that have no
;; text of their own — eof, error, and every delimiter. It is still a slice of ;; text of their own — eof, error, and every delimiter. It is still a slice of
;; the input rather than a slice of nothing, so `text` has one meaning for all ;; the input rather than a slice of nothing, so `text` has one meaning for all
;; token kinds. ;; token kinds.
(defn empty-at [c (Ptr Cursor) p i32] [u8] (defn- empty-at [c (Ptr Cursor) p i32] [u8]
(slice (.src c) p p)) (slice (.src c) p p))
(defn token [c (Ptr Cursor) kind i32 lo i32 hi i32 p i32] Token (defn- token [c (Ptr Cursor) kind i32 lo i32 hi i32 p i32] Token
(Token {.kind kind .text (slice (.src c) lo hi) .pos p})) (Token {.kind kind .text (slice (.src c) lo hi) .pos p}))
(defn error-token [c (Ptr Cursor)] Token (defn- error-token [c (Ptr Cursor)] Token
(Token {.kind tok-error .text (empty-at c (.err-pos c)) .pos (.err-pos c)})) (Token {.kind tok-error .text (empty-at c (.err-pos c)) .pos (.err-pos c)}))
;; Whitespace, commas, and `;` comments, which run to the newline or to the end ;; Whitespace, commas, and `;` comments, which run to the newline or to the end
;; of input — a comment on the last line of a file with no trailing newline is ;; of input — a comment on the last line of a file with no trailing newline is
;; the case that decides whether the loop tests the length before the byte. ;; the case that decides whether the loop tests the length before the byte.
(defn skip-trivia [c (Ptr Cursor)] () (defn- skip-trivia [c (Ptr Cursor)] ()
(while (not (at-end? c)) (while (not (at-end? c))
(let [b (at (.src c) (.pos c))] (let [b (at (.src c) (.pos c))]
(cond (cond
@ -313,7 +313,7 @@
(set (.depth c) (+ (.depth c) 1)) (set (.depth c) (+ (.depth c) 1))
true) true)
(defn pop-close [c (Ptr Cursor) closer i32 p i32] bool (defn- pop-close [c (Ptr Cursor) closer i32 p i32] bool
(when (or (= (.depth c) 0) (when (or (= (.depth c) 0)
(!= (at (.open c) (- (.depth c) 1)) closer)) (!= (at (.open c) (- (.depth c) 1)) closer))
(fail c err-unbalanced p) (fail c err-unbalanced p)
@ -325,7 +325,7 @@
;; A token starting with a digit, or with a sign or a dot followed by one. ;; A token starting with a digit, or with a sign or a dot followed by one.
;; `-` alone is a symbol in EDN and stays one here. ;; `-` alone is a symbol in EDN and stays one here.
(defn number-start? [c (Ptr Cursor) i i32] bool (defn- number-start? [c (Ptr Cursor) i i32] bool
(let [s (.src c)] (let [s (.src c)]
(when (>= i (length s)) (when (>= i (length s))
(return false)) (return false))
@ -335,7 +335,7 @@
(< (+ i 1) (length s)) (< (+ i 1) (length s))
(digit? (at s (+ i 1)))))) (digit? (at s (+ i 1))))))
(defn read-number [c (Ptr Cursor) lo i32] Token (defn- read-number [c (Ptr Cursor) lo i32] Token
(let [hi (scan-atom c lo)] (let [hi (scan-atom c lo)]
(set (.pos c) hi) (set (.pos c) hi)
(let [text (slice (.src c) lo hi)] (let [text (slice (.src c) lo hi)]
@ -362,7 +362,7 @@
;; A backslash anywhere inside is the refusal, reported at the backslash ;; A backslash anywhere inside is the refusal, reported at the backslash
;; rather than at the start of the string, because the backslash is what has ;; rather than at the start of the string, because the backslash is what has
;; to be removed. ;; to be removed.
(defn read-string [c (Ptr Cursor) lo i32] Token (defn- read-string [c (Ptr Cursor) lo i32] Token
(let [i (+ lo 1) (let [i (+ lo 1)
s (.src c)] s (.src c)]
(while (< i (length s)) (while (< i (length s))
@ -536,7 +536,7 @@
(Some (bytes=? (.text t) (bytes-view "true"))) (Some (bytes=? (.text t) (bytes-view "true")))
None)) None))
(defn text=? [t Token s string] bool (defn- text=? [t Token s string] bool
(bytes=? (.text t) (bytes-view s))) (bytes=? (.text t) (bytes-view s)))
;; A keyword whose name is s. The leading colon is not part of `text`, so this ;; A keyword whose name is s. The leading colon is not part of `text`, so this

View File

@ -57,13 +57,13 @@
;; ── Small string work, for the refusals and the names ─────────────── ;; ── Small string work, for the refusals and the names ───────────────
(defn joined [a string b string] string (defn- joined [a string b string] string
(let [v (vec-new u8)] (let [v (vec-new u8)]
(append (addr v) (bytes-view a)) (append (addr v) (bytes-view a))
(append (addr v) (bytes-view b)) (append (addr v) (bytes-view b))
(string (slice v)))) (string (slice v))))
(defn joined3 [a string b string c string] string (defn- joined3 [a string b string c string] string
(joined a (joined b c))) (joined a (joined b c)))
;; Copied out, and not `(string (i64->bytes n))`. The prelude's note over ;; Copied out, and not `(string (i64->bytes n))`. The prelude's note over
@ -71,7 +71,7 @@
;; in the runtime, so two of its results cannot be held at once — and `where` ;; in the runtime, so two of its results cannot be held at once — and `where`
;; below holds a line and a column at the same time, which read as the same ;; below holds a line and a column at the same time, which read as the same
;; number until this copied. ;; number until this copied.
(defn i64->string [n i64] string (defn- i64->string [n i64] string
(let [v (vec-new u8)] (let [v (vec-new u8)]
(append-i64 (addr v) n) (append-i64 (addr v) n)
(string (slice v)))) (string (slice v))))
@ -80,7 +80,7 @@
;; buffer costs nothing to produce. A person reading a refusal wants a line and ;; buffer costs nothing to produce. A person reading a refusal wants a line and
;; a column, so the newlines before the offset are counted here — once per ;; a column, so the newlines before the offset are counted here — once per
;; refusal, which is as often as this is ever called. ;; refusal, which is as often as this is ever called.
(defn where [src [u8] pos i32] string (defn- where [src [u8] pos i32] string
(let [line (i64 1) (let [line (i64 1)
col (i64 1) col (i64 1)
i (i32 0)] i (i32 0)]
@ -110,13 +110,13 @@
reader Form reader Form
bad string]) bad string])
(defn derived-bad [msg string] Derived (defn- derived-bad [msg string] Derived
(Derived {.ty `i64 .decls (form-nil) .reader `0 .bad msg})) (Derived {.ty `i64 .decls (form-nil) .reader `0 .bad msg}))
(defn ok-derived [ty Form decls [Form] reader Form] Derived (defn- ok-derived [ty Form decls [Form] reader Form] Derived
(Derived {.ty ty .decls decls .reader reader .bad ""})) (Derived {.ty ty .decls decls .reader reader .bad ""}))
(defn bad? [d Derived] bool (defn- bad? [d Derived] bool
(> (length (bytes-view (.bad d))) 0)) (> (length (bytes-view (.bad d))) 0))
;; ── The scalars a generated reader calls ──────────────────────────── ;; ── The scalars a generated reader calls ────────────────────────────
@ -212,7 +212,7 @@
;; One value, from the cursor's current position, consumed. `name` is what a ;; One value, from the cursor's current position, consumed. `name` is what a
;; struct here would be called; `src` is the whole buffer, for the positions a ;; struct here would be called; `src` is the whole buffer, for the positions a
;; refusal names. ;; refusal names.
(defn derive [c (Ptr Cursor) name string src [u8]] Derived (defn- derive [c (Ptr Cursor) name string src [u8]] Derived
(let [t (next c)] (let [t (next c)]
(when (not (ok? c)) (when (not (ok? c))
(return (derived-bad (return (derived-bad
@ -242,7 +242,7 @@
;; decides; every one after it is compared against that decision and both ;; decides; every one after it is compared against that decision and both
;; positions are named when they disagree, because "heterogeneous" without ;; positions are named when they disagree, because "heterogeneous" without
;; saying where sends someone to read the whole file. ;; saying where sends someone to read the whole file.
(defn derive-vec [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived (defn- derive-vec [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived
(when (at-byte? c \]) (when (at-byte? c \])
(return (derived-bad (return (derived-bad
(joined3 "the empty vector at " (where src at-pos) (joined3 "the empty vector at " (where src at-pos)
@ -286,12 +286,12 @@
;; a signature, and the bare call in the body gets it from `want`. It is also ;; a signature, and the bare call in the body gets it from `want`. It is also
;; the more readable expansion: the reader says `(cells-new a)` where it would ;; the more readable expansion: the reader says `(cells-new a)` where it would
;; otherwise carry a type nobody wrote. ;; otherwise carry a type nobody wrote.
(defn with-decl [decls [Form] d Form] [Form] (defn- with-decl [decls [Form] d Form] [Form]
(form-append decls (form-cons d (form-nil)))) (form-append decls (form-cons d (form-nil))))
;; A set becomes `(Map T bool)`, so its elements are map keys. `derive-key` is ;; A set becomes `(Map T bool)`, so its elements are map keys. `derive-key` is
;; where that constraint is enforced and said. ;; where that constraint is enforced and said.
(defn derive-set [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived (defn- derive-set [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived
(when (at-byte? c \}) (when (at-byte? c \})
(return (derived-bad (return (derived-bad
(joined3 "the empty set at " (where src at-pos) (joined3 "the empty set at " (where src at-pos)
@ -326,7 +326,7 @@
;; fixed array, which is one where a Vec is not; anything else is refused here ;; fixed array, which is one where a Vec is not; anything else is refused here
;; rather than at the `(Map ...)` the caller would build out of it, because a ;; rather than at the `(Map ...)` the caller would build out of it, because a
;; map-key refusal names a type nobody wrote. ;; map-key refusal names a type nobody wrote.
(defn derive-key [c (Ptr Cursor) name string src [u8]] Derived (defn- derive-key [c (Ptr Cursor) name string src [u8]] Derived
(when (at-byte? c \[) (when (at-byte? c \[)
(return (derive-array c name src))) (return (derive-array c name src)))
(let [d (derive c name src)] (let [d (derive c name src)]
@ -342,7 +342,7 @@
;; of the set has to be the same length as well as the same shape — which falls ;; of the set has to be the same length as well as the same shape — which falls
;; out of the type comparison the caller already makes, since the length is in ;; out of the type comparison the caller already makes, since the length is in
;; the type it compares. ;; the type it compares.
(defn derive-array [c (Ptr Cursor) name string src [u8]] Derived (defn- derive-array [c (Ptr Cursor) name string src [u8]] Derived
(let [open (next c)] (let [open (next c)]
(when (at-byte? c \]) (when (at-byte? c \])
(return (derived-bad (return (derived-bad
@ -379,7 +379,7 @@
(expect c tok-vec-close) (expect c tok-vec-close)
arr))))))) arr)))))))
(defn disagreement [what string src [u8] at-pos i32 n i64 (defn- disagreement [what string src [u8] at-pos i32 n i64
first Form second Form] string first Form second Form] string
(joined3 (joined3 "the " what " at ") (joined3 (joined3 "the " what " at ")
(where src at-pos) (where src at-pos)
@ -400,7 +400,7 @@
;; differently is the two arms a hand-written reader had no reason to have — a ;; differently is the two arms a hand-written reader had no reason to have — a
;; key that is not a field of the struct, and a field the file did not have. ;; key that is not a field of the struct, and a field the file did not have.
;; Both signal SchemaDrift. See the note over that type. ;; Both signal SchemaDrift. See the note over that type.
(defn derive-map [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived (defn- derive-map [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived
(when (at-byte? c \}) (when (at-byte? c \})
(return (derived-bad (return (derived-bad
(joined3 "the empty map at " (where src at-pos) (joined3 "the empty map at " (where src at-pos)
@ -485,7 +485,7 @@
;; ── Comparing and rendering a type form ───────────────────────────── ;; ── Comparing and rendering a type form ─────────────────────────────
(defn same-type? [a Form b Form] bool (defn- same-type? [a Form b Form] bool
(bytes=? (bytes-view (render a)) (bytes-view (render b)))) (bytes=? (bytes-view (render a)) (bytes-view (render b))))
;; A type form as text, for the refusals. Only the shapes this file builds — a ;; A type form as text, for the refusals. Only the shapes this file builds — a
@ -499,7 +499,7 @@
(Form.Vec xs) (joined3 "[" (render-items xs) "]") (Form.Vec xs) (joined3 "[" (render-items xs) "]")
_ "?")) _ "?"))
(defn render-items [xs [Form]] string (defn- render-items [xs [Form]] string
(let [out ""] (let [out ""]
(dotimes [i (length xs)] (dotimes [i (length xs)]
(set out (if (= i 0) (set out (if (= i 0)
@ -510,7 +510,7 @@
;; What check.ml takes as a map key, narrowed to what this file can produce. ;; What check.ml takes as a map key, narrowed to what this file can produce.
;; A float is deliberately absent and the checker says why: NaN is not equal to ;; A float is deliberately absent and the checker says why: NaN is not equal to
;; itself, so there is no equality for a map to hash. ;; itself, so there is no equality for a map to hash.
(defn key-type? [t Form] bool (defn- key-type? [t Form] bool
(let [s (bytes-view (render t))] (let [s (bytes-view (render t))]
(or (bytes=? s (bytes-view "i64")) (or (bytes=? s (bytes-view "i64"))
(or (bytes=? s (bytes-view "bool")) (or (bytes=? s (bytes-view "bool"))
@ -543,7 +543,7 @@
_ (refuse "defedn's first argument is the name of the struct to declare, written as a name")) _ (refuse "defedn's first argument is the name of the struct to declare, written as a name"))
_ (refuse "defedn's second argument is the path to the data file, written as a string literal — the file is read while this is being compiled, so there is nothing here to compute a path from")))) _ (refuse "defedn's second argument is the path to the data file, written as a string literal — the file is read while this is being compiled, so there is nothing here to compute a path from"))))
(defn provide [name string path string src [u8]] Form (defn- provide [name string path string src [u8]] Form
(let [cur (cursor src) (let [cur (cursor src)
d (derive (addr cur) name src)] d (derive (addr cur) name src)]
(if (bad? d) (if (bad? d)
@ -580,5 +580,5 @@
;; already put the report on the `defedn` the author wrote. The name is a ;; already put the report on the `defedn` the author wrote. The name is a
;; gensym, so two refusals in one file are two reports rather than a name ;; gensym, so two refusals in one file are two reports rather than a name
;; defined twice. ;; defined twice.
(defn refuse [msg string] Form (defn- refuse [msg string] Form
`(defn ~(gensym) [] () (compile-error ~(Form.Str {.s msg})))) `(defn ~(gensym) [] () (compile-error ~(Form.Str {.s msg}))))

32
vendor/json/json.flan vendored
View File

@ -281,21 +281,21 @@
;; start against the byte before it — `1//x` — and a scanner that stopped only ;; start against the byte before it — `1//x` — and a scanner that stopped only
;; on whitespace and brackets would read `1//x` as one atom and then report a ;; on whitespace and brackets would read `1//x` as one atom and then report a
;; bad number instead of a comment. ;; bad number instead of a comment.
(defn delim? [b u8] bool (defn- delim? [b u8] bool
(or (space? b) (or (space? b)
(= b \() (= b \)) (= b \[) (= b \]) (= b \{) (= b \}) (= b \() (= b \)) (= b \[) (= b \]) (= b \{) (= b \})
(= b \") (= b \') (= b \,) (= b \:) (= b \/))) (= b \") (= b \') (= b \,) (= b \:) (= b \/)))
(defn alpha? [b u8] bool (defn- alpha? [b u8] bool
(or (and (>= b \a) (<= b \z)) (or (and (>= b \a) (<= b \z))
(and (>= b \A) (<= b \Z)))) (and (>= b \A) (<= b \Z))))
(defn hex? [b u8] bool (defn- hex? [b u8] bool
(or (digit? b) (or (digit? b)
(and (>= b \a) (<= b \f)) (and (>= b \a) (<= b \f))
(and (>= b \A) (<= b \F)))) (and (>= b \A) (<= b \F))))
(defn hex-val [b u8] i32 (defn- hex-val [b u8] i32
(cond (cond
(digit? b) (- (i32 b) (i32 \0)) (digit? b) (- (i32 b) (i32 \0))
(and (>= b \a) (<= b \f)) (+ 10 (- (i32 b) (i32 \a))) (and (>= b \a) (<= b \f)) (+ 10 (- (i32 b) (i32 \a)))
@ -307,24 +307,24 @@
;; yet, so json/scan-atom and json/push-open are as callable as json/next is. ;; yet, so json/scan-atom and json/push-open are as callable as json/next is.
;; Nothing below is part of the API and none of it will keep its shape. ;; Nothing below is part of the API and none of it will keep its shape.
(defn at-end? [c (Ptr Cursor)] bool (defn- at-end? [c (Ptr Cursor)] bool
(>= (.pos c) (length (.src c)))) (>= (.pos c) (length (.src c))))
;; An empty slice of src, positioned at p. Used for the tokens that have no ;; An empty slice of src, positioned at p. Used for the tokens that have no
;; text of their own — eof, error, and every delimiter — so that `text` has one ;; text of their own — eof, error, and every delimiter — so that `text` has one
;; meaning for every token kind and not two. ;; meaning for every token kind and not two.
(defn empty-at [c (Ptr Cursor) p i32] [u8] (defn- empty-at [c (Ptr Cursor) p i32] [u8]
(slice (.src c) p p)) (slice (.src c) p p))
(defn token [c (Ptr Cursor) kind i32 lo i32 hi i32 p i32] Token (defn- token [c (Ptr Cursor) kind i32 lo i32 hi i32 p i32] Token
(Token {.kind kind .text (slice (.src c) lo hi) .pos p})) (Token {.kind kind .text (slice (.src c) lo hi) .pos p}))
(defn error-token [c (Ptr Cursor)] Token (defn- error-token [c (Ptr Cursor)] Token
(Token {.kind tok-error .text (empty-at c (.err-pos c)) .pos (.err-pos c)})) (Token {.kind tok-error .text (empty-at c (.err-pos c)) .pos (.err-pos c)}))
;; Whitespace only. There is no comment case here and there is deliberately no ;; Whitespace only. There is no comment case here and there is deliberately no
;; comment case anywhere: a `/` reaches the dispatch and is refused by name. ;; comment case anywhere: a `/` reaches the dispatch and is refused by name.
(defn skip-trivia [c (Ptr Cursor)] () (defn- skip-trivia [c (Ptr Cursor)] ()
(while (and (not (at-end? c)) (space? (at (.src c) (.pos c)))) (while (and (not (at-end? c)) (space? (at (.src c) (.pos c))))
(set (.pos c) (+ (.pos c) 1)))) (set (.pos c) (+ (.pos c) 1))))
@ -344,7 +344,7 @@
(set (.depth c) (+ (.depth c) 1)) (set (.depth c) (+ (.depth c) 1))
true) true)
(defn pop-close [c (Ptr Cursor) closer i32 p i32] bool (defn- pop-close [c (Ptr Cursor) closer i32 p i32] bool
(when (or (= (.depth c) 0) (when (or (= (.depth c) 0)
(!= (at (.open c) (- (.depth c) 1)) closer)) (!= (at (.open c) (- (.depth c) 1)) closer))
(fail c err-unbalanced p) (fail c err-unbalanced p)
@ -358,7 +358,7 @@
;; are here so that they reach the scanner and get the refusal that names them, ;; are here so that they reach the scanner and get the refusal that names them,
;; instead of falling through to "unexpected byte" — which would be true and ;; instead of falling through to "unexpected byte" — which would be true and
;; would not tell anyone that the fix is to write 0.5. ;; would not tell anyone that the fix is to write 0.5.
(defn number-start? [b u8] bool (defn- number-start? [b u8] bool
(or (digit? b) (= b \-) (= b \+) (= b \.))) (or (digit? b) (= b \-) (= b \+) (= b \.)))
;; JSON's number grammar, written out rather than left to parse-i64: -?(0 | ;; JSON's number grammar, written out rather than left to parse-i64: -?(0 |
@ -371,7 +371,7 @@
;; A number with neither a fraction nor an exponent is tok-int and anything ;; A number with neither a fraction nor an exponent is tok-int and anything
;; else is tok-float, so 1e3 is a float even though its value is whole. That is ;; else is tok-float, so 1e3 is a float even though its value is whole. That is
;; the grammar's own split and not a guess about the caller's field. ;; the grammar's own split and not a guess about the caller's field.
(defn read-number [c (Ptr Cursor) lo i32] Token (defn- read-number [c (Ptr Cursor) lo i32] Token
(let [s (.src c) (let [s (.src c)
i lo i lo
float? false] float? false]
@ -447,7 +447,7 @@
;; Four hex digits starting at i, as a code point, or -1. Used twice — for an ;; Four hex digits starting at i, as a code point, or -1. Used twice — for an
;; escape and for the low half of a surrogate pair — which is the whole reason ;; escape and for the low half of a surrogate pair — which is the whole reason
;; it is a function. ;; it is a function.
(defn hex4 [c (Ptr Cursor) i i32] i32 (defn- hex4 [c (Ptr Cursor) i i32] i32
(let [s (.src c)] (let [s (.src c)]
(when (> (+ i 4) (length s)) (when (> (+ i 4) (length s))
(return -1)) (return -1))
@ -459,8 +459,8 @@
(set v (+ (* v 16) (hex-val b))))) (set v (+ (* v 16) (hex-val b)))))
v))) v)))
(defn high-surrogate? [r i32] bool (and (>= r 0xd800) (<= r 0xdbff))) (defn- high-surrogate? [r i32] bool (and (>= r 0xd800) (<= r 0xdbff)))
(defn low-surrogate? [r i32] bool (and (>= r 0xdc00) (<= r 0xdfff))) (defn- low-surrogate? [r i32] bool (and (>= r 0xdc00) (<= r 0xdfff)))
;; The whole reason this is not three lines. `text` is the interior, between ;; The whole reason this is not three lines. `text` is the interior, between
;; the quotes and RAW; `pos` is the opening quote, so an editor underlines the ;; the quotes and RAW; `pos` is the opening quote, so an editor underlines the
@ -472,7 +472,7 @@
;; token rather than at the backslash. Surrogate PAIRING is checked here too, ;; token rather than at the backslash. Surrogate PAIRING is checked here too,
;; and not only escape syntax, so that string-of's encode-rune can never be ;; and not only escape syntax, so that string-of's encode-rune can never be
;; handed a code point the prelude refuses. ;; handed a code point the prelude refuses.
(defn read-string [c (Ptr Cursor) lo i32] Token (defn- read-string [c (Ptr Cursor) lo i32] Token
(let [s (.src c) (let [s (.src c)
i (+ lo 1)] i (+ lo 1)]
(while (< i (length s)) (while (< i (length s))

View File

@ -42,20 +42,20 @@
;; ── Small string work ─────────────────────────────────────────────── ;; ── Small string work ───────────────────────────────────────────────
(defn joined [a string b string] string (defn- joined [a string b string] string
(let [v (vec-new u8)] (let [v (vec-new u8)]
(append (addr v) (bytes-view a)) (append (addr v) (bytes-view a))
(append (addr v) (bytes-view b)) (append (addr v) (bytes-view b))
(string (slice v)))) (string (slice v))))
(defn joined3 [a string b string c string] string (defn- joined3 [a string b string c string] string
(joined a (joined b c))) (joined a (joined b c)))
;; Copied out, and not `(string (i64->bytes n))`: the prelude's note over ;; Copied out, and not `(string (i64->bytes n))`: the prelude's note over
;; append-i64 is the reason — i64->bytes renders into one shared static buffer ;; append-i64 is the reason — i64->bytes renders into one shared static buffer
;; in the runtime, so two of its results cannot be held at once, and `where` ;; in the runtime, so two of its results cannot be held at once, and `where`
;; holds a line and a column at the same time. ;; holds a line and a column at the same time.
(defn i64->string [n i64] string (defn- i64->string [n i64] string
(let [v (vec-new u8)] (let [v (vec-new u8)]
(append-i64 (addr v) n) (append-i64 (addr v) n)
(string (slice v)))) (string (slice v))))
@ -63,7 +63,7 @@
;; The tokenizer answers byte offsets. A person reading a refusal wants a line ;; The tokenizer answers byte offsets. A person reading a refusal wants a line
;; and a column, so the newlines before the offset are counted here — once per ;; and a column, so the newlines before the offset are counted here — once per
;; refusal, which is as often as this is ever called. ;; refusal, which is as often as this is ever called.
(defn where [src [u8] pos i32] string (defn- where [src [u8] pos i32] string
(let [line (i64 1) (let [line (i64 1)
col (i64 1) col (i64 1)
i (i32 0)] i (i32 0)]
@ -87,16 +87,16 @@
reader Form reader Form
bad string]) bad string])
(defn derived-bad [msg string] Derived (defn- derived-bad [msg string] Derived
(Derived {.ty `i64 .decls (form-nil) .reader `0 .bad msg})) (Derived {.ty `i64 .decls (form-nil) .reader `0 .bad msg}))
(defn ok-derived [ty Form decls [Form] reader Form] Derived (defn- ok-derived [ty Form decls [Form] reader Form] Derived
(Derived {.ty ty .decls decls .reader reader .bad ""})) (Derived {.ty ty .decls decls .reader reader .bad ""}))
(defn bad? [d Derived] bool (defn- bad? [d Derived] bool
(> (length (bytes-view (.bad d))) 0)) (> (length (bytes-view (.bad d))) 0))
(defn with-decl [decls [Form] d Form] [Form] (defn- with-decl [decls [Form] d Form] [Form]
(form-append decls (form-cons d (form-nil)))) (form-append decls (form-cons d (form-nil))))
;; ── The scalars a generated reader calls ──────────────────────────── ;; ── The scalars a generated reader calls ────────────────────────────
@ -173,7 +173,7 @@
;; ── Deriving ──────────────────────────────────────────────────────── ;; ── Deriving ────────────────────────────────────────────────────────
(defn derive [c (Ptr Cursor) name string src [u8]] Derived (defn- derive [c (Ptr Cursor) name string src [u8]] Derived
(let [t (next c)] (let [t (next c)]
(when (not (ok? c)) (when (not (ok? c))
(return (derived-bad (return (derived-bad
@ -202,7 +202,7 @@
;; every one after it is compared against that, and both positions are named ;; every one after it is compared against that, and both positions are named
;; when they disagree — "heterogeneous" on its own sends someone to read the ;; when they disagree — "heterogeneous" on its own sends someone to read the
;; whole file. ;; whole file.
(defn derive-array [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived (defn- derive-array [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived
(when (at-byte? c \]) (when (at-byte? c \])
(return (derived-bad (return (derived-bad
(joined3 "the empty array at " (where src at-pos) (joined3 "the empty array at " (where src at-pos)
@ -248,7 +248,7 @@
;; ── An object, which is a struct ──────────────────────────────────── ;; ── An object, which is a struct ────────────────────────────────────
(defn derive-object [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived (defn- derive-object [c (Ptr Cursor) name string at-pos i32 src [u8]] Derived
(when (at-byte? c \}) (when (at-byte? c \})
(return (derived-bad (return (derived-bad
(joined3 "the empty object at " (where src at-pos) (joined3 "the empty object at " (where src at-pos)
@ -349,7 +349,7 @@
(defn key=? [t Token s string] bool (defn key=? [t Token s string] bool
(bytes=? (.text t) (bytes-view s))) (bytes=? (.text t) (bytes-view s)))
(defn has-escape? [s [u8]] bool (defn- has-escape? [s [u8]] bool
(dotimes [i (length s)] (dotimes [i (length s)]
(when (= (at s i) \\) (when (= (at s i) \\)
(return true))) (return true)))
@ -358,7 +358,7 @@
;; What a field name may be made of. Deliberately narrower than what the reader ;; What a field name may be made of. Deliberately narrower than what the reader
;; would accept: this is the set a *person* would recognise as a name, and a ;; would accept: this is the set a *person* would recognise as a name, and a
;; member called "a b" or "x.y" has no field it could become. ;; member called "a b" or "x.y" has no field it could become.
(defn name-like? [s [u8]] bool (defn- name-like? [s [u8]] bool
(when (= (length s) 0) (when (= (length s) 0)
(return false)) (return false))
(dotimes [i (length s)] (dotimes [i (length s)]
@ -372,14 +372,14 @@
;; A copy of a token's raw text as a string. The Vec header is dropped here on ;; A copy of a token's raw text as a string. The Vec header is dropped here on
;; purpose: this runs inside the compiler, where an expansion is bounded by the ;; purpose: this runs inside the compiler, where an expansion is bounded by the
;; size of the program being compiled. ;; size of the program being compiled.
(defn copy-of [s [u8]] string (defn- copy-of [s [u8]] string
(let [b (vec-new u8)] (let [b (vec-new u8)]
(append (addr b) s) (append (addr b) s)
(string (slice b)))) (string (slice b))))
;; ── Comparing and rendering a type form ───────────────────────────── ;; ── Comparing and rendering a type form ─────────────────────────────
(defn same-type? [a Form b Form] bool (defn- same-type? [a Form b Form] bool
(bytes=? (bytes-view (render a)) (bytes-view (render b)))) (bytes=? (bytes-view (render a)) (bytes-view (render b))))
;; A type form as text, for the refusals. Only the shapes this file builds — a ;; A type form as text, for the refusals. Only the shapes this file builds — a
@ -392,7 +392,7 @@
(Form.Vec xs) (joined3 "[" (render-items xs) "]") (Form.Vec xs) (joined3 "[" (render-items xs) "]")
_ "?")) _ "?"))
(defn render-items [xs [Form]] string (defn- render-items [xs [Form]] string
(let [out ""] (let [out ""]
(dotimes [i (length xs)] (dotimes [i (length xs)]
(set out (if (= i 0) (set out (if (= i 0)
@ -424,7 +424,7 @@
_ (refuse "defjson's first argument is the name of the struct to declare, written as a name")) _ (refuse "defjson's first argument is the name of the struct to declare, written as a name"))
_ (refuse "defjson's second argument is the path to the data file, written as a string literal — the file is read while this is being compiled, so there is nothing here to compute a path from")))) _ (refuse "defjson's second argument is the path to the data file, written as a string literal — the file is read while this is being compiled, so there is nothing here to compute a path from"))))
(defn provide [name string path string src [u8]] Form (defn- provide [name string path string src [u8]] Form
(let [cur (cursor src) (let [cur (cursor src)
d (derive (addr cur) name src)] d (derive (addr cur) name src)]
(if (bad? d) (if (bad? d)
@ -460,5 +460,5 @@
;; calls: the checker walks it, the arm fires, and `Loc.from_macro` has already ;; calls: the checker walks it, the arm fires, and `Loc.from_macro` has already
;; put the report on the `defjson` the author wrote. The name is a gensym, so ;; put the report on the `defjson` the author wrote. The name is a gensym, so
;; two refusals in one file are two reports rather than a name defined twice. ;; two refusals in one file are two reports rather than a name defined twice.
(defn refuse [msg string] Form (defn- refuse [msg string] Form
`(defn ~(gensym) [] () (compile-error ~(Form.Str {.s msg})))) `(defn ~(gensym) [] () (compile-error ~(Form.Str {.s msg}))))

View File

@ -1526,8 +1526,14 @@ existed.</p>
refused at the line that wrote it.</li> refused at the line that wrote it.</li>
</ul> </ul>
<p>Visibility is that one rule and no more: there is no package-private marker for <p><strong>A function declared with <code>defn-</code> is private to its package.</strong>
anything other than <code>main</code> yet.</p> It is a <code>defn</code> in every other respect, and every file in the package's
directory can call it. For a package imported by naming a single file, the package is
that file alone, and the other files in its directory cannot call it. A use from
anywhere else — a call, or the name passed as a value — is refused at compile time,
and the message names the function and where it may be used. Code written by one of
the package's own macros counts as the package's, wherever the macro is called. Only
functions have a private form.</p>
<p>A package may carry the C it binds to. Every <code>.c</code> file in the directory is <p>A package may carry the C it binds to. Every <code>.c</code> file in the directory is
compiled into the build, and a file named <code>link</code> lists extra linker compiled into the build, and a file named <code>link</code> lists extra linker
@ -2382,7 +2388,7 @@ refused inside a loop or a branch (a <code>let</code> is fine — it has the fun
extent); <code>find-restart</code> and <code>compute-restarts</code> are blocked on a extent); <code>find-restart</code> and <code>compute-restarts</code> are blocked on a
<code>Restart</code> type rather than on effort; a restart with parameters cannot be <code>Restart</code> type rather than on effort; a restart with parameters cannot be
taken from the break loop, which aims at a frame by position and has nothing to fill them taken from the break loop, which aims at a frame by position and has nothing to fill them
with; there is no package-private marker other than <code>main</code> not being exported; with;
and there are no threads in the language. The class facility plan.org describes is and there are no threads in the language. The class facility plan.org describes is
built — see <a href="#dyn">dyn</a> — and what is not built of it is the named-slot built — see <a href="#dyn">dyn</a> — and what is not built of it is the named-slot
constructor spelling and the user-written migration hook.</p> constructor spelling and the user-written migration hook.</p>
@ -2485,7 +2491,7 @@ disagree with the first.</p>
<script> <script>
// A small hand-written highlighter for the Flan blocks. One pass, no library. // A small hand-written highlighter for the Flan blocks. One pass, no library.
(function () { (function () {
var FORMS = new Set(("defn defstruct defenum defdata defunion defconst defonce def defalias " + var FORMS = new Set(("defn defn- defstruct defenum defdata defunion defconst defonce def defalias " +
"declare declare-c import package let if when unless cond do and or not while " + "declare declare-c import package let if when unless cond do and or not while " +
"until dotimes match set return some try defer signal error handler-bind " + "until dotimes match set return some try defer signal error handler-bind " +
"restart-case invoke-restart fn quote defmacro gensym " + "restart-case invoke-restart fn quote defmacro gensym " +