add: generate ast

main
q1-silver 3 years ago
parent 78e21f9954
commit b37e34fd29

10
Cargo.lock generated

@ -50,6 +50,7 @@ dependencies = [
name = "dog-lexer" name = "dog-lexer"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"litrs",
"pest", "pest",
"pest_derive", "pest_derive",
] ]
@ -70,6 +71,15 @@ version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "litrs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b487d13a3f4b465df87895a37b24e364907019afa12d943528df5b7abe0836f1"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.16.0" version = "1.16.0"

@ -7,4 +7,5 @@ edition = "2021"
[dependencies] [dependencies]
pest = "2.5" pest = "2.5"
pest_derive = "2.5" pest_derive = "2.5"
litrs = "0.3"

@ -1,22 +1,23 @@
WHITESPACE = _{ " " | "\t" | "\n" | "\r" | "\r\n" | "\u{feff}" } WHITESPACE = _{ " " | "\t" | "\n" | "\r" | "\r\n" | "\u{feff}" }
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | (";" ~ (!"\n" ~ ANY)* ~ "\n") } COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | (";" ~ (!"\n" ~ ANY)* ~ "\n") }
script = _{ SOI ~ (command | globConfig )* ~ EOI} script = _{ SOI ~ (command | globConfig )* ~ EOI}
ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* } ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
integer = @{ ASCII_DIGIT+ } integer = @{ ASCII_DIGIT+ }
string = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" } string = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
rawLine = @{ (!"\n" ~ ANY)* }
command = { trigger ~ "::" ~ body } command = { trigger ~ "::" ~ body }
globConfig = { "SendMode" ~ "Input" } globConfig = { "SendMode" ~ "Input" | "#ifWinActive" ~ rawLine }
trigger = { modifier* ~ hotkey } trigger = { modifier* ~ hotkey }
modifier = { "^" | "~" | "!" | "+" } modifier = { "^" | "~" | "!" | "+" | "$" }
hotkey = { ident } hotkey = { ident }
body = { expr* ~ ("Return" | EOI)} body = { expr* ~ ("Return" | EOI)}
expr = { action | assgmtExpr | controlFlow } // | funcCall } expr = { action | assgmtExpr | controlFlow | funcCall }
assgmtExpr = { ident ~ "=" ~ integer } assgmtExpr = { ident ~ "=" ~ integer }
@ -31,10 +32,9 @@ action = { sendRawAction | sendAction | sleepAction }
sendRawAction = { "SendRaw" ~ target } sendRawAction = { "SendRaw" ~ target }
sendAction = { "Send" ~ target } sendAction = { "Send" ~ target }
sleepAction = { "Sleep" ~ (integer | variable) } sleepAction = { "Sleep" ~ (integer | variable) }
target = { button | variable | rawLine } target = _{ button | variable | rawLine }
button = { "{" ~ ident ~ "}" } button = { "{" ~ ident ~ "}" }
variable = { "%" ~ ident ~ "%" } variable = { "%" ~ ident ~ "%" }
rawLine = @{ (!"\n" ~ ANY)* }
funcCall = { ident ~ "(" ~ params ~ ")"} funcCall = { ident ~ "(" ~ params ~ ")"}
params = { param ~ ("," ~ param)* } params = { param ~ ("," ~ param)* }

@ -2,11 +2,285 @@ use pest::Parser;
use pest_derive::Parser; use pest_derive::Parser;
fn main() { fn main() {
let unparsed_file = std::fs::read_to_string("test/BnS.ahk").expect("cannot read file"); let mut args = std::env::args();
let successful_parse = AHKParser::parse(Rule::script, &unparsed_file).unwrap(); let exec_name = args.next().unwrap();
println!("{:?}", successful_parse); 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)] #[derive(Parser)]
#[grammar = "ahk.pest"] #[grammar = "ahk.pest"]
pub struct AHKParser; 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),
}
}

Loading…
Cancel
Save