Add pure modular source3 project compiler and update APIs

This commit is contained in:
2026-09-05 19:14:05 +03:00
parent e41b8de992
commit 06b33848ce
6 changed files with 2219 additions and 94 deletions
+119 -3
View File
@@ -745,7 +745,7 @@ sets and enum constructors are closed by sections 3–5.
.. code-block:: text
document = "arcadia" "2" ";" project
document = "arcadia" "3" ";" project
project = "project" alias string identity? "{" project-entry* "}"
project-entry = "description" string ";" | declaration
declaration = (kind | "diagram" | "artifact" | "trace")
@@ -784,6 +784,87 @@ Ordinary string fields require quoted strings and cannot contain an unquoted
constructor records. Typed values MUST NOT nest more than 64 levels; malformed
deeply nested drafts produce a diagnostic instead of exhausting the parser stack.
6.1. Modular source3 projects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The pure ``source_project`` core API compiles a virtual source bundle, containing
``entry`` and a ``files`` map from logical paths to UTF-8 strings. Its entry is a
root-level ``.arcadia`` file with the source3 project header above. A project entry
MAY also be ``include string ";"``. An included fragment consists only of ordinary
declarations and include statements; it MUST NOT contain another version header,
project wrapper, or project description. Includes MUST occur between declarations,
never inside a typed field value. For example::
arcadia 3;
project rescue "Rescue" id "00000000-0000-4000-8000-000000000001" {
description "Operational model";
include "oa/participants.arcadia";
include "oa/activities/activity_respond.arcadia";
include "views/diagram_operational.arcadia";
}
An include path is relative to its containing file. ``.`` and ``..`` segments
normalize within the virtual project root; escaping that root is an error. Bundle
keys MUST already be normalized, use forward slashes and the lowercase
``.arcadia`` suffix, and contain no empty path segments. Absolute, drive, UNC,
URL, backslash, control-character and non-portable Windows device paths are
rejected. Paths ending a segment in dot or space are rejected. Keys that collide
after Unicode lowercasing are rejected. References use the exact normalized
spelling; filesystems do not participate in resolution.
Every reachable file is compiled once, including through repeated or diamond
includes. Include cycles are errors with their chain and including location.
Missing files, duplicate JSON file keys and supplied unreachable files are
errors. The compiler MUST NOT read local files, network resources, environment
variables or the process working directory. A future native loader is responsible
for translating a directory into this explicit bundle and enforcing physical
path ownership; the pure compiler grants no filesystem access.
``inspect_includes(path, source, is_entry, limits)`` supports native discovery
before all included documents are available. It uses the same strict entry or
fragment syntax parser, applies document byte/record and local include-edge
budgets, and returns normalized targets with exact directive spans. Comments and
strings that mention include syntax are not directives. A syntax failure anywhere
in the document returns located errors and no partial include list; it does not
scan arbitrary token strings after failure. Global references and engineering
validation are deferred to bundle compilation. A native loader MUST retain the
unparsed draft on failure and enforce aggregate bytes/files/depth/edges while
loading; this inspection call does not own or inspect a directory.
All files share the existing global alias/UUID namespace. Files add no semantic
namespace, implicit parent, allocation or port contract. Forward references work
across files. Duplicate aliases and identities report the offending declaration
and the previous declaration location, including collisions with the project.
Moving a declaration between files MUST preserve the resulting model and UUIDs.
``ProjectLimits`` adds file, include-depth and include-edge budgets to the existing
``language::ParseLimits``. Model bytes, graph objects, records and ordered
references are counted across the complete bundle, never reset per file. The
default candidate policy is 256 files, depth 32 (entry depth zero), 1,024 include
edges and 64 MiB combined UTF-8 source. Other model counts remain caller policy.
Repeated include edges count toward the edge budget; file contents count once.
The compiler admits input and stabilized output against the same limits.
``compile_bundle`` returns either located errors and no candidate, or a
``CompiledSourceProject`` with the validated ``DecodeResult``, stabilized bundle,
derived ``SourceIndex`` and located engineering warnings. It inserts explicit
UUID clauses into headers that omitted them without otherwise reformatting the
input. The returned source index and diagnostics address the returned stabilized
bytes. Clients MUST retain that bundle after successful Apply; recompiling the
original buffers with missing identities would generate fresh identities.
Bundle diagnostics wrap the existing Diagnostic with a logical path and optional
related locations. Declaration spans, top-level field value spans, project
header/body/description spans and resolved include locations are available in
the source index. Columns remain one-based Unicode scalars within the real file;
spans never refer to concatenated or synthetic source.
Single-string parse/lint APIs remain filesystem-free and reject include
statements with an explicit source-bundle requirement. V1/V2 single-file imports
continue through their existing migration readers and reports. Modular projects
require source3; migration is performed before modular export, not independently
on included fragments. Modular source does not change the semantic JSON schema.
7. Canonical output and source tooling
--------------------------------------
@@ -806,11 +887,46 @@ relations; diagrams and their nodes follow, then artifacts, then traces.
Fields use deterministic name order. Output ends with LF. Alias values come
from the model, never from regenerating display names.
Comments and input whitespace are lexical trivia. Canonicalizing or applying
source does not preserve them; the editor MUST explain that behavior.
Comments and input whitespace are lexical trivia to the canonical writer.
Canonical export and the current desktop single-document Apply path do not
preserve them; the editor MUST explain that behavior. This existing path does not
define an acceptable lossless authoring policy for a future project editor.
The pure project compiler in section 6.1 preserves them while inserting missing
identities. Project update/export distinctions are described below.
Camera position, selection, source cursor, and undo history are not stored
model fields. Layout and collapse state inside NodePlacement are model data.
The pure ``format_bundle`` exporter uses the same typed declaration writer as
``format``. It creates a small entry file, layer/category modules, separate
activity/function roots and behavior contexts, and separate diagrams with their
placements. Ports follow their owner; relations follow their source/context.
Module names derive from persistent aliases, not display names or UUID chunks.
Declarations are ordered by semantic ancestry and alias, with owners before
descendants. Case-colliding generated file names receive deterministic numeric
suffixes. When more than 240 distinct modules would be produced, aggregate
folders consolidate into category modules to avoid thousands of tiny files.
For every valid P, ``compile_bundle(format_bundle(P)).decoded.project == P``
when the caller supplies sufficient admission budgets.
``update_bundle(compiled, P, limits)`` is a core prototype update facility. It
checks that the compiled document/index/model snapshot still agrees, preserves
existing declaration file ownership and all untouched document bytes, and
recompiles the result before returning it. New children inherit their owner's
file; new roots use canonical module rules and add an explicit entry include.
Removed declarations do not delete source files. Alias changes also update
declarations containing references to that alias. Invalid or stale updates
return errors without mutating the input workspace.
The current update facility canonicalizes each changed declaration in full.
It therefore DOES NOT preserve inline comments or custom layout inside that
declaration, even for a single scalar edit. This is an explicit prototype
limitation, not final authoring/save acceptance. A lossless project editor needs
minimal header/field/value edits that preserve unrelated tokens and comments;
the available source spans provide the starting point. Canonical modular export
is a distinct, explicit operation and does not promise original comment/layout
preservation. No native multi-file editor, project save transaction or REST
bundle endpoint is claimed by the pure core APIs.
``parse`` returns a complete structurally validated candidate or error
diagnostics and no model. Engineering warnings alone MUST NOT reject a valid
candidate. Parsing may stop at the first syntax/conversion error. Diagnostics