tame/tamer/src/ir/legacyir.rs

372 lines
12 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Legacy IR
//
// Copyright (C) 2014-2021 Ryan Specialty Group, LLC.
//
// This file is part of TAME.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! Legacy IR faithful to the XSLT-based compiler.
//!
//! This represents the intermediate format (IR) used by the `xmlo` files
//! (see [`crate::obj::xmlo`]) originally produced by the XSLT-based
//! compiler.
//! It consists largely of metadata for object symbols.
//!
//! 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::{st, GlobalSymbolResolve, SymbolId};
use std::convert::TryFrom;
use std::result::Result;
/// Toplevel package attributes.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct PackageAttrs {
/// Unique package identifier.
///
/// The package name is derived from the filename relative to the
/// project root during compilation (see `relroot`).
pub name: Option<SymbolId>,
/// Relative path from package to project root.
pub relroot: Option<SymbolId>,
/// Whether this package is a program.
///
/// A _program_ is a package intended to be linked into a final
/// executable.
/// Programs cannot be imported by other packages.
/// Non-program packages cannot be linked.
pub program: bool,
/// Symbol representing package eligibility.
///
/// A package is _eligible_ for computation if certain invariants are
/// met.
/// This symbol is responsible for including each of those invariants as
/// dependencies so that they are included at link-time.
pub elig: Option<SymbolId>,
}
/// Symbol attributes.
///
/// This is a subset of all available attributes available on the
/// `preproc:sym` nodes;
/// more will be added as needed.
///
/// Not all symbols share the same set of attributes,
/// so this represents the union of all possible attribute sets.
///
/// Due to the number of possible attributes,
/// this is not an opaque type.
/// Consequently,
/// valid values should be enforced by the Rust's type system.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct SymAttrs {
/// Relative path to the package that defined this symbol.
///
/// Object files store relative paths so that they are somewhat
/// portable—the
/// entire project root should be able to be relocated.
pub src: Option<SymbolId>,
/// Symbol type.
///
/// The type describes the purpose of the symbol and determines both how
/// it is compiled and its location in the final executable.
pub ty: Option<SymType>,
/// Number of dimensions.
///
/// This determines the number of subscripts needed to access a scalar
/// value.
/// A value of `0` indicates a scalar;
/// a value of `1` indicates a vector;
/// a value of `2` indicates a matrix;
/// and a value of `n` indicates a multi-dimensional array of
/// depth `n`.
pub dim: Option<u8>,
/// Type of underlying data.
///
/// This is not a primitive,
/// and mostly represents whether or not floating point computations
/// will take place.
pub dtype: Option<SymDtype>,
/// Whether the symbol's location will be determined at link-time.
///
/// Externs allow symbols to be referenced without having yet been given
/// a concrete definition,
/// provided that an eventual concrete definition matches the
/// provided declaration.
/// The linker (see [`crate::ld`]) is responsible for ensuring that the
/// extern is satisfied and properly located in the final executable.
pub extern_: bool,
/// Unique package identifier.
///
/// The name of a package is automatically derived from the package path
/// relative to the project root.
/// _Note that this is problematic if one wants to compile the equivalent
/// of shared libraries._
pub pkg_name: Option<SymbolId>,
/// The identifier from which this one is derived.
///
/// For example,
/// [`SymType::Cgen`] has a parent [`SymType::Class`] and
/// [`SymType::Gen`] has a parent [`SymType::Rate`].
pub parent: Option<SymbolId>,
/// Whether this identifier was generated by the compiler.
///
/// A generated identifier is representative of an internal
/// implementation detail that should remain encapsulated from the
/// user and is subject to change over time.
///
/// Identifiers created by templates are not considered to be generated.
pub generated: bool,
/// Child identifier associated with this identifier.
///
/// For [`SymType::Class`],
/// this represents an associated [`SymType::Cgen`].
pub yields: Option<SymbolId>,
/// User-friendly identifier description.
///
/// This is used primarily by [`SymType::Class`] and [`SymType::Gen`].
pub desc: Option<SymbolId>,
/// Related identifiers.
///
/// These data represent a kluge created to add additional symbol
/// information in two different contexts:
///
/// - [`SymType::Map`] includes the name of the source field; and
/// - [`SymType::Func`] lists params in order (so that the compiler
/// knows application order).
///
/// This system currently only handles the former.
pub from: Option<SymbolId>,
/// Whether symbol can be overridden.
///
/// See also [`override`][SymAttrs::override_].
pub virtual_: bool,
/// Whether symbol is an override of a virtual symbol.
///
/// See also [`virtual`][SymAttrs::virtual_].
pub override_: bool,
}
/// Legacy symbol types.
///
/// This enum represents all symbol types represented in the `xmlo` files.
/// They are overly specialized and will be deprecated in favor of more
/// generalized dependent types in later IRs.
#[derive(Debug, PartialEq, Eq)]
pub enum SymType {
/// Classification generator (from `lv:classify/@yields`).
Cgen,
/// Classification (from `lv:classify/@as`).
Class,
/// Constant (from `lv:const/@name`).
Const,
/// Function (from `lv:function/@name`).
Func,
/// Generator (from `lv:rate/@generates`).
Gen,
/// Local function parameter (from `lv:function/lv:param/@name`) or let
/// binding (from `lv:let/lv:values/lv:value/@name`).
Lparam,
/// Global parameter (from `lv:param/@name`).
Param,
/// Scalar calculation result (from `lv:rate/@yields`).
Rate,
/// Template (from `lv:template/@name`).
Tpl,
/// Typedef (from `lv:type/@name`).
Type,
/// Input map head (meta symbol generated by compiler for each input map).
MapHead,
/// Input field→param mapping (from `lvm:map`, `lvm:pass`).
Map,
/// Input map tail (meta symbol generated by compiler for each input map).
MapTail,
/// Return map head (meta symbol generated by compiler for each return map).
RetMapHead,
/// Return param→field mapping (from `lvm:map`, `lvm:pass`).
RetMap,
/// Return map tail (meta symbol generated by compiler for each return map).
RetMapTail,
/// Arbitrary metadata (from `lv:meta`).
Meta,
/// Rating worksheet (generated by compiler for worksheet packages).
Worksheet,
}
impl TryFrom<&[u8]> for SymType {
type Error = String;
/// Determine symbol type from source `preproc:sym/@type`.
///
/// This raises source `xmlo` data into this IR.
/// See [`crate::obj::xmlo::XmloReader`].
fn try_from(value: &[u8]) -> Result<SymType, Self::Error> {
match value {
b"cgen" => Ok(SymType::Cgen),
b"class" => Ok(SymType::Class),
b"const" => Ok(SymType::Const),
b"func" => Ok(SymType::Func),
b"gen" => Ok(SymType::Gen),
b"lparam" => Ok(SymType::Lparam),
b"param" => Ok(SymType::Param),
b"rate" => Ok(SymType::Rate),
b"tpl" => Ok(SymType::Tpl),
b"type" => Ok(SymType::Type),
b"retmap:head" => Ok(SymType::RetMapHead),
b"retmap" => Ok(SymType::RetMap),
b"retmap:tail" => Ok(SymType::RetMapTail),
b"map:head" => Ok(SymType::MapHead),
b"map" => Ok(SymType::Map),
b"map:tail" => Ok(SymType::MapTail),
b"meta" => Ok(SymType::Meta),
b"worksheet" => Ok(SymType::Worksheet),
_ => Err(format!(
"unknown symbol type `{}`",
String::from_utf8(value.to_vec())
.unwrap_or("(invalid UTF8)".into())
)),
}
}
}
/// Underlying datatype.
///
/// This is the type of scalar data stored within the given symbol.
///
/// *NB:* This was _not enforced_ by the XSLT-based compiler.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SymDtype {
/// {⊥,} = {0,1} ⊂ ℤ
Boolean,
///
Integer,
///
Float,
/// ∅
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 {
self.as_sym().lookup_str()
}
}
impl TryFrom<&[u8]> for SymDtype {
type Error = String;
/// Determine data type from source `preproc:sym/@dtype`.
///
/// This raises source `xmlo` data into this IR.
/// See [`crate::obj::xmlo::XmloReader`].
fn try_from(value: &[u8]) -> Result<SymDtype, Self::Error> {
match value {
b"boolean" => Ok(SymDtype::Boolean),
b"integer" => Ok(SymDtype::Integer),
b"float" => Ok(SymDtype::Float),
b"empty" => Ok(SymDtype::Empty),
_ => Err(format!(
"unknown symbol dtype `{}`",
String::from_utf8(value.to_vec())
.unwrap_or("(invalid UTF8)".into())
)),
}
}
}
impl std::fmt::Display for SymDtype {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{}", self.as_sym().lookup_str())
}
}
#[cfg(test)]
mod test {
use super::*;
// We're not going to check every possible value here since we'd be
// maintaining the mapping in two places; we can leave that to
// integration tests.
#[test]
fn symtype_from_u8() {
assert_eq!(Ok(SymType::Cgen), SymType::try_from(b"cgen" as &[u8]));
}
#[test]
fn symtype_failure_from_unknown_u8() {
match SymType::try_from(b"unknown" as &[u8]) {
Err(s) => assert!(s.contains("unknown")),
bad => panic!("expected error: {:?}", bad),
}
}
#[test]
fn symdtype_from_u8() {
assert_eq!(
Ok(SymDtype::Integer),
SymDtype::try_from(b"integer" as &[u8])
);
}
#[test]
fn symdtype_failure_from_unknown_u8() {
match SymDtype::try_from(b"unknownd" as &[u8]) {
Err(s) => assert!(s.contains("unknownd")),
bad => panic!("expected error: {:?}", bad),
}
}
#[test]
fn symdtype_as_str() {
let boolean: &str = SymDtype::Boolean.as_ref();
assert_eq!("boolean", boolean);
}
}