Skip to content

feat: support persistent ghosts #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,14 @@ pub enum Ast {
definition: String,
},
GhostFunction {
persistent: bool,
name: String,
ty_list: Vec<String>,
returns: String,
axioms: Option<String>,
},
GhostMapping {
persistent: bool,
name: String,
mapping: String,
axioms: Option<String>,
Expand Down
42 changes: 27 additions & 15 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,28 +92,40 @@ fn decl_parser() -> impl Parser<Token, Intermediate, Error = Simple<Token>> {
};

let ghost_decl = {
let with_mapping = just(Token::Ghost)
.ignore_then(ty())
let persistent_ghost = just(Token::Persistent).then(just(Token::Ghost)).to(true);
let regular_ghost = just(Token::Ghost).to(false);

let opening_tokens = persistent_ghost.or(regular_ghost);

let with_mapping = opening_tokens
.clone()
.then(ty())
.then(ident())
.then(optional_code_block())
.map(|((mapping, name), block)| Intermediate::GhostMapping {
mapping,
name,
axioms: block,
})
.map(
|(((persistent, mapping), name), axioms)| Intermediate::GhostMapping {
persistent,
mapping,
name,
axioms,
},
)
.labelled("ghost declaration (with mapping)");

let without_mapping = just(Token::Ghost)
.ignore_then(ident())
let without_mapping = opening_tokens
.then(ident())
.then(unnamed_param_list())
.then(returns_type())
.then(optional_code_block())
.map(|(((name, ty_list), returns), block)| Intermediate::Ghost {
name,
ty_list,
returns,
axioms: block,
})
.map(
|((((persistent, name), ty_list), returns), axioms)| Intermediate::GhostFunction {
persistent,
name,
ty_list,
returns,
axioms,
},
)
.labelled("ghost declaration (without mapping)");

with_mapping.or(without_mapping)
Expand Down
6 changes: 5 additions & 1 deletion src/parse/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,27 +261,31 @@ impl<'src> Builder<'src> {
DocOrAst::Ast(ast)
}
Intermediate::GhostMapping {
persistent,
mapping,
name,
axioms,
} => {
let axioms = axioms.map(|c| self.owned_slice(c));
let ast = Ast::GhostMapping {
persistent,
name,
mapping,
axioms,
};

DocOrAst::Ast(ast)
}
Intermediate::Ghost {
Intermediate::GhostFunction {
persistent,
name,
ty_list,
returns,
axioms,
} => {
let axioms = axioms.map(|c| self.owned_slice(c));
let ast = Ast::GhostFunction {
persistent,
name,
ty_list,
returns,
Expand Down
1 change: 1 addition & 0 deletions src/parse/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub fn cvl_lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>
"sig" => Token::Sig,
"description" => Token::Description,
"old" => Token::Old,
"persistent" => Token::Persistent,
_ => Token::Ident(ident),
});
let other = {
Expand Down
27 changes: 27 additions & 0 deletions src/parse/tests/cvl2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,3 +546,30 @@ fn unrecognized_tags() {
assert_matches!(tag3.kind, TagKind::Formula);
assert_eq!(tag3.description, "hello@withrevert(world)"); // @withrevert should not parse to a new tag
}

#[test]
fn persistent_ghosts() {
let src = indoc! {"
persistent ghost int pers;
ghost int non_pers;
"};

let parsed = Builder::new(src).build().unwrap();

let check_ghost = |element: &CvlElement, expected_name, expected_persistent| match &element.ast
{
Ast::GhostMapping {
persistent, name, ..
} => {
assert_eq!(name, expected_name);
assert_eq!(*persistent, expected_persistent);
}

ast => panic!("got unexpected ast: ${ast:?}"),
};

assert_eq!(parsed.len(), 2);

check_ghost(&parsed[0], "pers", true);
check_ghost(&parsed[1], "non_pers", false);
}
6 changes: 5 additions & 1 deletion src/parse/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub enum Token {
Old,
Offset,
Slot,
Persistent,
}

impl Display for Token {
Expand Down Expand Up @@ -108,6 +109,7 @@ impl Display for Token {
Token::Index => write!(f, "INDEX"),
Token::Slot => write!(f, "slot"),
Token::Offset => write!(f, "offset"),
Token::Persistent => write!(f, "persistent"),

Token::Ident(data) | Token::Other(data) | Token::Number(data) | Token::String(data) => {
write!(f, "{data}")
Expand Down Expand Up @@ -141,11 +143,13 @@ pub enum Intermediate {
block: Span,
},
GhostMapping {
persistent: bool,
name: String,
mapping: String,
axioms: Option<Span>,
},
Ghost {
GhostFunction {
persistent: bool,
name: String,
ty_list: Vec<String>,
returns: String,
Expand Down