Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 150 additions & 23 deletions tools/witx/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,71 @@ impl Id {

#[derive(Debug, Clone)]
pub struct Document {
pub definitions: Vec<Definition>,
pub entries: HashMap<Id, Entry>,
definitions: Vec<Definition>,
entries: HashMap<Id, Entry>,
}

impl Document {
pub(crate) fn new(definitions: Vec<Definition>, entries: HashMap<Id, Entry>) -> Self {
Document {
definitions,
entries,
}
}
pub fn datatype(&self, name: &Id) -> Option<Rc<Datatype>> {
self.entries.get(name).and_then(|e| match e {
Entry::Datatype(d) => Some(d.upgrade().expect("always possible to upgrade entry")),
_ => None,
})
}
pub fn datatypes<'a>(&'a self) -> impl Iterator<Item = Rc<Datatype>> + 'a {
self.definitions.iter().filter_map(|d| match d {
Definition::Datatype(d) => Some(d.clone()),
_ => None,
})
}
pub fn module(&self, name: &Id) -> Option<Rc<Module>> {
self.entries.get(&name).and_then(|e| match e {
Entry::Module(d) => Some(d.upgrade().expect("always possible to upgrade entry")),
_ => None,
})
}
pub fn modules<'a>(&'a self) -> impl Iterator<Item = Rc<Module>> + 'a {
self.definitions.iter().filter_map(|d| match d {
Definition::Module(d) => Some(d.clone()),
_ => None,
})
}
}

impl PartialEq for Document {
fn eq(&self, rhs: &Document) -> bool {
if self.definitions.len() != rhs.definitions.len() {
return false;
}
for d in self.datatypes() {
if let Some(d_rhs) = rhs.datatype(&d.name) {
if d != d_rhs {
return false;
}
} else {
return false;
}
}
for m in self.modules() {
if let Some(m_rhs) = rhs.module(&m.name) {
if m != m_rhs {
return false;
}
} else {
return false;
}
}
true
}
}
impl Eq for Document {}

#[derive(Debug, Clone)]
pub enum Definition {
Datatype(Rc<Datatype>),
Expand All @@ -43,7 +104,7 @@ impl Entry {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DatatypeIdent {
Builtin(BuiltinType),
Array(Box<DatatypeIdent>),
Expand All @@ -52,13 +113,13 @@ pub enum DatatypeIdent {
Ident(Rc<Datatype>),
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Datatype {
pub name: Id,
pub variant: DatatypeVariant,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DatatypeVariant {
Alias(AliasDatatype),
Enum(EnumDatatype),
Expand All @@ -67,53 +128,53 @@ pub enum DatatypeVariant {
Union(UnionDatatype),
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AliasDatatype {
pub name: Id,
pub to: DatatypeIdent,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IntRepr {
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnumDatatype {
pub name: Id,
pub repr: IntRepr,
pub variants: Vec<Id>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FlagsDatatype {
pub name: Id,
pub repr: IntRepr,
pub flags: Vec<Id>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructDatatype {
pub name: Id,
pub members: Vec<StructMember>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructMember {
pub name: Id,
pub type_: DatatypeIdent,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnionDatatype {
pub name: Id,
pub variants: Vec<UnionVariant>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnionVariant {
pub name: Id,
pub type_: DatatypeIdent,
Expand All @@ -122,10 +183,76 @@ pub struct UnionVariant {
#[derive(Debug, Clone)]
pub struct Module {
pub name: Id,
pub definitions: Vec<ModuleDefinition>,
pub entries: HashMap<Id, ModuleEntry>,
definitions: Vec<ModuleDefinition>,
entries: HashMap<Id, ModuleEntry>,
}

impl Module {
pub(crate) fn new(
name: Id,
definitions: Vec<ModuleDefinition>,
entries: HashMap<Id, ModuleEntry>,
) -> Self {
Module {
name,
definitions,
entries,
}
}
pub fn import(&self, name: &Id) -> Option<Rc<ModuleImport>> {
self.entries.get(name).and_then(|e| match e {
ModuleEntry::Import(d) => Some(d.upgrade().expect("always possible to upgrade entry")),
_ => None,
})
}
pub fn imports<'a>(&'a self) -> impl Iterator<Item = Rc<ModuleImport>> + 'a {
self.definitions.iter().filter_map(|d| match d {
ModuleDefinition::Import(d) => Some(d.clone()),
_ => None,
})
}
pub fn func(&self, name: &Id) -> Option<Rc<InterfaceFunc>> {
self.entries.get(name).and_then(|e| match e {
ModuleEntry::Func(d) => Some(d.upgrade().expect("always possible to upgrade entry")),
_ => None,
})
}
pub fn funcs<'a>(&'a self) -> impl Iterator<Item = Rc<InterfaceFunc>> + 'a {
self.definitions.iter().filter_map(|d| match d {
ModuleDefinition::Func(d) => Some(d.clone()),
_ => None,
})
}
}

impl PartialEq for Module {
fn eq(&self, rhs: &Module) -> bool {
if self.definitions.len() != rhs.definitions.len() {
return false;
}
for i in self.imports() {
if let Some(i_rhs) = rhs.import(&i.name) {
if i != i_rhs {
return false;
}
} else {
return false;
}
}
for f in self.funcs() {
if let Some(f_rhs) = rhs.func(&f.name) {
if f != f_rhs {
return false;
}
} else {
return false;
}
}
true
}
}
impl Eq for Module {}

#[derive(Debug, Clone)]
pub enum ModuleDefinition {
Import(Rc<ModuleImport>),
Expand All @@ -138,25 +265,25 @@ pub enum ModuleEntry {
Func(Weak<InterfaceFunc>),
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModuleImport {
pub name: Id,
pub variant: ModuleImportVariant,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModuleImportVariant {
Memory,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InterfaceFunc {
pub name: Id,
pub params: Vec<InterfaceFuncParam>,
pub results: Vec<InterfaceFuncParam>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InterfaceFuncParam {
pub name: Id,
pub type_: DatatypeIdent,
Expand Down
91 changes: 91 additions & 0 deletions tools/witx/src/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::WitxError;
use std::collections::HashMap;
use std::fs::{read_to_string, File};
use std::io::{BufRead, BufReader, Error, ErrorKind};
use std::path::{Path, PathBuf};

pub trait WitxIo {
/// Read the entire file into a String. Used to resolve `use` declarations.
fn fgets(&self, path: &Path) -> Result<String, WitxError>;
/// Read a line of a file into a String. Used for error reporting.
fn fget_line(&self, path: &Path, line_num: usize) -> Result<String, WitxError>;
/// Return the canonical (non-symlinked) path of a file. Used to resolve `use` declarations.
fn canonicalize(&self, path: &Path) -> Result<PathBuf, WitxError>;
}

pub struct Filesystem;

impl WitxIo for Filesystem {
fn fgets(&self, path: &Path) -> Result<String, WitxError> {
read_to_string(path).map_err(|e| WitxError::Io(path.to_path_buf(), e))
}
fn fget_line(&self, path: &Path, line_num: usize) -> Result<String, WitxError> {
let f = File::open(path).map_err(|e| WitxError::Io(path.into(), e))?;
let buf = BufReader::new(f);
let l = buf
.lines()
.skip(line_num - 1)
.next()
.ok_or_else(|| {
WitxError::Io(path.into(), Error::new(ErrorKind::Other, "Line not found"))
})?
.map_err(|e| WitxError::Io(path.into(), e))?;

Ok(l)
}
fn canonicalize(&self, path: &Path) -> Result<PathBuf, WitxError> {
path.canonicalize()
.map_err(|e| WitxError::Io(path.to_path_buf(), e))
}
}

pub struct MockFs {
map: HashMap<String, String>,
}

impl MockFs {
pub fn new(strings: &[(&str, &str)]) -> Self {
MockFs {
map: strings
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
}
}
}

impl WitxIo for MockFs {
fn fgets(&self, path: &Path) -> Result<String, WitxError> {
if let Some(entry) = self.map.get(path.to_str().unwrap()) {
Ok(entry.to_string())
} else {
Err(WitxError::Io(
path.to_path_buf(),
Error::new(ErrorKind::Other, "mock fs: file not found"),
))
}
}
fn fget_line(&self, path: &Path, line: usize) -> Result<String, WitxError> {
if let Some(entry) = self.map.get(path.to_str().unwrap()) {
entry
.lines()
.skip(line - 1)
.next()
.map(|s| s.to_string())
.ok_or_else(|| {
WitxError::Io(
path.to_path_buf(),
Error::new(ErrorKind::Other, "mock fs: file not found"),
)
})
} else {
Err(WitxError::Io(
path.to_path_buf(),
Error::new(ErrorKind::Other, "mock fs: file not found"),
))
}
}
fn canonicalize(&self, path: &Path) -> Result<PathBuf, WitxError> {
Ok(PathBuf::from(path))
}
}
Loading