tamer: obj::xmle::xir: Complete l:dep

The `l:dep` section of the `xmle` file, after formatting (since XIR writes
without newlines and indentation), is now identical to the existing xmle
writer.  I can now move on to the other sections.

Note that the attribute movement in this commit is simply to get the diff to
properly align.  Once the current xmle writer is removed, I'll organize them
a bit more sensibly.

`obj::xmle::xir` also needs documentation, now that it's shown to be viable.
main
Mike Gerwitz 2021-09-30 13:06:30 -04:00
parent acf55fad81
commit 7269e68b00
4 changed files with 319 additions and 54 deletions

View File

@ -343,6 +343,18 @@ impl AsRef<str> for Dim {
}
}
impl From<Dim> for u8 {
fn from(dim: Dim) -> Self {
dim.0
}
}
impl From<Dim> for SymbolId {
fn from(dim: Dim) -> Self {
st::decimal1(dim.0).as_sym()
}
}
impl std::fmt::Display for Dim {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
(self.0).fmt(fmt)

View File

@ -27,7 +27,7 @@
//! This IR should be converted into a higher-level IR quickly,
//! especially considering that it will be going away in the future.
use crate::sym::SymbolId;
use crate::sym::{GlobalSymbolResolve, SymbolId, st};
use std::convert::TryFrom;
use std::result::Result;
@ -271,15 +271,29 @@ pub enum SymDtype {
Empty,
}
impl SymDtype {
pub fn as_sym(&self) -> SymbolId {
match self {
SymDtype::Boolean => st::L_BOOLEAN,
SymDtype::Integer => st::L_INTEGER,
SymDtype::Float => st::L_FLOAT,
SymDtype::Empty => st::L_EMPTY,
}
.as_sym()
}
}
impl Into<SymbolId> for SymDtype {
fn into(self) -> SymbolId {
self.as_sym()
}
}
// TODO: Remove after xmle writer is removed
impl AsRef<str> for SymDtype {
/// Produce `xmlo`-compatible representation.
fn as_ref(&self) -> &str {
match self {
SymDtype::Boolean => &"boolean",
SymDtype::Integer => &"integer",
SymDtype::Float => &"float",
SymDtype::Empty => &"empty",
}
self.as_sym().lookup_str().as_str()
}
}
@ -307,12 +321,7 @@ impl TryFrom<&[u8]> for SymDtype {
impl std::fmt::Display for SymDtype {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Boolean => write!(fmt, "boolean"),
Self::Integer => write!(fmt, "integer"),
Self::Float => write!(fmt, "float"),
Self::Empty => write!(fmt, "(unknown)"),
}
write!(fmt, "{}", self.as_sym().lookup_str())
}
}

View File

@ -21,7 +21,9 @@
use crate::{
ir::{
asg::{IdentObject, IdentObjectData, Sections, SectionsIter},
asg::{
IdentKind, IdentObject, IdentObjectData, Sections, SectionsIter,
},
xir::{AttrValue, QName, Token},
},
ld::LSPAN,
@ -33,6 +35,8 @@ use std::iter::Chain;
qname_const! {
QN_DESC: :L_DESC,
QN_DIM: :L_DIM,
QN_DTYPE: :L_DTYPE,
QN_GENERATED: L_PREPROC:L_GENERATED,
QN_L_DEP: L_L:L_DEP,
QN_NAME: :L_NAME,
@ -91,11 +95,12 @@ struct DepListIter<'a, T: IdentObjectData> {
relroot: AttrValue,
}
type DepIter<'a, T> = DepListIter<'a, T>;
impl<'a, T: IdentObjectData> DepListIter<'a, T> {
fn refill_toks(&mut self) -> Option<Token> {
// Tokens will be popped, so push in reverse.
// They are arranged in the same order as the original writer so
// that we can diff the two;
// TODO: re-order sensibly once we're done.
self.iter.next().map(|obj| {
let ident = obj.as_ident().expect("unexpected non-identifier object");
@ -110,6 +115,10 @@ impl<'a, T: IdentObjectData> DepListIter<'a, T> {
}).and_then(|(sym, kind, src)| {
self.toks.push(Token::Close(None, LSPAN));
self.toks_push_attr(QN_DESC, src.desc);
self.toks_push_attr(QN_YIELDS, src.yields);
self.toks_push_attr(QN_PARENT, src.parent);
if let Some(pkg_name) = src.pkg_name {
self.toks.push(Token::AttrValue(AttrValue::Escaped(pkg_name), LSPAN));
self.toks.push(Token::AttrValueFragment(self.relroot, LSPAN));
@ -121,11 +130,8 @@ impl<'a, T: IdentObjectData> DepListIter<'a, T> {
false => None,
});
self.toks_push_attr(QN_DESC, src.desc);
self.toks_push_attr(QN_YIELDS, src.yields);
self.toks_push_attr(QN_PARENT, src.parent);
self.toks_push_attr(QN_NAME, Some(sym));
self.toks_push_attr(QN_TYPE, Some(kind.as_sym()));
self.toks_push_obj_attrs(kind);
Some(Token::Open(QN_P_SYM, LSPAN))
})
@ -139,6 +145,44 @@ impl<'a, T: IdentObjectData> DepListIter<'a, T> {
self.toks.push(Token::AttrName(name, LSPAN));
}
}
/// Generate object-specific attributes.
///
/// All objects will produce a [`QN_TYPE`] attribute.
fn toks_push_obj_attrs(&mut self, kind: &IdentKind) {
match kind {
IdentKind::Cgen(dim) | IdentKind::Class(dim) => {
self.toks_push_attr(QN_DIM, Some((*dim).into()));
}
IdentKind::Const(dim, dtype)
| IdentKind::Func(dim, dtype)
| IdentKind::Gen(dim, dtype)
| IdentKind::Lparam(dim, dtype)
| IdentKind::Param(dim, dtype) => {
self.toks_push_attr(QN_DTYPE, Some((*dtype).into()));
self.toks_push_attr(QN_DIM, Some((*dim).into()));
}
IdentKind::Rate(dtype) | IdentKind::Type(dtype) => {
self.toks_push_attr(QN_DTYPE, Some((*dtype).into()));
}
// No additional attributes (explicit match so that the
// exhaustiveness check will warn us if new ones are added)
IdentKind::Tpl
| IdentKind::MapHead
| IdentKind::Map
| IdentKind::MapTail
| IdentKind::RetMapHead
| IdentKind::RetMap
| IdentKind::RetMapTail
| IdentKind::Meta
| IdentKind::Worksheet => {}
}
self.toks_push_attr(QN_TYPE, Some(kind.as_sym()));
}
}
impl<'a, T: IdentObjectData> Iterator for DepListIter<'a, T> {
@ -152,7 +196,7 @@ impl<'a, T: IdentObjectData> Iterator for DepListIter<'a, T> {
fn deps<'a, T: IdentObjectData>(
sections: &'a Sections<T>,
relroot: SymbolId,
) -> DepIter<'a, T> {
) -> DepListIter<'a, T> {
DepListIter {
iter: sections.iter_all(),
toks: ArrayVec::new(),
@ -180,7 +224,7 @@ fn footer() -> FooterIter {
/// since this iterator will receive hundreds of thousands of calls for
/// large programs.
pub struct LowerIter<'a, T: IdentObjectData>(
Chain<Chain<HeaderIter, DepIter<'a, T>>, FooterIter>,
Chain<Chain<HeaderIter, DepListIter<'a, T>>, FooterIter>,
);
impl<'a, T: IdentObjectData> Iterator for LowerIter<'a, T> {
@ -209,6 +253,7 @@ pub fn lower_iter<'a, T: IdentObjectData>(
pub mod test {
use super::*;
use crate::convert::ExpectInto;
use crate::ir::legacyir::SymDtype;
use crate::ir::{
asg::{Dim, IdentKind, Source},
xir::{
@ -220,6 +265,16 @@ pub mod test {
type TestResult = Result<(), Box<dyn std::error::Error>>;
macro_rules! assert_attr{
($attrs:ident, $name:ident, $expected:expr, $($args:expr),*) => {
assert_eq!(
$attrs.find($name).and_then(|a| a.value_atom()),
$expected,
$($args),*
)
}
}
#[test]
fn test_produces_header() -> TestResult {
let empty = Sections::<IdentObject>::new();
@ -257,20 +312,7 @@ pub mod test {
let objs = [
IdentObject::Ident(
"a".intern(),
IdentKind::Meta,
Source {
desc: Some("test desc".intern()),
..Default::default()
},
),
IdentObject::Ident(
"b".intern(),
IdentKind::MapHead,
Default::default(),
),
IdentObject::Ident(
"c".intern(),
"cgentest".intern(),
IdentKind::Cgen(Dim::from_u8(1)),
Source {
yields: Some("yieldsValue".intern()),
@ -280,6 +322,94 @@ pub mod test {
..Default::default()
},
),
IdentObject::Ident(
"classtest".intern(),
IdentKind::Class(Dim::from_u8(2)),
Default::default(),
),
IdentObject::Ident(
"consttest".intern(),
IdentKind::Const(Dim::from_u8(0), SymDtype::Boolean),
Default::default(),
),
IdentObject::Ident(
"functest".intern(),
IdentKind::Func(Dim::from_u8(1), SymDtype::Integer),
Default::default(),
),
IdentObject::Ident(
"gentest".intern(),
IdentKind::Gen(Dim::from_u8(1), SymDtype::Boolean),
Default::default(),
),
IdentObject::Ident(
"lparamtest".intern(),
IdentKind::Gen(Dim::from_u8(2), SymDtype::Float),
Default::default(),
),
IdentObject::Ident(
"paramtest".intern(),
IdentKind::Gen(Dim::from_u8(0), SymDtype::Integer),
Default::default(),
),
IdentObject::Ident(
"ratetest".intern(),
IdentKind::Rate(SymDtype::Integer),
Default::default(),
),
IdentObject::Ident(
"tpltest".intern(),
IdentKind::Tpl,
Default::default(),
),
IdentObject::Ident(
"typetest".intern(),
IdentKind::Type(SymDtype::Integer),
Default::default(),
),
IdentObject::Ident(
"mapheadtest".intern(),
IdentKind::MapHead,
Default::default(),
),
IdentObject::Ident(
"maptest".intern(),
IdentKind::Map,
Default::default(),
),
IdentObject::Ident(
"maptailtest".intern(),
IdentKind::MapTail,
Default::default(),
),
IdentObject::Ident(
"retmapheadtest".intern(),
IdentKind::RetMapHead,
Default::default(),
),
IdentObject::Ident(
"retmaptest".intern(),
IdentKind::RetMap,
Default::default(),
),
IdentObject::Ident(
"retmaptailtest".intern(),
IdentKind::RetMapTail,
Default::default(),
),
IdentObject::Ident(
"metatest".intern(),
IdentKind::Meta,
Source {
desc: Some("test desc".intern()),
..Default::default()
},
),
IdentObject::Ident(
"worksheettest".intern(),
IdentKind::Worksheet,
Default::default(),
),
];
objs.iter().for_each(|x| sections.consts.push_body(x));
@ -316,9 +446,9 @@ pub mod test {
ele
});
p_syms.enumerate().for_each(|(i, sym)| {
p_syms.enumerate().for_each(|(i, ele)| {
let ident = objs[i].as_ident().unwrap();
let attrs = sym.attrs();
let attrs = ele.attrs();
assert_eq!(
attrs.find(QN_NAME).and_then(|a| a.value_atom()),
@ -346,19 +476,17 @@ pub mod test {
}
if let Some(Source { parent, .. }) = ident.src() {
assert_eq!(
attrs
.find("parent".unwrap_into())
.and_then(|a| a.value_atom()),
assert_attr!(
attrs,
QN_PARENT,
parent.map(|x| AttrValue::Escaped(x)),
);
}
if let Some(Source { yields, .. }) = ident.src() {
assert_eq!(
attrs
.find("yields".unwrap_into())
.and_then(|a| a.value_atom()),
assert_attr!(
attrs,
QN_YIELDS,
yields.map(|x| AttrValue::Escaped(x)),
);
}
@ -371,10 +499,7 @@ pub mod test {
// uninterned and therefore cannot be compared as a
// `SymbolId`. Once the reader takes care of creating the
// symbol, we'll have no such problem.
match attrs
.find("desc".unwrap_into())
.and_then(|a| a.value_atom())
{
match attrs.find(QN_DESC).and_then(|a| a.value_atom()) {
Some(AttrValue::Escaped(given)) => {
assert_eq!(desc.lookup_str(), given.lookup_str());
}
@ -400,6 +525,54 @@ pub mod test {
invalid => panic!("unexpected desc: {:?}", invalid),
}
}
// Object-specific attributes
match ident.kind().unwrap() {
IdentKind::Cgen(dim) | IdentKind::Class(dim) => {
assert_attr!(
attrs,
QN_DIM,
Some(AttrValue::Escaped((*dim).into())),
"invalid {:?} @dim",
ident.kind()
);
}
IdentKind::Const(dim, dtype)
| IdentKind::Func(dim, dtype)
| IdentKind::Gen(dim, dtype)
| IdentKind::Lparam(dim, dtype)
| IdentKind::Param(dim, dtype) => {
assert_attr!(
attrs,
QN_DIM,
Some(AttrValue::Escaped((*dim).into())),
"invalid {:?} @dim",
ident.kind()
);
assert_attr!(
attrs,
QN_DTYPE,
Some(AttrValue::Escaped((*dtype).into())),
"invalid {:?} @dtype",
ident.kind()
);
}
IdentKind::Rate(dtype) | IdentKind::Type(dtype) => {
assert_attr!(
attrs,
QN_DTYPE,
Some(AttrValue::Escaped((*dtype).into())),
"invalid {:?} @dim",
ident.kind()
);
}
// The others have no additional attributes
_ => {}
}
});
Ok(())

View File

@ -187,6 +187,9 @@ static_symbol_newtypes! {
/// common in many programming languages.
cid: CIdentStaticSymbolId<global::ProgSymSize>,
/// Base-10 (decimal) integer value as a string.
dec: DecStaticSymbolId<global::ProgSymSize>,
/// A symbol resembling a QName of the form `prefix:local`.
///
/// A symbol of this type does _not_ mean that the symbol is intended to
@ -241,18 +244,67 @@ static_symbol_newtypes! {
pub mod st {
use super::*;
// Convert `0 ≤ n ≤ 9` into a static symbol representing a single
// decimal digit.
//
// Panics
// ======
// This will panic if `n > 9`.
pub fn decimal1(n: u8) -> DecStaticSymbolId {
assert!(n < 10);
// The symbols are expected to be in a very specific position in the
// pool (n+1).
// This is verified by tests at the bottom of this file.
DecStaticSymbolId(unsafe {
<global::ProgSymSize as SymbolIndexSize>::NonZero::new_unchecked(
(n as global::ProgSymSize) + 1,
)
})
}
impl From<u8> for DecStaticSymbolId {
// Convert `0 ≤ n ≤ 9` into a static symbol representing a single
// decimal digit.
//
// See [`decimal1`].
fn from(n: u8) -> Self {
decimal1(n)
}
}
static_symbols! {
<global::ProgSymSize>;
// Decimal strings are expected to be at index (n+1).
// See `decimal1`.
N0: dec "0",
N1: dec "1",
N2: dec "2",
N3: dec "3",
N4: dec "4",
N5: dec "5",
N6: dec "6",
N7: dec "7",
N8: dec "8",
N9: dec "9",
L_BOOLEAN: cid "boolean",
L_CGEN: cid "cgen",
L_CLASS: cid "class",
L_CONST: cid "const",
L_DEP: cid "dep",
L_DESC: cid "desc",
L_DIM: cid "dim",
L_DTYPE: cid "dtype",
L_EMPTY: cid "empty",
L_FALSE: cid "false",
L_FLOAT: cid "float",
L_FUNC: cid "func",
L_GEN: cid "gen",
L_GENERATED: cid "generated",
L_INTEGER: cid "integer",
L_L: cid "l",
L_LPARAM: cid "lparam",
L_MAP: cid "map",
@ -281,8 +333,8 @@ pub mod st {
L_YIELDS: cid "yields",
URI_LV_RATER: uri "http://www.lovullo.com/rater",
URI_LV_PREPROC: uri "http://www.lovullo.com/preproc",
URI_LV_LINKER: uri "http://www.lovullo.com/linker",
URI_LV_PREPROC: uri "http://www.lovullo.com/rater/preproc",
URI_LV_LINKER: uri "http://www.lovullo.com/rater/linker",
// [Symbols will be added here as they are needed.]
@ -319,8 +371,8 @@ pub mod st16 {
#[cfg(test)]
mod test {
use super::{st, st16};
use crate::sym::{GlobalSymbolIntern, SymbolId};
use super::{st, st16, DecStaticSymbolId};
use crate::sym::{GlobalSymbolIntern, GlobalSymbolResolve, SymbolId};
#[test]
fn global_sanity_check_st() {
@ -356,4 +408,23 @@ mod test {
the prefill contains duplicate strings!"
);
}
#[test]
fn decimal1_0_to_9() {
for n in 0..=9 {
assert_eq!(st::decimal1(n).as_sym().lookup_str(), n.to_string());
// From<u8>
assert_eq!(
DecStaticSymbolId::from(n).as_sym().lookup_str(),
n.to_string()
);
}
}
#[test]
#[should_panic]
fn decimal1_gt_9_panics() {
st::decimal1(10);
}
}