tamer: obj::xmlo: Bind packages to canonical name

NOTE: This fixes the aforementioned commit that caused the linker to
temporarily fail (670c5d3a5d at time of
writing).  This does introduce an extra forward slash into
`l:dep/preproc:sym/@src`, but that does not appear to cause any
problems.  That will eventually go away, so I'm not going to bother with it
any further.

As the `xmlo` file is lowered into AIR, the name will be prefixed with a
leading slash (if necessary, which it is atm) and will emit an
`Air::BindIdent`.

This means that packages will be properly indexed by their canonical name on
load, which will be important when we share this with tamec.

DEV-13162
main
Mike Gerwitz 2023-05-05 10:22:12 -04:00
parent 13bac8382f
commit 00492ace01
4 changed files with 71 additions and 19 deletions

View File

@ -125,7 +125,7 @@ impl ParseState for XmloToAir {
ctx.prog_name = Some(name.symbol());
}
Transition(Package(name)).incomplete()
Transition(Package(name)).ok(Air::BindIdent(name))
}
(st @ Package(..), PkgRootPath(relroot)) => {

View File

@ -51,7 +51,7 @@ fn data_from_package_event() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // PkgRootPath
O(Air::PkgEnd(S4)),
]),
@ -81,7 +81,7 @@ fn adds_elig_as_root() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(SPair(name, S2))),
O(Air::IdentRoot(SPair(elig_sym, S3))),
O(Air::PkgEnd(S4)), // Eoh
]),
@ -91,6 +91,7 @@ fn adds_elig_as_root() {
#[test]
fn adds_sym_deps() {
let name = "name".into();
let sym_from = "from".into();
let sym_to1 = "to1".into();
let sym_to2 = "to2".into();
@ -98,7 +99,7 @@ fn adds_sym_deps() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgName(SPair("name".into(), S2)),
PkgName(SPair(name, S2)),
SymDepStart(SPair(sym_from, S3)),
Symbol(SPair(sym_to1, S4)),
@ -110,7 +111,7 @@ fn adds_sym_deps() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(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))),
@ -122,6 +123,7 @@ fn adds_sym_deps() {
#[test]
fn sym_decl_with_src_not_added_and_populates_found() {
let name = "name".into();
let sym = "sym".into();
let src_a = "src_a".into();
let src_b = "src_b".into();
@ -129,7 +131,7 @@ fn sym_decl_with_src_not_added_and_populates_found() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgName(SPair("name".into(), S2)),
PkgName(SPair(name, S2)),
SymDecl(
SPair(sym, S3),
@ -154,7 +156,7 @@ fn sym_decl_with_src_not_added_and_populates_found() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(SPair(name, S2))),
Incomplete, // SymDecl (@src)
Incomplete, // SymDecl (@src)
O(Air::PkgEnd(S5)),
@ -174,6 +176,7 @@ fn sym_decl_with_src_not_added_and_populates_found() {
#[test]
fn sym_decl_added_to_graph() {
let name = "name".into();
let sym_extern = "sym_extern".into();
let sym_non_extern = "sym_non_extern".into();
let sym_map = "sym_map".into();
@ -183,7 +186,7 @@ fn sym_decl_added_to_graph() {
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgName(SPair("name".into(), S2)),
PkgName(SPair(name, S2)),
SymDecl(
SPair(sym_extern, S3),
@ -226,7 +229,7 @@ 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(Incomplete)), sut.next()); // PkgName
assert_eq!(Some(Ok(O(Air::BindIdent(SPair(name, S2))))), sut.next());
assert_eq!(
Some(Ok(O(Air::IdentExternDecl(
SPair(sym_extern, S3),
@ -316,7 +319,7 @@ fn sym_decl_pkg_name_retained_if_not_first() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(SPair(pkg_name, S2))),
O(Air::IdentDecl(
SPair(sym, S3),
@ -364,7 +367,7 @@ fn sym_decl_pkg_name_set_if_empty_and_not_first() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(SPair(pkg_name, S2))),
O(Air::IdentDecl(
SPair(sym, S3),
@ -400,13 +403,14 @@ fn ident_kind_conversion_error_propagates() {
#[test]
fn sets_fragment() {
let name = "name".into();
let sym = "sym".into();
let frag = FragmentText::from("foo");
#[rustfmt::skip]
let toks = vec![
PkgStart(S1),
PkgName(SPair("name".into(), S2)),
PkgName(SPair(name, S2)),
Fragment(SPair(sym, S3), frag.clone()),
Eoh(S4),
];
@ -415,7 +419,7 @@ fn sets_fragment() {
#[rustfmt::skip]
Ok(vec![
O(Air::PkgStart(S1)),
Incomplete, // PkgName
O(Air::BindIdent(SPair(name, S2))),
O(Air::IdentFragment(SPair(sym, S3), frag)),
O(Air::PkgEnd(S4)),
]),

View File

@ -29,7 +29,7 @@ use crate::{
ParseState, Token, Transition, TransitionResult, Transitionable,
},
span::Span,
sym::{st::raw, SymbolId},
sym::{st::raw, GlobalSymbolIntern, GlobalSymbolResolve, SymbolId},
xir::{
attr::{Attr, AttrSpan},
flat::{Text, XirfToken as Xirf},
@ -227,9 +227,10 @@ impl<SS: XmloState, SD: XmloState, SF: XmloState> ParseState
// which can result in confusing output depending on the context;
// we ought to retain _both_ token- and value-spans.
Transition(Package(span)).ok(match name {
QN_NAME => {
XmloToken::PkgName(SPair(value, aspan.value_span()))
}
QN_NAME => XmloToken::PkgName(SPair(
canonical_slash(value),
aspan.value_span(),
)),
QN_UUROOTPATH => {
XmloToken::PkgRootPath(SPair(value, aspan.value_span()))
}
@ -321,6 +322,27 @@ impl<SS: XmloState, SD: XmloState, SF: XmloState> ParseState
}
}
/// Introduce a leading `/` to `name` if missing.
///
/// A new [`SymbolId`] will be allocated if the leading slash is missing
/// from `name.
///
/// The XSLT-based compiler at the time of writing produced canonical names
/// _without_ a leading slash.
/// This convention was not changed until TAMER,
/// so that canonical paths could be used as namespecs for import in an
/// unambiguous way.
/// We want to support both,
/// so that TAMER-compiled object files will also work.
fn canonical_slash(name: SymbolId) -> SymbolId {
let s = name.lookup_str();
match s.starts_with('/') {
true => name,
false => format!("/{s}").intern(),
}
}
impl<SS: XmloState, SD: XmloState, SF: XmloState> Display
for XmloReader<SS, SD, SF>
{

View File

@ -54,7 +54,7 @@ fn fails_on_invalid_root() {
}
fn common_parses_package_attrs(package: QName) {
let name = "pkgroot".into();
let name = "/pkgroot".into();
let relroot = "../../".into();
let elig = "elig-class-yields".into();
@ -101,11 +101,37 @@ fn parses_package_attrs_with_ns_prefix() {
common_parses_package_attrs(("lv", "package").unwrap_into());
}
// For compatibility with XSLT-based compiler.
#[test]
fn adds_missing_leading_slash_to_canonical_name() {
let name = "needs/leading".into();
#[rustfmt::skip]
let toks = [
open(QN_PACKAGE, S1, Depth(0)),
attr("name", name, (S2, S3)),
close(Some(QN_PACKAGE), S2, Depth(0)),
]
.into_iter();
let sut = Sut::parse(toks);
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(PkgStart(S1)),
O(PkgName(SPair("/needs/leading".into(), S3))),
Incomplete,
]),
sut.collect(),
);
}
// Maintains BC with existing system,
// but this ought to reject in the future.
#[test]
fn ignores_unknown_package_attr() {
let name = "pkgroot".into();
let name = "/pkgroot".into();
#[rustfmt::skip]
let toks = [