-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite ATL parsing for better parse errors and more flexibility (#216)
* Add parsing methods that logs errors * Add state to ATL parser * Small fixes * Beginning of homemade Lexer * Lexer utils cleanup and test * Add remaing tokens and lexing thereof * Add nice error formatting * Fix parse error introduced in latest merge * Allow parse_or_sync to be nested * Created AstAtl with spans * Parser with errors attempt * Add Parser, almost done for ATL * Working ATL parser * Keep parsing to EOF * Add simple parser recovery * Working parser * Clean up parser * Add conversion to phi * Also convert expr idents to propositions in conversion * Remove accidental 4 * Use new ATL parsing in cli * Improved styling and unknown character handling * Add dot expressions to ATL * Add negation to ATL expr * Add neXt path expression * Removed unsupported examples * Remove test atl file * Fix clippy and fmt * Do not expect EOF after unrecovered parsing error * Add batch of atl parsing tests * Add comments to new parser * Update ATL files to use && and || * fmt with updated rustfmt * Improvements to comments and lexer test * Improved some error messages * Bump version * Bump cli version * add ATL label access pass/fail tests --------- Co-authored-by: falkecarlsen <[email protected]>
- Loading branch information
1 parent
b5539d3
commit d8d0aab
Showing
53 changed files
with
1,982 additions
and
1,049 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "cgaal" | ||
version = "1.0.0" | ||
version = "1.0.1" | ||
authors = [ | ||
"d702e20 <[email protected]>", | ||
"d802f21 <[email protected]>", | ||
|
@@ -26,4 +26,4 @@ tracing-subscriber = "0.2.17" | |
serde_json = "1.0.83" | ||
regex = { version = "1", features = ["unicode-case"] } | ||
humantime = "2.1.0" | ||
cgaal-engine = { path = "../cgaal-engine", version = "1.0.0" } | ||
cgaal-engine = { path = "../cgaal-engine", version = "1.0.1" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "cgaal-engine" | ||
version = "1.0.0" | ||
version = "1.0.1" | ||
authors = [ | ||
"Asger Weirsøe <[email protected]>", | ||
"Falke Carlsen <[email protected]>", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
use crate::atl::Phi; | ||
use crate::game_structure::lcgs::ast::DeclKind; | ||
use crate::game_structure::lcgs::ir::intermediate::IntermediateLcgs; | ||
use crate::game_structure::lcgs::ir::symbol_table::Owner; | ||
use crate::game_structure::Player; | ||
use crate::parsing::ast::{BinaryOpKind, Coalition, CoalitionKind, Expr, ExprKind, UnaryOpKind}; | ||
use crate::parsing::errors::ErrorLog; | ||
|
||
/// Convert an ATL expression to a Phi formula. | ||
/// Players and labels must be defined in the game and are compiled to their respective indexes. | ||
/// Returns None if there were errors. See the error log for details. | ||
pub fn convert_expr_to_phi( | ||
expr: &Expr, | ||
game: &IntermediateLcgs, | ||
errors: &mut ErrorLog, | ||
) -> Option<Phi> { | ||
let Expr { span, kind } = expr; | ||
match kind { | ||
ExprKind::True => Some(Phi::True), | ||
ExprKind::False => Some(Phi::False), | ||
ExprKind::Paren(e) => convert_expr_to_phi(e, game, errors), | ||
ExprKind::Ident(ident) => { | ||
let decl = game.get_decl(&Owner::Global.symbol_id(ident)); | ||
match &decl.map(|d| &d.kind) { | ||
Some(DeclKind::Label(l)) => Some(Phi::Proposition(l.index)), | ||
Some(d) => { | ||
errors.log( | ||
*span, | ||
format!( | ||
"Expected proposition label, '{}' is a {}", | ||
ident, | ||
d.kind_name() | ||
), | ||
); | ||
None | ||
} | ||
None => { | ||
errors.log( | ||
*span, | ||
format!("Expected proposition label, '{}' is not defined", ident), | ||
); | ||
None | ||
} | ||
} | ||
} | ||
ExprKind::Unary(op, e) => match op { | ||
UnaryOpKind::Not => Some(Phi::Not(convert_expr_to_phi(e, game, errors)?.into())), | ||
UnaryOpKind::Next | UnaryOpKind::Eventually | UnaryOpKind::Invariantly => { | ||
errors.log( | ||
*span, | ||
"Temporal operators are only allowed after a coalition".to_string(), | ||
); | ||
None | ||
} | ||
}, | ||
ExprKind::Binary(op, lhs, rhs) => match op { | ||
BinaryOpKind::And => Some(Phi::And( | ||
convert_expr_to_phi(lhs, game, errors)?.into(), | ||
convert_expr_to_phi(rhs, game, errors)?.into(), | ||
)), | ||
BinaryOpKind::Or => Some(Phi::Or( | ||
convert_expr_to_phi(lhs, game, errors)?.into(), | ||
convert_expr_to_phi(rhs, game, errors)?.into(), | ||
)), | ||
BinaryOpKind::Dot => { | ||
let ExprKind::Ident(owner) = &lhs.kind else { | ||
errors.log(lhs.span, "Expected player name".to_string()); | ||
return None; | ||
}; | ||
let ExprKind::Ident(prop) = &rhs.kind else { | ||
errors.log(rhs.span, "Expected proposition label".to_string()); | ||
return None; | ||
}; | ||
match game | ||
.get_decl(&Owner::Global.symbol_id(owner)) | ||
.map(|d| &d.kind) | ||
{ | ||
Some(DeclKind::Player(_)) => { | ||
let symb = Owner::Player(owner.clone()).symbol_id(prop); | ||
let decl = game.get_decl(&symb); | ||
match decl.map(|d| &d.kind) { | ||
Some(DeclKind::Label(l)) => Some(Phi::Proposition(l.index)), | ||
Some(d) => { | ||
errors.log( | ||
rhs.span, | ||
format!( | ||
"Expected proposition label, '{}' is a {}", | ||
prop, | ||
d.kind_name(), | ||
), | ||
); | ||
None | ||
} | ||
None => { | ||
errors.log( | ||
rhs.span, | ||
format!( | ||
"Expected proposition label, '{}' is not defined", | ||
symb, | ||
), | ||
); | ||
None | ||
} | ||
} | ||
} | ||
Some(d) => { | ||
errors.log( | ||
lhs.span, | ||
format!("Expected player, '{}' is a {}", owner, d.kind_name()), | ||
); | ||
None | ||
} | ||
None => { | ||
errors.log( | ||
lhs.span, | ||
format!("Expected player, '{}' is not defined", owner), | ||
); | ||
None | ||
} | ||
} | ||
} | ||
BinaryOpKind::Until => { | ||
errors.log( | ||
*span, | ||
"Temporal operators are only allowed after a coalition".to_string(), | ||
); | ||
None | ||
} | ||
}, | ||
ExprKind::Coalition(Coalition { | ||
players, | ||
kind, | ||
expr: path_expr, | ||
.. | ||
}) => match (kind, &path_expr.kind) { | ||
(CoalitionKind::Enforce, ExprKind::Unary(UnaryOpKind::Next, sub_expr)) => { | ||
let phi = convert_expr_to_phi(sub_expr, game, errors)?; | ||
Some(Phi::EnforceNext { | ||
players: convert_players(players, game, errors)?, | ||
formula: phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Despite, ExprKind::Unary(UnaryOpKind::Next, sub_expr)) => { | ||
let phi = convert_expr_to_phi(sub_expr, game, errors)?; | ||
Some(Phi::DespiteNext { | ||
players: convert_players(players, game, errors)?, | ||
formula: phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Enforce, ExprKind::Unary(UnaryOpKind::Eventually, sub_expr)) => { | ||
let phi = convert_expr_to_phi(sub_expr, game, errors)?; | ||
Some(Phi::EnforceEventually { | ||
players: convert_players(players, game, errors)?, | ||
formula: phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Despite, ExprKind::Unary(UnaryOpKind::Eventually, sub_expr)) => { | ||
let phi = convert_expr_to_phi(sub_expr, game, errors)?; | ||
Some(Phi::DespiteEventually { | ||
players: convert_players(players, game, errors)?, | ||
formula: phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Enforce, ExprKind::Unary(UnaryOpKind::Invariantly, sub_expr)) => { | ||
let phi = convert_expr_to_phi(sub_expr, game, errors)?; | ||
Some(Phi::EnforceInvariant { | ||
players: convert_players(players, game, errors)?, | ||
formula: phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Despite, ExprKind::Unary(UnaryOpKind::Invariantly, sub_expr)) => { | ||
let phi = convert_expr_to_phi(sub_expr, game, errors)?; | ||
Some(Phi::DespiteInvariant { | ||
players: convert_players(players, game, errors)?, | ||
formula: phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Enforce, ExprKind::Binary(BinaryOpKind::Until, lhs, rhs)) => { | ||
let lhs_phi = convert_expr_to_phi(lhs, game, errors)?; | ||
let rhs_phi = convert_expr_to_phi(rhs, game, errors)?; | ||
Some(Phi::EnforceUntil { | ||
players: convert_players(players, game, errors)?, | ||
pre: lhs_phi.into(), | ||
until: rhs_phi.into(), | ||
}) | ||
} | ||
(CoalitionKind::Despite, ExprKind::Binary(BinaryOpKind::Until, lhs, rhs)) => { | ||
let lhs_phi = convert_expr_to_phi(lhs, game, errors)?; | ||
let rhs_phi = convert_expr_to_phi(rhs, game, errors)?; | ||
Some(Phi::DespiteUntil { | ||
players: convert_players(players, game, errors)?, | ||
pre: lhs_phi.into(), | ||
until: rhs_phi.into(), | ||
}) | ||
} | ||
_ => { | ||
errors.log( | ||
path_expr.span, | ||
"Coalitions must be followed by a path formula".to_string(), | ||
); | ||
None | ||
} | ||
}, | ||
ExprKind::Error => None, | ||
} | ||
} | ||
|
||
/// Helper function for converting a list of player names to a list of player indexes. | ||
/// Returns None if there were errors. See the error log for details. | ||
fn convert_players( | ||
players: &[Expr], | ||
game: &IntermediateLcgs, | ||
errors: &mut ErrorLog, | ||
) -> Option<Vec<Player>> { | ||
players | ||
.iter() | ||
.map(|expr| match &expr.kind { | ||
ExprKind::Ident(name) => match game | ||
.get_decl(&Owner::Global.symbol_id(name)) | ||
.map(|d| &d.kind) | ||
{ | ||
Some(DeclKind::Player(p)) => Some(p.index), | ||
Some(d) => { | ||
errors.log( | ||
expr.span, | ||
format!("Expected player, '{}' is a {}", name, d.kind_name()), | ||
); | ||
None | ||
} | ||
None => { | ||
errors.log( | ||
expr.span, | ||
format!("Expected player, '{}' is not defined", name), | ||
); | ||
None | ||
} | ||
}, | ||
_ => { | ||
errors.log(expr.span, "Expected player name".to_string()); | ||
None | ||
} | ||
}) | ||
.collect() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.