tamer: asg::air::ir::AirPkg::PkgStart: Require name

This requires the name as part of the package definition, which in turn
removes a state (and all the combinations resulting from it) from
AirAggregate, which results in significant complexity reduction for a very
complex part of the system.

Pushing this complexity outward results in a reduction of overall
complexity, and obviates the question of where NIR will receive a generated
name.

DEV-13162
main
Mike Gerwitz 2023-05-10 12:42:57 -04:00
parent 7a6aef00b2
commit dd6a6dd196
13 changed files with 161 additions and 202 deletions

View File

@ -138,25 +138,13 @@ impl ParseState for AirAggregate {
tok: Self::Token,
ctx: &mut Self::Context,
) -> crate::parse::TransitionResult<Self> {
use ir::{AirBind::BindIdent, AirSubsets::*, AirTodo::*};
use ir::{AirSubsets::*, AirTodo::*};
use AirAggregate::*;
use AirPkgAggregate::{Toplevel, UnnamedPkg};
match (self, tok.into()) {
(st, AirTodo(Todo(_))) => Transition(st).incomplete(),
// TODO: Remove this kluge; transitionary (no package name required)
(Pkg(UnnamedPkg(span)), tok)
if !matches!(tok, AirBind(BindIdent(..))) =>
{
match ctx.pkg_begin(span, SPair("/TODO".into(), span)) {
Ok(oi_pkg) => Transition(Pkg(Toplevel(oi_pkg)))
.incomplete()
.with_lookahead(tok),
Err(e) => Transition(Pkg(UnnamedPkg(span))).err(e),
}
}
// Package
(st @ (Empty | PkgExpr(..) | PkgTpl(..)), tok @ AirPkg(..)) => {
ctx.ret_or_transfer(st, tok, AirPkgAggregate::new())
}
@ -165,6 +153,7 @@ impl ParseState for AirAggregate {
(Pkg(pkg), AirIdent(etok)) => ctx.proxy(pkg, etok),
(Pkg(pkg), AirDoc(etok)) => ctx.proxy(pkg, etok),
// Expression
(st @ (Pkg(_) | PkgTpl(_)), tok @ AirExpr(..)) => {
ctx.ret_or_transfer(st, tok, AirExprAggregate::new())
}
@ -172,7 +161,7 @@ impl ParseState for AirAggregate {
(PkgExpr(expr), AirBind(etok)) => ctx.proxy(expr, etok),
(PkgExpr(expr), AirDoc(etok)) => ctx.proxy(expr, etok),
// Template parsing.
// Template
(st @ (Pkg(_) | PkgExpr(_)), tok @ AirTpl(..)) => {
ctx.ret_or_transfer(st, tok, AirTplAggregate::new())
}
@ -303,6 +292,10 @@ impl AirAggregateCtx {
self.as_mut()
}
fn asg_ref(&self) -> &Asg {
self.as_ref()
}
fn stack(&mut self) -> &mut AirStack {
let Self(_, stack, _) = self;
stack

View File

@ -73,7 +73,7 @@ fn expr_without_pkg() {
// (because we're not parsing with `parse_as_pkg_body` below)
Air::ExprStart(ExprOp::Sum, S1),
// RECOVERY
Air::PkgStart(S2),
Air::PkgStart(S2, SPair("/pkg".into(), S2)),
Air::PkgEnd(S3),
];
@ -95,7 +95,7 @@ fn close_pkg_mid_expr() {
#[rustfmt::skip]
let toks = vec![
Air::PkgStart(S1),
Air::PkgStart(S1, SPair("/pkg".into(), S1)),
Air::ExprStart(ExprOp::Sum, S2),
Air::PkgEnd(S3),
// RECOVERY: Let's finish the expression first...
@ -126,13 +126,15 @@ fn close_pkg_mid_expr() {
#[test]
fn open_pkg_mid_expr() {
let pkg_a = SPair("/pkg".into(), S1);
let pkg_nested = SPair("/pkg-nested".into(), S3);
let id = SPair("foo".into(), S4);
#[rustfmt::skip]
let toks = vec![
Air::PkgStart(S1),
Air::PkgStart(S1, pkg_a),
Air::ExprStart(ExprOp::Sum, S2),
Air::PkgStart(S3),
Air::PkgStart(S3, pkg_nested),
// RECOVERY: We should still be able to complete successfully.
Air::BindIdent(id),
Air::ExprEnd(S5),
@ -145,7 +147,10 @@ fn open_pkg_mid_expr() {
vec![
Ok(Parsed::Incomplete), // PkgStart
Ok(Parsed::Incomplete), // ExprStart
Err(ParseError::StateError(AsgError::NestedPkgStart(S3, S1))),
Err(ParseError::StateError(AsgError::NestedPkgStart(
(S3, pkg_nested),
(S1, pkg_a),
))),
// RECOVERY: Ignore the open and continue.
// Of course,
// this means that any identifiers would be defined in a
@ -756,7 +761,7 @@ fn idents_share_defining_pkg() {
// An expression nested within another.
#[rustfmt::skip]
let toks = vec![
Air::PkgStart(S1),
Air::PkgStart(S1, SPair("/pkg".into(), S1)),
Air::ExprStart(ExprOp::Sum, S2),
Air::BindIdent(id_foo),

View File

@ -466,10 +466,18 @@ sum_ir! {
/// within a given package,
/// but we have no such restriction.
///
/// TODO: The package needs a name,
/// and we'll need to determine how to best represent that relative to
/// the project root and be considerate of symlinks.
PkgStart(span: Span) => {
/// Packages are assigned unique names in a similar way to
/// identifiers.
/// This name is generally expected to be generated from the
/// path to the package on the host filesystem,
/// so no guarantees are made as to the [`Span`] associated
/// with the provided `name`.
/// For clarity,
/// the first [`Span`] is intended to represent the start of
/// the package declaration,
/// however that is represented by the source stream;
/// the `name` [`Span`] may or may not duplicate it.
PkgStart(span: Span, name: SPair) => {
span: span,
display: |f| write!(f, "open package"),
},

View File

@ -26,9 +26,7 @@ use super::{
ir::AirLoadablePkg,
AirAggregate, AirAggregateCtx,
};
use crate::{
diagnose::Annotate, diagnostic_todo, parse::prelude::*, span::Span,
};
use crate::{diagnose::Annotate, diagnostic_todo, parse::prelude::*};
/// Package parsing with support for loaded identifiers.
///
@ -41,10 +39,6 @@ pub enum AirPkgAggregate {
/// expression stack is empty.
Ready,
/// Package definition or declaration started,
/// but the name is not yet known.
UnnamedPkg(Span),
/// Expecting a package-level token.
Toplevel(ObjectIndex<Pkg>),
}
@ -57,9 +51,6 @@ impl Display for AirPkgAggregate {
Ready => {
write!(f, "expecting package definition")
}
UnnamedPkg(_) => {
write!(f, "expecting canonical package name")
}
Toplevel(_) => {
write!(f, "expecting package header or an expression")
}
@ -84,34 +75,28 @@ impl ParseState for AirPkgAggregate {
use AirPkgAggregate::*;
match (self, tok) {
(Ready, AirPkg(PkgStart(span))) => {
if let Some(first_span) = ctx.pkg_oi().map(|oi| oi.span()) {
Transition(Ready)
.err(AsgError::NestedPkgStart(span, first_span))
(st @ (Ready | Toplevel(..)), AirPkg(PkgStart(span, name))) => {
if let Some(first) =
ctx.pkg_oi().map(|oi| oi.resolve(ctx.asg_ref()))
{
let first_span = first.span();
let first_name = first.canonical_name();
Transition(st).err(AsgError::NestedPkgStart(
(span, name),
(first_span, first_name),
))
} else {
Transition(UnnamedPkg(span)).incomplete()
match ctx.pkg_begin(span, name) {
Ok(oi_pkg) => Transition(Toplevel(oi_pkg)).incomplete(),
Err(e) => Transition(Ready).err(e),
}
}
}
(Toplevel(oi_pkg), AirPkg(PkgStart(span))) => {
(Toplevel(oi_pkg), AirBind(BindIdent(name))) => {
Transition(Toplevel(oi_pkg))
.err(AsgError::NestedPkgStart(span, oi_pkg.span()))
}
// Packages are identified by canonical paths relative to the
// project root.
(UnnamedPkg(span), AirBind(BindIdent(name))) => {
match ctx.pkg_begin(span, name) {
Ok(oi_pkg) => Transition(Toplevel(oi_pkg)).incomplete(),
Err(e) => Transition(UnnamedPkg(span)).err(e),
}
}
(Toplevel(oi_pkg), AirBind(BindIdent(rename))) => {
let name = oi_pkg.resolve(ctx.asg_mut()).canonical_name();
Transition(Toplevel(oi_pkg))
.err(AsgError::PkgRename(name, rename))
.err(AsgError::InvalidBindContext(name))
}
(Toplevel(oi_pkg), AirPkg(PkgEnd(span))) => {
@ -195,19 +180,6 @@ impl ParseState for AirPkgAggregate {
Transition(Ready).err(AsgError::InvalidPkgEndContext(span))
}
// TODO: See superstate
(UnnamedPkg(span), tok) => {
diagnostic_todo!(
vec![
span.note("for this package"),
tok.internal_error(
"package name expected before this token"
),
],
"package name expected",
)
}
// Token may refer to a parent context.
(st @ Ready, tok @ (AirBind(..) | AirIdent(..) | AirDoc(..))) => {
Transition(st).dead(tok)
@ -231,7 +203,7 @@ impl AirPkgAggregate {
use AirPkgAggregate::*;
match self {
Ready | UnnamedPkg(_) => None,
Ready => None,
Toplevel(oi_pkg) => Some(*oi_pkg),
}
}

View File

@ -46,7 +46,7 @@ fn ident_decl() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
IdentDecl(id, kind.clone(), src.clone()),
// Attempt re-declaration.
IdentDecl(id, kind.clone(), src.clone()),
@ -97,7 +97,7 @@ fn ident_extern_decl() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
IdentExternDecl(id, kind.clone(), src.clone()),
// Redeclare with a different kind
IdentExternDecl(re_id, different_kind.clone(), src.clone()),
@ -144,7 +144,7 @@ fn ident_dep() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
IdentDep(id, dep),
PkgEnd(S4),
].into_iter();
@ -182,7 +182,7 @@ fn ident_fragment() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
// Identifier must be declared before it can be given a
// fragment.
IdentDecl(id, kind.clone(), src.clone()),
@ -234,7 +234,7 @@ fn ident_root_missing() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
IdentRoot(id),
PkgEnd(S3),
].into_iter();
@ -280,7 +280,7 @@ fn ident_root_existing() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
IdentDecl(id, kind.clone(), src.clone()),
IdentRoot(SPair(id.symbol(), S3)),
PkgEnd(S3),
@ -337,7 +337,7 @@ fn declare_kind_auto_root() {
#[rustfmt::skip]
let toks = [
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
// auto-rooting
IdentDecl(id_auto, auto_kind, src.clone()),
// non-auto-rooting
@ -369,7 +369,11 @@ fn declare_kind_auto_root() {
#[test]
fn pkg_is_rooted() {
let toks = vec![PkgStart(S1), PkgEnd(S2)];
#[rustfmt::skip]
let toks = vec![
PkgStart(S1, SPair("/pkg".into(), S1)),
PkgEnd(S2),
];
let mut sut = Sut::parse(toks.into_iter());
assert!(sut.all(|x| x.is_ok()));
@ -391,7 +395,7 @@ fn close_pkg_without_open() {
let toks = vec![
PkgEnd(S1),
// RECOVERY: Try again.
PkgStart(S2),
PkgStart(S2, SPair("/pkg".into(), S2)),
PkgEnd(S3),
];
@ -408,23 +412,25 @@ fn close_pkg_without_open() {
#[test]
fn nested_open_pkg() {
let name_a = SPair("/pkg-a".into(), S2);
let name_b = SPair("/pkg-b".into(), S4);
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
BindIdent(SPair("/foo".into(), S2)),
PkgStart(S1, name_a),
// Cannot nest package
PkgStart(S3),
PkgStart(S3, name_b),
// RECOVERY
PkgEnd(S4),
PkgEnd(S5),
];
assert_eq!(
#[rustfmt::skip]
vec![
Ok(Incomplete), // PkgStart
Ok(Incomplete), // BindIdent
Err(ParseError::StateError(AsgError::NestedPkgStart(S3, S1))),
Err(ParseError::StateError(AsgError::NestedPkgStart(
(S3, name_b), (S1, name_a),
))),
// RECOVERY
Ok(Incomplete), // PkgEnd
],
@ -438,8 +444,7 @@ fn pkg_canonical_name() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
BindIdent(name),
PkgStart(S1, name),
PkgEnd(S3),
];
@ -472,21 +477,19 @@ fn pkg_canonical_name() {
fn pkg_cannot_redeclare() {
let name = SPair("/foo/bar".into(), S2);
let name2 = SPair("/foo/bar".into(), S5);
let namefix = SPair("/foo/fix".into(), S6);
let namefix = SPair("/foo/fix".into(), S7);
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
BindIdent(name),
PkgStart(S1, name),
PkgEnd(S3),
PkgStart(S4),
// Attempt to define a package of the same name.
BindIdent(name2),
// Attempt to define a package of the same name.
PkgStart(S4, name2),
// RECOVERY: Use a proper name.
BindIdent(namefix),
PkgEnd(S7),
// RECOVERY: Use a proper name.
PkgStart(S6, namefix),
PkgEnd(S8),
];
let mut sut = Sut::parse(toks.into_iter());
@ -495,15 +498,14 @@ fn pkg_cannot_redeclare() {
#[rustfmt::skip]
vec![
Ok(Incomplete), // PkgStart
Ok(Incomplete), // BindIdent
Ok(Incomplete), // PkgEnd
Err(ParseError::StateError(
AsgError::PkgRedeclare(name, name2)
)),
// RECOVERY: Retry with a proper name
Ok(Incomplete), // PkgStart
Err(ParseError::StateError(
AsgError::PkgRedeclare(name, name2)
)),
// RECOVERY: Ignore the attempted name
Ok(Incomplete), // BindIdent
Ok(Incomplete), // PkgEnd
],
sut.by_ref().collect::<Vec<_>>(),
@ -516,45 +518,37 @@ fn pkg_cannot_redeclare() {
let oi_pkg = asg
.lookup::<Pkg>(oi_root, namefix)
.expect("failed to locate package by its recovery name");
assert_eq!(S4.merge(S7).unwrap(), oi_pkg.resolve(&asg).span());
assert_eq!(S6.merge(S8).unwrap(), oi_pkg.resolve(&asg).span());
}
#[test]
fn pkg_cannot_rename() {
let name = SPair("/foo/bar".into(), S2);
let name2 = SPair("/bad/rename".into(), S3);
let pkg_name = SPair("/foo/bar".into(), S1);
let name = SPair("baz".into(), S2);
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, pkg_name),
BindIdent(name),
// Attempt to provide a name a second time.
BindIdent(name2),
// RECOVERY: Just ignore it.
PkgEnd(S4),
PkgEnd(S3),
];
let mut sut = Sut::parse(toks.into_iter());
let sut = Sut::parse(toks.into_iter());
assert_eq!(
#[rustfmt::skip]
vec![
Ok(Incomplete), // PkgStart
Ok(Incomplete), // BindIdent
Err(ParseError::StateError(AsgError::PkgRename(name, name2))),
// RECOVERY: Ignore the attempted rename
Ok(Incomplete), // PkgEnd
Ok(Incomplete), // PkgStart
Err(ParseError::StateError(
AsgError::InvalidBindContext(name),
)),
// RECOVERY
Ok(Incomplete), // PkgEnd
],
sut.by_ref().collect::<Vec<_>>(),
sut.collect::<Vec<_>>(),
);
let asg = sut.finalize().unwrap().into_context();
// The original name should have been kept.
let oi_root = asg.root(S1);
let oi_pkg = asg
.lookup::<Pkg>(oi_root, name)
.expect("failed to locate package by its original name");
assert_eq!(S1.merge(S4).unwrap(), oi_pkg.resolve(&asg).span());
}
#[test]
@ -564,8 +558,7 @@ fn pkg_import_canonicalized_against_current_pkg() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
BindIdent(pkg_name),
PkgStart(S1, pkg_name),
RefIdent(pkg_rel),
PkgEnd(S3),
];
@ -634,7 +627,7 @@ where
use std::iter;
Sut::parse(
iter::once(PkgStart(S1))
iter::once(PkgStart(S1, SPair("/pkg".into(), S1)))
.chain(toks.into_iter())
.chain(iter::once(PkgEnd(S1))),
)

View File

@ -42,7 +42,7 @@ fn tpl_defining_pkg() {
#[rustfmt::skip]
let toks = vec![
Air::PkgStart(S1),
Air::PkgStart(S1, SPair("/pkg".into(), S1)),
// This also tests tpl as a transition away from the package header.
Air::TplStart(S2),
Air::BindIdent(id_tpl),
@ -71,7 +71,7 @@ fn tpl_after_expr() {
#[rustfmt::skip]
let toks = vec![
Air::PkgStart(S1),
Air::PkgStart(S1, SPair("/pkg".into(), S1)),
// This expression is incidental to this test;
// it need only parse.
Air::ExprStart(ExprOp::Sum, S2),
@ -110,7 +110,7 @@ fn tpl_within_expr() {
#[rustfmt::skip]
let toks = vec![
Air::PkgStart(S1),
Air::PkgStart(S1, SPair("/pkg".into(), S1)),
Air::ExprStart(ExprOp::Sum, S2),
Air::BindIdent(id_expr),

View File

@ -72,21 +72,14 @@ pub enum AsgError {
/// respectively.
PkgRedeclare(SPair, SPair),
/// Attempted to rename a package from the first [`SPair`] to the
/// second.
///
/// "Rename" here means that the package already had a name and we were
/// provided another,
/// which is almost certainly a mistake.
PkgRename(SPair, SPair),
/// Attempted to open a package while defining another package.
///
/// Packages cannot be nested.
/// The first span represents the location of the second package open,
/// and the second span represents the location of the package already
/// being defined.
NestedPkgStart(Span, Span),
/// The [`SPair`]s are the respective package names.
NestedPkgStart((Span, SPair), (Span, SPair)),
/// Attempted to close a package when not in a package toplevel context.
InvalidPkgEndContext(Span),
@ -175,13 +168,12 @@ impl Display for AsgError {
"attempted to redeclare or redefine package {}",
TtQuote::wrap(orig),
),
PkgRename(from, to) => write!(
NestedPkgStart((_, child), (_, parent)) => write!(
f,
"attempted to rename package {} to {}",
TtQuote::wrap(from),
TtQuote::wrap(to)
"cannot define package {} while defining package {}",
TtQuote::wrap(child),
TtQuote::wrap(parent),
),
NestedPkgStart(_, _) => write!(f, "cannot nest packages"),
InvalidPkgEndContext(_) => {
write!(f, "invalid context for package close",)
}
@ -197,7 +189,7 @@ impl Display for AsgError {
UnbalancedExpr(_) => write!(f, "unbalanced expression"),
UnbalancedTpl(_) => write!(f, "unbalanced template definition"),
InvalidBindContext(_) => {
write!(f, "invalid expression identifier binding context")
write!(f, "invalid identifier binding context")
}
InvalidRefContext(ident) => {
write!(
@ -276,19 +268,15 @@ impl Diagnostic for AsgError {
redef.error("attempting to redeclare or redefine package here"),
],
PkgRename(from, to) => vec![
from.note("package was originally named here"),
to.error("attempted to rename package here"),
to.help("a package cannot have its name changed"),
],
NestedPkgStart(second, first) => vec![
NestedPkgStart((second, sname), (first, fname)) => vec![
first.note("this package is still being defined"),
second.error("attempted to open another package here"),
second.help(
"close the package to complete its definition before \
attempting to open another",
),
second.help(format!(
"end the package {} complete its definition before \
attempting to start the definition of {}",
TtQuote::wrap(fname),
TtQuote::wrap(sname),
)),
],
InvalidPkgEndContext(span) => vec![
@ -334,8 +322,8 @@ impl Diagnostic for AsgError {
vec![span.error("there is no open template to close here")]
}
InvalidBindContext(span) => vec![span
.error("there is no active object to bind this identifier to")],
InvalidBindContext(name) => vec![name
.error("an identifier binding is not valid in this context")],
InvalidRefContext(ident) => vec![ident.error(
"cannot reference the value of an expression from outside \

View File

@ -77,7 +77,7 @@ fn traverses_ontological_tree() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a),
@ -141,7 +141,7 @@ fn traverses_ontological_tree_tpl_with_sibling_at_increasing_depth() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
TplStart(S2),
BindIdent(id_tpl),
@ -201,7 +201,7 @@ fn traverses_ontological_tree_tpl_apply() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
// The template that will be applied.
TplStart(S2),
BindIdent(id_tpl),
@ -260,7 +260,7 @@ fn traverses_ontological_tree_tpl_within_template() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
TplStart(S2),
BindIdent(id_tpl_outer),

View File

@ -86,7 +86,7 @@ fn sorts_objects_given_single_root() {
let toks = vec![
// Packages are auto-rooted as part of the graph's ontology.
// There is only one for this test.
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
// Before this can be computed,
// its dependencies must be.
ExprStart(ExprOp::Sum, S2), // -.
@ -173,7 +173,7 @@ fn sorts_objects_given_single_root_more_complex() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a),
RefIdent(SPair(id_b.symbol(), S4)), // ---.
@ -233,7 +233,7 @@ fn omits_unreachable() {
// We will only use a portion of this graph.
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg".into(), S1)),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a),
RefIdent(SPair(id_b.symbol(), S4)), // ---.
@ -310,9 +310,7 @@ fn sorts_objects_given_multiple_roots() {
#[rustfmt::skip]
let toks = vec![
// First root
PkgStart(S1),
BindIdent(pkg_a_name),
PkgStart(S1, pkg_a_name),
ExprStart(ExprOp::Sum, S3),
BindIdent(id_a),
ExprEnd(S5),
@ -320,9 +318,7 @@ fn sorts_objects_given_multiple_roots() {
// Second root,
// independent of the first.
PkgStart(S7),
BindIdent(pkg_b_name),
PkgStart(S7, pkg_b_name),
ExprStart(ExprOp::Sum, S9),
BindIdent(id_b),
ExprEnd(S11),
@ -361,7 +357,7 @@ fn unsupported_cycles_with_recovery() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg-a".into(), S1)),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a), // <----. self-cycle
RefIdent(SPair(id_a.symbol(), S4)), // ____/ \
@ -434,7 +430,7 @@ fn supported_cycles() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgStart(S1, SPair("/pkg-a".into(), S1)),
// Two mutually recursive functions.
IdentDecl(id_a, kind.clone(), Default::default()), // <--.
IdentDep(id_a, id_b), // -. |

View File

@ -119,7 +119,10 @@ impl ParseState for NirToAir {
// responsibilities betwen XIR->NIR, NIR->AIR, and AIR->ASG.
match (self, tok) {
(Ready, Open(Package, span)) => {
Transition(Ready).ok(Air::PkgStart(span))
// TODO: Package name needs to be generated and provided to us;
// this is transitionary.
Transition(Ready)
.ok(Air::PkgStart(span, SPair("/TODO".into(), span)))
}
(Ready, Close(Package, span)) => {

View File

@ -29,7 +29,10 @@ fn package_to_pkg() {
let toks = vec![Open(Package, S1), Close(Package, S2)];
assert_eq!(
Ok(vec![O(Air::PkgStart(S1)), O(Air::PkgEnd(S2)),]),
Ok(vec![
O(Air::PkgStart(S1, SPair("/TODO".into(), S1))),
O(Air::PkgEnd(S2)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}
@ -388,7 +391,7 @@ fn text_as_arbitrary_doc() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::PkgStart(S1, SPair("/TODO".into(), S1))),
O(Air::DocText(text)),
O(Air::PkgEnd(S3)),
]),

View File

@ -115,17 +115,17 @@ impl ParseState for XmloToAir {
match (self, tok) {
(PackageExpected, PkgStart(span)) => {
Transition(PackageFound(span)).ok(Air::PkgStart(span))
Transition(PackageFound(span)).incomplete()
}
(PackageExpected, tok) => Transition(PackageExpected).dead(tok),
(PackageFound(_), PkgName(name)) => {
(PackageFound(span), PkgName(name)) => {
if ctx.is_first() {
ctx.prog_name = Some(name.symbol());
}
Transition(Package(name)).ok(Air::BindIdent(name))
Transition(Package(name)).ok(Air::PkgStart(span, name))
}
(st @ Package(..), PkgRootPath(relroot)) => {

View File

@ -50,8 +50,8 @@ fn data_from_package_event() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(name, S2))),
Incomplete, // PkgRootPath
O(Air::PkgEnd(S4)),
]),
@ -80,8 +80,8 @@ fn adds_elig_as_root() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(name, S2))),
O(Air::IdentRoot(SPair(elig_sym, S3))),
O(Air::PkgEnd(S4)), // Eoh
]),
@ -110,8 +110,8 @@ fn adds_sym_deps() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(name, S2))),
Incomplete, // SymDepStart
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to1, S4))),
O(Air::IdentDep(SPair(sym_from, S3), SPair(sym_to2, S5))),
@ -155,8 +155,8 @@ fn sym_decl_with_src_not_added_and_populates_found() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(name, S2))),
Incomplete, // SymDecl (@src)
Incomplete, // SymDecl (@src)
O(Air::PkgEnd(S5)),
@ -228,8 +228,8 @@ fn sym_decl_added_to_graph() {
// Note that each of these will have their package names cleared
// since this is considered to be the first package encountered.
assert_eq!(Some(Ok(O(Air::PkgStart(S1)))), sut.next()); // PkgStart
assert_eq!(Some(Ok(O(Air::BindIdent(SPair(name, S2))))), sut.next());
assert_eq!(Some(Ok(Incomplete)), sut.next()); // PkgStart
assert_eq!(Some(Ok(O(Air::PkgStart(S1, SPair(name, S2))))), sut.next()); // PkgName
assert_eq!(
Some(Ok(O(Air::IdentExternDecl(
SPair(sym_extern, S3),
@ -318,9 +318,8 @@ fn sym_decl_pkg_name_retained_if_not_first() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(pkg_name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(pkg_name, S2))),
O(Air::IdentDecl(
SPair(sym, S3),
IdentKind::Meta,
@ -366,9 +365,8 @@ fn sym_decl_pkg_name_set_if_empty_and_not_first() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(pkg_name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(pkg_name, S2))),
O(Air::IdentDecl(
SPair(sym, S3),
IdentKind::Meta,
@ -418,8 +416,8 @@ fn sets_fragment() {
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // PkgStart
O(Air::PkgStart(S1, SPair(name, S2))),
O(Air::IdentFragment(SPair(sym, S3), frag)),
O(Air::PkgEnd(S4)),
]),