Changeset - 9774ef9fe888
[Not reviewed]
1 7 1
MH - 4 years ago 2021-03-28 16:02:36
henger@cwi.nl
small cleanup pass, added (failing) monomorph test
9 files changed with 260 insertions and 968 deletions:
0 comments (0 inline, 0 general)
src/protocol/mod.rs
Show inline comments
 
@@ -51,21 +51,22 @@ impl std::fmt::Debug for ProtocolDescription {
 
    }
 
}
 
impl ProtocolDescription {
 
    // TODO: Allow for multi-file compilation
 
    pub fn parse(buffer: &[u8]) -> Result<Self, String> {
 
        // TODO: @fixme, keep code compilable, but needs support for multiple
 
        //  input files.
 
        let source = InputSource::from_buffer(buffer).unwrap();
 
        let mut parser = Parser::new();
 
        parser.feed(source).expect("failed to parse source");
 
        match parser.parse() {
 
            Ok(root) => {
 
                return Ok(ProtocolDescription { heap: parser.heap, source: parser.modules[0].source.clone(), root });
 
            }
 
            Err(err) => {
 
                println!("ERROR:\n{}", err);
 
                Err(format!("{}", err))
 
            }
 
        parser.feed(source).expect("failed to lex source");
 
        
 
        if let Err(err) = parser.parse() {
 
            println!("ERROR:\n{}", err);
 
            return Err(format!("{}", err))
 
        }
 

	
 
        debug_assert_eq!(parser.modules.len(), 1, "only supporting one module here for now");
 
        let root = parser.modules[0].root_id;
 
        return Ok(ProtocolDescription { heap: parser.heap, source: parser.modules[0].source.clone(), root });
 
    }
 
    pub(crate) fn component_polarities(
 
        &self,
src/protocol/parser/mod.rs
Show inline comments
 
mod depth_visitor;
 
mod symbol_table;
 
// mod type_table_old;
 
mod type_table;
 
pub(crate) mod symbol_table;
 
pub(crate) mod type_table;
 
mod type_resolver;
 
mod visitor;
 
mod visitor_linker;
 
@@ -26,13 +25,15 @@ pub(crate) struct LexedModule {
 
    pub(crate) source: InputSource,
 
    module_name: Vec<u8>,
 
    version: Option<u64>,
 
    root_id: RootId,
 
    pub(crate) root_id: RootId,
 
}
 

	
 
pub struct Parser {
 
    pub(crate) heap: Heap,
 
    pub(crate) modules: Vec<LexedModule>,
 
    pub(crate) module_lookup: HashMap<Vec<u8>, usize>, // from (optional) module name to `modules` idx
 
    pub(crate) symbol_table: SymbolTable,
 
    pub(crate) type_table: TypeTable,
 
}
 

	
 
impl Parser {
 
@@ -40,17 +41,12 @@ impl Parser {
 
        Parser{
 
            heap: Heap::new(),
 
            modules: Vec::new(),
 
            module_lookup: HashMap::new()
 
            module_lookup: HashMap::new(),
 
            symbol_table: SymbolTable::new(),
 
            type_table: TypeTable::new(),
 
        }
 
    }
 

	
 
    // TODO: @fix, temporary implementation to keep code compilable
 
    pub fn new_with_source(source: InputSource) -> Result<Self, ParseError2> {
 
        let mut parser = Parser::new();
 
        parser.feed(source)?;
 
        Ok(parser)
 
    }
 

	
 
    pub fn feed(&mut self, mut source: InputSource) -> Result<RootId, ParseError2> {
 
        // Lex the input source
 
        let mut lex = Lexer::new(&mut source);
 
@@ -127,23 +123,16 @@ impl Parser {
 
        Ok(pd)
 
    }
 

	
 
    pub fn compile(&mut self) {
 
        // Build module lookup
 
    }
 

	
 
    fn resolve_symbols_and_types(&mut self) -> Result<(SymbolTable, TypeTable), ParseError2> {
 
    fn resolve_symbols_and_types(&mut self) -> Result<(), ParseError2> {
 
        // Construct the symbol table to resolve any imports and/or definitions,
 
        // then use the symbol table to actually annotate all of the imports.
 
        // If the type table is constructed correctly then all imports MUST be
 
        // resolvable.
 
        // TODO: Update once namespaced identifiers are implemented
 
        let symbol_table = SymbolTable::new(&self.heap, &self.modules)?;
 
        self.symbol_table.build(&self.heap, &self.modules)?;
 

	
 
        // Not pretty, but we need to work around rust's borrowing rules, it is
 
        // totally safe to mutate the contents of an AST element that we are
 
        // not borrowing anywhere else.
 
        // TODO: Maybe directly access heap's members to allow borrowing from
 
        //  mutliple members of Heap? Not pretty though...
 
        let mut module_index = 0;
 
        let mut import_index = 0;
 
        loop {
 
@@ -166,19 +155,19 @@ impl Parser {
 
            match import {
 
                Import::Module(import) => {
 
                    debug_assert!(import.module_id.is_none(), "module import already resolved");
 
                    let target_module_id = symbol_table.resolve_module(&import.module_name)
 
                    let target_module_id = self.symbol_table.resolve_module(&import.module_name)
 
                        .expect("module import is resolved by symbol table");
 
                    import.module_id = Some(target_module_id)
 
                },
 
                Import::Symbols(import) => {
 
                    debug_assert!(import.module_id.is_none(), "module of symbol import already resolved");
 
                    let target_module_id = symbol_table.resolve_module(&import.module_name)
 
                    let target_module_id = self.symbol_table.resolve_module(&import.module_name)
 
                        .expect("symbol import's module is resolved by symbol table");
 
                    import.module_id = Some(target_module_id);
 

	
 
                    for symbol in &mut import.symbols {
 
                        debug_assert!(symbol.definition_id.is_none(), "symbol import already resolved");
 
                        let (_, target_definition_id) = symbol_table.resolve_symbol(module_root_id, &symbol.alias)
 
                        let (_, target_definition_id) = self.symbol_table.resolve_symbol(module_root_id, &symbol.alias)
 
                            .expect("symbol import is resolved by symbol table")
 
                            .as_definition()
 
                            .expect("symbol import does not resolve to namespace symbol");
 
@@ -190,46 +179,64 @@ impl Parser {
 

	
 
        // All imports in the AST are now annotated. We now use the symbol table
 
        // to construct the type table.
 
        let mut type_ctx = TypeCtx::new(&symbol_table, &mut self.heap, &self.modules);
 
        let type_table = TypeTable::new(&mut type_ctx)?;
 
        let mut type_ctx = TypeCtx::new(&self.symbol_table, &mut self.heap, &self.modules);
 
        self.type_table.build_base_types(&mut type_ctx)?;
 

	
 
        Ok((symbol_table, type_table))
 
        Ok(())
 
    }
 

	
 
    // TODO: @fix, temporary impl to keep code compilable
 
    pub fn parse(&mut self) -> Result<RootId, ParseError2> {
 
        assert_eq!(self.modules.len(), 1, "Fix meeeee");
 
        let root_id = self.modules[0].root_id;
 

	
 
        let (mut symbol_table, mut type_table) = self.resolve_symbols_and_types()?;
 
    pub fn parse(&mut self) -> Result<(), ParseError2> {
 
        self.resolve_symbols_and_types()?;
 

	
 
        // TODO: @cleanup
 
        let mut ctx = visitor::Ctx{
 
            heap: &mut self.heap,
 
            module: &self.modules[0],
 
            symbols: &mut symbol_table,
 
            types: &mut type_table,
 
        };
 
        // Validate and link all modules
 
        let mut visit = ValidityAndLinkerVisitor::new();
 
        visit.visit_module(&mut ctx)?;
 
        let mut type_visit = TypeResolvingVisitor::new();
 
        for module in &self.modules {
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                module,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
            };
 
            visit.visit_module(&mut ctx)?;
 
        }
 

	
 
        // Perform typechecking on all modules
 
        let mut visit = TypeResolvingVisitor::new();
 
        let mut queue = ResolveQueue::new();
 
        TypeResolvingVisitor::queue_module_definitions(&ctx, &mut queue);
 
        for module in &self.modules {
 
            let ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                module,
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
            };
 
            TypeResolvingVisitor::queue_module_definitions(&ctx, &mut queue);   
 
        };
 
        while !queue.is_empty() {
 
            let top = queue.pop().unwrap();
 
            println!("Resolving root={}, def={}, mono={:?}", top.root_id.index, top.definition_id.index, top.monomorph_types);
 
            type_visit.handle_module_definition(&mut ctx, &mut queue, top)?;
 
            let mut ctx = visitor::Ctx{
 
                heap: &mut self.heap,
 
                module: &self.modules[top.root_id.index as usize],
 
                symbols: &mut self.symbol_table,
 
                types: &mut self.type_table,
 
            };
 
            visit.handle_module_definition(&mut ctx, &mut queue, top)?;
 
        }
 

	
 
        if let Err((position, message)) = Self::parse_inner(&mut self.heap, root_id) {
 
            return Err(ParseError2::new_error(&self.modules[0].source, position, &message))
 
        // Perform remaining steps
 
        // TODO: Phase out at some point
 
        for module in &self.modules {
 
            let root_id = module.root_id;
 
            if let Err((position, message)) = Self::parse_inner(&mut self.heap, root_id) {
 
                return Err(ParseError2::new_error(&self.modules[0].source, position, &message))
 
            }
 
        }
 

	
 
        // let mut writer = ASTWriter::new();
 
        // let mut file = std::fs::File::create(std::path::Path::new("ast.txt")).unwrap();
 
        // writer.write_ast(&mut file, &self.heap);
 

	
 
        Ok(root_id)
 
        Ok(())
 
    }
 

	
 
    pub fn parse_inner(h: &mut Heap, pd: RootId) -> VisitorResult {
 
@@ -252,143 +259,4 @@ impl Parser {
 

	
 
        Ok(())
 
    }
 
}
 

	
 
#[cfg(test)]
 
mod tests {
 
    use std::fs::File;
 
    use std::io::Read;
 
    use std::path::Path;
 

	
 
    use super::*;
 

	
 
    // #[test]
 
    fn positive_tests() {
 
        for resource in TestFileIter::new("testdata/parser/positive", "pdl") {
 
            let resource = resource.expect("read testdata filepath");
 
            // println!(" * running: {}", &resource);
 
            let path = Path::new(&resource);
 
            let source = InputSource::from_file(&path).unwrap();
 
            // println!("DEBUG -- input:\n{}", String::from_utf8_lossy(&source.input));
 
            let mut parser = Parser::new_with_source(source).expect("parse source");
 
            match parser.parse() {
 
                Ok(_) => {}
 
                Err(err) => {
 
                    println!(" > file: {}", &resource);
 
                    println!("{}", err);
 
                    assert!(false);
 
                }
 
            }
 
        }
 
    }
 

	
 
    // #[test]
 
    fn negative_tests() {
 
        for resource in TestFileIter::new("testdata/parser/negative", "pdl") {
 
            let resource = resource.expect("read testdata filepath");
 
            let path = Path::new(&resource);
 
            let expect = path.with_extension("txt");
 
            let mut source = InputSource::from_file(&path).unwrap();
 
            let mut parser = Parser::new_with_source(source).expect("construct parser");
 
            match parser.parse() {
 
                Ok(pd) => {
 
                    println!("Expected parse error:");
 

	
 
                    let mut cev: Vec<u8> = Vec::new();
 
                    let mut f = File::open(expect).unwrap();
 
                    f.read_to_end(&mut cev).unwrap();
 
                    println!("{}", String::from_utf8_lossy(&cev));
 
                    assert!(false);
 
                }
 
                Err(err) => {
 
                    let expected = format!("{}", err);
 
                    println!("{}", &expected);
 

	
 
                    let mut cev: Vec<u8> = Vec::new();
 
                    let mut f = File::open(expect).unwrap();
 
                    f.read_to_end(&mut cev).unwrap();
 
                    println!("{}", String::from_utf8_lossy(&cev));
 

	
 
                    assert_eq!(expected.as_bytes(), cev);
 
                }
 
            }
 
        }
 
    }
 

	
 
    // #[test]
 
    fn counterexample_tests() {
 
        for resource in TestFileIter::new("testdata/parser/counterexamples", "pdl") {
 
            let resource = resource.expect("read testdata filepath");
 
            let path = Path::new(&resource);
 
            let source = InputSource::from_file(&path).unwrap();
 
            let mut parser = Parser::new_with_source(source).expect("construct parser");
 

	
 
            fn print_header(s: &str) {
 
                println!("{}", "=".repeat(80));
 
                println!(" > File: {}", s);
 
                println!("{}", "=".repeat(80));
 
            }
 

	
 
            match parser.parse() {
 
                Ok(parsed) => {
 
                    print_header(&resource);
 
                    println!("\n  SUCCESS\n\n --- source:\n{}", String::from_utf8_lossy(&parser.modules[0].source.input));
 
                },
 
                Err(err) => {
 
                    print_header(&resource);
 
                    println!(
 
                        "\n  FAILURE\n\n --- error:\n{}\n --- source:\n{}",
 
                        err,
 
                        String::from_utf8_lossy(&parser.modules[0].source.input)
 
                    )
 
                }
 
            }
 
        }
 
    }
 

	
 
    struct TestFileIter {
 
        iter: std::fs::ReadDir,
 
        root: String,
 
        extension: String
 
    }
 

	
 
    impl TestFileIter {
 
        fn new(root_dir: &str, extension: &str) -> Self {
 
            let path = Path::new(root_dir);
 
            assert!(path.is_dir(), "root '{}' is not a directory", root_dir);
 

	
 
            let iter = std::fs::read_dir(path).expect("list dir contents");
 

	
 
            Self {
 
                iter,
 
                root: root_dir.to_string(),
 
                extension: extension.to_string(),
 
            }
 
        }
 
    }
 

	
 
    impl Iterator for TestFileIter {
 
        type Item = Result<String, String>;
 

	
 
        fn next(&mut self) -> Option<Self::Item> {
 
            while let Some(entry) = self.iter.next() {
 
                if let Err(e) = entry {
 
                    return Some(Err(format!("failed to read dir entry, because: {}", e)));
 
                }
 
                let entry = entry.unwrap();
 

	
 
                let path = entry.path();
 
                if !path.is_file() { continue; }
 

	
 
                let extension = path.extension();
 
                if extension.is_none() { continue; }
 
                let extension = extension.unwrap().to_string_lossy();
 
                if extension != self.extension { continue; }
 

	
 
                return Some(Ok(path.to_string_lossy().to_string()));
 
            }
 

	
 
            None
 
        }
 
    }
 
}
 
}
 
\ No newline at end of file
src/protocol/parser/symbol_table.rs
Show inline comments
 
@@ -75,8 +75,14 @@ pub(crate) struct SymbolTable {
 
}
 

	
 
impl SymbolTable {
 
    pub(crate) fn new(heap: &Heap, modules: &[LexedModule]) -> Result<Self, ParseError2> {
 
        // Sanity check
 
    pub(crate) fn new() -> Self {
 
        Self{ module_lookup: HashMap::new(), symbol_lookup: HashMap::new() }
 
    }
 

	
 
    pub(crate) fn build(&mut self, heap: &Heap, modules: &[LexedModule]) -> Result<(), ParseError2> {
 
        // Sanity checks
 
        debug_assert!(self.module_lookup.is_empty());
 
        debug_assert!(self.symbol_lookup.is_empty());
 
        if cfg!(debug_assertions) {
 
            for (index, module) in modules.iter().enumerate() {
 
                debug_assert_eq!(
 
@@ -88,11 +94,11 @@ impl SymbolTable {
 

	
 
        // Preparation: create a lookup from module name to root id. This does
 
        // not take aliasing into account.
 
        let mut module_lookup = HashMap::with_capacity(modules.len());
 
        self.module_lookup.reserve(modules.len());
 
        for module in modules {
 
            // TODO: Maybe put duplicate module name checking here?
 
            // TODO: @string
 
            module_lookup.insert(module.module_name.clone(), module.root_id);
 
            self.module_lookup.insert(module.module_name.clone(), module.root_id);
 
        }
 

	
 
        // Preparation: determine total number of imports we will be inserting
 
@@ -107,7 +113,7 @@ impl SymbolTable {
 
                    Import::Symbols(import) => {
 
                        if import.symbols.is_empty() {
 
                            // Add all symbols from the other module
 
                            match module_lookup.get(&import.module_name) {
 
                            match self.module_lookup.get(&import.module_name) {
 
                                Some(target_module_id) => {
 
                                    lookup_reserve_size += heap[*target_module_id].definitions.len()
 
                                },
 
@@ -127,10 +133,7 @@ impl SymbolTable {
 
            lookup_reserve_size += module_root.definitions.len();
 
        }
 

	
 
        let mut table = Self{
 
            module_lookup,
 
            symbol_lookup: HashMap::with_capacity(lookup_reserve_size)
 
        };
 
        self.symbol_lookup.reserve(lookup_reserve_size);
 

	
 
        // First pass: we go through all of the modules and add lookups to
 
        // symbols that are defined within that module. Cross-module imports are
 
@@ -140,7 +143,7 @@ impl SymbolTable {
 
            for definition_id in &root.definitions {
 
                let definition = &heap[*definition_id];
 
                let identifier = definition.identifier();
 
                if let Err(previous_position) = table.add_definition_symbol(
 
                if let Err(previous_position) = self.add_definition_symbol(
 
                    module.root_id, identifier.position, &identifier.value,
 
                    module.root_id, *definition_id
 
                ) {
 
@@ -161,7 +164,7 @@ impl SymbolTable {
 
                match import {
 
                    Import::Module(import) => {
 
                        // Find the module using its name
 
                        let target_root_id = table.resolve_module(&import.module_name);
 
                        let target_root_id = self.resolve_module(&import.module_name);
 
                        if target_root_id.is_none() {
 
                            return Err(ParseError2::new_error(&module.source, import.position, "Could not resolve module"));
 
                        }
 
@@ -171,7 +174,7 @@ impl SymbolTable {
 
                        }
 

	
 
                        // Add the target module under its alias
 
                        if let Err(previous_position) = table.add_namespace_symbol(
 
                        if let Err(previous_position) = self.add_namespace_symbol(
 
                            module.root_id, import.position,
 
                            &import.alias, target_root_id
 
                        ) {
 
@@ -183,7 +186,7 @@ impl SymbolTable {
 
                    },
 
                    Import::Symbols(import) => {
 
                        // Find the target module using its name
 
                        let target_root_id = table.resolve_module(&import.module_name);
 
                        let target_root_id = self.resolve_module(&import.module_name);
 
                        if target_root_id.is_none() {
 
                            return Err(ParseError2::new_error(&module.source, import.position, "Could not resolve module of symbol imports"));
 
                        }
 
@@ -198,7 +201,7 @@ impl SymbolTable {
 
                            for definition_id in &heap[target_root_id].definitions {
 
                                let definition = &heap[*definition_id];
 
                                let identifier = definition.identifier();
 
                                if let Err(previous_position) = table.add_definition_symbol(
 
                                if let Err(previous_position) = self.add_definition_symbol(
 
                                    module.root_id, import.position, &identifier.value,
 
                                    target_root_id, *definition_id
 
                                ) {
 
@@ -229,7 +232,7 @@ impl SymbolTable {
 
                                // to "import a module's imported symbol". And so if we do find
 
                                // a symbol match, we need to make sure it is a definition from
 
                                // within that module by checking `source_root_id == target_root_id`
 
                                let target_symbol = table.resolve_symbol(target_root_id, &symbol.name);
 
                                let target_symbol = self.resolve_symbol(target_root_id, &symbol.name);
 
                                let symbol_definition_id = match target_symbol {
 
                                    Some(target_symbol) => {
 
                                        match target_symbol.symbol {
 
@@ -258,7 +261,7 @@ impl SymbolTable {
 
                                }
 
                                let symbol_definition_id = symbol_definition_id.unwrap();
 

	
 
                                if let Err(previous_position) = table.add_definition_symbol(
 
                                if let Err(previous_position) = self.add_definition_symbol(
 
                                    module.root_id, symbol.position, &symbol.alias,
 
                                    target_root_id, symbol_definition_id
 
                                ) {
 
@@ -288,10 +291,10 @@ impl SymbolTable {
 
        }
 

	
 
        debug_assert_eq!(
 
            table.symbol_lookup.len(), lookup_reserve_size,
 
            self.symbol_lookup.len(), lookup_reserve_size,
 
            "miscalculated reserved size for symbol lookup table"
 
        );
 
        Ok(table)
 
        Ok(())
 
    }
 

	
 
    /// Resolves a module by its defined name
src/protocol/parser/type_table.rs
Show inline comments
 
@@ -309,10 +309,21 @@ impl<'a> TypeCtx<'a> {
 
}
 

	
 
impl TypeTable {
 
    /// Construct a new type table without any resolved types. Types will be
 
    /// resolved on-demand.
 
    pub(crate) fn new(ctx: &mut TypeCtx) -> Result<Self, ParseError2> {
 
    /// Construct a new type table without any resolved types.
 
    pub(crate) fn new() -> Self {
 
        Self{ 
 
            lookup: HashMap::new(), 
 
            iter: TypeIterator::new(), 
 
            parser_type_iter: VecDeque::with_capacity(64), 
 
        }
 
    }
 

	
 
    pub(crate) fn build_base_types(&mut self, ctx: &mut TypeCtx) -> Result<(), ParseError2> {
 
        // Make sure we're allowed to cast root_id to index into ctx.modules
 
        debug_assert!(self.lookup.is_empty());
 
        debug_assert!(self.iter.top().is_none());
 
        debug_assert!(self.parser_type_iter.is_empty());
 

	
 
        if cfg!(debug_assertions) {
 
            for (index, module) in ctx.modules.iter().enumerate() {
 
                debug_assert_eq!(index, module.root_id.index as usize);
 
@@ -321,24 +332,20 @@ impl TypeTable {
 

	
 
        // Use context to guess hashmap size
 
        let reserve_size = ctx.heap.definitions.len();
 
        let mut table = Self{
 
            lookup: HashMap::with_capacity(reserve_size),
 
            iter: TypeIterator::new(),
 
            parser_type_iter: VecDeque::with_capacity(64),
 
        };
 
        self.lookup.reserve(reserve_size);
 

	
 
        // TODO: @cleanup Rework this hack
 
        for root_idx in 0..ctx.modules.len() {
 
            let last_definition_idx = ctx.heap[ctx.modules[root_idx].root_id].definitions.len();
 
            for definition_idx in 0..last_definition_idx {
 
                let definition_id = ctx.heap[ctx.modules[root_idx].root_id].definitions[definition_idx];
 
                table.resolve_base_definition(ctx, definition_id)?;
 
                self.resolve_base_definition(ctx, definition_id)?;
 
            }
 
        }
 

	
 
        debug_assert_eq!(table.lookup.len(), reserve_size, "mismatch in reserved size of type table");
 
        debug_assert_eq!(self.lookup.len(), reserve_size, "mismatch in reserved size of type table");
 

	
 
        Ok(table)
 
        Ok(())
 
    }
 

	
 
    /// Retrieves base definition from type table. We must be able to retrieve
src/protocol/parser/type_table_old.rs
Show inline comments
 
deleted file
src/protocol/parser/visitor_linker.rs
Show inline comments
 
@@ -14,7 +14,6 @@ use super::visitor::{
 
    Visitor2, 
 
    VisitorResult
 
};
 
use std::hint::unreachable_unchecked;
 

	
 
#[derive(PartialEq, Eq)]
 
enum DefinitionType {
src/protocol/tests/mod.rs
Show inline comments
 
@@ -2,5 +2,6 @@ mod utils;
 
mod lexer;
 
mod parser_validation;
 
mod parser_inference;
 
mod parser_monomorphs;
 

	
 
pub(crate) use utils::{Tester};
 
\ No newline at end of file
src/protocol/tests/parser_monomorphs.rs
Show inline comments
 
new file 100644
 
/// parser_monomorphs.rs
 
///
 
/// Simple tests to make sure that all of the appropriate monomorphs are 
 
/// instantiated
 

	
 
use super::*;
 

	
 
#[test]
 
fn test_struct_monomorphs() {
 
    Tester::new_single_source_expect_ok(
 
        "no polymorph",
 
        "struct Integer{ int field }"
 
    ).for_struct("Integer", |s| { s
 
        .assert_num_monomorphs(0);
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "single polymorph",
 
        "
 
        struct Number<T>{ T number }
 
        int instantiator() {
 
            auto a = Number<byte>{ number: 0 };
 
            auto b = Number<byte>{ number: 1 };
 
            auto c = Number<int>{ number: 2 };
 
            auto d = Number<long>{ number: 3 };
 
            auto e = Number<Number<short>>{ number: Number{ number: 4 }};
 
            return 0;
 
        }
 
        "
 
    ).for_struct("Number", |s| { s
 
        .assert_has_monomorph("byte")
 
        .assert_has_monomorph("short")
 
        .assert_has_monomorph("int")
 
        .assert_has_monomorph("long")
 
        .assert_has_monomorph("Number<short>")
 
        .assert_num_monomorphs(5);
 
    });
 
}
 
\ No newline at end of file
src/protocol/tests/utils.rs
Show inline comments
 
use crate::protocol::ast::*;
 
use crate::protocol::inputsource::*;
 
use crate::protocol::parser::*;
 
use crate::protocol::{
 
    ast::*,
 
    inputsource::*,
 
    parser::{
 
        *,
 
        type_table::TypeTable,
 
        symbol_table::SymbolTable,
 
    },
 
};
 

	
 
// Carries information about the test into utility structures for builder-like
 
// assertions
 
#[derive(Clone, Copy)]
 
struct TestCtx<'a> {
 
    test_name: &'a str,
 
    heap: &'a Heap,
 
    modules: &'a Vec<LexedModule>,
 
    types: &'a TypeTable,
 
    symbols: &'a SymbolTable,
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Interface for parsing and compiling
 
@@ -55,7 +72,6 @@ impl Tester {
 
            }
 
        }
 

	
 
        parser.compile();
 
        if let Err(err) = parser.parse() {
 
            return AstTesterResult::Err(AstErrTester::new(self.test_name, err))
 
        }
 
@@ -105,6 +121,8 @@ pub(crate) struct AstOkTester {
 
    test_name: String,
 
    modules: Vec<LexedModule>,
 
    heap: Heap,
 
    symbols: SymbolTable,
 
    types: TypeTable,
 
}
 

	
 
impl AstOkTester {
 
@@ -112,7 +130,9 @@ impl AstOkTester {
 
        Self {
 
            test_name,
 
            modules: parser.modules,
 
            heap: parser.heap
 
            heap: parser.heap,
 
            symbols: parser.symbol_table,
 
            types: parser.type_table,
 
        }
 
    }
 

	
 
@@ -125,7 +145,7 @@ impl AstOkTester {
 
                }
 

	
 
                // Found struct with the same name
 
                let tester = StructTester::new(&self.test_name, definition, &self.heap);
 
                let tester = StructTester::new(self.ctx(), definition);
 
                f(tester);
 
                found = true;
 
                break
 
@@ -150,7 +170,7 @@ impl AstOkTester {
 
                }
 

	
 
                // Found function
 
                let tester = FunctionTester::new(&self.test_name, definition, &self.heap);
 
                let tester = FunctionTester::new(self.ctx(), definition);
 
                f(tester);
 
                found = true;
 
                break;
 
@@ -165,6 +185,16 @@ impl AstOkTester {
 
        );
 
        unreachable!();
 
    }
 

	
 
    fn ctx(&self) -> TestCtx {
 
        TestCtx{
 
            test_name: &self.test_name,
 
            modules: &self.modules,
 
            heap: &self.heap,
 
            types: &self.types,
 
            symbols: &self.symbols,
 
        }
 
    }
 
}
 

	
 
//------------------------------------------------------------------------------
 
@@ -172,21 +202,66 @@ impl AstOkTester {
 
//------------------------------------------------------------------------------
 

	
 
pub(crate) struct StructTester<'a> {
 
    test_name: &'a str,
 
    ctx: TestCtx<'a>,
 
    def: &'a StructDefinition,
 
    heap: &'a Heap,
 
}
 

	
 
impl<'a> StructTester<'a> {
 
    fn new(test_name: &'a str, def: &'a StructDefinition, heap: &'a Heap) -> Self {
 
        Self{ test_name, def, heap }
 
    fn new(ctx: TestCtx<'a>, def: &'a StructDefinition) -> Self {
 
        Self{ ctx, def }
 
    }
 

	
 
    pub(crate) fn assert_num_fields(self, num: usize) -> Self {
 
        assert_eq!(
 
            num, self.def.fields.len(),
 
            "[{}] Expected {} struct fields, but found {} for {}",
 
            self.test_name, num, self.def.fields.len(), self.assert_postfix()
 
            self.ctx.test_name, num, self.def.fields.len(), self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    pub(crate) fn assert_num_monomorphs(self, num: usize) -> Self {
 
        let type_def = self.ctx.types.get_base_definition(&self.def.this.upcast()).unwrap();
 
        assert_eq!(
 
            num, type_def.monomorphs.len(),
 
            "[{}] Expected {} monomorphs, but found {} for {}",
 
            self.ctx.test_name, num, type_def.monomorphs.len(), self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    /// Asserts that a monomorph exist, separate polymorphic variable types by
 
    /// a semicolon.
 
    pub(crate) fn assert_has_monomorph(self, serialized_monomorph: &str) -> Self {
 
        let definition_id = self.def.this.upcast();
 
        let type_def = self.ctx.types.get_base_definition(&definition_id).unwrap();
 

	
 
        let mut full_buffer = String::new();
 
        full_buffer.push('[');
 
        for (monomorph_idx, monomorph) in type_def.monomorphs.iter().enumerate() {
 
            let mut buffer = String::new();
 
            for (element_idx, monomorph_element) in monomorph.iter().enumerate() {
 
                if element_idx != 0 { buffer.push(';'); }
 
                serialize_concrete_type(&mut buffer, self.ctx.heap, definition_id, monomorph_element);
 
            }
 

	
 
            if buffer == serialized_monomorph {
 
                // Found an exact match
 
                return self
 
            }
 

	
 
            if monomorph_idx != 0 {
 
                full_buffer.push_str(", ");
 
            }
 
            full_buffer.push('"');
 
            full_buffer.push_str(&buffer);
 
            full_buffer.push('"');
 
        }
 
        full_buffer.push(']');
 

	
 
        assert!(
 
            false, "[{}] Expected to find monomorph {}, but got {} for {}",
 
            self.ctx.test_name, serialized_monomorph, &full_buffer, self.assert_postfix()
 
        );
 
        self
 
    }
 
@@ -195,7 +270,7 @@ impl<'a> StructTester<'a> {
 
        // Find field with specified name
 
        for field in &self.def.fields {
 
            if String::from_utf8_lossy(&field.field.value) == name {
 
                let tester = StructFieldTester::new(self.test_name, field, self.heap);
 
                let tester = StructFieldTester::new(self.ctx, field);
 
                f(tester);
 
                return self;
 
            }
 
@@ -203,7 +278,7 @@ impl<'a> StructTester<'a> {
 

	
 
        assert!(
 
            false, "[{}] Could not find struct field '{}' for {}",
 
            self.test_name, name, self.assert_postfix()
 
            self.ctx.test_name, name, self.assert_postfix()
 
        );
 
        unreachable!();
 
    }
 
@@ -223,30 +298,29 @@ impl<'a> StructTester<'a> {
 
}
 

	
 
pub(crate) struct StructFieldTester<'a> {
 
    test_name: &'a str,
 
    ctx: TestCtx<'a>,
 
    def: &'a StructFieldDefinition,
 
    heap: &'a Heap,
 
}
 

	
 
impl<'a> StructFieldTester<'a> {
 
    fn new(test_name: &'a str, def: &'a StructFieldDefinition, heap: &'a Heap) -> Self {
 
        Self{ test_name, def, heap }
 
    fn new(ctx: TestCtx<'a>, def: &'a StructFieldDefinition) -> Self {
 
        Self{ ctx, def }
 
    }
 

	
 
    pub(crate) fn assert_parser_type(self, expected: &str) -> Self {
 
        let mut serialized_type = String::new();
 
        serialize_parser_type(&mut serialized_type, &self.heap, self.def.parser_type);
 
        serialize_parser_type(&mut serialized_type, &self.ctx.heap, self.def.parser_type);
 
        assert_eq!(
 
            expected, &serialized_type,
 
            "[{}] Expected type '{}', but got '{}' for {}",
 
            self.test_name, expected, &serialized_type, self.assert_postfix()
 
            self.ctx.test_name, expected, &serialized_type, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        let mut serialized_type = String::new();
 
        serialize_parser_type(&mut serialized_type, &self.heap, self.def.parser_type);
 
        serialize_parser_type(&mut serialized_type, &self.ctx.heap, self.def.parser_type);
 
        format!(
 
            "StructField{{ name: {}, parser_type: {} }}",
 
            String::from_utf8_lossy(&self.def.field.value), serialized_type
 
@@ -255,24 +329,23 @@ impl<'a> StructFieldTester<'a> {
 
}
 

	
 
pub(crate) struct FunctionTester<'a> {
 
    test_name: &'a str,
 
    ctx: TestCtx<'a>,
 
    def: &'a Function,
 
    heap: &'a Heap,
 
}
 

	
 
impl<'a> FunctionTester<'a> {
 
    fn new(test_name: &'a str, def: &'a Function, heap: &'a Heap) -> Self {
 
        Self{ test_name, def, heap }
 
    fn new(ctx: TestCtx<'a>, def: &'a Function) -> Self {
 
        Self{ ctx, def }
 
    }
 

	
 
    pub(crate) fn for_variable<F: Fn(VariableTester)>(self, name: &str, f: F) -> Self {
 
        // Find the memory statement in order to find the local
 
        let mem_stmt_id = seek_stmt(
 
            self.heap, self.def.body,
 
            self.ctx.heap, self.def.body,
 
            &|stmt| {
 
                if let Statement::Local(local) = stmt {
 
                    if let LocalStatement::Memory(memory) = local {
 
                        let local = &self.heap[memory.variable];
 
                        let local = &self.ctx.heap[memory.variable];
 
                        if local.identifier.value == name.as_bytes() {
 
                            return true;
 
                        }
 
@@ -285,19 +358,19 @@ impl<'a> FunctionTester<'a> {
 

	
 
        assert!(
 
            mem_stmt_id.is_some(), "[{}] Failed to find variable '{}' in {}",
 
            self.test_name, name, self.assert_postfix()
 
            self.ctx.test_name, name, self.assert_postfix()
 
        );
 

	
 
        let mem_stmt_id = mem_stmt_id.unwrap();
 
        let local_id = self.heap[mem_stmt_id].as_memory().variable;
 
        let local = &self.heap[local_id];
 
        let local_id = self.ctx.heap[mem_stmt_id].as_memory().variable;
 
        let local = &self.ctx.heap[local_id];
 

	
 
        // Find the assignment expression that follows it
 
        let assignment_id = seek_expr_in_stmt(
 
            self.heap, self.def.body,
 
            self.ctx.heap, self.def.body,
 
            &|expr| {
 
                if let Expression::Assignment(assign_expr) = expr {
 
                    if let Expression::Variable(variable_expr) = &self.heap[assign_expr.left] {
 
                    if let Expression::Variable(variable_expr) = &self.ctx.heap[assign_expr.left] {
 
                        if variable_expr.position.offset == local.identifier.position.offset {
 
                            return true;
 
                        }
 
@@ -310,15 +383,15 @@ impl<'a> FunctionTester<'a> {
 

	
 
        assert!(
 
            assignment_id.is_some(), "[{}] Failed to find assignment to variable '{}' in {}",
 
            self.test_name, name, self.assert_postfix()
 
            self.ctx.test_name, name, self.assert_postfix()
 
        );
 

	
 
        let assignment = &self.heap[assignment_id.unwrap()];
 
        let assignment = &self.ctx.heap[assignment_id.unwrap()];
 

	
 
        // Construct tester and pass to tester function
 
        let tester = VariableTester::new(
 
            self.test_name, self.def.this.upcast(), local, 
 
            assignment.as_assignment(), self.heap
 
            self.ctx, self.def.this.upcast(), local, 
 
            assignment.as_assignment()
 
        );
 
        f(tester);
 

	
 
@@ -335,28 +408,27 @@ impl<'a> FunctionTester<'a> {
 

	
 

	
 
pub(crate) struct VariableTester<'a> {
 
    test_name: &'a str,
 
    ctx: TestCtx<'a>,
 
    definition_id: DefinitionId,
 
    local: &'a Local,
 
    assignment: &'a AssignmentExpression,
 
    heap: &'a Heap,
 
}
 

	
 
impl<'a> VariableTester<'a> {
 
    fn new(
 
        test_name: &'a str, definition_id: DefinitionId, local: &'a Local, assignment: &'a AssignmentExpression, heap: &'a Heap
 
        ctx: TestCtx<'a>, definition_id: DefinitionId, local: &'a Local, assignment: &'a AssignmentExpression
 
    ) -> Self {
 
        Self{ test_name, definition_id, local, assignment, heap }
 
        Self{ ctx, definition_id, local, assignment }
 
    }
 

	
 
    pub(crate) fn assert_parser_type(self, expected: &str) -> Self {
 
        let mut serialized = String::new();
 
        serialize_parser_type(&mut serialized, self.heap, self.local.parser_type);
 
        serialize_parser_type(&mut serialized, self.ctx.heap, self.local.parser_type);
 

	
 
        assert_eq!(
 
            expected, &serialized,
 
            "[{}] Expected parser type '{}', but got '{}' for {}",
 
            self.test_name, expected, &serialized, self.assert_postfix()
 
            self.ctx.test_name, expected, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 
@@ -364,14 +436,14 @@ impl<'a> VariableTester<'a> {
 
    pub(crate) fn assert_concrete_type(self, expected: &str) -> Self {
 
        let mut serialized = String::new();
 
        serialize_concrete_type(
 
            &mut serialized, self.heap, self.definition_id, 
 
            &mut serialized, self.ctx.heap, self.definition_id, 
 
            &self.assignment.concrete_type
 
        );
 

	
 
        assert_eq!(
 
            expected, &serialized,
 
            "[{}] Expected concrete type '{}', but got '{}' for {}",
 
            self.test_name, expected, &serialized, self.assert_postfix()
 
            self.ctx.test_name, expected, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
0 comments (0 inline, 0 general)