Skip to content

Commit a94d361

Browse files
committed
feat: support persistent ghosts
1 parent b4a58e6 commit a94d361

File tree

6 files changed

+67
-17
lines changed

6 files changed

+67
-17
lines changed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@ pub enum Ast {
7575
definition: String,
7676
},
7777
GhostFunction {
78+
persistent: bool,
7879
name: String,
7980
ty_list: Vec<String>,
8081
returns: String,
8182
axioms: Option<String>,
8283
},
8384
GhostMapping {
85+
persistent: bool,
8486
name: String,
8587
mapping: String,
8688
axioms: Option<String>,

src/parse.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,28 +92,40 @@ fn decl_parser() -> impl Parser<Token, Intermediate, Error = Simple<Token>> {
9292
};
9393

9494
let ghost_decl = {
95-
let with_mapping = just(Token::Ghost)
96-
.ignore_then(ty())
95+
let persistent_ghost = just(Token::Persistent).then(just(Token::Ghost)).to(true);
96+
let regular_ghost = just(Token::Ghost).to(false);
97+
98+
let opening_tokens = persistent_ghost.or(regular_ghost);
99+
100+
let with_mapping = opening_tokens
101+
.clone()
102+
.then(ty())
97103
.then(ident())
98104
.then(optional_code_block())
99-
.map(|((mapping, name), block)| Intermediate::GhostMapping {
100-
mapping,
101-
name,
102-
axioms: block,
103-
})
105+
.map(
106+
|(((persistent, mapping), name), axioms)| Intermediate::GhostMapping {
107+
persistent,
108+
mapping,
109+
name,
110+
axioms,
111+
},
112+
)
104113
.labelled("ghost declaration (with mapping)");
105114

106-
let without_mapping = just(Token::Ghost)
107-
.ignore_then(ident())
115+
let without_mapping = opening_tokens
116+
.then(ident())
108117
.then(unnamed_param_list())
109118
.then(returns_type())
110119
.then(optional_code_block())
111-
.map(|(((name, ty_list), returns), block)| Intermediate::Ghost {
112-
name,
113-
ty_list,
114-
returns,
115-
axioms: block,
116-
})
120+
.map(
121+
|((((persistent, name), ty_list), returns), axioms)| Intermediate::GhostFunction {
122+
persistent,
123+
name,
124+
ty_list,
125+
returns,
126+
axioms,
127+
},
128+
)
117129
.labelled("ghost declaration (without mapping)");
118130

119131
with_mapping.or(without_mapping)

src/parse/builder.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,27 +261,31 @@ impl<'src> Builder<'src> {
261261
DocOrAst::Ast(ast)
262262
}
263263
Intermediate::GhostMapping {
264+
persistent,
264265
mapping,
265266
name,
266267
axioms,
267268
} => {
268269
let axioms = axioms.map(|c| self.owned_slice(c));
269270
let ast = Ast::GhostMapping {
271+
persistent,
270272
name,
271273
mapping,
272274
axioms,
273275
};
274276

275277
DocOrAst::Ast(ast)
276278
}
277-
Intermediate::Ghost {
279+
Intermediate::GhostFunction {
280+
persistent,
278281
name,
279282
ty_list,
280283
returns,
281284
axioms,
282285
} => {
283286
let axioms = axioms.map(|c| self.owned_slice(c));
284287
let ast = Ast::GhostFunction {
288+
persistent,
285289
name,
286290
ty_list,
287291
returns,

src/parse/lexer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub fn cvl_lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>
127127
"sig" => Token::Sig,
128128
"description" => Token::Description,
129129
"old" => Token::Old,
130+
"persistent" => Token::Persistent,
130131
_ => Token::Ident(ident),
131132
});
132133
let other = {

src/parse/tests/cvl2.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,30 @@ fn unrecognized_tags() {
546546
assert_matches!(tag3.kind, TagKind::Formula);
547547
assert_eq!(tag3.description, "hello@withrevert(world)"); // @withrevert should not parse to a new tag
548548
}
549+
550+
#[test]
551+
fn persistent_ghosts() {
552+
let src = indoc! {"
553+
persistent ghost int pers;
554+
ghost int non_pers;
555+
"};
556+
557+
let parsed = Builder::new(src).build().unwrap();
558+
559+
let check_ghost = |element: &CvlElement, expected_name, expected_persistent| {
560+
let Ast::GhostMapping {
561+
persistent, name, ..
562+
} = &element.ast
563+
else {
564+
panic!()
565+
};
566+
567+
assert_eq!(name, expected_name);
568+
assert_eq!(*persistent, expected_persistent);
569+
};
570+
571+
assert_eq!(parsed.len(), 2);
572+
573+
check_ghost(&parsed[0], "pers", true);
574+
check_ghost(&parsed[1], "non_pers", false);
575+
}

src/parse/types.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub enum Token {
5959
Old,
6060
Offset,
6161
Slot,
62+
Persistent,
6263
}
6364

6465
impl Display for Token {
@@ -108,6 +109,7 @@ impl Display for Token {
108109
Token::Index => write!(f, "INDEX"),
109110
Token::Slot => write!(f, "slot"),
110111
Token::Offset => write!(f, "offset"),
112+
Token::Persistent => write!(f, "persistent"),
111113

112114
Token::Ident(data) | Token::Other(data) | Token::Number(data) | Token::String(data) => {
113115
write!(f, "{data}")
@@ -141,11 +143,13 @@ pub enum Intermediate {
141143
block: Span,
142144
},
143145
GhostMapping {
146+
persistent: bool,
144147
name: String,
145148
mapping: String,
146149
axioms: Option<Span>,
147150
},
148-
Ghost {
151+
GhostFunction {
152+
persistent: bool,
149153
name: String,
150154
ty_list: Vec<String>,
151155
returns: String,

0 commit comments

Comments
 (0)