Changeset - 8da6a7632169
[Not reviewed]
0 6 0
MH - 4 years ago 2021-03-28 12:31:53
contact@maxhenger.nl
added a test, pending bugfix
6 files changed with 344 insertions and 25 deletions:
0 comments (0 inline, 0 general)
src/protocol/inputsource.rs
Show inline comments
 
@@ -151,7 +151,7 @@ impl fmt::Display for InputSource {
 
pub struct InputPosition {
 
    line: usize,
 
    column: usize,
 
    offset: usize,
 
    pub(crate) offset: usize,
 
}
 

	
 
impl InputPosition {
src/protocol/parser/type_resolver.rs
Show inline comments
 
@@ -655,8 +655,13 @@ impl InferenceType {
 
        use InferenceTypePart as ITP;
 

	
 
        match &parts[idx] {
 
            ITP::MarkerDefinition(_) | ITP::MarkerBody(_) => {
 
                idx = Self::write_display_name(buffer, heap, parts, idx + 1)
 
            ITP::MarkerDefinition(thing) => {
 
                buffer.push_str(&format!("{{D:{}}}", *thing));
 
                idx = Self::write_display_name(buffer, heap, parts, idx + 1);
 
            }, 
 
            ITP::MarkerBody(thing) => {
 
                buffer.push_str(&format!("{{B:{}}}", *thing));
 
                idx = Self::write_display_name(buffer, heap, parts, idx + 1);
 
            },
 
            ITP::Unknown => buffer.push_str("?"),
 
            ITP::NumberLike => buffer.push_str("num?"),
 
@@ -1860,14 +1865,14 @@ impl TypeResolvingVisitor {
 
                let signature_type: *mut _ = &mut poly_data.embedded[0];
 
                let subject_type: *mut _ = self.expr_types.get_mut(&subject_id).unwrap();
 
                
 
                let progress_subject = Self::apply_equal2_polyvar_constraint(
 
                let progress_subject = Self::apply_equal2_polyvar_constraint(&ctx.heap,
 
                    poly_data, &poly_progress, signature_type, subject_type
 
                );
 

	
 
                let signature_type: *mut _ = &mut poly_data.returned;
 
                let expr_type: *mut _ = self.expr_types.get_mut(&upcast_id).unwrap();
 

	
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(&ctx.heap,
 
                    poly_data, &poly_progress, signature_type, expr_type
 
                );
 

	
 
@@ -2006,7 +2011,7 @@ impl TypeResolvingVisitor {
 
                    let field_expr_id = data.fields[field_idx].value;
 
                    let field_type: *mut _ = self.expr_types.get_mut(&field_expr_id).unwrap();
 

	
 
                    let progress_arg = Self::apply_equal2_polyvar_constraint(
 
                    let progress_arg = Self::apply_equal2_polyvar_constraint(&ctx.heap,
 
                        extra, &poly_progress, signature_type, field_type
 
                    );
 

	
 
@@ -2024,7 +2029,7 @@ impl TypeResolvingVisitor {
 
                let signature_type: *mut _ = &mut extra.returned;
 
                let expr_type: *mut _ = self.expr_types.get_mut(&upcast_id).unwrap();
 

	
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(
 
                let progress_expr = Self::apply_equal2_polyvar_constraint(&ctx.heap,
 
                    extra, &poly_progress, signature_type, expr_type
 
                );
 

	
 
@@ -2109,6 +2114,9 @@ impl TypeResolvingVisitor {
 
        // reapplying the polymorph type to each argument type and the return
 
        // type should always succeed.
 
        debug_log!(" * During (reinferring from progressed polyvars):");
 
        for (poly_idx, poly_var) in extra.poly_vars.iter().enumerate() {
 
            debug_log!("   - Poly {} | sig: {}", poly_idx, poly_var.display_name(&ctx.heap));
 
        }
 
        // TODO: @performance If the algorithm is changed to be more "on demand
 
        //  argument re-evaluation", instead of "all-argument re-evaluation",
 
        //  then this is no longer true
 
@@ -2117,7 +2125,7 @@ impl TypeResolvingVisitor {
 
            let arg_expr_id = expr.arguments[arg_idx];
 
            let arg_type: *mut _ = self.expr_types.get_mut(&arg_expr_id).unwrap();
 
            
 
            let progress_arg = Self::apply_equal2_polyvar_constraint(
 
            let progress_arg = Self::apply_equal2_polyvar_constraint(&ctx.heap,
 
                extra, &poly_progress,
 
                signature_type, arg_type
 
            );
 
@@ -2136,7 +2144,7 @@ impl TypeResolvingVisitor {
 
        let signature_type: *mut _ = &mut extra.returned;
 
        let ret_type: *mut _ = self.expr_types.get_mut(&upcast_id).unwrap();
 

	
 
        let progress_ret = Self::apply_equal2_polyvar_constraint(
 
        let progress_ret = Self::apply_equal2_polyvar_constraint(&ctx.heap,
 
            extra, &poly_progress, signature_type, ret_type
 
        );
 
        debug_log!(
 
@@ -2406,6 +2414,7 @@ impl TypeResolvingVisitor {
 
    ///
 
    /// This function returns true if the expression's type has been progressed
 
    fn apply_equal2_polyvar_constraint(
 
        heap: &Heap,
 
        polymorph_data: &ExtraData, polymorph_progress: &HashSet<usize>,
 
        signature_type: *mut InferenceType, expr_type: *mut InferenceType
 
    ) -> bool {
 
@@ -2425,6 +2434,7 @@ impl TypeResolvingVisitor {
 
            if polymorph_progress.contains(&poly_idx) {
 
                // Need to match subtrees
 
                let polymorph_type = &polymorph_data.poly_vars[poly_idx];
 
                debug_log!("   - DEBUG: Applying {} to '{}' from '{}'", polymorph_type.display_name(heap), InferenceType::partial_display_name(heap, &signature_type.parts[start_idx..]), signature_type.display_name(heap));
 
                let modified_at_marker = Self::apply_forced_constraint_types(
 
                    signature_type, start_idx, 
 
                    &polymorph_type.parts, 0
src/protocol/tests/mod.rs
Show inline comments
 
mod utils;
 
mod lexer;
 
mod parser_validation;
 
mod parser_inference;
 

	
 
pub(crate) use utils::{Tester};
 
\ No newline at end of file
src/protocol/tests/parser_inference.rs
Show inline comments
 
/// parser_inference.rs
 
///
 
/// Simple tests for the type inferences
 

	
 
use super::*;
 

	
 
#[test]
 
fn test_integer_inference() {
 
    Tester::new_single_source_expect_ok(
 
        "by arguments",
 
        "
 
        int call(byte b, short s, int i, long l) {
 
            auto b2 = b;
 
            auto s2 = s;
 
            auto i2 = i;
 
            auto l2 = l;
 
            return i2;
 
        }
 
        "
 
    ).for_function("call", |f| { f
 
        .for_variable("b2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("byte");
 
        })
 
        .for_variable("s2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("short");
 
        })
 
        .for_variable("i2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("int");
 
        })
 
        .for_variable("l2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("long");
 
        });
 
    });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "by assignment",
 
        "
 
        int call() {
 
            byte b1 = 0; short s1 = 0; int i1 = 0; long l1 = 0;
 
            auto b2 = b1;
 
            auto s2 = s1;
 
            auto i2 = i1;
 
            auto l2 = l1;
 
            return 0;
 
        }"
 
    ).for_function("call", |f| { f
 
        .for_variable("b2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("byte");
 
        })
 
        .for_variable("s2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("short");
 
        })
 
        .for_variable("i2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("int");
 
        })
 
        .for_variable("l2", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("long");
 
        });
 
    });
 
}
 

	
 
#[test]
 
fn test_struct_inference() {
 
    // Tester::new_single_source_expect_ok(
 
    //     "by function calls",
 
    //     "
 
    //     struct Pair<T1, T2>{ T1 first, T2 second }
 
    //     Pair<T1, T2> construct<T1, T2>(T1 first, T2 second) { 
 
    //         return Pair{ first: first, second: second };
 
    //     }
 
    //     int fix_t1<T2>(Pair<byte, T2> arg) { return 0; }
 
    //     int fix_t2<T1>(Pair<T1, int> arg) { return 0; }
 
    //     int test() {
 
    //         auto first = 0;
 
    //         auto second = 1;
 
    //         auto pair = construct(first, second);
 
    //         fix_t1(pair);
 
    //         fix_t2(pair);
 
    //         return 0;
 
    //     }
 
    //     "
 
    // ).for_function("test", |f| { f
 
    //     .for_variable("first", |v| { v
 
    //         .assert_parser_type("auto")
 
    //         .assert_concrete_type("byte");
 
    //     })
 
    //     .for_variable("second", |v| { v
 
    //         .assert_parser_type("auto")
 
    //         .assert_concrete_type("int");
 
    //     })
 
    //     .for_variable("pair", |v| { v
 
    //         .assert_parser_type("auto")
 
    //         .assert_concrete_type("Pair<byte,int>");
 
    //     });
 
    // });
 

	
 
    // Tester::new_single_source_expect_ok(
 
    //     "by field access",
 
    //     "
 
    //     struct Pair<T1, T2>{ T1 first, T2 second }
 
    //     Pair<T1, T2> construct<T1, T2>(T1 first, T2 second) {
 
    //         return Pair{ first: first, second: second };
 
    //     }
 
    //     int test() {
 
    //         auto first = 0;
 
    //         auto second = 1;
 
    //         auto pair = construct(first, second);
 
    //         byte assign_first = 0;
 
    //         long assign_second = 1;
 
    //         pair.first = assign_first;
 
    //         pair.second = assign_second;
 
    //         return 0;
 
    //     }
 
    //     "
 
    // ).for_function("test", |f| { f
 
    //     .for_variable("first", |v| { v
 
    //         .assert_parser_type("auto")
 
    //         .assert_concrete_type("byte");
 
    //     })
 
    //     .for_variable("second", |v| { v
 
    //         .assert_parser_type("auto")
 
    //         .assert_concrete_type("long");
 
    //     })
 
    //     .for_variable("pair", |v| { v
 
    //         .assert_parser_type("auto")
 
    //         .assert_concrete_type("Pair<byte,long>");
 
    //     });
 
    // });
 

	
 
    Tester::new_single_source_expect_ok(
 
        "by nested field access",
 
        "
 
        struct Node<T1, T2>{ T1 l, T2 r }
 
        Node<T1, T2> construct<T1, T2>(T1 l, T2 r) { return Node{ l: l, r: r }; }
 
        int fix_poly<T>(Node<T, T> a) { return 0; }
 
        int test() {
 
            byte assigned = 0;
 
            auto thing = construct(assigned, construct(0, 1));
 
            fix_poly(thing.r);
 
            thing.r.r = assigned;
 
            return 0;
 
        }
 
        ",
 
    ).for_function("test", |f| { f
 
        .for_variable("thing", |v| { v
 
            .assert_parser_type("auto")
 
            .assert_concrete_type("Pair<byte,Pair<byte,byte>>");
 
        });
 
    });
 
}
 
\ No newline at end of file
src/protocol/tests/parser_validation.rs
Show inline comments
 
@@ -64,7 +64,7 @@ fn test_correct_struct_instance() {
 
        "
 
    );
 

	
 
    Tester::new_single_source_expr_ok(
 
    Tester::new_single_source_expect_ok(
 
        "multiple fields, different explicit polymorph",
 
        "
 
        struct Pair<T1, T2>{ T1 first, T2 second }
src/protocol/tests/utils.rs
Show inline comments
 
@@ -75,6 +75,7 @@ impl AstTesterResult {
 
            AstTesterResult::Ok(v) => v,
 
            AstTesterResult::Err(err) => {
 
                let wrapped = ErrorTester{ test_name: &err.test_name, error: &err.error };
 
                println!("DEBUG: Full error:\n{}", &err.error);
 
                assert!(
 
                    false,
 
                    "[{}] Expected compilation to succeed, but it failed with {}",
 
@@ -182,7 +183,7 @@ impl<'a> StructTester<'a> {
 
    }
 

	
 
    pub(crate) fn assert_num_fields(self, num: usize) -> Self {
 
        debug_assert_eq!(
 
        assert_eq!(
 
            num, self.def.fields.len(),
 
            "[{}] Expected {} struct fields, but found {} for {}",
 
            self.test_name, num, self.def.fields.len(), self.assert_postfix()
 
@@ -235,7 +236,7 @@ impl<'a> StructFieldTester<'a> {
 
    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);
 
        debug_assert_eq!(
 
        assert_eq!(
 
            expected, &serialized_type,
 
            "[{}] Expected type '{}', but got '{}' for {}",
 
            self.test_name, expected, &serialized_type, self.assert_postfix()
 
@@ -265,9 +266,10 @@ impl<'a> FunctionTester<'a> {
 
    }
 

	
 
    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,
 
            |stmt| {
 
            &|stmt| {
 
                if let Statement::Local(local) = stmt {
 
                    if let LocalStatement::Memory(memory) = local {
 
                        let local = &self.heap[memory.variable];
 
@@ -281,26 +283,106 @@ impl<'a> FunctionTester<'a> {
 
            }
 
        );
 

	
 
        match mem_stmt_id {
 
            Some(mem_stmt_id) => {
 
                // TODO: Retrieve shit
 
            },
 
            None => {
 
                // TODO: Throw error
 
        assert!(
 
            mem_stmt_id.is_some(), "[{}] Failed to find variable '{}' in {}",
 
            self.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];
 

	
 
        // Find the assignment expression that follows it
 
        let assignment_id = seek_expr_in_stmt(
 
            self.heap, self.def.body,
 
            &|expr| {
 
                if let Expression::Assignment(assign_expr) = expr {
 
                    if let Expression::Variable(variable_expr) = &self.heap[assign_expr.left] {
 
                        if variable_expr.position.offset == local.identifier.position.offset {
 
                            return true;
 
                        }
 
                    }
 
                }
 

	
 
                false
 
            }
 
        }
 
        );
 

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

	
 
        let assignment = &self.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
 
        );
 
        f(tester);
 

	
 
        self
 
    }
 

	
 
    fn assert_postfix(&self) -> String {
 
        format!(
 
            "Function{{ name: {} }}",
 
            &String::from_utf8_lossy(&self.def.identifier.value)
 
        )
 
    }
 
}
 

	
 

	
 
pub(crate) struct VariableTester<'a> {
 
    test_name: &'a str,
 
    def: &'a Local,
 
    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
 
    ) -> Self {
 
        Self{ test_name, definition_id, local, assignment, heap }
 
    }
 

	
 
    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);
 

	
 
        assert_eq!(
 
            expected, &serialized,
 
            "[{}] Expected parser type '{}', but got '{}' for {}",
 
            self.test_name, expected, &serialized, self.assert_postfix()
 
        );
 
        self
 
    }
 

	
 
    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, 
 
            &self.assignment.concrete_type
 
        );
 

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

	
 
    fn assert_postfix(&self) -> String {
 
        format!(
 
            "Variable{{ name: {} }}",
 
            &String::from_utf8_lossy(&self.local.identifier.value)
 
        )
 
    }
 
}
 

	
 
//------------------------------------------------------------------------------
 
// Interface for failed compilation
 
@@ -444,7 +526,75 @@ fn serialize_parser_type(buffer: &mut String, heap: &Heap, id: ParserTypeId) {
 
    }
 
}
 

	
 
fn seek_stmt<F: Fn(&Statement) -> bool>(heap: &Heap, start: StatementId, f: F) -> Option<StatementId> {
 
fn serialize_concrete_type(buffer: &mut String, heap: &Heap, def: DefinitionId, concrete: &ConcreteType) {
 
    // Retrieve polymorphic variables, if present (since we're dealing with a 
 
    // concrete type we only expect procedure types)
 
    let poly_vars = match &heap[def] {
 
        Definition::Function(func) => &func.poly_vars,
 
        Definition::Component(comp) => &comp.poly_vars,
 
        _ => unreachable!("Error in testing utility: did not expect non-procedure type for concrete type serialization"),
 
    };
 

	
 
    fn serialize_recursive(
 
        buffer: &mut String, heap: &Heap, poly_vars: &Vec<Identifier>, concrete: &ConcreteType, mut idx: usize
 
    ) -> usize {
 
        use ConcreteTypePart as CTP;
 

	
 
        let part = &concrete.parts[idx];
 
        match part {
 
            CTP::Marker(poly_idx) => {
 
                buffer.push_str(&String::from_utf8_lossy(&poly_vars[*poly_idx].value));
 
            },
 
            CTP::Void => buffer.push_str("void"),
 
            CTP::Message => buffer.push_str("msg"),
 
            CTP::Bool => buffer.push_str("bool"),
 
            CTP::Byte => buffer.push_str("byte"),
 
            CTP::Short => buffer.push_str("short"),
 
            CTP::Int => buffer.push_str("int"),
 
            CTP::Long => buffer.push_str("long"),
 
            CTP::String => buffer.push_str("string"),
 
            CTP::Array => {
 
                idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1);
 
                buffer.push_str("[]");
 
                idx += 1;
 
            },
 
            CTP::Slice => {
 
                idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1);
 
                buffer.push_str("[..]");
 
                idx += 1;
 
            },
 
            CTP::Input => {
 
                buffer.push_str("in<");
 
                idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1);
 
                buffer.push('>');
 
                idx += 1;
 
            },
 
            CTP::Output => {
 
                buffer.push_str("out<");
 
                idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1);
 
                buffer.push('>');
 
                idx += 1
 
            },
 
            CTP::Instance(definition_id, num_sub) => {
 
                let definition_name = heap[*definition_id].identifier();
 
                buffer.push_str(&String::from_utf8_lossy(&definition_name.value));
 
                buffer.push('<');
 
                for sub_idx in 0..*num_sub {
 
                    if sub_idx != 0 { buffer.push(','); }
 
                    idx = serialize_recursive(buffer, heap, poly_vars, concrete, idx + 1);
 
                }
 
                buffer.push('>');
 
                idx += 1;
 
            }
 
        }
 

	
 
        idx
 
    }
 

	
 
    serialize_recursive(buffer, heap, poly_vars, concrete, 0);
 
}
 

	
 
fn seek_stmt<F: Fn(&Statement) -> bool>(heap: &Heap, start: StatementId, f: &F) -> Option<StatementId> {
 
    let stmt = &heap[start];
 
    if f(stmt) { return Some(start); }
 

	
 
@@ -476,7 +626,7 @@ fn seek_stmt<F: Fn(&Statement) -> bool>(heap: &Heap, start: StatementId, f: F) -
 
    matched
 
}
 

	
 
fn seek_expr_in_expr<F: Fn(&Expression) -> bool>(heap: &Heap, start: ExpressionId, f: F) -> Option<ExpressionId> {
 
fn seek_expr_in_expr<F: Fn(&Expression) -> bool>(heap: &Heap, start: ExpressionId, f: &F) -> Option<ExpressionId> {
 
    let expr = &heap[start];
 
    if f(expr) { return Some(start); }
 

	
 
@@ -523,7 +673,7 @@ fn seek_expr_in_expr<F: Fn(&Expression) -> bool>(heap: &Heap, start: ExpressionI
 
            None
 
        },
 
        Expression::Literal(expr) => {
 
            if let Literal::Struct(lit) = expr.value {
 
            if let Literal::Struct(lit) = &expr.value {
 
                for field in &lit.fields {
 
                    if let Some(id) = seek_expr_in_expr(heap, field.value, f) {
 
                        return Some(id)
 
@@ -546,7 +696,7 @@ fn seek_expr_in_expr<F: Fn(&Expression) -> bool>(heap: &Heap, start: ExpressionI
 
    }
 
}
 

	
 
fn seek_expr_in_stmt<F: Fn(&Expression) -> bool>(heap: &Heap, start: StatementId, f: F) -> Option<ExpressionId> {
 
fn seek_expr_in_stmt<F: Fn(&Expression) -> bool>(heap: &Heap, start: StatementId, f: &F) -> Option<ExpressionId> {
 
    let stmt = &heap[start];
 

	
 
    match stmt {
0 comments (0 inline, 0 general)