|
|
|
|
@ -2,11 +2,285 @@ use pest::Parser;
|
|
|
|
|
use pest_derive::Parser;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let unparsed_file = std::fs::read_to_string("test/BnS.ahk").expect("cannot read file");
|
|
|
|
|
let successful_parse = AHKParser::parse(Rule::script, &unparsed_file).unwrap();
|
|
|
|
|
println!("{:?}", successful_parse);
|
|
|
|
|
let mut args = std::env::args();
|
|
|
|
|
let exec_name = args.next().unwrap();
|
|
|
|
|
let unparsed_file = std::fs::read_to_string(
|
|
|
|
|
args.next()
|
|
|
|
|
.expect(&format!("Usage: {} FILE_TO_PARSE", exec_name)),
|
|
|
|
|
)
|
|
|
|
|
.expect("cannot read file");
|
|
|
|
|
|
|
|
|
|
let astnode = parse(&unparsed_file).expect("unsuccessful parse");
|
|
|
|
|
println!("{:?}", &astnode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum AstNode {
|
|
|
|
|
Print(Box<AstNode>),
|
|
|
|
|
GlobConfig(Vec<GlobConfigNode>),
|
|
|
|
|
Integer(i32),
|
|
|
|
|
Ident(String),
|
|
|
|
|
Trigger {
|
|
|
|
|
modifiers: Modifiers,
|
|
|
|
|
hotkey: String,
|
|
|
|
|
},
|
|
|
|
|
Sleep(usize),
|
|
|
|
|
Send(Box<AstNode>),
|
|
|
|
|
SendRaw(Box<AstNode>),
|
|
|
|
|
Assignment {
|
|
|
|
|
ident: String,
|
|
|
|
|
expr: Box<AstNode>,
|
|
|
|
|
},
|
|
|
|
|
Button(String),
|
|
|
|
|
Variable(Box<AstNode>),
|
|
|
|
|
RawLine(String),
|
|
|
|
|
Body(Vec<AstNode>),
|
|
|
|
|
Command {
|
|
|
|
|
trigger: Box<AstNode>,
|
|
|
|
|
body: Box<AstNode>,
|
|
|
|
|
},
|
|
|
|
|
LoopFlow(Box<AstNode>),
|
|
|
|
|
IfFlow {
|
|
|
|
|
condition: Box<AstNode>,
|
|
|
|
|
body: Box<AstNode>,
|
|
|
|
|
},
|
|
|
|
|
WhileFlow {
|
|
|
|
|
condition: Box<AstNode>,
|
|
|
|
|
body: Box<AstNode>,
|
|
|
|
|
},
|
|
|
|
|
FuncCall {
|
|
|
|
|
ident: String,
|
|
|
|
|
params: Box<AstNode>,
|
|
|
|
|
},
|
|
|
|
|
Params(Vec<AstNode>),
|
|
|
|
|
Break,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct GlobConfigNode {
|
|
|
|
|
lhs: String,
|
|
|
|
|
rhs: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<pest::iterators::Pair<'_, Rule>> for GlobConfigNode {
|
|
|
|
|
fn from(pair: pest::iterators::Pair<Rule>) -> Self {
|
|
|
|
|
let words = pair.as_str().split_once(' ').unwrap();
|
|
|
|
|
GlobConfigNode {
|
|
|
|
|
lhs: words.0.into(),
|
|
|
|
|
rhs: words.1.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct Modifiers {
|
|
|
|
|
pub exclusive: bool,
|
|
|
|
|
pub control: bool,
|
|
|
|
|
pub shift: bool,
|
|
|
|
|
pub alt: bool,
|
|
|
|
|
pub hook_mode: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Modifiers {
|
|
|
|
|
fn new() -> Modifiers {
|
|
|
|
|
Modifiers {
|
|
|
|
|
exclusive: false,
|
|
|
|
|
control: false,
|
|
|
|
|
shift: false,
|
|
|
|
|
alt: false,
|
|
|
|
|
hook_mode: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
|
|
#[grammar = "ahk.pest"]
|
|
|
|
|
pub struct AHKParser;
|
|
|
|
|
|
|
|
|
|
fn parse(source: &str) -> Result<Vec<AstNode>, pest::error::Error<Rule>> {
|
|
|
|
|
let mut ast = vec![];
|
|
|
|
|
let mut glob_config: Vec<GlobConfigNode> = vec![];
|
|
|
|
|
|
|
|
|
|
let pairs = AHKParser::parse(Rule::script, source)?;
|
|
|
|
|
for pair in pairs {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::command => {
|
|
|
|
|
ast.push(AstNode::Print(Box::new(parse_command(pair))));
|
|
|
|
|
}
|
|
|
|
|
Rule::globConfig => glob_config.push(pair.into()),
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast.push(AstNode::GlobConfig(glob_config));
|
|
|
|
|
|
|
|
|
|
Ok(ast)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_command(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
let trigger = parse_trigger(pairs.next().unwrap());
|
|
|
|
|
let body = parse_body(pairs.next().unwrap().into_inner());
|
|
|
|
|
AstNode::Command {
|
|
|
|
|
trigger: Box::new(trigger),
|
|
|
|
|
body: Box::new(body),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_body(pairs: pest::iterators::Pairs<Rule>) -> AstNode {
|
|
|
|
|
let mut body = vec![];
|
|
|
|
|
|
|
|
|
|
for pair in pairs {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::expr => body.push(build_ast_from_expr(pair)),
|
|
|
|
|
Rule::EOI => {}
|
|
|
|
|
_ => unreachable!("Unknown command part: {:?}", pair),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode::Body(body)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_assgmt(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
let ident = String::from(pairs.next().unwrap().as_str());
|
|
|
|
|
|
|
|
|
|
let expr = build_ast_from_expr(pairs.next().unwrap());
|
|
|
|
|
AstNode::Assignment {
|
|
|
|
|
ident,
|
|
|
|
|
expr: Box::new(expr),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_ast_from_expr(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::expr => build_ast_from_expr(pair.into_inner().next().unwrap()),
|
|
|
|
|
Rule::action => parse_action(pair.into_inner().next().unwrap()),
|
|
|
|
|
Rule::assgmtExpr => {
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
let ident = pairs.next().unwrap().as_str().into();
|
|
|
|
|
let expr = build_ast_from_expr(pairs.next().unwrap());
|
|
|
|
|
AstNode::Assignment {
|
|
|
|
|
ident,
|
|
|
|
|
expr: Box::new(expr),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Rule::controlFlow => parse_control_flow(pair.into_inner().next().unwrap()),
|
|
|
|
|
Rule::funcCall => {
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
let ident = pairs.next().unwrap().as_str().into();
|
|
|
|
|
let params = pairs
|
|
|
|
|
.next()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.into_inner()
|
|
|
|
|
.map(build_ast_from_expr)
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
AstNode::FuncCall {
|
|
|
|
|
ident,
|
|
|
|
|
params: Box::new(AstNode::Params(params)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Rule::integer => AstNode::Integer(pair.as_str().parse().unwrap()),
|
|
|
|
|
Rule::string => AstNode::Ident(
|
|
|
|
|
litrs::StringLit::parse(pair.as_str())
|
|
|
|
|
.unwrap()
|
|
|
|
|
.value()
|
|
|
|
|
.to_string(),
|
|
|
|
|
),
|
|
|
|
|
_ => unreachable!("Unknown expression: {:?}", pair),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_control_flow(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::loopFlow => AstNode::LoopFlow(Box::new(parse_body(
|
|
|
|
|
pair.into_inner().next().unwrap().into_inner(),
|
|
|
|
|
))),
|
|
|
|
|
Rule::ifFlow => {
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
let condition = build_ast_from_expr(pairs.next().unwrap());
|
|
|
|
|
let body = parse_body(pairs.next().unwrap().into_inner());
|
|
|
|
|
AstNode::IfFlow {
|
|
|
|
|
condition: Box::new(condition),
|
|
|
|
|
body: Box::new(body),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Rule::whileFlow => {
|
|
|
|
|
let mut pairs = pair.into_inner();
|
|
|
|
|
let condition = build_ast_from_expr(pairs.next().unwrap());
|
|
|
|
|
let body = parse_body(pairs.next().unwrap().into_inner());
|
|
|
|
|
AstNode::WhileFlow {
|
|
|
|
|
condition: Box::new(condition),
|
|
|
|
|
body: Box::new(body),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Rule::breakFlow => AstNode::Break,
|
|
|
|
|
_ => unreachable!("Unknown flow control: {:?}", pair),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_action(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::sleepAction => AstNode::Sleep(
|
|
|
|
|
pair.into_inner()
|
|
|
|
|
.next()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.as_str()
|
|
|
|
|
.parse::<usize>()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
),
|
|
|
|
|
Rule::sendAction => {
|
|
|
|
|
AstNode::Send(Box::new(parse_target(pair.into_inner().next().unwrap())))
|
|
|
|
|
}
|
|
|
|
|
Rule::sendRawAction => {
|
|
|
|
|
AstNode::Send(Box::new(parse_target(pair.into_inner().next().unwrap())))
|
|
|
|
|
}
|
|
|
|
|
_ => unreachable!("Unexpected action: {:?}", pair),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_target(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::button => AstNode::Button(String::from(pair.into_inner().next().unwrap().as_str())),
|
|
|
|
|
Rule::variable => AstNode::Variable(Box::new(build_ast_from_term(
|
|
|
|
|
pair.into_inner().next().unwrap(),
|
|
|
|
|
))),
|
|
|
|
|
Rule::rawLine => AstNode::RawLine(String::from(pair.as_str())),
|
|
|
|
|
_ => unreachable!("Unexpected target: {:?}", pair),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_trigger(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
let mut modifiers = Modifiers::new();
|
|
|
|
|
let mut hotkey = String::from("");
|
|
|
|
|
for pair in pair.into_inner() {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::modifier => {
|
|
|
|
|
let modifier = pair;
|
|
|
|
|
match modifier.as_str() {
|
|
|
|
|
"~" => modifiers.exclusive = true,
|
|
|
|
|
"^" => modifiers.control = true,
|
|
|
|
|
"!" => modifiers.alt = true,
|
|
|
|
|
"+" => modifiers.shift = true,
|
|
|
|
|
"$" => modifiers.hook_mode = true,
|
|
|
|
|
unknown_mod => {
|
|
|
|
|
unreachable!("Unexpected modifier: {}", unknown_mod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Rule::hotkey => {
|
|
|
|
|
hotkey = pair.into_inner().next().unwrap().as_str().to_string();
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
AstNode::Trigger { modifiers, hotkey }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_ast_from_term(pair: pest::iterators::Pair<Rule>) -> AstNode {
|
|
|
|
|
match pair.as_rule() {
|
|
|
|
|
Rule::ident => AstNode::Ident(String::from(pair.as_str())),
|
|
|
|
|
unknown_expr => unreachable!("Unexpected term: {:?}", unknown_expr),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|