From d7baa792c2c28e87c4b4f76b722ad112a4ca56fc 2021-05-05 14:36:56 From: MH Date: 2021-05-05 14:36:56 Subject: [PATCH] fix all parsing tests, onto rewriting the runtime --- diff --git a/src/protocol/input_source.rs b/src/protocol/input_source.rs index dc417536f7ceec7def8586545923f51961a16642..0a5304926c32cb91d49059c198624c10c6bd0e59 100644 --- a/src/protocol/input_source.rs +++ b/src/protocol/input_source.rs @@ -161,7 +161,7 @@ impl InputSource { } } - lookup.push(self.input.len() as u32); // for lookup_line_end + lookup.push(self.input.len() as u32 + 1); // for lookup_line_end, intentionally adding one character debug_assert_eq!(self.line as usize + 2, lookup.len(), "remove me: i am a testing assert and sometimes invalid"); // Return created lookup @@ -183,13 +183,11 @@ impl InputSource { let offset = lookup[(line_number + 1) as usize] - 1; let offset_usize = offset as usize; - // Compensate for newlines and a potential carriage feed - if self.input[offset_usize] == b'\n' { - if offset_usize > 0 && self.input[offset_usize] == b'\r' { - offset - 2 - } else { - offset - 1 - } + // Compensate for newlines and a potential carriage feed. Note that the + // end position is exclusive. So we only need to compensate for a + // "\r\n" + if offset_usize > 0 && offset_usize < self.input.len() && self.input[offset_usize] == b'\n' && self.input[offset_usize - 1] == b'\r' { + offset - 1 } else { offset } @@ -319,17 +317,28 @@ impl fmt::Display for ParseErrorStatement { fn extend_annotation(first_col: u32, last_col: u32, source: &str, target: &mut String, extend_char: char) { debug_assert!(first_col > 0 && last_col > first_col); + + // If the first index exceeds the size of the context then we should + // have a message placed at the newline character let first_idx = first_col as usize - 1; let last_idx = last_col as usize - 1; - for (char_idx, char) in source.chars().enumerate().skip(first_idx) { - if char_idx == last_idx as usize { - break; - } - - if char == '\t' { - for _ in 0..4 { target.push(extend_char); } - } else { - target.push(extend_char); + if first_idx >= source.len() { + // If any of these fail then the logic behind reporting errors + // is incorrect. + debug_assert_eq!(first_idx, source.len()); + debug_assert_eq!(first_idx + 1, last_idx); + target.push(extend_char); + } else { + for (char_idx, char) in source.chars().enumerate().skip(first_idx) { + if char_idx == last_idx as usize { + break; + } + + if char == '\t' { + for _ in 0..4 { target.push(extend_char); } + } else { + target.push(extend_char); + } } } } diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 7b16fe286b09b2b0c81cb9835770db2a059d27a1..b3a5379de5a7b858e7ec8e23f784d7648686b1b0 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -52,12 +52,16 @@ impl PassDefinitions { match cur_range.range_kind { TokenRangeKind::Module => unreachable!(), // should not be reachable - TokenRangeKind::Pragma | TokenRangeKind::Import => continue, // already fully parsed - TokenRangeKind::Definition | TokenRangeKind::Code => {} + TokenRangeKind::Pragma | TokenRangeKind::Import => { + // Already fully parsed, fall through and go to next range + }, + TokenRangeKind::Definition | TokenRangeKind::Code => { + // Visit range even if it is a "code" range to provide + // proper error messages. + self.visit_range(modules, module_idx, ctx, range_idx_usize)?; + }, } - self.visit_range(modules, module_idx, ctx, range_idx_usize)?; - if cur_range.next_sibling_idx == NO_SIBLING { break; } else { @@ -1290,7 +1294,7 @@ impl PassDefinitions { let value = self.consume_expression(module, iter, ctx)?; Ok(LiteralStructField{ identifier, value, field_idx: 0 }) }, - &mut struct_fields, "a struct field", "a list of struct field", Some(&mut last_token) + &mut struct_fields, "a struct field", "a list of struct fields", Some(&mut last_token) )?; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ @@ -1329,8 +1333,12 @@ impl PassDefinitions { let variant = consume_ident_interned(&module.source, iter, ctx)?; // Consume any possible embedded values - let mut end_pos = iter.last_valid_pos(); - let values = self.consume_expression_list(module, iter, ctx, Some(&mut end_pos))?; + let mut end_pos = variant.span.end; + let values = if Some(TokenKind::OpenParen) == iter.next() { + self.consume_expression_list(module, iter, ctx, Some(&mut end_pos))? + } else { + Vec::new() + }; ctx.heap.alloc_literal_expression(|this| LiteralExpression{ this, @@ -1402,6 +1410,8 @@ impl PassDefinitions { } else { // Check for builtin keywords or builtin functions if ident_text == KW_LIT_NULL || ident_text == KW_LIT_TRUE || ident_text == KW_LIT_FALSE { + iter.consume(); + // Parse builtin literal let value = match ident_text { KW_LIT_NULL => Literal::Null, @@ -1418,13 +1428,27 @@ impl PassDefinitions { concrete_type: ConcreteType::default(), }).upcast() } else { - // I'm a bit unsure about this. One may as well have wrongfully - // typed `TypeWithTypo::`, then we assume that - // `TypeWithTypo` is a variable. So might want to come back to - // this later to do some silly heuristics. + // Not a builtin literal, but also not a known type. So we + // assume it is a variable expression. Although if we do, + // then if a programmer mistyped a struct/function name the + // error messages will be rather cryptic. For polymorphic + // arguments we can't really do anything at all (because it + // uses the '<' token). In the other cases we try to provide + // a better error message. iter.consume(); - if Some(TokenKind::ColonColon) == iter.next() { + let next = iter.next(); + if Some(TokenKind::ColonColon) == next { return Err(ParseError::new_error_str_at_span(&module.source, ident_span, "unknown identifier")); + } else if Some(TokenKind::OpenParen) == next { + return Err(ParseError::new_error_str_at_span( + &module.source, ident_span, + "unknown identifier, did you mistype a union variant's or a function's name?" + )); + } else if Some(TokenKind::OpenCurly) == next { + return Err(ParseError::new_error_str_at_span( + &module.source, ident_span, + "unknown identifier, did you mistype a struct type's name?" + )) } let ident_text = ctx.pool.intern(ident_text); @@ -1498,7 +1522,7 @@ impl PassDefinitions { /// these. /// /// Note that the first depth index is used as a hack. -// TODO: @Optimize, @Span fix +// TODO: @Optimize, @Span fix, @Cleanup fn consume_parser_type( source: &InputSource, iter: &mut TokenIter, symbols: &SymbolTable, heap: &Heap, poly_vars: &[Identifier], cur_scope: SymbolScope, wrapping_definition: DefinitionId, allow_inference: bool, first_angle_depth: i32, @@ -1508,6 +1532,8 @@ fn consume_parser_type( depth: i32, } + // After parsing the array modified "[]", we need to insert an array type + // before the most recently parsed type. fn insert_array_before(elements: &mut Vec, depth: i32, span: InputSpan) { let index = elements.iter().rposition(|e| e.depth == depth).unwrap(); elements.insert(index, Entry{ @@ -1517,9 +1543,11 @@ fn consume_parser_type( } // Most common case we just have one type, perhaps with some array - // annotations. + // annotations. This is both the hot-path, and simplifies the state machine + // that follows and is responsible for parsing more complicated types. let element = consume_parser_type_ident(source, iter, symbols, heap, poly_vars, cur_scope, wrapping_definition, allow_inference)?; if iter.next() != Some(TokenKind::OpenAngle) { + let num_embedded = element.variant.num_embedded(); let mut num_array = 0; while iter.next() == Some(TokenKind::OpenSquare) { iter.consume(); @@ -1528,12 +1556,22 @@ fn consume_parser_type( } let array_span = element.full_span; - let mut elements = Vec::with_capacity(num_array + 1); + let mut elements = Vec::with_capacity(num_array + num_embedded + 1); for _ in 0..num_array { elements.push(ParserTypeElement{ full_span: array_span, variant: ParserTypeVariant::Array }); } elements.push(element); + if num_embedded != 0 { + if !allow_inference { + return Err(ParseError::new_error_str_at_span(source, array_span, "type inference is not allowed here")); + } + + for _ in 0..num_embedded { + elements.push(ParserTypeElement { full_span: array_span, variant: ParserTypeVariant::Inferred }); + } + } + return Ok(ParserType{ elements }); }; @@ -1696,12 +1734,26 @@ fn consume_parser_type( }); } } else { - // Mismatch in number of embedded types - let expected_args_text = if expected_subtypes == 1 { - "polymorphic argument" - } else { - "polymorphic arguments" - }; + // Mismatch in number of embedded types, produce a neat error + // message. + let type_name = String::from_utf8_lossy(source.section_at_span(cur_element.element.full_span)); + fn polymorphic_name_text(num: usize) -> &'static str { + if num == 1 { "polymorphic argument" } else { "polymorphic arguments" } + } + fn were_or_was(num: usize) -> &'static str { + if num == 1 { "was" } else { "were" } + } + + if expected_subtypes == 0 { + return Err(ParseError::new_error_at_span( + source, cur_element.element.full_span, + format!( + "the type '{}' is not polymorphic, yet {} {} {} provided", + type_name, encountered_subtypes, polymorphic_name_text(encountered_subtypes), + were_or_was(encountered_subtypes) + ) + )); + } let maybe_infer_text = if allow_inference { " (or none, to perform implicit type inference)" @@ -1712,8 +1764,10 @@ fn consume_parser_type( return Err(ParseError::new_error_at_span( source, cur_element.element.full_span, format!( - "expected {} {}{}, but {} were provided", - expected_subtypes, expected_args_text, maybe_infer_text, encountered_subtypes + "expected {} {}{} for the type '{}', but {} {} provided", + expected_subtypes, polymorphic_name_text(expected_subtypes), + maybe_infer_text, type_name, encountered_subtypes, + were_or_was(encountered_subtypes) ) )); } @@ -1785,7 +1839,7 @@ fn consume_parser_type_ident( SymbolVariant::Module(symbol_module) => { // Expecting more identifiers if Some(TokenKind::ColonColon) != iter.next() { - return Err(ParseError::new_error_str_at_span(source, type_span, "expected type but got module")); + return Err(ParseError::new_error_str_at_span(source, type_span, "expected a type but got a module")); } consume_token(source, iter, TokenKind::ColonColon)?; @@ -1800,13 +1854,22 @@ fn consume_parser_type_ident( let new_symbol = symbols.get_symbol_by_name_defined_in_scope(scope, type_text); if new_symbol.is_none() { - return Err(ParseError::new_error_at_span( - source, next_span, + // If the type is imported in the module then notify the programmer + // that imports do not leak outside of a module + let type_name = String::from_utf8_lossy(type_text); + let module_name = String::from_utf8_lossy(old_text); + let suffix = if symbols.get_symbol_by_name(scope, type_text).is_some() { format!( - "unknown type '{}' in module '{}'", - String::from_utf8_lossy(type_text), - String::from_utf8_lossy(old_text) + ". The module '{}' does import '{}', but these imports are not visible to other modules", + &module_name, &type_name ) + } else { + String::new() + }; + + return Err(ParseError::new_error_at_span( + source, next_span, + format!("unknown type '{}' in module '{}'{}", type_name, module_name, suffix) )); } diff --git a/src/protocol/parser/pass_imports.rs b/src/protocol/parser/pass_imports.rs index 3b40d46fcdc1361c0c76b7d1cde34f523bfd7df7..98c06ff2fa3f4c2d752e446b720efc40f160e2ca 100644 --- a/src/protocol/parser/pass_imports.rs +++ b/src/protocol/parser/pass_imports.rs @@ -161,7 +161,6 @@ impl PassImport { if Some(TokenKind::Ident) == next { // Importing a single symbol - iter.consume(); let (imported_symbol, symbol_definition) = consume_symbol_and_maybe_alias( &module.source, &mut iter, ctx, &module_identifier.value, target_root_id )?; diff --git a/src/protocol/parser/pass_tokenizer.rs b/src/protocol/parser/pass_tokenizer.rs index 0a883bd282504f553249ccdbb0ac2b108ae9539e..23537ca2aea79b2119ac7ad10eddca89a7ce271c 100644 --- a/src/protocol/parser/pass_tokenizer.rs +++ b/src/protocol/parser/pass_tokenizer.rs @@ -154,7 +154,7 @@ impl PassTokenizer { let last_registered_idx = target.ranges[0].end; let last_token_idx = target.tokens.len() as u32; if last_registered_idx != last_token_idx { - self.add_code_range(target, 0, last_registered_idx, last_token_idx); + self.add_code_range(target, 0, last_registered_idx, last_token_idx, NO_RELATION); } // TODO: @remove once I'm sure the algorithm works. For now it is better @@ -644,11 +644,11 @@ impl PassTokenizer { fn add_code_range( &mut self, target: &mut TokenBuffer, parent_idx: i32, - code_start_idx: u32, code_end_idx: u32 + code_start_idx: u32, code_end_idx: u32, next_sibling_idx: i32 ) { let new_range_idx = target.ranges.len() as i32; let parent_range = &mut target.ranges[parent_idx as usize]; - debug_assert_ne!(parent_range.end, code_start_idx, "called push_code_range without a need to do so"); + debug_assert_ne!(parent_range.end, code_end_idx, "called push_code_range without a need to do so"); let sibling_idx = parent_range.last_child_idx; @@ -666,7 +666,7 @@ impl PassTokenizer { num_child_ranges: 0, first_child_idx: NO_RELATION, last_child_idx: NO_RELATION, - next_sibling_idx: new_range_idx + 1, // we're going to push this range below + next_sibling_idx, }); // Fix up the sibling indices @@ -687,7 +687,7 @@ impl PassTokenizer { let last_registered_idx = parent_range.end; if last_registered_idx != first_token_idx { - self.add_code_range(target, parent_idx, last_registered_idx, first_token_idx); + self.add_code_range(target, parent_idx, last_registered_idx, first_token_idx, new_range_idx + 1); } // Push the new range @@ -798,9 +798,10 @@ fn is_integer_literal_start(c: u8) -> bool { } fn maybe_number_remaining(c: u8) -> bool { + // Note: hex range includes the possible binary indicator 'b' and 'B'; return - (c == b'b' || c == b'B' || c == b'o' || c == b'O' || c == b'x' || c == b'X') || - (c >= b'0' && c <= b'9') || + (c == b'o' || c == b'O' || c == b'x' || c == b'X') || + (c >= b'0' && c <= b'9') || (c >= b'A' && c <= b'F') || (c >= b'a' && c <= b'f') || c == b'_'; } diff --git a/src/protocol/parser/pass_validation_linking.rs b/src/protocol/parser/pass_validation_linking.rs index 92b91e316ac6232307795e4a299c0de610e365d9..3d77f3d5fe7b04ff9f8221ff4de2b3472e04b587 100644 --- a/src/protocol/parser/pass_validation_linking.rs +++ b/src/protocol/parser/pass_validation_linking.rs @@ -594,7 +594,7 @@ impl Visitor2 for PassValidationLinking { let ast_definition = ctx.heap[literal.definition].as_union(); return Err(ParseError::new_error_at_span( &ctx.module.source, literal.parser_type.elements[0].full_span, format!( - "the variant does '{}' does not exist on the union '{}'", + "the variant '{}' does not exist on the union '{}'", literal.variant.value.as_str(), ast_definition.identifier.value.as_str() ) )); diff --git a/src/protocol/parser/symbol_table.rs b/src/protocol/parser/symbol_table.rs index 007eb112b223f81bc9ffa940a89f5c2b6ce7e832..1e0b7cd6a6b1b9a74af356007f4d81690deba7aa 100644 --- a/src/protocol/parser/symbol_table.rs +++ b/src/protocol/parser/symbol_table.rs @@ -191,8 +191,6 @@ impl SymbolTable { ); debug_assert!(!self.scope_lookup.contains_key(&new_scope), "inserting scope {:?}, but it already exists", new_scope); - println!("DEBUG: Inserting scope {:?} with parent {:?}", new_scope, parent_scope); - if let Some(parent_scope) = parent_scope { let parent = self.scope_lookup.get_mut(&parent_scope).unwrap(); parent.child_scopes.push(new_scope); @@ -214,7 +212,6 @@ impl SymbolTable { // Note: we do not return a reference because Rust doesn't like it. pub(crate) fn insert_symbol(&mut self, in_scope: SymbolScope, symbol: Symbol) -> Result<(), (Symbol, Symbol)> { debug_assert!(self.scope_lookup.contains_key(&in_scope), "inserting symbol {}, but scope {:?} does not exist", symbol.name.as_str(), in_scope); - println!("DEBUG: Inserting symbol {:?} in scope {:?}", symbol, in_scope); let mut seek_scope = in_scope; loop { let scoped_symbols = self.scope_lookup.get(&seek_scope).unwrap(); diff --git a/src/protocol/parser/token_parsing.rs b/src/protocol/parser/token_parsing.rs index 94f5a43993af91fe6c37bf4e2493ee1a8c0135a2..419f3cb0d86860d563777aa9b4bbb9743bac367a 100644 --- a/src/protocol/parser/token_parsing.rs +++ b/src/protocol/parser/token_parsing.rs @@ -283,6 +283,7 @@ pub(crate) fn consume_comma_separated( /// Consumes an integer literal, may be binary, octal, hexadecimal or decimal, /// and may have separating '_'-characters. +/// TODO: @Cleanup, @Performance pub(crate) fn consume_integer_literal(source: &InputSource, iter: &mut TokenIter, buffer: &mut String) -> Result<(u64, InputSpan), ParseError> { if Some(TokenKind::Integer) != iter.next() { return Err(ParseError::new_error_str_at_pos(source, iter.last_valid_pos(), "expected an integer literal")); @@ -314,7 +315,8 @@ pub(crate) fn consume_integer_literal(source: &InputSource, iter: &mut TokenIter if char == b'_' { continue; } - if !char.is_ascii_digit() { + + if !((char >= b'0' && char <= b'9') || (char >= b'A' && char <= b'F') || (char >= b'a' || char <= b'f')) { return Err(ParseError::new_error_at_span( source, integer_span, format!("incorrectly formatted {} number", radix_name) diff --git a/src/protocol/tests/parser_imports.rs b/src/protocol/tests/parser_imports.rs index b8749160d0afbe78a0de33e822efeca21390be18..7db92555292b4e29f855c9c2bf7fd63f9e7ad59b 100644 --- a/src/protocol/tests/parser_imports.rs +++ b/src/protocol/tests/parser_imports.rs @@ -13,7 +13,7 @@ fn test_module_import() { ") .with_source(" import external; - s32 caller() { + func caller() -> s32 { auto a = external::Foo{ field: 0 }; return a.field; } @@ -28,7 +28,7 @@ fn test_module_import() { ") .with_source(" import external.domain; - s32 caller() { + func caller() -> s32 { auto a = domain::Foo{ field: 0 }; return a.field; } @@ -43,7 +43,7 @@ fn test_module_import() { ") .with_source(" import external as aliased; - s32 caller() { + func caller() -> s32 { auto a = aliased::Foo{ field: 0 }; return a.field; } @@ -61,7 +61,7 @@ fn test_single_symbol_import() { ") .with_source(" import external::Foo; - s32 caller() { + func caller() -> s32 { auto a = Foo{ field: 1 }; auto b = Foo{ field: 2 }; return a.field + b.field; @@ -76,7 +76,7 @@ fn test_single_symbol_import() { ") .with_source(" import external::Foo as Bar; - s32 caller() { + func caller() -> s32 { return Bar{ field: 0 }.field; } ") @@ -107,7 +107,7 @@ fn test_multi_symbol_import() { ") .with_source(" import external::{Foo, Bar}; - s8 caller() { + func caller() -> s8 { return Foo{f:0}.f + Bar{b:1}.b; } ") @@ -122,29 +122,28 @@ fn test_multi_symbol_import() { ") .with_source(" import external::{Foo as Bar, Bar as Foo}; - s8 caller() { + func caller() -> s8 { return Foo{in_bar:0}.in_bar + Bar{in_foo:0}.in_foo; }") .compile() .expect_ok(); - // TODO: Re-enable once std lib is properly implemented - // Tester::new("import all") - // .with_source(" - // #module external - // struct Foo { s8 f }; - // struct Bar { s8 b }; - // ") - // .with_source(" - // import external::*; - // s8 caller() { - // auto f = Foo{f:0}; - // auto b = Bar{b:0}; - // return f.f + b.b; - // } - // ") - // .compile() - // .expect_ok(); + Tester::new("import all") + .with_source(" + #module external + struct Foo { s8 f } + struct Bar { s8 b } + ") + .with_source(" + import external::*; + func caller() -> s8 { + auto f = Foo{f:0}; + auto b = Bar{b:0}; + return f.f + b.b; + } + ") + .compile() + .expect_ok(); } #[test] @@ -156,7 +155,7 @@ fn test_illegal_import_use() { ") .with_source(" import external; - s8 caller() { + func caller() -> s8 { auto foo = external::Foo{ f: 0 }; return foo.f; } @@ -164,7 +163,7 @@ fn test_illegal_import_use() { .compile() .expect_err() .error(|e| { e - .assert_msg_has(0, "the type Foo is not polymorphic"); + .assert_msg_has(0, "the type 'external::Foo' is not polymorphic"); }); Tester::new("mismatched polymorphic args") @@ -174,7 +173,7 @@ fn test_illegal_import_use() { ") .with_source(" import external; - s8 caller() { + func caller() -> s8 { auto foo = external::Foo{ f: 0 }; return foo.f; }") @@ -182,7 +181,7 @@ fn test_illegal_import_use() { .expect_err() .error(|e| { e .assert_msg_has(0, "expected 1 polymorphic") - .assert_msg_has(0, "2 were specified"); + .assert_msg_has(0, "2 were provided"); }); Tester::new("module as type") @@ -191,7 +190,7 @@ fn test_illegal_import_use() { ") .with_source(" import external; - s8 caller() { + func caller() -> s8 { auto foo = external{ f: 0 }; return 0; } @@ -199,25 +198,22 @@ fn test_illegal_import_use() { .compile() .expect_err() .error(|e| { e - .assert_msg_has(0, "resolved to a namespace"); + .assert_msg_has(0, "expected a type but got a module"); }); - Tester::new("more namespaces than needed, not polymorphic") + Tester::new("missing type") .with_source(" #module external - struct Foo { s8 f } + struct Bar {} ") .with_source(" - import external; - s8 caller() { - auto foo = external::Foo::f{ f: 0 }; - return 0; - }") + import external::Foo; + ") .compile() .expect_err() .error(|e| { e - .assert_msg_has(0, "not fully resolve this identifier") - .assert_msg_has(0, "able to match 'external::Foo'"); + .assert_msg_has(0, "could not find symbol 'Foo' within module 'external'") + .assert_occurs_at(0, "Foo"); }); Tester::new("import from another import") @@ -232,14 +228,15 @@ fn test_illegal_import_use() { ") .with_source(" import mod2; - s8 caller() { + func caller() -> s8 { auto bar = mod2::Bar{ f: mod2::Foo{ f: 0 } }; return var.f.f; }") .compile() .expect_err() .error(|e| { e - .assert_msg_has(0, "Could not resolve this identifier") - .assert_occurs_at(0, "mod2::Foo"); + .assert_msg_has(0, "unknown type 'Foo' in module 'mod2'") + .assert_msg_has(0, "module 'mod2' does import 'Foo'") + .assert_occurs_at(0, "Foo"); }); } \ No newline at end of file diff --git a/src/protocol/tests/parser_inference.rs b/src/protocol/tests/parser_inference.rs index 94f4d355af411249eaaea90dea7e5649ca980530..bfc15f41d6b1fa9c9ba7ad9b6ff7b2a79c124534 100644 --- a/src/protocol/tests/parser_inference.rs +++ b/src/protocol/tests/parser_inference.rs @@ -159,61 +159,61 @@ fn test_struct_inference() { }); }); - // Tester::new_single_source_expect_ok( - // "by field access", - // " - // struct Pair{ T1 first, T2 second } - // func construct(T1 first, T2 second) -> Pair { - // return Pair{ first: first, second: second }; - // } - // test() -> s32 { - // auto first = 0; - // auto second = 1; - // auto pair = construct(first, second); - // s8 assign_first = 0; - // s64 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("s8"); - // }) - // .for_variable("second", |v| { v - // .assert_parser_type("auto") - // .assert_concrete_type("s64"); - // }) - // .for_variable("pair", |v| { v - // .assert_parser_type("auto") - // .assert_concrete_type("Pair"); - // }); - // }); - // - // Tester::new_single_source_expect_ok( - // "by nested field access", - // " - // struct Node{ T1 l, T2 r } - // func construct(T1 l, T2 r) -> Node { - // return Node{ l: l, r: r }; - // } - // func fix_poly(Node a) -> s32 { return 0; } - // func test() -> s32 { - // s8 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("Node>"); - // }); - // }); + Tester::new_single_source_expect_ok( + "by field access", + " + struct Pair{ T1 first, T2 second } + func construct(T1 first, T2 second) -> Pair { + return Pair{ first: first, second: second }; + } + func test() -> s32 { + auto first = 0; + auto second = 1; + auto pair = construct(first, second); + s8 assign_first = 0; + s64 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("s8"); + }) + .for_variable("second", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("s64"); + }) + .for_variable("pair", |v| { v + .assert_parser_type("auto") + .assert_concrete_type("Pair"); + }); + }); + + Tester::new_single_source_expect_ok( + "by nested field access", + " + struct Node{ T1 l, T2 r } + func construct(T1 l, T2 r) -> Node { + return Node{ l: l, r: r }; + } + func fix_poly(Node a) -> s32 { return 0; } + func test() -> s32 { + s8 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("Node>"); + }); + }); } #[test] @@ -222,7 +222,7 @@ fn test_enum_inference() { "no polymorphic vars", " enum Choice { A, B } - test_instances() -> s32 { + func test_instances() -> s32 { auto foo = Choice::A; auto bar = Choice::B; return 0; @@ -247,14 +247,14 @@ fn test_enum_inference() { B, } func fix_as_s8(Choice arg) -> s32 { return 0; } - fix_as_s32(Choice arg) -> s32 { return 0; } - test_instances() -> s32 { + func fix_as_s32(Choice arg) -> s32 { return 0; } + func test_instances() -> s32 { auto choice_s8 = Choice::A; auto choice_s32_1 = Choice::B; Choice choice_s32_2 = Choice::B; fix_as_s8(choice_s8); fix_as_s32(choice_s32_1); - return fix_as_int(choice_s32_2); + return fix_as_s32(choice_s32_2); } " ).for_function("test_instances", |f| { f @@ -262,13 +262,13 @@ fn test_enum_inference() { .assert_parser_type("auto") .assert_concrete_type("Choice"); }) - .for_variable("choice_int1", |v| { v + .for_variable("choice_s32_1", |v| { v .assert_parser_type("auto") - .assert_concrete_type("Choice"); + .assert_concrete_type("Choice"); }) - .for_variable("choice_int2", |v| { v + .for_variable("choice_s32_2", |v| { v .assert_parser_type("Choice") - .assert_concrete_type("Choice"); + .assert_concrete_type("Choice"); }); }); @@ -276,11 +276,11 @@ fn test_enum_inference() { "two polymorphic vars", " enum Choice{ A, B, } - fix_t1(Choice arg) -> s32 { return 0; } - fix_t2(Choice arg) -> s32 { return 0; } - test_instances() -> int { + func fix_t1(Choice arg) -> s32 { return 0; } + func fix_t2(Choice arg) -> s32 { return 0; } + func test_instances() -> s32 { Choice choice1 = Choice::A; - Choice choice2 = Choice::A; + Choice choice2 = Choice::A; Choice choice3 = Choice::B; auto choice4 = Choice::B; fix_t1(choice1); fix_t1(choice2); fix_t1(choice3); fix_t1(choice4); @@ -291,19 +291,19 @@ fn test_enum_inference() { ).for_function("test_instances", |f| { f .for_variable("choice1", |v| { v .assert_parser_type("Choice") - .assert_concrete_type("Choice"); + .assert_concrete_type("Choice"); }) .for_variable("choice2", |v| { v - .assert_parser_type("Choice") - .assert_concrete_type("Choice"); + .assert_parser_type("Choice") + .assert_concrete_type("Choice"); }) .for_variable("choice3", |v| { v .assert_parser_type("Choice") - .assert_concrete_type("Choice"); + .assert_concrete_type("Choice"); }) .for_variable("choice4", |v| { v .assert_parser_type("auto") - .assert_concrete_type("Choice"); + .assert_concrete_type("Choice"); }); }); } @@ -335,7 +335,7 @@ fn test_failed_polymorph_inference() { "struct literal inference mismatch", " struct Pair{ T first, T second } - call() -> s32 { + func call() -> s32 { s8 first_arg = 5; s64 second_arg = 2; auto pair = Pair{ first: first_arg, second: second_arg }; @@ -362,14 +362,14 @@ fn test_failed_polymorph_inference() { func call() -> s32 { auto a = Uninteresting::Variant; fix_t(a); - fix_t(a); + fix_t(a); return 4; } " ).error(|e| { e .assert_num(2) .assert_any_msg_has("type 'Uninteresting'") - .assert_any_msg_has("type 'Uninteresting'"); + .assert_any_msg_has("type 'Uninteresting'"); }); Tester::new_single_source_expect_err( @@ -388,7 +388,7 @@ fn test_failed_polymorph_inference() { .assert_occurs_at(0, ".") .assert_msg_has(0, "Conflicting type for polymorphic variable 'Shazam'") .assert_msg_has(1, "inferred it to 's8'") - .assert_msg_has(2, "inferred it to 'int'"); + .assert_msg_has(2, "inferred it to 's32'"); }); // TODO: Needs better error messages anyway, but this failed before diff --git a/src/protocol/tests/parser_monomorphs.rs b/src/protocol/tests/parser_monomorphs.rs index 4f5a6fc4258f097e5ce3dd8439220b664e225e3e..d4d0ee270ead1db7ba6feefbb0c88b91478a2b40 100644 --- a/src/protocol/tests/parser_monomorphs.rs +++ b/src/protocol/tests/parser_monomorphs.rs @@ -18,7 +18,7 @@ fn test_struct_monomorphs() { "single polymorph", " struct Number{ T number } - s32 instantiator() { + func instantiator() -> s32 { auto a = Number{ number: 0 }; auto b = Number{ number: 1 }; auto c = Number{ number: 2 }; @@ -46,7 +46,7 @@ fn test_enum_monomorphs() { "no polymorph", " enum Answer{ Yes, No } - s32 do_it() { auto a = Answer::Yes; return 0; } + func do_it() -> s32 { auto a = Answer::Yes; return 0; } " ).for_enum("Answer", |e| { e .assert_num_monomorphs(0); @@ -56,7 +56,7 @@ fn test_enum_monomorphs() { "single polymorph", " enum Answer { Yes, No } - s32 instantiator() { + func instantiator() -> s32 { auto a = Answer::Yes; auto b = Answer::No; auto c = Answer::Yes; @@ -77,8 +77,8 @@ fn test_union_monomorphs() { Tester::new_single_source_expect_ok( "no polymorph", " - union Trinary { Undefined, Value(boolean) } - s32 do_it() { auto a = Trinary::Value(true); return 0; } + union Trinary { Undefined, Value(bool) } + func do_it() -> s32 { auto a = Trinary::Value(true); return 0; } " ).for_union("Trinary", |e| { e .assert_num_monomorphs(0); @@ -90,10 +90,10 @@ fn test_union_monomorphs() { "polymorphs", " union Result{ Ok(T), Err(E) } - s32 instantiator() { + func instantiator() -> s32 { s16 a_s16 = 5; - auto a = Result::Ok(0); - auto b = Result::Ok(true); + auto a = Result::Ok(0); + auto b = Result::Ok(true); auto c = Result, Result>::Err(Result::Ok(5)); auto d = Result, auto>::Err(Result::Ok(a_s16)); return 0; diff --git a/src/protocol/tests/parser_validation.rs b/src/protocol/tests/parser_validation.rs index bf9e3b8f311230cfd5de5d805fb1c6c4d4505e84..bc6d059c5e2a3de48e42595a44266bffbce77247 100644 --- a/src/protocol/tests/parser_validation.rs +++ b/src/protocol/tests/parser_validation.rs @@ -11,7 +11,7 @@ fn test_correct_struct_instance() { "single field", " struct Foo { s32 a } - Foo bar(s32 arg) { return Foo{ a: arg }; } + func bar(s32 arg) -> Foo { return Foo{ a: arg }; } " ); @@ -19,7 +19,7 @@ fn test_correct_struct_instance() { "multiple fields", " struct Foo { s32 a, s32 b } - Foo bar(s32 arg) { return Foo{ a: arg, b: arg }; } + func bar(s32 arg) -> Foo { return Foo{ a: arg, b: arg }; } " ); @@ -27,7 +27,7 @@ fn test_correct_struct_instance() { "single field, explicit polymorph", " struct Foo{ T field } - Foo bar(s32 arg) { return Foo{ field: arg }; } + func bar(s32 arg) -> Foo { return Foo{ field: arg }; } " ); @@ -35,7 +35,7 @@ fn test_correct_struct_instance() { "single field, implicit polymorph", " struct Foo{ T field } - s32 bar(s32 arg) { + func bar(s32 arg) -> s32 { auto thingo = Foo{ field: arg }; return arg; } @@ -46,7 +46,7 @@ fn test_correct_struct_instance() { "multiple fields, same explicit polymorph", " struct Pair{ T1 first, T2 second } - s32 bar(s32 arg) { + func bar(s32 arg) -> s32 { auto qux = Pair{ first: arg, second: arg }; return arg; } @@ -57,7 +57,7 @@ fn test_correct_struct_instance() { "multiple fields, same implicit polymorph", " struct Pair{ T1 first, T2 second } - s32 bar(s32 arg) { + func bar(s32 arg) -> s32 { auto wup = Pair{ first: arg, second: arg }; return arg; } @@ -68,7 +68,7 @@ fn test_correct_struct_instance() { "multiple fields, different explicit polymorph", " struct Pair{ T1 first, T2 second } - s32 bar(s32 arg1, s8 arg2) { + func bar(s32 arg1, s8 arg2) -> s32 { auto shoo = Pair{ first: arg1, second: arg2 }; return arg1; } @@ -79,7 +79,7 @@ fn test_correct_struct_instance() { "multiple fields, different implicit polymorph", " struct Pair{ T1 first, T2 second } - s32 bar(s32 arg1, s8 arg2) { + func bar(s32 arg1, s8 arg2) -> s32 { auto shrubbery = Pair{ first: arg1, second: arg2 }; return arg1; } @@ -103,8 +103,8 @@ fn test_incorrect_struct_instance() { Tester::new_single_source_expect_err( "reused field in instance", " - struct Foo{ int a, int b } - int bar() { + struct Foo{ s32 a, s32 b } + func bar() -> s32 { auto foo = Foo{ a: 5, a: 3 }; return 0; } @@ -117,8 +117,8 @@ fn test_incorrect_struct_instance() { Tester::new_single_source_expect_err( "missing field", " - struct Foo { int a, int b } - int bar() { + struct Foo { s32 a, s32 b } + func bar() -> s32 { auto foo = Foo{ a: 2 }; return 0; } @@ -131,8 +131,8 @@ fn test_incorrect_struct_instance() { Tester::new_single_source_expect_err( "missing fields", " - struct Foo { int a, int b, int c } - int bar() { + struct Foo { s32 a, s32 b, s32 c } + func bar() -> s32 { auto foo = Foo{ a: 2 }; return 0; } @@ -149,7 +149,7 @@ fn test_correct_enum_instance() { "single variant", " enum Foo { A } - Foo bar() { return Foo::A; } + func bar() -> Foo { return Foo::A; } " ); @@ -157,7 +157,7 @@ fn test_correct_enum_instance() { "multiple variants", " enum Foo { A=15, B = 0xF } - Foo bar() { auto a = Foo::A; return Foo::B; } + func bar() -> Foo { auto a = Foo::A; return Foo::B; } " ); @@ -165,7 +165,7 @@ fn test_correct_enum_instance() { "explicit single polymorph", " enum Foo{ A } - Foo bar() { return Foo::A; } + func bar() -> Foo { return Foo::A; } " ); @@ -173,7 +173,7 @@ fn test_correct_enum_instance() { "explicit multi-polymorph", " enum Foo{ A, B } - Foo bar() { return Foo::B; } + func bar() -> Foo { return Foo::B; } " ); } @@ -184,7 +184,7 @@ fn test_incorrect_enum_instance() { "variant name reuse", " enum Foo { A, A } - Foo bar() { return Foo::A; } + func bar() -> Foo { return Foo::A; } " ).error(|e| { e .assert_num(2) @@ -198,7 +198,7 @@ fn test_incorrect_enum_instance() { "undefined variant", " enum Foo { A } - Foo bar() { return Foo::B; } + func bar() -> Foo { return Foo::B; } " ).error(|e| { e .assert_num(1) @@ -212,7 +212,7 @@ fn test_correct_union_instance() { "single tag", " union Foo { A } - Foo bar() { return Foo::A; } + func bar() -> Foo { return Foo::A; } " ); @@ -220,39 +220,39 @@ fn test_correct_union_instance() { "multiple tags", " union Foo { A, B } - Foo bar() { return Foo::B; } + func bar() -> Foo { return Foo::B; } " ); Tester::new_single_source_expect_ok( "single embedded", " - union Foo { A(int) } - Foo bar() { return Foo::A(5); } + union Foo { A(s32) } + func bar() -> Foo { return Foo::A(5); } " ); Tester::new_single_source_expect_ok( "multiple embedded", " - union Foo { A(int), B(s8) } - Foo bar() { return Foo::B(2); } + union Foo { A(s32), B(s8) } + func bar() -> Foo { return Foo::B(2); } " ); Tester::new_single_source_expect_ok( "multiple values in embedded", " - union Foo { A(int, s8) } - Foo bar() { return Foo::A(0, 2); } + union Foo { A(s32, s8) } + func bar() -> Foo { return Foo::A(0, 2); } " ); Tester::new_single_source_expect_ok( "mixed tag/embedded", " - union OptionInt { None, Some(int) } - OptionInt bar() { return OptionInt::Some(3); } + union OptionInt { None, Some(s32) } + func bar() -> OptionInt { return OptionInt::Some(3); } " ); @@ -260,14 +260,14 @@ fn test_correct_union_instance() { "single polymorphic var", " union Option { None, Some(T) } - Option bar() { return Option::Some(3); }" + func bar() -> Option { return Option::Some(3); }" ); Tester::new_single_source_expect_ok( "multiple polymorphic vars", " union Result { Ok(T), Err(E), } - Result bar() { return Result::Ok(3); } + func bar() -> Result { return Result::Ok(3); } " ); @@ -275,7 +275,7 @@ fn test_correct_union_instance() { "multiple polymorphic in one variant", " union MaybePair{ None, Some(T1, T2) } - MaybePair bar() { return MaybePair::Some(1, 2); } + func bar() -> MaybePair { return MaybePair::Some(1, 2); } " ); } @@ -298,13 +298,13 @@ fn test_incorrect_union_instance() { Tester::new_single_source_expect_err( "embedded-variant name reuse", " - union Foo{ A(int), A(s8) } + union Foo{ A(s32), A(s8) } " ).error(|e| { e .assert_num(2) .assert_occurs_at(0, "A(s8)") .assert_msg_has(0, "union variant is defined more than once") - .assert_occurs_at(1, "A(int)") + .assert_occurs_at(1, "A(s32)") .assert_msg_has(1, "other union variant"); }); @@ -312,7 +312,7 @@ fn test_incorrect_union_instance() { "undefined variant", " union Silly{ Thing(s8) } - Silly bar() { return Silly::Undefined(5); } + func bar() -> Silly { return Silly::Undefined(5); } " ).error(|e| { e .assert_msg_has(0, "variant 'Undefined' does not exist on the union 'Silly'"); @@ -321,8 +321,8 @@ fn test_incorrect_union_instance() { Tester::new_single_source_expect_err( "using tag instead of embedded", " - union Foo{ A(int) } - Foo bar() { return Foo::A; } + union Foo{ A(s32) } + func bar() -> Foo { return Foo::A; } " ).error(|e| { e .assert_msg_has(0, "variant 'A' of union 'Foo' expects 1 embedded values, but 0 were"); @@ -332,7 +332,7 @@ fn test_incorrect_union_instance() { "using embedded instead of tag", " union Foo{ A } - Foo bar() { return Foo::A(3); } + func bar() -> Foo { return Foo::A(3); } " ).error(|e| { e .assert_msg_has(0, "The variant 'A' of union 'Foo' expects 0"); @@ -341,14 +341,14 @@ fn test_incorrect_union_instance() { Tester::new_single_source_expect_err( "wrong embedded value", " - union Foo{ A(int) } - Foo bar() { return Foo::A(false); } + union Foo{ A(s32) } + func bar() -> Foo { return Foo::A(false); } " ).error(|e| { e .assert_occurs_at(0, "Foo::A") - .assert_msg_has(0, "Failed to fully resolve") + .assert_msg_has(0, "failed to fully resolve") .assert_occurs_at(1, "false") - .assert_msg_has(1, "has been resolved to 'int'") + .assert_msg_has(1, "has been resolved to 's32'") .assert_msg_has(1, "has been resolved to 'bool'"); }); } \ No newline at end of file diff --git a/src/protocol/tests/utils.rs b/src/protocol/tests/utils.rs index 9a4552e1bb1cec481978aad5ca51698f20b7094a..7b08b933e661ab2493b5e9dcd350a6044fb53c7b 100644 --- a/src/protocol/tests/utils.rs +++ b/src/protocol/tests/utils.rs @@ -124,7 +124,7 @@ pub(crate) struct AstOkTester { heap: Heap, symbols: SymbolTable, types: TypeTable, - pool: StringPool, + pool: StringPool, // This is stored because if we drop it on the floor, we lose all our `StringRef<'static>`s } impl AstOkTester { @@ -212,7 +212,6 @@ impl AstOkTester { let mut found = false; for definition in self.heap.definitions.iter() { if let Definition::Function(definition) = definition { - println!("DEBUG: Have {}", definition.identifier.value.as_str()); if definition.identifier.value.as_str() != name { continue; } @@ -222,8 +221,6 @@ impl AstOkTester { f(tester); found = true; break; - } else { - println!("DEBUG: Have (not a function, but) {}", definition.identifier().value.as_str()); } }