Skip to content

Commit 444fa85

Browse files
authored
refactor: Drop the string type annotation (#489)
1 parent 7837a02 commit 444fa85

72 files changed

Lines changed: 360 additions & 692 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎AGENTS.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ Validate once, at the boundary. Two smells to fix on sight: a swallowed must-hol
2828
| `(_)` | Any named node |
2929
| `_` | Any node |
3030
| `@name` | Capture (snake_case only) |
31-
| `@x :: T` | Type annotation |
32-
| `@x :: string` | Extract node text |
31+
| `@x :: T` | Type annotation (T is PascalCase) |
3332
| `field: pattern` | Field constraint |
3433
| `-field` | Negated field (assert absent) |
3534
| `?` `*` `+` | Quantifiers (0-1, 0+, 1+) |

‎README.md‎

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,17 @@ Extract function signatures from Rust. `Type` references itself to handle nested
7575

7676
```clojure
7777
Type = [
78-
Simple: [(type_identifier) (primitive_type)] @name :: string
78+
Simple: [(type_identifier) (primitive_type)] @name
7979
Generic: (generic_type
80-
type: (type_identifier) @name :: string
80+
type: (type_identifier) @name
8181
type_arguments: (type_arguments (Type)* @args))
8282
]
8383

8484
Func = (function_item
85-
name: (identifier) @name :: string
85+
name: (identifier) @name
8686
parameters: (parameters
8787
(parameter
88-
pattern: (identifier) @param :: string
88+
pattern: (identifier) @param
8989
type: (Type) @type
9090
)* @params))
9191

@@ -104,13 +104,32 @@ Plotnik infers TypeScript types from the query structure. `Type` is recursive: `
104104

105105
```sh
106106
❯ plotnik infer query.ptk --lang rust
107-
export type Type =
108-
| { $tag: "Simple"; $data: { name: string } }
109-
| { $tag: "Generic"; $data: { name: string; args: Type[] } };
107+
export interface Node {
108+
kind: string;
109+
text: string;
110+
span: [number, number];
111+
}
112+
113+
export interface TypeSimple {
114+
$tag: "Simple";
115+
$data: { name: Node };
116+
}
117+
118+
export interface TypeGeneric {
119+
$tag: "Generic";
120+
$data: { args: Type[]; name: Node };
121+
}
122+
123+
export type Type = TypeSimple | TypeGeneric;
124+
125+
export interface FuncParams {
126+
param: Node;
127+
type: Type;
128+
}
110129

111130
export interface Func {
112-
name: string;
113-
params: { param: string; type: Type }[];
131+
name: Node;
132+
params: FuncParams[];
114133
}
115134

116135
export interface Funcs {
@@ -121,33 +140,42 @@ export interface Funcs {
121140
Run the query against `lib.rs` to extract structured JSON:
122141
123142
```sh
124-
❯ plotnik exec query.ptk lib.rs
143+
❯ plotnik exec query.ptk lib.rs --entry Funcs
125144
{
126145
"funcs": [
127146
{
128-
"name": "get",
147+
"name": { "kind": "identifier", "text": "get", "span": [3, 6] },
129148
"params": [{
130-
"param": "key",
149+
"param": { "kind": "identifier", "text": "key", "span": [7, 10] },
131150
"type": {
132151
"$tag": "Generic",
133152
"$data": {
134-
"name": "Option",
153+
"name": { "kind": "type_identifier", "text": "Option", "span": [12, 18] },
135154
"args": [{
136155
"$tag": "Generic",
137156
"$data": {
138-
"name": "Vec",
139-
"args": [{ "$tag": "Simple", "$data": { "name": "String" } }]
157+
"name": { "kind": "type_identifier", "text": "Vec", "span": [19, 22] },
158+
"args": [{
159+
"$tag": "Simple",
160+
"$data": { "name": { "kind": "type_identifier", "text": "String", "span": [23, 29] } }
161+
}]
140162
}
141163
}]
142164
}
143165
}
144166
}]
145167
},
146168
{
147-
"name": "set",
169+
"name": { "kind": "identifier", "text": "set", "span": [40, 43] },
148170
"params": [
149-
{ "param": "key", "type": { "$tag": "Simple", "$data": { "name": "String" } } },
150-
{ "param": "val", "type": { "$tag": "Simple", "$data": { "name": "i32" } } }
171+
{
172+
"param": { "kind": "identifier", "text": "key", "span": [44, 47] },
173+
"type": { "$tag": "Simple", "$data": { "name": { "kind": "type_identifier", "text": "String", "span": [49, 55] } } }
174+
},
175+
{
176+
"param": { "kind": "identifier", "text": "val", "span": [57, 60] },
177+
"type": { "$tag": "Simple", "$data": { "name": { "kind": "primitive_type", "text": "i32", "span": [62, 65] } } }
178+
}
151179
]
152180
}
153181
]

‎crates/plotnik-bytecode/src/bytecode/constants.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ pub const MAGIC: [u8; 4] = *b"PTKQ";
99
/// v4: Removed the trivia section.
1010
/// v5: Added extras-only anchor navigation modes.
1111
/// v6: Reserved bit 7 of a Nav byte for the Up family (uniform 5-bit level).
12-
pub const VERSION: u32 = 6;
12+
/// v7: Type kind and effect opcode discriminants renumbered contiguously.
13+
pub const VERSION: u32 = 7;
1314

1415
/// Section alignment in bytes.
1516
pub const SECTION_ALIGN: usize = 64;

‎crates/plotnik-bytecode/src/bytecode/dump.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ fn dump_types_defs(out: &mut String, module: &Module, ctx: &DumpContext) {
184184
let name = match kind {
185185
TypeKind::Void => "<Void>",
186186
TypeKind::Node => "<Node>",
187-
TypeKind::String => "<String>",
188187
_ => unreachable!(),
189188
};
190189
(name.to_string(), String::new())

‎crates/plotnik-bytecode/src/bytecode/effects.rs‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub const EFFECT_PAYLOAD_BITS: u32 = 10;
77
/// Largest representable effect payload (the low [`EFFECT_PAYLOAD_BITS`]).
88
pub const EFFECT_PAYLOAD_MAX: usize = (1 << EFFECT_PAYLOAD_BITS) - 1;
99

10-
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1111
#[repr(u8)]
1212
pub enum EffectOpcode {
1313
Node = 0,
@@ -19,11 +19,10 @@ pub enum EffectOpcode {
1919
Set = 6,
2020
Enum = 7,
2121
EndEnum = 8,
22-
Text = 9,
23-
Clear = 10,
24-
Null = 11,
25-
SuppressBegin = 12,
26-
SuppressEnd = 13,
22+
Clear = 9,
23+
Null = 10,
24+
SuppressBegin = 11,
25+
SuppressEnd = 12,
2726
}
2827

2928
impl EffectOpcode {
@@ -44,11 +43,10 @@ impl EffectOpcode {
4443
6 => Self::Set,
4544
7 => Self::Enum,
4645
8 => Self::EndEnum,
47-
9 => Self::Text,
48-
10 => Self::Clear,
49-
11 => Self::Null,
50-
12 => Self::SuppressBegin,
51-
13 => Self::SuppressEnd,
46+
9 => Self::Clear,
47+
10 => Self::Null,
48+
11 => Self::SuppressBegin,
49+
12 => Self::SuppressEnd,
5250
_ => return None,
5351
};
5452
Some(op)

‎crates/plotnik-bytecode/src/bytecode/format.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ pub fn format_effect(effect: &EffectOp) -> String {
277277
EffectOpcode::Set => format!("Set(M{})", effect.payload),
278278
EffectOpcode::Enum => format!("Enum(M{})", effect.payload),
279279
EffectOpcode::EndEnum => "EndEnum".to_string(),
280-
EffectOpcode::Text => "Text".to_string(),
281280
EffectOpcode::Clear => "Clear".to_string(),
282281
EffectOpcode::Null => "Null".to_string(),
283282
EffectOpcode::SuppressBegin => "SuppressBegin".to_string(),

‎crates/plotnik-bytecode/src/bytecode/instructions_tests.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,6 @@ fn arb_effect() -> impl Strategy<Value = EffectOp> {
193193
EffectOpcode::Set,
194194
EffectOpcode::Enum,
195195
EffectOpcode::EndEnum,
196-
EffectOpcode::Text,
197196
EffectOpcode::Clear,
198197
EffectOpcode::Null,
199198
EffectOpcode::SuppressBegin,

‎crates/plotnik-bytecode/src/bytecode/module/effect_stack.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ fn apply_effect(
276276

277277
let err = || ModuleError::EffectStackImbalance(step);
278278
match op {
279-
Node | Text | Null | Clear => {}
279+
Node | Null | Clear => {}
280280
SuppressBegin => *suppress += 1,
281281
// At depth 0 a `SuppressEnd` would drive the counter negative — the
282282
// exact underflow the VM panics on.

‎crates/plotnik-bytecode/src/bytecode/module/forged_tests.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,12 +394,12 @@ fn forged_invalid_nav_is_rejected() {
394394

395395
#[test]
396396
fn forged_invalid_effect_opcode_is_rejected() {
397-
// `14` is past the 0..=13 effect range; `EffectOpcode::from_u8` would panic
397+
// `13` is past the 0..=12 effect range; `EffectOpcode::from_u8` would panic
398398
// when the VM emits this effect.
399399
let mut bytes = emit_bytes(STRUCT_QUERY);
400400
let slot = effect_slots(&bytes)[0];
401401
let existing = u16::from_le_bytes([bytes[slot], bytes[slot + 1]]);
402-
let forged = (14u16 << 10) | (existing & 0x3FF);
402+
let forged = (13u16 << 10) | (existing & 0x3FF);
403403
bytes[slot..slot + 2].copy_from_slice(&forged.to_le_bytes());
404404
reseal(&mut bytes);
405405

@@ -639,12 +639,12 @@ fn forged_dropped_scope_close_is_rejected() {
639639

640640
#[test]
641641
fn forged_suppress_underflow_is_rejected() {
642-
// Replace a data effect with a bare `SuppressEnd` (opcode 13). With no
642+
// Replace a data effect with a bare `SuppressEnd` (opcode 12). With no
643643
// matching `SuppressBegin` on the path, the VM's suppression counter would
644644
// underflow and `.expect()` panic; the verifier rejects it at load.
645645
let mut bytes = emit_bytes(STRUCT_QUERY);
646646
let slot = first_effect_op(&bytes, |op| op == 4 || op == 6);
647-
bytes[slot..slot + 2].copy_from_slice(&(13u16 << 10).to_le_bytes());
647+
bytes[slot..slot + 2].copy_from_slice(&(12u16 << 10).to_le_bytes());
648648
reseal(&mut bytes);
649649

650650
let err = Module::load(&bytes).expect_err("forged SuppressEnd underflow must be rejected");

‎crates/plotnik-bytecode/src/bytecode/type_meta.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const _: () = assert!(std::mem::size_of::<TypeDef>() == TypeDef::SIZE);
3535
/// Structured view of TypeDef data, eliminating the need for Option-returning accessors.
3636
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3737
pub enum TypeData {
38-
/// Primitive types: Void, Node, String.
38+
/// Primitive types: Void, Node.
3939
Primitive(TypeKind),
4040
/// Wrapper types: Optional, ArrayZeroOrMore, ArrayOneOrMore, Alias.
4141
Wrapper { kind: TypeKind, inner: TypeId },
@@ -51,7 +51,7 @@ impl TypeDef {
5151
/// Serialized size in bytes.
5252
pub const SIZE: usize = 4;
5353

54-
/// Create a builtin type (Void, Node, String).
54+
/// Create a builtin type (Void, Node).
5555
pub fn builtin(kind: TypeKind) -> Self {
5656
Self {
5757
data: 0,
@@ -160,7 +160,7 @@ impl TypeDef {
160160
pub fn try_classify(&self) -> Option<TypeData> {
161161
let kind = TypeKind::from_u8(self.kind)?;
162162
Some(match kind {
163-
TypeKind::Void | TypeKind::Node | TypeKind::String => TypeData::Primitive(kind),
163+
TypeKind::Void | TypeKind::Node => TypeData::Primitive(kind),
164164
TypeKind::Optional
165165
| TypeKind::ArrayZeroOrMore
166166
| TypeKind::ArrayOneOrMore

0 commit comments

Comments
 (0)