Compare commits
3 Commits
415dea6fd2
...
eda4f92900
| Author | SHA1 | Date | |
|---|---|---|---|
| eda4f92900 | |||
| 7cdcad7df3 | |||
| 53c66a3d03 |
17
GRAMMAR.ebnf
17
GRAMMAR.ebnf
@@ -271,13 +271,20 @@ named_type = IDENT ;
|
|||||||
|
|
||||||
(* --- Pointer types --- *)
|
(* --- Pointer types --- *)
|
||||||
(* *)
|
(* *)
|
||||||
(* "*" type — typed pointer; the pointee type is known. *)
|
(* "*" type — immutable typed pointer (read-only through ptr) *)
|
||||||
(* "*opaque" — untyped/opaque pointer (no pointee type info). *)
|
(* "*mut" type — mutable typed pointer (read-write through ptr) *)
|
||||||
|
(* "*opaque" — immutable untyped pointer (like C's const void* *)
|
||||||
|
(* "*mut opaque" — mutable untyped pointer (like C's void* *)
|
||||||
(* *)
|
(* *)
|
||||||
(* LL(1) note: after "*", "opaque" is not in FIRST(type), so the *)
|
(* LL(1) note: after "*", peek at next token: *)
|
||||||
(* two alternatives are always distinguishable with one token. *)
|
(* "mut" → mutable pointer; consume "mut", then parse *)
|
||||||
|
(* "opaque" | type *)
|
||||||
|
(* "opaque" → immutable opaque pointer *)
|
||||||
|
(* other → immutable typed pointer; parse type directly *)
|
||||||
|
(* "mut" is a keyword; it is not in FIRST(type) and is distinct *)
|
||||||
|
(* from "opaque", so all three cases are unambiguous with one token.*)
|
||||||
|
|
||||||
pointer_type = "*" , ( "opaque" | type ) ;
|
pointer_type = "*" , [ "mut" ] , ( "opaque" | type ) ;
|
||||||
|
|
||||||
|
|
||||||
(* --- Array types --- *)
|
(* --- Array types --- *)
|
||||||
|
|||||||
1388
SEMANTICS.md
Normal file
1388
SEMANTICS.md
Normal file
File diff suppressed because it is too large
Load Diff
38
SYNTAX.md
38
SYNTAX.md
@@ -291,20 +291,36 @@ Node // struct Node { value: i64, next: *Node }
|
|||||||
|
|
||||||
### Pointer Types
|
### Pointer Types
|
||||||
|
|
||||||
A pointer type is written with a leading `*`.
|
A pointer type is written with a leading `*`. The optional `mut` keyword following
|
||||||
|
`*` marks the pointer as **mutable** — the pointee can be written through.
|
||||||
|
Without `mut` the pointer is **immutable** — the pointee can only be read.
|
||||||
|
|
||||||
| Syntax | Description |
|
| Syntax | C equivalent | Description |
|
||||||
| --------- | ------------------------------------------------------------------------------------- |
|
| ------------- | ----------------- | -------------------------------------------------- |
|
||||||
| `*T` | Typed pointer — points to a value of type `T` |
|
| `*T` | `const T *` | Immutable typed pointer — read-only through ptr |
|
||||||
| `*opaque` | Opaque pointer — no compile-time pointee type information; equivalent to C's `void *` |
|
| `*mut T` | `T *` | Mutable typed pointer — read-write through ptr |
|
||||||
|
| `*opaque` | `const void *` | Immutable untyped pointer — no pointee type info |
|
||||||
|
| `*mut opaque` | `void *` | Mutable untyped pointer — no pointee type info |
|
||||||
|
|
||||||
Pointer types may be nested: `**u8` is a pointer to a pointer to `u8`.
|
Pointer mutability (`*mut`) and binding mutability (`let mut`) are **independent**:
|
||||||
|
|
||||||
|
| Declaration | Can reassign ptr? | Can write through ptr? |
|
||||||
|
| ------------------------- | ----------------- | ---------------------- |
|
||||||
|
| `let p: *T` | no | no |
|
||||||
|
| `let mut p: *T` | yes | no |
|
||||||
|
| `let p: *mut T` | no | yes |
|
||||||
|
| `let mut p: *mut T` | yes | yes |
|
||||||
|
|
||||||
|
Pointer types may be nested. The `mut` qualifier applies only to the outermost
|
||||||
|
indirection; each `*` in the chain can independently carry `mut`.
|
||||||
|
|
||||||
```flux
|
```flux
|
||||||
*u8 // pointer to u8
|
*u8 // immutable pointer to u8 (cannot write *p)
|
||||||
**i32 // pointer to pointer to i32
|
*mut u8 // mutable pointer to u8 (can write *p)
|
||||||
*opaque // untyped pointer
|
**u8 // immutable pointer to immutable pointer to u8
|
||||||
**opaque // pointer to untyped pointer
|
*mut *mut u8 // mutable pointer to mutable pointer to u8
|
||||||
|
*opaque // immutable untyped pointer
|
||||||
|
*mut opaque // mutable untyped pointer
|
||||||
```
|
```
|
||||||
|
|
||||||
### Array Types
|
### Array Types
|
||||||
@@ -333,7 +349,7 @@ primitive_type = "u8" | "u16" | "u32" | "u64"
|
|||||||
| "i8" | "i16" | "i32" | "i64"
|
| "i8" | "i16" | "i32" | "i64"
|
||||||
| "f32" | "f64" | "bool" | "char" ;
|
| "f32" | "f64" | "bool" | "char" ;
|
||||||
named_type = IDENT ;
|
named_type = IDENT ;
|
||||||
pointer_type = "*" , ( "opaque" | type ) ;
|
pointer_type = "*" , [ "mut" ] , ( "opaque" | type ) ;
|
||||||
array_type = "[" , type , ";" , INT_LIT , "]" ;
|
array_type = "[" , type , ";" , INT_LIT , "]" ;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fn fibonacci_iter(n: u8) -> u64 {
|
|||||||
a = b;
|
a = b;
|
||||||
b = temp;
|
b = temp;
|
||||||
|
|
||||||
counter = counter + 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
|
|||||||
@@ -76,10 +76,10 @@ pub enum Type {
|
|||||||
Char,
|
Char,
|
||||||
// User-defined named type (e.g. a struct)
|
// User-defined named type (e.g. a struct)
|
||||||
Named(String, Span),
|
Named(String, Span),
|
||||||
// Typed pointer: `*type`
|
// Typed pointer: `*type` (immutable) or `*mut type` (mutable)
|
||||||
Pointer(Box<Type>),
|
Pointer { mutable: bool, pointee: Box<Type> },
|
||||||
// Opaque (untyped) pointer: `*opaque`
|
// Opaque (untyped) pointer: `*opaque` (immutable) or `*mut opaque` (mutable)
|
||||||
OpaquePointer,
|
OpaquePointer { mutable: bool },
|
||||||
// Fixed-size array: `[type; INT_LIT]`
|
// Fixed-size array: `[type; INT_LIT]`
|
||||||
Array { elem: Box<Type>, size: String },
|
Array { elem: Box<Type>, size: String },
|
||||||
// Error placeholder for recovery
|
// Error placeholder for recovery
|
||||||
|
|||||||
@@ -223,13 +223,19 @@ impl<'src> Parser<'src> {
|
|||||||
// Named type (user-defined struct, etc.)
|
// Named type (user-defined struct, etc.)
|
||||||
TokenKind::Ident => Type::Named(tok.text.to_owned(), tok.span),
|
TokenKind::Ident => Type::Named(tok.text.to_owned(), tok.span),
|
||||||
|
|
||||||
// Pointer: `*opaque` or `*<type>`
|
// Pointer: `*[mut] opaque` or `*[mut] <type>`
|
||||||
TokenKind::Star => {
|
TokenKind::Star => {
|
||||||
|
let mutable = if self.current().kind == TokenKind::Mut {
|
||||||
|
self.advance();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
if self.current().kind == TokenKind::Opaque {
|
if self.current().kind == TokenKind::Opaque {
|
||||||
self.advance();
|
self.advance();
|
||||||
Type::OpaquePointer
|
Type::OpaquePointer { mutable }
|
||||||
} else {
|
} else {
|
||||||
Type::Pointer(Box::new(self.parse_type()))
|
Type::Pointer { mutable, pointee: Box::new(self.parse_type()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1177,12 +1183,34 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_pointer() {
|
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]
|
#[test]
|
||||||
fn type_opaque_pointer() {
|
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]
|
#[test]
|
||||||
@@ -1194,8 +1222,17 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_nested_pointer() {
|
fn type_nested_pointer() {
|
||||||
// `**i32` → Pointer(Pointer(I32))
|
// `**i32` → Pointer { Pointer { I32 } }
|
||||||
assert!(matches!(parse_type_str("**i32"), Type::Pointer(_)));
|
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 ───────────────────────────────────────────────────────
|
// ── Statement tests ───────────────────────────────────────────────────────
|
||||||
@@ -1466,7 +1503,7 @@ mod tests {
|
|||||||
let d = top("fn foo(p: *i32) { }");
|
let d = top("fn foo(p: *i32) { }");
|
||||||
match &d.kind {
|
match &d.kind {
|
||||||
TopLevelDefKind::Func(f) => {
|
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"),
|
_ => panic!("expected func def"),
|
||||||
}
|
}
|
||||||
@@ -1506,7 +1543,7 @@ mod tests {
|
|||||||
let d = top("struct Node { value: i32, next: *Node }");
|
let d = top("struct Node { value: i32, next: *Node }");
|
||||||
match &d.kind {
|
match &d.kind {
|
||||||
TopLevelDefKind::Struct(s) => {
|
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"),
|
_ => panic!("expected struct def"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,19 +156,37 @@
|
|||||||
"operators": {
|
"operators": {
|
||||||
"patterns": [
|
"patterns": [
|
||||||
{
|
{
|
||||||
"comment": "Arrow (return type separator)",
|
"comment": "Arrow (return type separator) — must precede - and -=",
|
||||||
"name": "keyword.operator.arrow.flux",
|
"name": "keyword.operator.arrow.flux",
|
||||||
"match": "->"
|
"match": "->"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"comment": "Compound assignment operators — must precede shift, comparison, and arithmetic",
|
||||||
|
"name": "keyword.operator.assignment.compound.flux",
|
||||||
|
"match": "<<=|>>=|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Shift operators — must precede < and >",
|
||||||
|
"name": "keyword.operator.bitwise.shift.flux",
|
||||||
|
"match": "<<|>>"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"comment": "Comparison operators",
|
"comment": "Comparison operators",
|
||||||
"name": "keyword.operator.comparison.flux",
|
"name": "keyword.operator.comparison.flux",
|
||||||
"match": "==|!=|<=|>=|<|>"
|
"match": "==|!=|<=|>=|<|>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Assignment",
|
"comment": "Assignment — negative lookbehind excludes all compound-assign prefixes",
|
||||||
"name": "keyword.operator.assignment.flux",
|
"name": "keyword.operator.assignment.flux",
|
||||||
"match": "(?<![=!<>])=(?!=)"
|
"match": "(?<![=!<>+\\-*/%&|^])=(?!=)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"comment": "Mutable pointer type prefix *mut — must precede arithmetic *",
|
||||||
|
"match": "(\\*)(mut)\\b",
|
||||||
|
"captures": {
|
||||||
|
"1": { "name": "keyword.operator.type.pointer.flux" },
|
||||||
|
"2": { "name": "storage.modifier.mut.flux" }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Arithmetic: + - * / %",
|
"comment": "Arithmetic: + - * / %",
|
||||||
@@ -176,7 +194,7 @@
|
|||||||
"match": "[+\\-*/%]"
|
"match": "[+\\-*/%]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"comment": "Bitwise: & | ^ ~ and unary ! ~",
|
"comment": "Bitwise and unary: & | ^ ~ !",
|
||||||
"name": "keyword.operator.bitwise.flux",
|
"name": "keyword.operator.bitwise.flux",
|
||||||
"match": "[&|^~!]"
|
"match": "[&|^~!]"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user