feat(source): expose borrowed read-only mixed source eight validation
This commit is contained in:
@@ -3,10 +3,9 @@ mod raw_tokens;
|
|||||||
#[path = "language/semantic_source.rs"]
|
#[path = "language/semantic_source.rs"]
|
||||||
mod semantic_source;
|
mod semantic_source;
|
||||||
mod source3_bridge;
|
mod source3_bridge;
|
||||||
// Staged typed analysis only. Public mixed source8 admission waits for complete
|
// Mixed source8 is exposed only through the separate borrowed read-only checker.
|
||||||
// declaration lowering, source updates and the root-owned resource gate.
|
|
||||||
#[cfg(test)]
|
|
||||||
mod analysis_source;
|
mod analysis_source;
|
||||||
|
pub use analysis_source::{CheckedMixed, check_mixed_bundle};
|
||||||
#[path = "migration_v2/source.rs"]
|
#[path = "migration_v2/source.rs"]
|
||||||
mod v2_source;
|
mod v2_source;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
@@ -480,7 +479,6 @@ fn parse_document(source: &str, limits: ParseLimits) -> Result<ParsedDocument, V
|
|||||||
cursor: 0,
|
cursor: 0,
|
||||||
value_depth: 0,
|
value_depth: 0,
|
||||||
limits,
|
limits,
|
||||||
#[cfg(test)]
|
|
||||||
mixed: None,
|
mixed: None,
|
||||||
}
|
}
|
||||||
.document()
|
.document()
|
||||||
@@ -916,7 +914,6 @@ pub(crate) fn parse_unit(
|
|||||||
cursor: 0,
|
cursor: 0,
|
||||||
value_depth: 0,
|
value_depth: 0,
|
||||||
limits,
|
limits,
|
||||||
#[cfg(test)]
|
|
||||||
mixed: None,
|
mixed: None,
|
||||||
}
|
}
|
||||||
.unit(entry, true)
|
.unit(entry, true)
|
||||||
@@ -1215,12 +1212,10 @@ struct Parser<'a> {
|
|||||||
cursor: usize,
|
cursor: usize,
|
||||||
value_depth: usize,
|
value_depth: usize,
|
||||||
limits: ParseLimits,
|
limits: ParseLimits,
|
||||||
#[cfg(test)]
|
|
||||||
mixed: Option<analysis_source::mixed::State<'a>>,
|
mixed: Option<analysis_source::mixed::State<'a>>,
|
||||||
}
|
}
|
||||||
impl Parser<'_> {
|
impl Parser<'_> {
|
||||||
fn token(&self, index: usize) -> Option<Lexeme> {
|
fn token(&self, index: usize) -> Option<Lexeme> {
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &self.mixed {
|
if let Some(mixed) = &self.mixed {
|
||||||
return mixed.token(index);
|
return mixed.token(index);
|
||||||
}
|
}
|
||||||
@@ -1236,7 +1231,6 @@ impl Parser<'_> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn syntax_entry(&mut self) -> Result<(), Error> {
|
fn syntax_entry(&mut self) -> Result<(), Error> {
|
||||||
#[cfg(test)]
|
|
||||||
{
|
{
|
||||||
let span = self.current().span;
|
let span = self.current().span;
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
@@ -1266,9 +1260,7 @@ impl Parser<'_> {
|
|||||||
if self.current().class != TokenClass::String {
|
if self.current().class != TokenClass::String {
|
||||||
return Err(self.error("Expected a double-quoted string."));
|
return Err(self.error("Expected a double-quoted string."));
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
|
||||||
let span = self.current().span;
|
let span = self.current().span;
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
let raw = &self.source[span.start..span.end];
|
let raw = &self.source[span.start..span.end];
|
||||||
let value = mixed.quoted_text(raw, span)?;
|
let value = mixed.quoted_text(raw, span)?;
|
||||||
@@ -1290,9 +1282,8 @@ impl Parser<'_> {
|
|||||||
}
|
}
|
||||||
fn optional_id(&mut self) -> Result<Id, Error> {
|
fn optional_id(&mut self) -> Result<Id, Error> {
|
||||||
if self.text() != "id" {
|
if self.text() != "id" {
|
||||||
#[cfg(test)]
|
|
||||||
if self.mixed.is_some() {
|
if self.mixed.is_some() {
|
||||||
return Err(self.error("Private mixed syntax requires explicit IDs."));
|
return Err(self.error("Mixed source8 requires explicit UUIDs."));
|
||||||
}
|
}
|
||||||
return Ok(Id::new_v4());
|
return Ok(Id::new_v4());
|
||||||
}
|
}
|
||||||
@@ -1321,21 +1312,15 @@ impl Parser<'_> {
|
|||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
fn own_text(&mut self, bytes: usize, span: Span) -> Result<(), Error> {
|
fn own_text(&mut self, bytes: usize, span: Span) -> Result<(), Error> {
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
mixed.own_text(bytes, span)?;
|
mixed.own_text(bytes, span)?;
|
||||||
}
|
}
|
||||||
#[cfg(not(test))]
|
|
||||||
let _ = (bytes, span);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn release_text(&mut self, bytes: usize) {
|
fn release_text(&mut self, bytes: usize) {
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
mixed.release_text(bytes);
|
mixed.release_text(bytes);
|
||||||
}
|
}
|
||||||
#[cfg(not(test))]
|
|
||||||
let _ = bytes;
|
|
||||||
}
|
}
|
||||||
fn fields(&mut self) -> Result<BTreeMap<String, Ast>, Error> {
|
fn fields(&mut self) -> Result<BTreeMap<String, Ast>, Error> {
|
||||||
self.take("{")?;
|
self.take("{")?;
|
||||||
@@ -1383,7 +1368,6 @@ impl Parser<'_> {
|
|||||||
.span
|
.span
|
||||||
.end;
|
.end;
|
||||||
self.take(";")?;
|
self.take(";")?;
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
mixed.projection(1, Span { start, end })?;
|
mixed.projection(1, Span { start, end })?;
|
||||||
}
|
}
|
||||||
@@ -1412,7 +1396,6 @@ impl Parser<'_> {
|
|||||||
self.value_depth += 1;
|
self.value_depth += 1;
|
||||||
let result = self.value_inner();
|
let result = self.value_inner();
|
||||||
self.value_depth -= 1;
|
self.value_depth -= 1;
|
||||||
#[cfg(test)]
|
|
||||||
if let Ok(ast) = &result {
|
if let Ok(ast) = &result {
|
||||||
let end = self
|
let end = self
|
||||||
.token(self.cursor - 1)
|
.token(self.cursor - 1)
|
||||||
@@ -1433,7 +1416,6 @@ impl Parser<'_> {
|
|||||||
fn value_inner(&mut self) -> Result<Ast, Error> {
|
fn value_inner(&mut self) -> Result<Ast, Error> {
|
||||||
let token = self.current();
|
let token = self.current();
|
||||||
let offset = token.span.start;
|
let offset = token.span.start;
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &self.mixed
|
if let Some(mixed) = &self.mixed
|
||||||
&& (token.class == TokenClass::Number || matches!(self.text(), "-" | "+"))
|
&& (token.class == TokenClass::Number || matches!(self.text(), "-" | "+"))
|
||||||
{
|
{
|
||||||
@@ -1541,17 +1523,14 @@ impl Parser<'_> {
|
|||||||
fn unit(&mut self, entry: bool, includes_allowed: bool) -> Result<SourceUnit, Error> {
|
fn unit(&mut self, entry: bool, includes_allowed: bool) -> Result<SourceUnit, Error> {
|
||||||
let mut header = if entry {
|
let mut header = if entry {
|
||||||
self.take("arcadia")?;
|
self.take("arcadia")?;
|
||||||
#[cfg(test)]
|
|
||||||
let mixed_mode = self.mixed.is_some();
|
let mixed_mode = self.mixed.is_some();
|
||||||
#[cfg(not(test))]
|
|
||||||
let mixed_mode = false;
|
|
||||||
if if mixed_mode {
|
if if mixed_mode {
|
||||||
self.text() != "8"
|
self.text() != "8"
|
||||||
} else {
|
} else {
|
||||||
!matches!(self.text(), "3" | "4" | "5" | "6" | "7" | "8")
|
!matches!(self.text(), "3" | "4" | "5" | "6" | "7" | "8")
|
||||||
} {
|
} {
|
||||||
return Err(self.error(if mixed_mode {
|
return Err(self.error(if mixed_mode {
|
||||||
"Private mixed syntax requires arcadia 8."
|
"Mixed source requires arcadia 8."
|
||||||
} else {
|
} else {
|
||||||
"Unsupported schema version: expected arcadia 3, 4, 5, 6, 7 or 8."
|
"Unsupported schema version: expected arcadia 3, 4, 5, 6, 7 or 8."
|
||||||
}));
|
}));
|
||||||
@@ -1561,7 +1540,6 @@ impl Parser<'_> {
|
|||||||
self.take(";")?;
|
self.take(";")?;
|
||||||
let start = self.current().span.start;
|
let start = self.current().span.start;
|
||||||
self.take("project")?;
|
self.take("project")?;
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
mixed.record(false, Span { start, end: start })?;
|
mixed.record(false, Span { start, end: start })?;
|
||||||
}
|
}
|
||||||
@@ -1606,7 +1584,6 @@ impl Parser<'_> {
|
|||||||
let mut open: Vec<usize> = Vec::new();
|
let mut open: Vec<usize> = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let offset = self.current().span.start;
|
let offset = self.current().span.start;
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed
|
if let Some(mixed) = &mut self.mixed
|
||||||
&& mixed.typed_header(self.cursor, open.is_empty())
|
&& mixed.typed_header(self.cursor, open.is_empty())
|
||||||
{
|
{
|
||||||
@@ -1658,7 +1635,6 @@ impl Parser<'_> {
|
|||||||
if !includes_allowed {
|
if !includes_allowed {
|
||||||
return Err(self.error("An include requires a source bundle; single-string parsing never reads files."));
|
return Err(self.error("An include requires a source bundle; single-string parsing never reads files."));
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
mixed.include(Span {
|
mixed.include(Span {
|
||||||
start: offset,
|
start: offset,
|
||||||
@@ -1705,7 +1681,6 @@ impl Parser<'_> {
|
|||||||
header = Some(entry_header);
|
header = Some(entry_header);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
|
||||||
if self.mixed.is_some() && analysis_source::mixed::long_analysis_word(self.text()) {
|
if self.mixed.is_some() && analysis_source::mixed::long_analysis_word(self.text()) {
|
||||||
return Err(self.error("Use the short source8 analysis declaration word."));
|
return Err(self.error("Use the short source8 analysis declaration word."));
|
||||||
}
|
}
|
||||||
@@ -1713,7 +1688,6 @@ impl Parser<'_> {
|
|||||||
let word = self.current().text(self.source);
|
let word = self.current().text(self.source);
|
||||||
let kind = declaration_kind(word)
|
let kind = declaration_kind(word)
|
||||||
.ok_or_else(|| self.error(format!("Unknown declaration '{word}'.")))?;
|
.ok_or_else(|| self.error(format!("Unknown declaration '{word}'.")))?;
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
mixed.record(
|
mixed.record(
|
||||||
matches!(kind, DeclKind::Element(..) | DeclKind::Relation(..)),
|
matches!(kind, DeclKind::Element(..) | DeclKind::Relation(..)),
|
||||||
@@ -1750,7 +1724,6 @@ impl Parser<'_> {
|
|||||||
let body_start = self.current().span.end;
|
let body_start = self.current().span.end;
|
||||||
self.take("{")?;
|
self.take("{")?;
|
||||||
let lexical_owner = open.last().map(|&owner| declarations[owner].id);
|
let lexical_owner = open.last().map(|&owner| declarations[owner].id);
|
||||||
#[cfg(test)]
|
|
||||||
if let Some(mixed) = &mut self.mixed {
|
if let Some(mixed) = &mut self.mixed {
|
||||||
let span = Span {
|
let span = Span {
|
||||||
start: offset,
|
start: offset,
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
//! Unresolved typed source8 syntax, staged for the complete source adapter.
|
//! Private typed syntax and complete read-only mixed source8 resolution.
|
||||||
//! This module does not admit a Project or enable public source8.
|
|
||||||
mod budget;
|
mod budget;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod declarations;
|
mod declarations;
|
||||||
|
#[cfg(test)]
|
||||||
mod index;
|
mod index;
|
||||||
mod lex;
|
mod lex;
|
||||||
mod lower;
|
mod lower;
|
||||||
pub(super) mod mixed;
|
pub(super) mod mixed;
|
||||||
mod syntax;
|
mod syntax;
|
||||||
|
#[cfg(test)]
|
||||||
mod update;
|
mod update;
|
||||||
|
#[cfg(test)]
|
||||||
mod write;
|
mod write;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -18,6 +20,7 @@ use super::Span;
|
|||||||
use crate::analysis::{BinaryOp, UnaryOp};
|
use crate::analysis::{BinaryOp, UnaryOp};
|
||||||
use crate::analysis_numeric::Exact;
|
use crate::analysis_numeric::Exact;
|
||||||
use budget::Budget;
|
use budget::Budget;
|
||||||
|
pub use mixed::project::{CheckedMixed, check_mixed_bundle};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
@@ -104,6 +107,7 @@ pub enum Binding {
|
|||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn parse(source: &str, budget: &mut Budget) -> Result<Binding, Error> {
|
pub fn parse(source: &str, budget: &mut Budget) -> Result<Binding, Error> {
|
||||||
let tokens = lex::tokens(source, budget)?;
|
let tokens = lex::tokens(source, budget)?;
|
||||||
let mut cursor = cursor::Cursor::new(source, &tokens, budget);
|
let mut cursor = cursor::Cursor::new(source, &tokens, budget);
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ impl Budget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
pub fn numeric_units(&self) -> u64 {
|
pub fn numeric_units(&self) -> u64 {
|
||||||
self.numeric.usage().numeric_units
|
self.numeric.usage().numeric_units
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ impl Cursor<'_, '_> {
|
|||||||
self.cursor += 1;
|
self.cursor += 1;
|
||||||
Ok(span)
|
Ok(span)
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
pub(super) fn end(&self) -> Result<(), Error> {
|
pub(super) fn end(&self) -> Result<(), Error> {
|
||||||
if self.cursor != self.tokens.len() {
|
if self.cursor != self.tokens.len() {
|
||||||
Err(error(
|
Err(error(
|
||||||
|
|||||||
@@ -2,12 +2,15 @@
|
|||||||
//! single complete-Project semantic admission after merging these additions.
|
//! single complete-Project semantic admission after merging these additions.
|
||||||
use super::declarations::{self as d, Declaration, DeclarationKind};
|
use super::declarations::{self as d, Declaration, DeclarationKind};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::Project;
|
||||||
use crate::analysis as a;
|
use crate::analysis as a;
|
||||||
use crate::analysis_numeric::Dimension;
|
use crate::analysis_numeric::Dimension;
|
||||||
use crate::{Element, ElementBody, Id, Kind, Layer, Project, SharedBody};
|
use crate::{Element, ElementBody, Id, Kind, Layer, SharedBody};
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
/// Caller-assigned file key; spans are original offsets within that file.
|
/// Caller-assigned file key; spans are original offsets within that file.
|
||||||
|
#[cfg(test)]
|
||||||
pub(super) struct Unit {
|
pub(super) struct Unit {
|
||||||
pub file: usize,
|
pub file: usize,
|
||||||
pub declarations: Vec<Declaration>,
|
pub declarations: Vec<Declaration>,
|
||||||
@@ -180,6 +183,7 @@ impl Tables {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
fn ordinary(&mut self, project: &Project, budget: &Budget) -> Result<(), Error> {
|
fn ordinary(&mut self, project: &Project, budget: &Budget) -> Result<(), Error> {
|
||||||
let at = Span::default();
|
let at = Span::default();
|
||||||
self.insert(project.id, &project.alias, Category::Project, at, budget)?;
|
self.insert(project.id, &project.alias, Category::Project, at, budget)?;
|
||||||
@@ -270,6 +274,7 @@ impl Tables {
|
|||||||
/// Inputs must come from the same live Budget and stabilization pass. No scan,
|
/// Inputs must come from the same live Budget and stabilization pass. No scan,
|
||||||
/// exact conversion or reference charge occurs here. The ordinary skeleton is
|
/// exact conversion or reference charge occurs here. The ordinary skeleton is
|
||||||
/// not semantically admitted: its typed forward references may need additions.
|
/// not semantically admitted: its typed forward references may need additions.
|
||||||
|
#[cfg(test)]
|
||||||
pub(super) fn lower(
|
pub(super) fn lower(
|
||||||
ordinary: &Project,
|
ordinary: &Project,
|
||||||
units: &[Unit],
|
units: &[Unit],
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//! value-end entries, projected value/field/header/declaration positions, lexical
|
//! value-end entries, projected value/field/header/declaration positions, lexical
|
||||||
//! owner slots and the file table. Checks do not recharge syntax or latch errors;
|
//! owner slots and the file table. Checks do not recharge syntax or latch errors;
|
||||||
//! every failure must return immediately. Tokens themselves use the token bound.
|
//! every failure must return immediately. Tokens themselves use the token bound.
|
||||||
//! This deliberately bounded staging policy is not calibrated public source8.
|
//! The public read checker selects both producers; editing remains separately staged.
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
budget::{Budget, Limits},
|
budget::{Budget, Limits},
|
||||||
@@ -53,11 +53,15 @@ pub(super) struct ParsedMixedSyntax<'s> {
|
|||||||
struct FileSyntax<'s> {
|
struct FileSyntax<'s> {
|
||||||
path: &'s str,
|
path: &'s str,
|
||||||
source: &'s str,
|
source: &'s str,
|
||||||
|
// Retained original indexing metadata; the read result preserves its accounted ownership.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
tokens: Vec<lex::Token>,
|
tokens: Vec<lex::Token>,
|
||||||
ordinary: SourceUnit,
|
ordinary: SourceUnit,
|
||||||
typed: Vec<declarations::Declaration>,
|
typed: Vec<declarations::Declaration>,
|
||||||
positions: Vec<(Id, EditDeclaration)>,
|
positions: Vec<(Id, EditDeclaration)>,
|
||||||
// A lexical owner is an occurrence index, not only a possibly repeated UUID.
|
// A lexical owner is an occurrence index, not only a possibly repeated UUID.
|
||||||
|
// Retained original indexing metadata; the read result preserves its accounted ownership.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
lexical_owners: Vec<Option<usize>>,
|
lexical_owners: Vec<Option<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +260,7 @@ impl State<'_> {
|
|||||||
return Err(Error {
|
return Err(Error {
|
||||||
code: "invalid_source",
|
code: "invalid_source",
|
||||||
offset: declaration.span.start,
|
offset: declaration.span.start,
|
||||||
message: "Private mixed syntax requires explicit IDs.".into(),
|
message: "Mixed source8 requires explicit UUIDs.".into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.typed.push(declaration);
|
self.typed.push(declaration);
|
||||||
@@ -276,17 +280,22 @@ pub(in crate::language) fn long_analysis_word(word: &str) -> bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub(super) fn parse(
|
pub(super) fn parse(
|
||||||
bundle: &SourceBundle,
|
bundle: &SourceBundle,
|
||||||
limits: ProjectLimits,
|
limits: ProjectLimits,
|
||||||
) -> Result<ParsedMixedSyntax<'_>, Vec<SourceDiagnostic>> {
|
) -> Result<ParsedMixedSyntax<'_>, Vec<SourceDiagnostic>> {
|
||||||
let profile = Limits::source8(ParseLimits {
|
let profile = source_profile(limits);
|
||||||
|
parse_with_profile(bundle, limits, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_profile(limits: ProjectLimits) -> Limits {
|
||||||
|
Limits::source8(ParseLimits {
|
||||||
// Storage is not the caller's final model record/ordered-step accounting.
|
// Storage is not the caller's final model record/ordered-step accounting.
|
||||||
max_source_bytes: limits.model.max_source_bytes,
|
max_source_bytes: limits.model.max_source_bytes,
|
||||||
max_step_references: limits.model.max_step_references,
|
max_step_references: limits.model.max_step_references,
|
||||||
..ParseLimits::UNLIMITED
|
..ParseLimits::UNLIMITED
|
||||||
});
|
})
|
||||||
parse_with_profile(bundle, limits, profile)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn located(path: &str, source: &str, error: Error) -> Vec<SourceDiagnostic> {
|
fn located(path: &str, source: &str, error: Error) -> Vec<SourceDiagnostic> {
|
||||||
@@ -297,6 +306,7 @@ fn located(path: &str, source: &str, error: Error) -> Vec<SourceDiagnostic> {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
fn parse_with_profile(
|
fn parse_with_profile(
|
||||||
bundle: &SourceBundle,
|
bundle: &SourceBundle,
|
||||||
limits: ProjectLimits,
|
limits: ProjectLimits,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//! Checks existing logical phase totals; does not meter every parser allocation.
|
//! Checks existing logical phase totals; does not meter every parser allocation.
|
||||||
use super::*;
|
use super::*;
|
||||||
use project::accounting::{Ledger, Slots};
|
use project::accounting::{Ledger, Slots};
|
||||||
|
#[cfg(test)]
|
||||||
use project::ordinary_index::rename::emission::PendingRename;
|
use project::ordinary_index::rename::emission::PendingRename;
|
||||||
|
|
||||||
struct SnapshotPolicy {
|
struct SnapshotPolicy {
|
||||||
@@ -10,17 +11,20 @@ struct SnapshotPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The allocation-owning bundle guard. No production constructor exists yet.
|
/// The allocation-owning bundle guard. No production constructor exists yet.
|
||||||
|
#[cfg(test)]
|
||||||
struct RetainedSnapshot {
|
struct RetainedSnapshot {
|
||||||
bundle: SourceBundle,
|
bundle: SourceBundle,
|
||||||
policy: SnapshotPolicy,
|
policy: SnapshotPolicy,
|
||||||
retained: Slots,
|
retained: Slots,
|
||||||
}
|
}
|
||||||
enum SnapshotBacking<'s> {
|
enum SnapshotBacking<'s> {
|
||||||
|
#[cfg(test)]
|
||||||
Pending(&'s PendingRename<'s>),
|
Pending(&'s PendingRename<'s>),
|
||||||
Borrowed {
|
Borrowed {
|
||||||
bundle: &'s SourceBundle,
|
bundle: &'s SourceBundle,
|
||||||
policy: SnapshotPolicy,
|
policy: SnapshotPolicy,
|
||||||
},
|
},
|
||||||
|
#[cfg(test)]
|
||||||
Retained(&'s RetainedSnapshot),
|
Retained(&'s RetainedSnapshot),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,8 +33,12 @@ pub(super) struct CarriedInput<'s> {
|
|||||||
backing: SnapshotBacking<'s>,
|
backing: SnapshotBacking<'s>,
|
||||||
}
|
}
|
||||||
enum GuardBacking<'s> {
|
enum GuardBacking<'s> {
|
||||||
|
#[cfg(test)]
|
||||||
Pending(&'s PendingRename<'s>),
|
Pending(&'s PendingRename<'s>),
|
||||||
Borrowed,
|
Borrowed {
|
||||||
|
_bundle: &'s SourceBundle,
|
||||||
|
},
|
||||||
|
#[cfg(test)]
|
||||||
Retained(&'s RetainedSnapshot),
|
Retained(&'s RetainedSnapshot),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +48,18 @@ pub(super) struct CarriedGuard<'s> {
|
|||||||
backing: GuardBacking<'s>,
|
backing: GuardBacking<'s>,
|
||||||
}
|
}
|
||||||
impl<'s> CarriedInput<'s> {
|
impl<'s> CarriedInput<'s> {
|
||||||
|
fn fresh(bundle: &'s SourceBundle, limits: ProjectLimits) -> Self {
|
||||||
|
Self {
|
||||||
|
backing: SnapshotBacking::Borrowed {
|
||||||
|
bundle,
|
||||||
|
policy: SnapshotPolicy {
|
||||||
|
limits: source_profile(limits),
|
||||||
|
project_limits: limits,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
pub(super) fn from_pending<'t: 's>(pending: &'s PendingRename<'t>) -> Self {
|
pub(super) fn from_pending<'t: 's>(pending: &'s PendingRename<'t>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
backing: SnapshotBacking::Pending(pending),
|
backing: SnapshotBacking::Pending(pending),
|
||||||
@@ -47,6 +67,7 @@ impl<'s> CarriedInput<'s> {
|
|||||||
}
|
}
|
||||||
fn into_parts(self) -> (&'s SourceBundle, SnapshotPolicy, CarriedGuard<'s>) {
|
fn into_parts(self) -> (&'s SourceBundle, SnapshotPolicy, CarriedGuard<'s>) {
|
||||||
match self.backing {
|
match self.backing {
|
||||||
|
#[cfg(test)]
|
||||||
SnapshotBacking::Pending(pending) => {
|
SnapshotBacking::Pending(pending) => {
|
||||||
let (limits, project_limits) = pending.carried_policy();
|
let (limits, project_limits) = pending.carried_policy();
|
||||||
(
|
(
|
||||||
@@ -64,9 +85,10 @@ impl<'s> CarriedInput<'s> {
|
|||||||
bundle,
|
bundle,
|
||||||
policy,
|
policy,
|
||||||
CarriedGuard {
|
CarriedGuard {
|
||||||
backing: GuardBacking::Borrowed,
|
backing: GuardBacking::Borrowed { _bundle: bundle },
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
#[cfg(test)]
|
||||||
SnapshotBacking::Retained(owner) => (
|
SnapshotBacking::Retained(owner) => (
|
||||||
&owner.bundle,
|
&owner.bundle,
|
||||||
SnapshotPolicy {
|
SnapshotPolicy {
|
||||||
@@ -83,8 +105,10 @@ impl<'s> CarriedInput<'s> {
|
|||||||
impl CarriedGuard<'_> {
|
impl CarriedGuard<'_> {
|
||||||
pub(super) fn slots(&self) -> Slots {
|
pub(super) fn slots(&self) -> Slots {
|
||||||
match &self.backing {
|
match &self.backing {
|
||||||
|
#[cfg(test)]
|
||||||
GuardBacking::Pending(pending) => pending.carried_slots(),
|
GuardBacking::Pending(pending) => pending.carried_slots(),
|
||||||
GuardBacking::Borrowed => Slots::default(),
|
GuardBacking::Borrowed { .. } => Slots::default(),
|
||||||
|
#[cfg(test)]
|
||||||
GuardBacking::Retained(owner) => owner.retained,
|
GuardBacking::Retained(owner) => owner.retained,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,6 +129,7 @@ impl CarriedGuard<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Input already binds the actual bundle; callers cannot supply a replacement.
|
/// Input already binds the actual bundle; callers cannot supply a replacement.
|
||||||
|
#[cfg(test)]
|
||||||
fn parse_carried(input: CarriedInput<'_>) -> Result<ParsedMixedSyntax<'_>, Vec<SourceDiagnostic>> {
|
fn parse_carried(input: CarriedInput<'_>) -> Result<ParsedMixedSyntax<'_>, Vec<SourceDiagnostic>> {
|
||||||
let (bundle, policy, guard) = input.into_parts();
|
let (bundle, policy, guard) = input.into_parts();
|
||||||
parse_impl(
|
parse_impl(
|
||||||
@@ -205,3 +230,12 @@ mod text_producer;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod entry_producer;
|
mod entry_producer;
|
||||||
|
|
||||||
|
pub(super) fn parse_fresh(
|
||||||
|
bundle: &SourceBundle,
|
||||||
|
limits: ProjectLimits,
|
||||||
|
) -> Result<ParsedMixedSyntax<'_>, Vec<SourceDiagnostic>> {
|
||||||
|
parse_entry_producers(CarriedInput::fresh(bundle, limits))
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod public_read;
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//! Real public-constructor wiring; arbitrary fixture profiles are not public policy.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn public_read_fresh_constructor_keeps_zero_carry_both_modes_and_exact_policy() {
|
||||||
|
let bundle = SourceBundle {
|
||||||
|
entry: "main.arcadia".into(),
|
||||||
|
files: [("main.arcadia".into(), "arcadia 8;\nproject p \"Project\" id \"00000000-0000-0000-0000-000000000001\" {\n\n}\n".into())].into(),
|
||||||
|
};
|
||||||
|
let original = bundle.clone();
|
||||||
|
for (source_bytes, references, expected_bytes, expected_tokens, expected_references) in [
|
||||||
|
(4096, 17, 4096, 4096, 17),
|
||||||
|
(usize::MAX, usize::MAX, 64 * 1024 * 1024, 1_048_576, 500_000),
|
||||||
|
] {
|
||||||
|
let limits = ProjectLimits {
|
||||||
|
max_files: 7,
|
||||||
|
max_include_depth: 9,
|
||||||
|
max_include_edges: 11,
|
||||||
|
model: ParseLimits {
|
||||||
|
max_source_bytes: source_bytes,
|
||||||
|
max_graph_objects: 3,
|
||||||
|
max_records: 1,
|
||||||
|
max_step_references: references,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// This is the same production helper the exported checker calls.
|
||||||
|
let syntax = parse_fresh(&bundle, limits).unwrap();
|
||||||
|
let guard = syntax.carried.as_ref().expect("explicit carried mode");
|
||||||
|
assert!(matches!(guard.backing, GuardBacking::Borrowed { .. }));
|
||||||
|
assert_eq!(guard.slots(), Slots::default());
|
||||||
|
assert_eq!(syntax.budget.producer_text(), Some((8, 0)));
|
||||||
|
assert_eq!(syntax.budget.producer_entries(), Some(11));
|
||||||
|
assert_eq!(syntax.metadata, 11);
|
||||||
|
let profile = syntax.budget.limits();
|
||||||
|
assert_eq!(profile.bytes, expected_bytes);
|
||||||
|
assert_eq!(profile.tokens, expected_tokens);
|
||||||
|
assert_eq!(profile.entries, 262_144);
|
||||||
|
assert_eq!(profile.references, expected_references);
|
||||||
|
assert_eq!(profile.frames, 256);
|
||||||
|
assert_eq!(profile.numeric_units, 50_000_000);
|
||||||
|
assert_eq!(syntax.limits.max_files, 7);
|
||||||
|
assert_eq!(syntax.limits.max_include_depth, 9);
|
||||||
|
assert_eq!(syntax.limits.max_include_edges, 11);
|
||||||
|
assert_eq!(syntax.limits.model.max_source_bytes, source_bytes);
|
||||||
|
assert_eq!(syntax.limits.model.max_graph_objects, 3);
|
||||||
|
assert_eq!(syntax.limits.model.max_records, 1);
|
||||||
|
assert_eq!(syntax.limits.model.max_step_references, references);
|
||||||
|
let prior = parse(&bundle, limits).unwrap();
|
||||||
|
assert_eq!(syntax.budget.usage, prior.budget.usage);
|
||||||
|
assert_eq!(syntax.budget.numeric_units(), prior.budget.numeric_units());
|
||||||
|
let resolved = project::resolve(syntax).unwrap();
|
||||||
|
let prior = project::resolve(prior).unwrap();
|
||||||
|
let checked = project::check_mixed_bundle(&bundle, limits).unwrap();
|
||||||
|
let mut expected = crate::Project::empty();
|
||||||
|
expected.id = Id::from_u128(1);
|
||||||
|
expected.alias = "p".into();
|
||||||
|
expected.name = "Project".into();
|
||||||
|
assert_eq!(resolved.project(), &expected);
|
||||||
|
assert_eq!(prior.project(), &expected);
|
||||||
|
assert_eq!(checked.project(), &expected);
|
||||||
|
assert!(checked.warnings().is_empty());
|
||||||
|
assert_eq!(resolved.warnings(), prior.warnings());
|
||||||
|
}
|
||||||
|
assert_eq!(bundle, original);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//! Private complete-Project admission from the actual complete syntax witness.
|
//! Complete-Project admission and opaque public read-only mixed source8 witness.
|
||||||
//! Consumed read-only typed index; no update authority, public source8 dispatch or replacement policy.
|
//! The separate typed index/edit chain remains test-only; no replacement policy.
|
||||||
use super::super::{
|
use super::super::{
|
||||||
Binding, Budget, Error as TypedError, Expression, ExpressionKind, Reference, Span,
|
Binding, Budget, Error as TypedError, Expression, ExpressionKind, Reference, Span,
|
||||||
declarations as d, error, lower,
|
declarations as d, error, lower,
|
||||||
@@ -22,17 +22,108 @@ pub(super) fn text_producer_inventory(syntax: &ParsedMixedSyntax<'_>) -> Result<
|
|||||||
accounting::syntax(syntax, &mut ledger)?;
|
accounting::syntax(syntax, &mut ledger)?;
|
||||||
Ok(ledger.live)
|
Ok(ledger.live)
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
pub(in crate::language::analysis_source) mod ordinary_index;
|
pub(in crate::language::analysis_source) mod ordinary_index;
|
||||||
|
|
||||||
|
/// An admitted mixed source8 model borrowing its complete original source bundle.
|
||||||
|
///
|
||||||
|
/// This non-Clone result grants immutable validation queries only. It is not an
|
||||||
|
/// editable source index, stabilized output, or permission to format/save a typed
|
||||||
|
/// model through the ordinary source APIs. All input UUIDs must be explicit.
|
||||||
|
///
|
||||||
|
/// The source cannot be mutated while a later query uses the result:
|
||||||
|
///
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use oa_core::{language::check_mixed_bundle, source_project::{SourceBundle, ProjectLimits}};
|
||||||
|
/// let mut bundle = SourceBundle {
|
||||||
|
/// entry: "main.arcadia".into(),
|
||||||
|
/// files: [("main.arcadia".into(),
|
||||||
|
/// "arcadia 8; project p \"P\" id \"00000000-0000-0000-0000-000000000001\" {}".into())].into(),
|
||||||
|
/// };
|
||||||
|
/// let checked = check_mixed_bundle(&bundle, ProjectLimits::default()).unwrap();
|
||||||
|
/// bundle.files.clear();
|
||||||
|
/// assert_eq!(checked.project().alias, "p");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Nor can the result outlive that source:
|
||||||
|
///
|
||||||
|
/// ```compile_fail
|
||||||
|
/// use oa_core::{language::check_mixed_bundle, source_project::{SourceBundle, ProjectLimits}};
|
||||||
|
/// let checked = {
|
||||||
|
/// let bundle = SourceBundle {
|
||||||
|
/// entry: "main.arcadia".into(),
|
||||||
|
/// files: [("main.arcadia".into(),
|
||||||
|
/// "arcadia 8; project p \"P\" id \"00000000-0000-0000-0000-000000000001\" {}".into())].into(),
|
||||||
|
/// };
|
||||||
|
/// check_mixed_bundle(&bundle, ProjectLimits::default()).unwrap()
|
||||||
|
/// };
|
||||||
|
/// assert_eq!(checked.project().alias, "p");
|
||||||
|
/// ```
|
||||||
|
pub struct CheckedMixed<'s> {
|
||||||
|
resolved: ResolvedMixedProject<'s>,
|
||||||
|
}
|
||||||
|
impl CheckedMixed<'_> {
|
||||||
|
/// The complete admitted model, without mutation or ownership extraction.
|
||||||
|
pub fn project(&self) -> &Project {
|
||||||
|
self.resolved.project()
|
||||||
|
}
|
||||||
|
/// Actual located non-error validation diagnostics in validation order.
|
||||||
|
pub fn warnings(&self) -> &[SourceDiagnostic] {
|
||||||
|
self.resolved.warnings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a complete mixed ordinary/typed source8 bundle without changing bytes.
|
||||||
|
///
|
||||||
|
/// Every project, ordinary/typed declaration and typed parameter requires an
|
||||||
|
/// explicit UUID. Supplied files must be canonical and reachable from the entry.
|
||||||
|
/// Errors return no partial admitted model. There is no migration, ID generation,
|
||||||
|
/// filesystem discovery, formatting or editable-index construction.
|
||||||
|
///
|
||||||
|
/// Caller policy is applied across the entire bundle with the existing bounded
|
||||||
|
/// source8 syntax/storage/numeric policy; this is not an all-heap memory bound.
|
||||||
|
/// Ordinary parse/compile/edit/save APIs and CLI discovery remain separate.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use oa_core::{language::check_mixed_bundle, source_project::{SourceBundle, ProjectLimits}};
|
||||||
|
/// let bundle = SourceBundle {
|
||||||
|
/// entry: "main.arcadia".into(),
|
||||||
|
/// files: [("main.arcadia".into(),
|
||||||
|
/// "arcadia 8; project p \"P\" id \"00000000-0000-0000-0000-000000000001\" {}".into())].into(),
|
||||||
|
/// };
|
||||||
|
/// let checked = check_mixed_bundle(&bundle, ProjectLimits::default()).unwrap();
|
||||||
|
/// assert_eq!(checked.project().alias, "p");
|
||||||
|
/// assert!(checked.warnings().is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn check_mixed_bundle(
|
||||||
|
bundle: &crate::source_project::SourceBundle,
|
||||||
|
limits: crate::source_project::ProjectLimits,
|
||||||
|
) -> Result<CheckedMixed<'_>, Vec<SourceDiagnostic>> {
|
||||||
|
let syntax = super::carried::parse_fresh(bundle, limits)?;
|
||||||
|
let resolved = resolve(syntax).map_err(|failure| failure.diagnostics)?;
|
||||||
|
Ok(CheckedMixed { resolved })
|
||||||
|
}
|
||||||
/// Read-only authority: every component originates in the consumed syntax.
|
/// Read-only authority: every component originates in the consumed syntax.
|
||||||
pub(in crate::language::analysis_source) struct ResolvedMixedProject<'s> {
|
pub(in crate::language::analysis_source) struct ResolvedMixedProject<'s> {
|
||||||
|
// Retained ownership/admission evidence; consumed by the private test-only edit chain.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
syntax: ParsedMixedSyntax<'s>,
|
syntax: ParsedMixedSyntax<'s>,
|
||||||
project: Project,
|
project: Project,
|
||||||
warnings: Vec<SourceDiagnostic>,
|
warnings: Vec<SourceDiagnostic>,
|
||||||
|
// Retained ownership/admission evidence; consumed by the private test-only edit chain.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
static_limits: analysis::AdmissionLimits,
|
static_limits: analysis::AdmissionLimits,
|
||||||
|
// Retained ownership/admission evidence; consumed by the private test-only edit chain.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
static_usage: analysis::AdmissionUsage,
|
static_usage: analysis::AdmissionUsage,
|
||||||
|
// Retained ownership/admission evidence; consumed by the private test-only edit chain.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
expressions: Vec<lower::ExpressionSite>,
|
expressions: Vec<lower::ExpressionSite>,
|
||||||
|
// Retained ownership/admission evidence; consumed by the private test-only edit chain.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
occurrences: Vec<lower::Occurrence>,
|
occurrences: Vec<lower::Occurrence>,
|
||||||
|
// Retained ownership/admission evidence; consumed by the private test-only edit chain.
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
storage: Ledger,
|
storage: Ledger,
|
||||||
}
|
}
|
||||||
impl ResolvedMixedProject<'_> {
|
impl ResolvedMixedProject<'_> {
|
||||||
@@ -51,10 +142,12 @@ impl ResolvedMixedProject<'_> {
|
|||||||
/// Complete original bundle authority with a typed-only occurrence view.
|
/// Complete original bundle authority with a typed-only occurrence view.
|
||||||
/// Ordinary globals are catalogued, but their authored occurrences are not indexed.
|
/// Ordinary globals are catalogued, but their authored occurrences are not indexed.
|
||||||
/// This owner cannot be converted into the legacy typed-only edit authority.
|
/// This owner cannot be converted into the legacy typed-only edit authority.
|
||||||
|
#[cfg(test)]
|
||||||
pub(in crate::language::analysis_source) struct IndexedMixedProject<'s> {
|
pub(in crate::language::analysis_source) struct IndexedMixedProject<'s> {
|
||||||
resolved: ResolvedMixedProject<'s>,
|
resolved: ResolvedMixedProject<'s>,
|
||||||
data: super::super::index::IndexData,
|
data: super::super::index::IndexData,
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
impl<'s> ResolvedMixedProject<'s> {
|
impl<'s> ResolvedMixedProject<'s> {
|
||||||
pub fn index(mut self) -> Result<IndexedMixedProject<'s>, Failure> {
|
pub fn index(mut self) -> Result<IndexedMixedProject<'s>, Failure> {
|
||||||
let input = IndexInput {
|
let input = IndexInput {
|
||||||
@@ -72,6 +165,7 @@ impl<'s> ResolvedMixedProject<'s> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
impl IndexedMixedProject<'_> {
|
impl IndexedMixedProject<'_> {
|
||||||
pub fn project(&self) -> &Project {
|
pub fn project(&self) -> &Project {
|
||||||
self.resolved.project()
|
self.resolved.project()
|
||||||
@@ -108,12 +202,14 @@ impl IndexedMixedProject<'_> {
|
|||||||
|
|
||||||
/// Constructor-only borrowed adapter. Its private file table prevents fabrication;
|
/// Constructor-only borrowed adapter. Its private file table prevents fabrication;
|
||||||
/// no API returns it and no alternative source/token table can be installed.
|
/// no API returns it and no alternative source/token table can be installed.
|
||||||
|
#[cfg(test)]
|
||||||
pub(in crate::language::analysis_source) struct IndexInput<'a> {
|
pub(in crate::language::analysis_source) struct IndexInput<'a> {
|
||||||
files: &'a [FileSyntax<'a>],
|
files: &'a [FileSyntax<'a>],
|
||||||
pub(in crate::language::analysis_source) project: &'a Project,
|
pub(in crate::language::analysis_source) project: &'a Project,
|
||||||
pub(in crate::language::analysis_source) expressions: &'a [lower::ExpressionSite],
|
pub(in crate::language::analysis_source) expressions: &'a [lower::ExpressionSite],
|
||||||
pub(in crate::language::analysis_source) occurrences: &'a [lower::Occurrence],
|
pub(in crate::language::analysis_source) occurrences: &'a [lower::Occurrence],
|
||||||
}
|
}
|
||||||
|
#[cfg(test)]
|
||||||
impl IndexInput<'_> {
|
impl IndexInput<'_> {
|
||||||
pub(in crate::language::analysis_source) fn declarations(
|
pub(in crate::language::analysis_source) fn declarations(
|
||||||
&self,
|
&self,
|
||||||
@@ -140,6 +236,7 @@ pub(in crate::language::analysis_source) struct Failure {
|
|||||||
}
|
}
|
||||||
impl Failure {
|
impl Failure {
|
||||||
/// Canonical attribution when justified; otherwise a safe explicit start fallback.
|
/// Canonical attribution when justified; otherwise a safe explicit start fallback.
|
||||||
|
#[cfg(test)]
|
||||||
fn index(syntax: &ParsedMixedSyntax<'_>, mut failure: lower::Failure) -> Self {
|
fn index(syntax: &ParsedMixedSyntax<'_>, mut failure: lower::Failure) -> Self {
|
||||||
failure.file = failure.file.filter(|&f| f < syntax.files.len());
|
failure.file = failure.file.filter(|&f| f < syntax.files.len());
|
||||||
if failure.file.is_none_or(|file| {
|
if failure.file.is_none_or(|file| {
|
||||||
|
|||||||
@@ -633,6 +633,23 @@ a { text-decoration: none; }
|
|||||||
<dd class="text-suffix"><p><span class="docutils literal">.arcadia</span></p>
|
<dd class="text-suffix"><p><span class="docutils literal">.arcadia</span></p>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
<section id="staged-source8-rust-read-api">
|
||||||
|
<h2>Staged source8 Rust read API</h2>
|
||||||
|
<p>The core library separately offers <span class="docutils literal"><span class="pre">language::check_mixed_bundle(&SourceBundle,</span> ProjectLimits)</span> for complete ordinary/typed source8 validation. Its opaque
|
||||||
|
<span class="docutils literal">CheckedMixed</span> result borrows the unchanged input and exposes only <span class="docutils literal">project()</span>
|
||||||
|
and <span class="docutils literal">warnings()</span>. Explicit UUIDs are required for the project, every ordinary
|
||||||
|
and typed declaration, and typed parameters; no IDs are generated. All supplied
|
||||||
|
canonical files must be reachable from the entry. Errors return no admitted
|
||||||
|
model; success preserves actual located warnings and the complete model.</p>
|
||||||
|
<p>This bounded API selects both retained text and entry producer checks and carries
|
||||||
|
the caller's whole-project policy through admission. It does not promise an
|
||||||
|
all-heap memory bound. It performs no filesystem discovery, migration, formatting,
|
||||||
|
source stabilization, indexing, saving or history installation. Existing ordinary
|
||||||
|
parse/compile/update APIs, CLI commands, desktop and server flows remain separate;
|
||||||
|
a read result does not grant their typed editing or save authority. Generic and
|
||||||
|
typed depth policies and platform qualification limits remain unchanged. This
|
||||||
|
staged capability does not replace the broader conformance obligations below.</p>
|
||||||
|
</section>
|
||||||
<section id="scope-and-conformance">
|
<section id="scope-and-conformance">
|
||||||
<h2>1. Scope and conformance</h2>
|
<h2>1. Scope and conformance</h2>
|
||||||
<p>This specification defines the shared Operational Analysis (OA) and System
|
<p>This specification defines the shared Operational Analysis (OA) and System
|
||||||
|
|||||||
@@ -6,6 +6,26 @@ Arcadia Studio OA / SA Language and Editing Contract
|
|||||||
:Core implementation: ``crates/oa-core``
|
:Core implementation: ``crates/oa-core``
|
||||||
:Text suffix: ``.arcadia``
|
:Text suffix: ``.arcadia``
|
||||||
|
|
||||||
|
Staged source8 Rust read API
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
The core library separately offers ``language::check_mixed_bundle(&SourceBundle,
|
||||||
|
ProjectLimits)`` for complete ordinary/typed source8 validation. Its opaque
|
||||||
|
``CheckedMixed`` result borrows the unchanged input and exposes only ``project()``
|
||||||
|
and ``warnings()``. Explicit UUIDs are required for the project, every ordinary
|
||||||
|
and typed declaration, and typed parameters; no IDs are generated. All supplied
|
||||||
|
canonical files must be reachable from the entry. Errors return no admitted
|
||||||
|
model; success preserves actual located warnings and the complete model.
|
||||||
|
|
||||||
|
This bounded API selects both retained text and entry producer checks and carries
|
||||||
|
the caller's whole-project policy through admission. It does not promise an
|
||||||
|
all-heap memory bound. It performs no filesystem discovery, migration, formatting,
|
||||||
|
source stabilization, indexing, saving or history installation. Existing ordinary
|
||||||
|
parse/compile/update APIs, CLI commands, desktop and server flows remain separate;
|
||||||
|
a read result does not grant their typed editing or save authority. Generic and
|
||||||
|
typed depth policies and platform qualification limits remain unchanged. This
|
||||||
|
staged capability does not replace the broader conformance obligations below.
|
||||||
|
|
||||||
1. Scope and conformance
|
1. Scope and conformance
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Public mixed read implementation
|
||||||
|
|
||||||
|
Base7b06b8ea, selected root contract bcddaba3. Public
|
||||||
|
language::check_mixed_bundle returns opaque non-Clone CheckedMixed borrowing the
|
||||||
|
complete original SourceBundle, with only immutable project/warnings accessors.
|
||||||
|
Its private resolved field retains syntax, expression/occurrence vectors, ledger
|
||||||
|
and admission evidence; there is no index, output, save or extraction authority.
|
||||||
|
|
||||||
|
The shared parser mixed hooks now compile in production. Ordinary callers still
|
||||||
|
select None. The fresh constructor binds the actual bundle and exact ProjectLimits,
|
||||||
|
derives existing internal policy once, selects Some(Borrowed) with zero carry and
|
||||||
|
both producers, then consumes syntax through complete resolution/admission.
|
||||||
|
The borrowed guard retains the actual source reference even when test-only
|
||||||
|
Pending/Retained variants are absent. No duplicate live policy/model is added.
|
||||||
|
Public schema/UUID requirement wording replaces private-staging wording only;
|
||||||
|
all limiter phase/category/span transport remains inherited.
|
||||||
|
|
||||||
|
Private legacy index/update/write, ordinary-index/rename/emission/acceptance and
|
||||||
|
fixture/pending constructors remain test-only. Shared parse/lower code remains
|
||||||
|
private. Necessary retained fields have individual documented non-test dead-code
|
||||||
|
allowances; no module-wide suppression, fake read, or new public lint-only API.
|
||||||
|
Exact production compiler lint findings remain a parent gate, not inferred clean
|
||||||
|
from source inspection. Existing298 tests are not edited.
|
||||||
|
|
||||||
|
One internal public_read test invokes the actual production parse helper and
|
||||||
|
observes borrowedC0, both producer inventories, every inherited/derived policy
|
||||||
|
field (including262144 entries independent of model records1), full empty-Project
|
||||||
|
and warning parity, Usage/numeric parity and unchanged input. Three rustdoc blocks
|
||||||
|
provide a compiling real usage example plus source-drop and source-mutation
|
||||||
|
compile-fail cases with later result use. Independent worker owns the non-test
|
||||||
|
mixed_source8_read integration target and its separate complete-model oracles.
|
||||||
|
|
||||||
|
Established docs/language.rst gains a bounded staged-read note without changing
|
||||||
|
its broader conformance/version obligations. Existing docutils HTML5 generation
|
||||||
|
with halt/report level2 regenerated language.html successfully. An initial local
|
||||||
|
RST generation refused the new section formatting; the section separator and
|
||||||
|
blank line were corrected before successful regeneration. No Rust compiler,
|
||||||
|
Cargo test, CLI, debugger or publication was run by this worker.
|
||||||
|
|
||||||
|
Parent owns pinned compilation, all old/new runtime tests, doctests, strict
|
||||||
|
Clippy, formatting disposition and production Parser stack regression checks.
|
||||||
|
DEPTH_128_VALIDATED=false, existing depth policies, logical-accounting exclusions,
|
||||||
|
full-pipeline limitation and deferred Linux qualification remain unchanged.
|
||||||
Reference in New Issue
Block a user