Feat: add mutable pointer type *mut T and *mut opaque
- Grammar: update pointer_type to support optional mut keyword; LL(1) verified (56 named rules, no conflicts) - AST: update Type enum with mutable: bool field for Pointer and OpaquePointer variants - Parser: consume optional mut token in parse_type; update all existing pointer tests; add 4 new mut pointer tests (85 pass) - VSCode extension: add *mut capture-group pattern for syntax highlighting; update SYNTAX.md with pointer mutability table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -76,10 +76,10 @@ pub enum Type {
|
||||
Char,
|
||||
// User-defined named type (e.g. a struct)
|
||||
Named(String, Span),
|
||||
// Typed pointer: `*type`
|
||||
Pointer(Box<Type>),
|
||||
// Opaque (untyped) pointer: `*opaque`
|
||||
OpaquePointer,
|
||||
// Typed pointer: `*type` (immutable) or `*mut type` (mutable)
|
||||
Pointer { mutable: bool, pointee: Box<Type> },
|
||||
// Opaque (untyped) pointer: `*opaque` (immutable) or `*mut opaque` (mutable)
|
||||
OpaquePointer { mutable: bool },
|
||||
// Fixed-size array: `[type; INT_LIT]`
|
||||
Array { elem: Box<Type>, size: String },
|
||||
// Error placeholder for recovery
|
||||
|
||||
@@ -223,13 +223,19 @@ impl<'src> Parser<'src> {
|
||||
// Named type (user-defined struct, etc.)
|
||||
TokenKind::Ident => Type::Named(tok.text.to_owned(), tok.span),
|
||||
|
||||
// Pointer: `*opaque` or `*<type>`
|
||||
// Pointer: `*[mut] opaque` or `*[mut] <type>`
|
||||
TokenKind::Star => {
|
||||
let mutable = if self.current().kind == TokenKind::Mut {
|
||||
self.advance();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if self.current().kind == TokenKind::Opaque {
|
||||
self.advance();
|
||||
Type::OpaquePointer
|
||||
Type::OpaquePointer { mutable }
|
||||
} else {
|
||||
Type::Pointer(Box::new(self.parse_type()))
|
||||
Type::Pointer { mutable, pointee: Box::new(self.parse_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1177,12 +1183,34 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn type_pointer() {
|
||||
assert!(matches!(parse_type_str("*i32"), Type::Pointer(_)));
|
||||
assert!(matches!(
|
||||
parse_type_str("*i32"),
|
||||
Type::Pointer { mutable: false, .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_mut_pointer() {
|
||||
assert!(matches!(
|
||||
parse_type_str("*mut i32"),
|
||||
Type::Pointer { mutable: true, .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_opaque_pointer() {
|
||||
assert!(matches!(parse_type_str("*opaque"), Type::OpaquePointer));
|
||||
assert!(matches!(
|
||||
parse_type_str("*opaque"),
|
||||
Type::OpaquePointer { mutable: false }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_mut_opaque_pointer() {
|
||||
assert!(matches!(
|
||||
parse_type_str("*mut opaque"),
|
||||
Type::OpaquePointer { mutable: true }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1194,8 +1222,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn type_nested_pointer() {
|
||||
// `**i32` → Pointer(Pointer(I32))
|
||||
assert!(matches!(parse_type_str("**i32"), Type::Pointer(_)));
|
||||
// `**i32` → Pointer { Pointer { I32 } }
|
||||
assert!(matches!(parse_type_str("**i32"), Type::Pointer { mutable: false, .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_nested_mut_pointer() {
|
||||
// `*mut *mut i32` → Pointer { mutable: true, Pointer { mutable: true, I32 } }
|
||||
assert!(matches!(
|
||||
parse_type_str("*mut *mut i32"),
|
||||
Type::Pointer { mutable: true, .. }
|
||||
));
|
||||
}
|
||||
|
||||
// ── Statement tests ───────────────────────────────────────────────────────
|
||||
@@ -1466,7 +1503,7 @@ mod tests {
|
||||
let d = top("fn foo(p: *i32) { }");
|
||||
match &d.kind {
|
||||
TopLevelDefKind::Func(f) => {
|
||||
assert!(matches!(f.params[0].ty, Type::Pointer(_)));
|
||||
assert!(matches!(f.params[0].ty, Type::Pointer { mutable: false, .. }));
|
||||
}
|
||||
_ => panic!("expected func def"),
|
||||
}
|
||||
@@ -1506,7 +1543,7 @@ mod tests {
|
||||
let d = top("struct Node { value: i32, next: *Node }");
|
||||
match &d.kind {
|
||||
TopLevelDefKind::Struct(s) => {
|
||||
assert!(matches!(s.fields[1].ty, Type::Pointer(_)));
|
||||
assert!(matches!(s.fields[1].ty, Type::Pointer { mutable: false, .. }));
|
||||
}
|
||||
_ => panic!("expected struct def"),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user