better parser errors
This commit is contained in:
parent
872c8e356d
commit
f83a8e3ca1
13 changed files with 639 additions and 78 deletions
22
error_test/parser/attribute_errors.sui
Normal file
22
error_test/parser/attribute_errors.sui
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Attribute errors
|
||||
|
||||
# Missing attribute name
|
||||
@ 42
|
||||
|
||||
# Invalid attribute argument
|
||||
@deprecated invalid
|
||||
|
||||
# Missing equals in key-value attribute
|
||||
@version "1.0"
|
||||
|
||||
# Missing closing parenthesis in attribute
|
||||
@deprecated("old", reason = "new"
|
||||
|
||||
# Invalid attribute syntax (missing value after =)
|
||||
@version =
|
||||
|
||||
# Attribute on invalid location
|
||||
fn test() -> int
|
||||
@inline
|
||||
let x = 5
|
||||
x
|
||||
47
error_test/parser/bracket_errors.sui
Normal file
47
error_test/parser/bracket_errors.sui
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Bracket and parenthesis mismatch errors
|
||||
|
||||
# Missing closing parenthesis in function call
|
||||
fn test() -> int
|
||||
let f = lambda(x) x
|
||||
f(5
|
||||
42
|
||||
|
||||
# Missing closing bracket in array access
|
||||
fn test() -> int
|
||||
let arr = [1, 2, 3]
|
||||
arr[0
|
||||
42
|
||||
|
||||
# Missing closing brace in struct literal
|
||||
fn test() -> int
|
||||
struct Point
|
||||
x: int
|
||||
y: int
|
||||
end
|
||||
let p = Point { x: 5, y: 10
|
||||
p.x
|
||||
|
||||
# Missing closing parenthesis in tuple
|
||||
fn test() -> int
|
||||
let t = (1, 2, 3
|
||||
42
|
||||
|
||||
# Missing closing angle bracket in generic
|
||||
fn test() -> int
|
||||
let v = Vec<int
|
||||
42
|
||||
|
||||
# Extra closing parenthesis
|
||||
fn test() -> int
|
||||
let x = (5))
|
||||
x
|
||||
|
||||
# Mismatched brackets
|
||||
fn test() -> int
|
||||
let x = [1, 2, 3}
|
||||
x[0]
|
||||
|
||||
# Missing opening parenthesis in expression
|
||||
fn test() -> int
|
||||
let x = 5 + 3)
|
||||
x
|
||||
49
error_test/parser/control_flow_errors.sui
Normal file
49
error_test/parser/control_flow_errors.sui
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Control flow errors
|
||||
|
||||
# Missing condition in if
|
||||
fn test() -> int
|
||||
if
|
||||
42
|
||||
else
|
||||
0
|
||||
|
||||
# Missing body in if
|
||||
fn test() -> int
|
||||
if true
|
||||
else
|
||||
0
|
||||
|
||||
# Missing variable in for loop
|
||||
fn test() -> int
|
||||
for in [1, 2, 3]
|
||||
item
|
||||
0
|
||||
|
||||
# Missing 'in' keyword in for loop
|
||||
fn test() -> int
|
||||
for item [1, 2, 3]
|
||||
item
|
||||
0
|
||||
|
||||
# Missing iterable in for loop
|
||||
fn test() -> int
|
||||
for item in
|
||||
item
|
||||
0
|
||||
|
||||
# Invalid while syntax
|
||||
fn test() -> int
|
||||
while
|
||||
42
|
||||
|
||||
# Missing parameters in lambda
|
||||
fn test() -> int
|
||||
let f = lambda
|
||||
42
|
||||
f()
|
||||
|
||||
# Missing closing parenthesis in lambda
|
||||
fn test() -> int
|
||||
let f = lambda(x
|
||||
x
|
||||
f(5)
|
||||
29
error_test/parser/enum_errors.sui
Normal file
29
error_test/parser/enum_errors.sui
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Enum syntax errors
|
||||
|
||||
# Missing enum name
|
||||
enum
|
||||
Red
|
||||
Green
|
||||
end
|
||||
|
||||
# Missing variant name
|
||||
enum Color
|
||||
Red
|
||||
Green
|
||||
end
|
||||
|
||||
# Invalid variant syntax (missing parentheses for tuple variant)
|
||||
enum Result
|
||||
Ok int
|
||||
Err string
|
||||
end
|
||||
|
||||
# Missing comma between variants
|
||||
enum Color
|
||||
Red Green
|
||||
end
|
||||
|
||||
# Missing end keyword
|
||||
enum Color
|
||||
Red
|
||||
Green
|
||||
43
error_test/parser/expression_errors.sui
Normal file
43
error_test/parser/expression_errors.sui
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Expression and statement errors
|
||||
|
||||
# Missing variable name in let
|
||||
fn test() -> int
|
||||
let = 5
|
||||
42
|
||||
|
||||
# Missing equals in assignment
|
||||
fn test() -> int
|
||||
let x 5
|
||||
x
|
||||
|
||||
# Missing expression after operator
|
||||
fn test() -> int
|
||||
5 +
|
||||
|
||||
# Invalid array syntax (missing comma)
|
||||
fn test() -> int
|
||||
let arr = [1 2 3]
|
||||
arr[0]
|
||||
|
||||
# Missing closing bracket in array
|
||||
fn test() -> int
|
||||
let arr = [1, 2, 3
|
||||
arr[0]
|
||||
|
||||
# Invalid struct literal (missing field value)
|
||||
fn test() -> int
|
||||
struct Point
|
||||
x: int
|
||||
y: int
|
||||
end
|
||||
let p = Point { x: 5, y }
|
||||
p.x
|
||||
|
||||
# Missing colon in struct literal
|
||||
fn test() -> int
|
||||
struct Point
|
||||
x: int
|
||||
y: int
|
||||
end
|
||||
let p = Point { x 5, y: 10 }
|
||||
p.x
|
||||
24
error_test/parser/function_errors.sui
Normal file
24
error_test/parser/function_errors.sui
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Function syntax errors
|
||||
|
||||
# Missing function name
|
||||
fn -> int
|
||||
42
|
||||
|
||||
# Missing parameter type annotation
|
||||
fn add(x, y: int) -> int
|
||||
x + y
|
||||
|
||||
# Missing comma in parameters
|
||||
fn add(x: int y: int) -> int
|
||||
x + y
|
||||
|
||||
# Missing closing parenthesis
|
||||
fn add(x: int, y: int -> int
|
||||
x + y
|
||||
|
||||
# Missing return type arrow
|
||||
fn add(x: int, y: int) int
|
||||
x + y
|
||||
|
||||
# Invalid function body (missing expression)
|
||||
fn test() -> int
|
||||
41
error_test/parser/pattern_errors.sui
Normal file
41
error_test/parser/pattern_errors.sui
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Pattern matching errors
|
||||
|
||||
# Invalid pattern in match
|
||||
fn test() -> int
|
||||
match 42
|
||||
x => x
|
||||
end
|
||||
|
||||
# Missing colon in struct pattern
|
||||
fn test() -> int
|
||||
struct Point
|
||||
x: int
|
||||
y: int
|
||||
end
|
||||
let p = Point { x: 5, y: 10 }
|
||||
match p
|
||||
Point { x 5, y: 10 } => 1
|
||||
end
|
||||
|
||||
# Invalid enum pattern
|
||||
fn test() -> int
|
||||
enum Result
|
||||
Ok(int)
|
||||
Err(string)
|
||||
end
|
||||
let r = Result::Ok(42)
|
||||
match r
|
||||
Result::Ok value => value
|
||||
end
|
||||
|
||||
# Missing pattern in match arm
|
||||
fn test() -> int
|
||||
match 42
|
||||
=> 0
|
||||
end
|
||||
|
||||
# Missing fat arrow in match arm
|
||||
fn test() -> int
|
||||
match 42
|
||||
x 0
|
||||
end
|
||||
30
error_test/parser/struct_errors.sui
Normal file
30
error_test/parser/struct_errors.sui
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Struct syntax errors
|
||||
|
||||
# Missing struct name
|
||||
struct
|
||||
x: int
|
||||
y: int
|
||||
end
|
||||
|
||||
# Missing field type
|
||||
struct Point
|
||||
x
|
||||
y: int
|
||||
end
|
||||
|
||||
# Missing colon in field
|
||||
struct Point
|
||||
x int
|
||||
y: int
|
||||
end
|
||||
|
||||
# Missing end keyword
|
||||
struct Point
|
||||
x: int
|
||||
y: int
|
||||
|
||||
# Invalid field separator (semicolon instead of comma)
|
||||
struct Point
|
||||
x: int;
|
||||
y: int
|
||||
end
|
||||
45
error_test/parser/top_level_errors.sui
Normal file
45
error_test/parser/top_level_errors.sui
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Top-level declaration errors
|
||||
|
||||
# Invalid top-level token
|
||||
invalid_token
|
||||
|
||||
# Incomplete use statement
|
||||
use
|
||||
|
||||
# Missing path in use
|
||||
use as alias
|
||||
|
||||
# Invalid use syntax
|
||||
use "path" invalid
|
||||
|
||||
# Incomplete load statement
|
||||
load
|
||||
|
||||
# Missing alias in load
|
||||
load "lib"
|
||||
|
||||
# Invalid load syntax
|
||||
load "lib" invalid alias
|
||||
|
||||
# Incomplete extern declaration
|
||||
extern
|
||||
|
||||
# Missing return type in extern
|
||||
extern add(x: int, y: int)
|
||||
|
||||
# Invalid extern syntax
|
||||
extern add(x: int, y: int) -> int invalid
|
||||
|
||||
# Incomplete impl declaration
|
||||
impl
|
||||
|
||||
# Missing methods in impl
|
||||
impl MyType
|
||||
end
|
||||
|
||||
# Incomplete trait declaration
|
||||
trait
|
||||
|
||||
# Missing methods in trait
|
||||
trait MyTrait
|
||||
end
|
||||
29
error_test/parser/type_errors.sui
Normal file
29
error_test/parser/type_errors.sui
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Type annotation errors
|
||||
|
||||
# Invalid type name
|
||||
fn test() -> invalidtype
|
||||
42
|
||||
|
||||
# Missing closing bracket in array type
|
||||
fn test() -> [int
|
||||
[]
|
||||
|
||||
# Missing type parameter
|
||||
fn test() -> Option
|
||||
none
|
||||
|
||||
# Invalid generic syntax
|
||||
fn test() -> Vec int
|
||||
[]
|
||||
|
||||
# Missing closing angle bracket
|
||||
fn test() -> HashMap<string, int
|
||||
{}
|
||||
|
||||
# Invalid pointer syntax
|
||||
fn test() -> *int
|
||||
42
|
||||
|
||||
# Invalid function type syntax
|
||||
fn test() -> fn int -> int
|
||||
42
|
||||
348
src/parser.rs
348
src/parser.rs
|
|
@ -57,16 +57,52 @@ impl Parser {
|
|||
Ok(span)
|
||||
}
|
||||
Some((token, span)) => Err(ParseError {
|
||||
message: format!("Expected {:?}, found {:?}", expected, token),
|
||||
message: self.expect_error_message(&expected, &token),
|
||||
span: Span::new(&span, self.file.clone()),
|
||||
}),
|
||||
None => Err(ParseError {
|
||||
message: format!("Expected {:?}, found EOF", expected),
|
||||
message: self.expect_error_message(&expected, &Token::Variable("EOF".to_string())),
|
||||
span: Span::new(&(0..0), self.file.clone()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_error_message(&self, expected: &Token, found: &Token) -> String {
|
||||
match expected {
|
||||
Token::LParen => format!(
|
||||
"Expected '(' to start parameter list or grouping. Found {:?}",
|
||||
found
|
||||
),
|
||||
Token::RParen => format!(
|
||||
"Expected ')' to close parameter list or grouping. Found {:?}",
|
||||
found
|
||||
),
|
||||
Token::LBrace => format!(
|
||||
"Expected '{{' to start block or struct literal. Found {:?}",
|
||||
found
|
||||
),
|
||||
Token::RBrace => format!(
|
||||
"Expected '}}' to close block or struct literal. Found {:?}",
|
||||
found
|
||||
),
|
||||
Token::LBracket => format!("Expected '[' to start array literal. Found {:?}", found),
|
||||
Token::RBracket => format!("Expected ']' to close array literal. Found {:?}", found),
|
||||
Token::Colon => format!(
|
||||
"Expected ':' for type annotation or struct field. Found {:?}",
|
||||
found
|
||||
),
|
||||
Token::Semicolon => format!("Expected ';' to end statement. Found {:?}", found),
|
||||
Token::Comma => format!("Expected ',' to separate items. Found {:?}", found),
|
||||
Token::Arrow => format!("Expected '->' for function return type. Found {:?}", found),
|
||||
Token::Assign => format!(
|
||||
"Expected '=' for assignment or initialization. Found {:?}",
|
||||
found
|
||||
),
|
||||
Token::KeywordEnd => format!("Expected 'end' to close block. Found {:?}", found),
|
||||
_ => format!("Expected {:?}, found {:?}", expected, found),
|
||||
}
|
||||
}
|
||||
|
||||
fn error<T>(&self, msg: String, span: Range<usize>) -> Result<T, ParseError> {
|
||||
Err(ParseError {
|
||||
message: msg,
|
||||
|
|
@ -90,10 +126,10 @@ impl Parser {
|
|||
let path = match self.next() {
|
||||
Some((Token::String(s), _)) => s,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected string after 'use'".to_string(), span);
|
||||
return self.error("Expected library path after 'use'. Example: use \"std/io\"".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected string after 'use'".to_string(), start..start);
|
||||
return self.error("Expected library path after 'use'. Example: use \"std/io\"".to_string(), start..start);
|
||||
}
|
||||
};
|
||||
let end = self.peek_span().unwrap_or(start..start).end;
|
||||
|
|
@ -175,9 +211,9 @@ impl Parser {
|
|||
}
|
||||
Some(token) => {
|
||||
let span = self.peek_span().unwrap_or(start..start);
|
||||
self.error(format!("Unexpected token at top level: {:?}", token), span)
|
||||
self.error(format!("Unexpected token at top level: {:?}. Expected declarations like 'fn', 'struct', 'enum', 'impl', 'trait', 'use', 'load', or 'extern'", token), span)
|
||||
}
|
||||
None => self.error("Unexpected EOF".to_string(), start..start),
|
||||
None => self.error("Unexpected end of file at top level. Expected declarations like 'fn', 'struct', 'enum', etc.".to_string(), start..start),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,8 +222,18 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(name), _)) => name,
|
||||
Some((_, span)) => return self.error("Expected attribute name".to_string(), span),
|
||||
None => return self.error("Expected attribute name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected attribute name after '@'. Example: @deprecated".to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected attribute name after '@'. Example: @deprecated".to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Parentheses are optional
|
||||
|
|
@ -215,11 +261,11 @@ impl Parser {
|
|||
args.push(AttributeArg::KeyValue(id, val))
|
||||
}
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected value after =".to_string(), span);
|
||||
return self.error("Expected attribute value after '='. Example: @version = \"1.0\"".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self
|
||||
.error("Expected value after =".to_string(), start..start);
|
||||
.error("Expected attribute value after '='. Example: @version = \"1.0\"".to_string(), start..start);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -232,7 +278,7 @@ impl Parser {
|
|||
.error(format!("Unexpected token in attribute: {:?}", token), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected attribute argument".to_string(), start..start);
|
||||
return self.error("Expected attribute argument. Examples: \"value\", key = \"value\", or just key".to_string(), start..start);
|
||||
}
|
||||
}
|
||||
let next_token = self.peek().cloned();
|
||||
|
|
@ -243,7 +289,7 @@ impl Parser {
|
|||
} else {
|
||||
{
|
||||
let span = self.peek_span().unwrap_or(start..start);
|
||||
return self.error("Expected , or )".to_string(), span);
|
||||
return self.error("Expected ',' to separate arguments or ')' to close attribute. Example: @deprecated(\"old\", reason = \"use new\")".to_string(), span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -260,8 +306,8 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected function name".to_string(), span),
|
||||
None => return self.error("Expected function name".to_string(), start..start),
|
||||
Some((_, span)) => return self.error("Expected function name after 'fn' keyword. Example: fn add(x: int, y: int) -> int x + y".to_string(), span),
|
||||
None => return self.error("Expected function name after 'fn' keyword. Example: fn add(x: int, y: int) -> int x + y ".to_string(), start..start),
|
||||
};
|
||||
|
||||
// Parse type parameters if present
|
||||
|
|
@ -283,8 +329,20 @@ impl Parser {
|
|||
|
||||
let arg_name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected argument name".to_string(), span),
|
||||
None => return self.error("Expected argument name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected argument name. Arguments should be like: x: int, y: int"
|
||||
.to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected argument name. Arguments should be like: x: int, y: int"
|
||||
.to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let arg_type = if matches!(self.peek(), Some(Token::Colon)) {
|
||||
|
|
@ -300,7 +358,7 @@ impl Parser {
|
|||
self.next();
|
||||
} else if !matches!(self.peek(), Some(Token::RParen)) {
|
||||
let span = self.peek_span().unwrap_or(start..start);
|
||||
return self.error("Expected , or )".to_string(), span);
|
||||
return self.error("Expected ',' between arguments or ')' to close parameter list. Example: fn add(x: int, y: int)".to_string(), span);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -328,8 +386,8 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected struct name".to_string(), span),
|
||||
None => return self.error("Expected struct name".to_string(), start..start),
|
||||
Some((_, span)) => return self.error("Expected struct name after 'struct' keyword. Example: struct Point { x: int, y: int }".to_string(), span),
|
||||
None => return self.error("Expected struct name after 'struct' keyword. Example: struct Point { x: int, y: int }".to_string(), start..start),
|
||||
};
|
||||
|
||||
// Parse type parameters if present
|
||||
|
|
@ -351,8 +409,18 @@ impl Parser {
|
|||
let field_start = self.peek_span().unwrap_or(0..0).start;
|
||||
let field_name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected field name".to_string(), span),
|
||||
None => return self.error("Expected field name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected field name. Fields should be like: name: string,".to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected field name. Fields should be like: name: string,".to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
self.expect(Token::Colon)?;
|
||||
|
|
@ -381,8 +449,16 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected enum name".to_string(), span),
|
||||
None => return self.error("Expected enum name".to_string(), start..start),
|
||||
Some((_, span)) => return self.error(
|
||||
"Expected enum name after 'enum' keyword. Example: enum Color { Red(int), Green(int), Blue(int) }"
|
||||
.to_string(),
|
||||
span,
|
||||
),
|
||||
None => return self.error(
|
||||
"Expected enum name after 'enum' keyword. Example: enum Color { Red(int), Green(int), Blue(int) }"
|
||||
.to_string(),
|
||||
start..start,
|
||||
),
|
||||
};
|
||||
|
||||
// Parse type parameters if present
|
||||
|
|
@ -404,8 +480,20 @@ impl Parser {
|
|||
let variant_start = self.peek_span().unwrap_or(0..0).start;
|
||||
let variant_name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected variant name".to_string(), span),
|
||||
None => return self.error("Expected variant name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected variant name. Variants should be like: Red, or Ok(T),"
|
||||
.to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected variant name. Variants should be like: Red, or Ok(T),"
|
||||
.to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let mut fields = Vec::new();
|
||||
|
|
@ -458,23 +546,32 @@ impl Parser {
|
|||
TypeAnnot::Cons(name, _) => name,
|
||||
_ => {
|
||||
return self.error(
|
||||
"Expected type name for impl target".to_string(),
|
||||
"Expected type name for impl target. Example: impl MyType { ... }".to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse optional trait name
|
||||
let trait_name = if matches!(self.peek(), Some(Token::Colon)) {
|
||||
self.next();
|
||||
match self.next() {
|
||||
Some((Token::Variable(n), _)) => Some(n),
|
||||
Some((_, span)) => return self.error("Expected trait name".to_string(), span),
|
||||
None => return self.error("Expected trait name".to_string(), start..start),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let trait_name =
|
||||
if matches!(self.peek(), Some(Token::Colon)) {
|
||||
self.next();
|
||||
match self.next() {
|
||||
Some((Token::Variable(n), _)) => Some(n),
|
||||
Some((_, span)) => return self.error(
|
||||
"Expected trait name after colon. Example: impl MyType : MyTrait { ... }"
|
||||
.to_string(),
|
||||
span,
|
||||
),
|
||||
None => return self.error(
|
||||
"Expected trait name after colon. Example: impl MyType : MyTrait { ... }"
|
||||
.to_string(),
|
||||
start..start,
|
||||
),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Parse methods
|
||||
let mut methods = Vec::new();
|
||||
|
|
@ -499,8 +596,8 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected trait name".to_string(), span),
|
||||
None => return self.error("Expected trait name".to_string(), start..start),
|
||||
Some((_, span)) => return self.error("Expected trait name after 'trait' keyword. Example: trait Display { fn to_string() -> string; }".to_string(), span),
|
||||
None => return self.error("Expected trait name after 'trait' keyword. Example: trait Display { fn to_string() -> string; }".to_string(), start..start),
|
||||
};
|
||||
|
||||
// Parse type parameters if present
|
||||
|
|
@ -545,8 +642,18 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected extern name".to_string(), span),
|
||||
None => return self.error("Expected extern name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected function name after 'extern'. Example: extern add".to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected function name after 'extern'. Example: extern add".to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse argument types (with optional parameter names)
|
||||
|
|
@ -573,8 +680,16 @@ impl Parser {
|
|||
self.expect(Token::KeywordFrom)?;
|
||||
let from = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected library identifier".to_string(), span),
|
||||
None => return self.error("Expected library identifier".to_string(), start..start),
|
||||
Some((_, span)) => return self.error(
|
||||
"Expected library name after 'from'. Example: extern add() -> int from \"libc\""
|
||||
.to_string(),
|
||||
span,
|
||||
),
|
||||
None => return self.error(
|
||||
"Expected library name after 'from'. Example: extern add() -> int from \"libc\""
|
||||
.to_string(),
|
||||
start..start,
|
||||
),
|
||||
};
|
||||
|
||||
let end = self.peek_span().unwrap_or(start..start).end;
|
||||
|
|
@ -591,15 +706,37 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let library = match self.next() {
|
||||
Some((Token::String(s), _)) => s,
|
||||
Some((_, span)) => return self.error("Expected library name".to_string(), span),
|
||||
None => return self.error("Expected library name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected library name after 'load'. Example: load \"mylib\" as mylib"
|
||||
.to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected library name after 'load'. Example: load \"mylib\" as mylib"
|
||||
.to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
self.expect(Token::KeywordAs)?;
|
||||
let alias = match self.next() {
|
||||
Some((Token::Variable(a), _)) => a,
|
||||
Some((_, span)) => return self.error("Expected alias".to_string(), span),
|
||||
None => return self.error("Expected alias".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected alias after 'as'. Example: load \"mylib\" as mylib".to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected alias after 'as'. Example: load \"mylib\" as mylib".to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let end = self.peek_span().unwrap_or(start..start).end;
|
||||
|
|
@ -623,8 +760,18 @@ impl Parser {
|
|||
let param_start = self.peek_span().unwrap_or(0..0).start;
|
||||
let param_name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected parameter name".to_string(), span),
|
||||
None => return self.error("Expected parameter name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected type parameter name. Example: <T, U>".to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected type parameter name. Example: <T, U>".to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let bounds = if matches!(self.peek(), Some(Token::Colon)) {
|
||||
|
|
@ -662,8 +809,18 @@ impl Parser {
|
|||
loop {
|
||||
match self.next() {
|
||||
Some((Token::Variable(n), _)) => bounds.push(n),
|
||||
Some((_, span)) => return self.error("Expected trait name".to_string(), span),
|
||||
None => return self.error("Expected trait name".to_string(), 0..0),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected trait name in bounds. Example: T: Clone + Debug".to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected trait name in bounds. Example: T: Clone + Debug".to_string(),
|
||||
0..0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !matches!(self.peek(), Some(Token::Plus)) {
|
||||
|
|
@ -759,8 +916,20 @@ impl Parser {
|
|||
TypeAnnot::Array(Box::new(inner))
|
||||
}
|
||||
Some((Token::Bang, _)) => TypeAnnot::Cons("never".to_string(), vec![]),
|
||||
Some((_, span)) => return self.error("Expected type name".to_string(), span),
|
||||
None => return self.error("Expected type name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected type name. Examples: int, string, bool, MyStruct, Option<int>"
|
||||
.to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected type name. Examples: int, string, bool, MyStruct, Option<int>"
|
||||
.to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse type arguments if present
|
||||
|
|
@ -781,7 +950,11 @@ impl Parser {
|
|||
base_type = match base_type {
|
||||
TypeAnnot::Cons(name, _) => TypeAnnot::Cons(name, args),
|
||||
_ => {
|
||||
return self.error("Expected type name for generic".to_string(), start..start);
|
||||
return self.error(
|
||||
"Expected type name for generic. Example: Vec<int>, HashMap<string, int>"
|
||||
.to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -800,8 +973,20 @@ impl Parser {
|
|||
let start = self.peek_span().unwrap_or(0..0).start;
|
||||
let name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected function name".to_string(), span),
|
||||
None => return self.error("Expected function name".to_string(), start..start),
|
||||
Some((_, span)) => {
|
||||
return self.error(
|
||||
"Expected function name in signature. Example: fn to_string() -> string"
|
||||
.to_string(),
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
return self.error(
|
||||
"Expected function name in signature. Example: fn to_string() -> string"
|
||||
.to_string(),
|
||||
start..start,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
self.expect(Token::LParen)?;
|
||||
|
|
@ -813,11 +998,20 @@ impl Parser {
|
|||
}
|
||||
|
||||
let param_start = self.peek_span().unwrap_or(0..0).start;
|
||||
let param_name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error("Expected parameter name".to_string(), span),
|
||||
None => return self.error("Expected parameter name".to_string(), start..start),
|
||||
};
|
||||
let param_name =
|
||||
match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => return self.error(
|
||||
"Expected parameter name in trait method. Example: fn method(self, x: int)"
|
||||
.to_string(),
|
||||
span,
|
||||
),
|
||||
None => return self.error(
|
||||
"Expected parameter name in trait method. Example: fn method(self, x: int)"
|
||||
.to_string(),
|
||||
start..start,
|
||||
),
|
||||
};
|
||||
|
||||
// Parameters in trait methods may have type annotations
|
||||
if matches!(self.peek(), Some(Token::Colon)) {
|
||||
|
|
@ -1131,7 +1325,7 @@ impl Parser {
|
|||
let field = match self.next() {
|
||||
Some((Token::Variable(f), _)) => f,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected field name".to_string(), span);
|
||||
return self.error("Expected field name in struct pattern. Example: Point { x: 5, y: 10 }".to_string(), span);
|
||||
}
|
||||
None => return self.error("Expected field name".to_string(), start..start),
|
||||
};
|
||||
|
|
@ -1149,7 +1343,7 @@ impl Parser {
|
|||
let field = match self.next() {
|
||||
Some((Token::Variable(f), _)) => f,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected field name".to_string(), span);
|
||||
return self.error("Expected field name in struct pattern. Example: Point { x: 5, y: 10 }".to_string(), span);
|
||||
}
|
||||
None => return self.error("Expected field name".to_string(), start..start),
|
||||
};
|
||||
|
|
@ -1246,7 +1440,7 @@ impl Parser {
|
|||
let field_name = match self.next() {
|
||||
Some((Token::Variable(f), _)) => f,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected field name".to_string(), span);
|
||||
return self.error("Expected field name in struct pattern. Example: Point { x: 5, y: 10 }".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected field name".to_string(), start..start);
|
||||
|
|
@ -1274,7 +1468,7 @@ impl Parser {
|
|||
let variant = match self.next() {
|
||||
Some((Token::Variable(v), _)) => v,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected variant name".to_string(), span);
|
||||
return self.error("Expected variant name in enum pattern. Example: Result::Ok(value) or Color::Red()".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected variant name".to_string(), start..start);
|
||||
|
|
@ -1403,9 +1597,9 @@ impl Parser {
|
|||
let var_name = match self.next() {
|
||||
Some((Token::Variable(n), _)) => n,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected variable name".to_string(), span);
|
||||
return self.error("Expected variable name after 'let'. Example: let x = 5; or let mut y = 10;".to_string(), span);
|
||||
}
|
||||
None => return self.error("Expected variable name".to_string(), start..start),
|
||||
None => return self.error("Expected variable name after 'let'. Example: let x = 5; or let mut y = 10;".to_string(), start..start),
|
||||
};
|
||||
|
||||
// Parse optional type annotation
|
||||
|
|
@ -1492,7 +1686,7 @@ impl Parser {
|
|||
let var = match self.next() {
|
||||
Some((Token::Variable(v), _)) => v,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected variable name".to_string(), span);
|
||||
return self.error("Expected variable name in for loop. Example: for item in collection { ... }".to_string(), span);
|
||||
}
|
||||
None => return self.error("Expected variable name".to_string(), start..start),
|
||||
};
|
||||
|
|
@ -1545,7 +1739,7 @@ impl Parser {
|
|||
let param_name = match self.next() {
|
||||
Some((Token::Variable(p), _)) => p,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected parameter name".to_string(), span);
|
||||
return self.error("Expected parameter name in lambda. Example: lambda(x, y) { x + y }".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected parameter name".to_string(), start..start);
|
||||
|
|
@ -1615,9 +1809,12 @@ impl Parser {
|
|||
}
|
||||
Some(token) => {
|
||||
let span = self.peek_span().unwrap_or(start..start);
|
||||
self.error(format!("Unexpected token: {:?}", token), span)
|
||||
self.error(format!("Unexpected token in pattern: {:?}. Expected variable names, struct patterns like Struct {{ field }}, or enum patterns like Enum::Variant", token), span)
|
||||
}
|
||||
None => self.error("Unexpected EOF".to_string(), start..start),
|
||||
None => self.error(
|
||||
"Unexpected end of file in pattern. Expected a complete pattern.".to_string(),
|
||||
start..start,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1644,7 +1841,7 @@ impl Parser {
|
|||
let field_name = match self.next() {
|
||||
Some((Token::Variable(f), _)) => f,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected field name".to_string(), span);
|
||||
return self.error("Expected field name in struct pattern. Example: Point { x: 5, y: 10 }".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected field name".to_string(), start..start);
|
||||
|
|
@ -1671,7 +1868,7 @@ impl Parser {
|
|||
let variant = match self.next() {
|
||||
Some((Token::Variable(v), _)) => v,
|
||||
Some((_, span)) => {
|
||||
return self.error("Expected variant name".to_string(), span);
|
||||
return self.error("Expected variant name in enum pattern. Example: Result::Ok(value) or Color::Red()".to_string(), span);
|
||||
}
|
||||
None => {
|
||||
return self.error("Expected variant name".to_string(), start..start);
|
||||
|
|
@ -1769,9 +1966,12 @@ impl Parser {
|
|||
}
|
||||
Some(token) => {
|
||||
let span = self.peek_span().unwrap_or(start..start);
|
||||
self.error(format!("Unexpected token in pattern: {:?}", token), span)
|
||||
self.error(format!("Unexpected token in expression: {:?}. Expected literals, variables, or keywords like 'let', 'if', etc.", token), span)
|
||||
}
|
||||
None => self.error("Unexpected EOF".to_string(), start..start),
|
||||
None => self.error(
|
||||
"Unexpected end of file in expression. Expected a complete expression.".to_string(),
|
||||
start..start,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1629,9 +1629,7 @@ impl TypeChecker {
|
|||
TypeAnnot::Array(inner) => {
|
||||
Type::Array(Box::new(self.substitute_type(inner, subst_map)))
|
||||
}
|
||||
TypeAnnot::Ptr(inner) => {
|
||||
Type::Ptr(Box::new(self.substitute_type(inner, subst_map)))
|
||||
}
|
||||
TypeAnnot::Ptr(inner) => Type::Ptr(Box::new(self.substitute_type(inner, subst_map))),
|
||||
TypeAnnot::Tuple(types) => {
|
||||
let substituted_types: Vec<Type> = types
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
fn test_scoping() -> int do
|
||||
let x = 5
|
||||
let f = lambda (y) y + 1 # simple lambda
|
||||
f = lambda (z) z+1
|
||||
# f = lambda (z) z+1
|
||||
let x = 10 # shadow x
|
||||
f(3) # should work
|
||||
end
|
||||
|
||||
fn stuff(f: fn(int, int) -> int, v: int) -> int do
|
||||
f(v, v)
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue