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
parent
13bac8382f
commit
00492ace01
|
@ -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)) => {
|
||||
|
|
|
@ -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)),
|
||||
]),
|
||||
|
|
|
@ -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>
|
||||
{
|
||||
|
|
|
@ -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 = [
|
||||
|
|
Loading…
Reference in New Issue