[RUST/PEG] parser! 매크로 : 미니 언어 만들기
■ parser! 매크로를 사용해 미니 언어를 만드는 방법을 보여준다. ▶ Cargo.toml
1 2 3 4 5 6 7 8 9 |
[package] name = "test_project" version = "0.1.0" edition = "2021" [dependencies] peg = "0.8" |
▶ src/node.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#[derive(Debug, Clone)] pub enum Node { Nop, Number(i64), // 숫자 값 Expression(char, Box<Node>, Box<Node>), // 계산식 If(Box<Node>, Box<Vec<Node>>, Box<Vec<Node>>), // if문 For(String, i64, i64, Box<Vec<Node>>), // for문 Print(Box<Node>), // print문(계산 출력) PrintString(String), // print문(문자열 출력) SetVariable(String, Box<Node>), // 변수 대입 GetVariable(String) // 변수 참조 } impl Node { pub fn expression(operator : char, left_value : Node, right_value : Node) -> Node { return Node::Expression(operator, Box::new(left_value), Box::new(right_value)); } pub fn if_(condition_node : Node, true_node_vector : Vec<Node>, false_node_vector : Vec<Node>) -> Node { return Node::If(Box::new(condition_node), Box::new(true_node_vector), Box::new(false_node_vector)); } } |
▶ src/parser.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
use peg; use crate::node; peg::parser! ( pub grammar custom_language() for str { pub rule parse() -> Vec<node::Node> = value : sentences() rule sentences() -> Vec<node::Node> = sentence() ** end_of_line() rule sentence() -> node::Node = print() / if() / for() / let() / _ { node::Node::Nop } rule print() -> node::Node = "print" _ "\"" value : $([^ '"']*) "\"" { node::Node::PrintString(value.to_string()) } / "print" _ value : calc() { node::Node::Print(Box::new(value)) } rule if() -> node::Node = "if" _ value : if_cond() { value } rule if_cond() -> node::Node = if_elif() / if_else() / if_true_only() rule if_elif() -> node::Node = condition_node : calc() true_node_vector : block() lf() "elif" _ false_node_vector : if_cond() { node::Node::if_(condition_node, true_node_vector, vec![false_node_vector]) } rule if_else() -> node::Node = condition_node : calc() true_node_vector : block() lf() "else" _ false_node_vector : block() { node::Node::if_(condition_node, true_node_vector, false_node_vector) } rule if_true_only() -> node::Node = condition_node : calc() true_node_vector : block() { node::Node::if_(condition_node, true_node_vector, vec![]) } rule block() -> Vec<node::Node> = "{" _ value : sentences() _ "}" _ { value } rule for() -> node::Node = "for" _ variable_name : word() _ "=" _ start : number() _ "to" _ end : number() _ body : block() { node::Node::For(variable_name, start, end, Box::new(body)) } rule let() -> node::Node = variable_name : word() _ "=" _ value : calc() { node::Node::SetVariable(variable_name, Box::new(value))} rule calc() -> node::Node = comp() rule comp() -> node::Node = left : expr() "==" _ right : comp() { node::Node::expression('=', left, right) } / left : expr() "!=" _ right : comp() { node::Node::expression('!', left, right) } / left : expr() ">" _ right : comp() { node::Node::expression('>', left, right) } / left : expr() ">=" _ right : comp() { node::Node::expression('g', left, right) } / left : expr() "<" _ right : comp() { node::Node::expression('<', left, right) } / left : expr() "<=" _ right : comp() { node::Node::expression('l', left, right) } / expr() rule expr() -> node::Node = left : term() "+" _ right : calc() { node::Node::expression('+', left, right) } / left : term() "-" _ right : calc() { node::Node::expression('-', left, right) } / term() rule term() -> node::Node = left : val() "*" _ right : term() { node::Node::expression('*', left, right) } / left : val() "/" _ right : term() { node::Node::expression('/', left, right) } / left : val() "%" _ right : term() { node::Node::expression('%', left, right) } / val() rule val() -> node::Node = "(" _ value : calc() _ ")" _ { value } / value : number() _ { node::Node::Number(value) } / value : word() _ { node::Node::GetVariable(value) } rule number() -> i64 = value : $(['0'..='9']+) { value.parse().unwrap() } rule word() -> String = value : $(['a'..='z'|'A'..='Z'|'_']+ ['0'..='9']*) { String::from(value) } rule end_of_line() = [';' | '\n']+ _ // 문장 나누기 rule lf() = _ ['\n']* _ // 줄바꿈 rule _ = [' ' | '\t']* // 공백문자 } ); |
▶ src/runner.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
use std::collections; use crate::node; use crate::parser; struct Context { variable_hashmap : collections::HashMap<String, i64> } fn run_node(context : &mut Context, node : node::Node) -> i64 { match node { node::Node::Number(value) => value, node::Node::Expression(operator, left_value, right_value) => { return calculate(operator, run_node(context, *left_value), run_node(context, *right_value)); }, node::Node::GetVariable(variable_name) => { return match context.variable_hashmap.get(&variable_name) { Some(value) => *value, None => 0 }; }, node::Node::SetVariable(variable_name, node) => { let value : i64 = run_node(context, *node); context.variable_hashmap.insert(variable_name, value); return value; }, node::Node::If(condition_node_box, true_node_vector_box, false_node_vector_box) => { let result : i64 = run_node(context, *condition_node_box); if result > 0 { return run_nodes(context, &*true_node_vector_box); } else { return run_nodes(context, &*false_node_vector_box); } }, node::Node::For(control_variable_name, start_value, end_value, body_node_vector_box) => { let mut result : i64 = 0; let node_vector : Vec<node::Node> = *body_node_vector_box; for i in start_value..=end_value { context.variable_hashmap.insert(control_variable_name.clone(), i); result = run_nodes(context, &node_vector); } return result; }, node::Node::PrintString(value) => { println!("{}", value); return 0; }, node::Node::Print(node_box) => { let value : i64 = run_node(context, *node_box); println!("{}", value); return value; }, _ => 0 } } fn calculate(operator : char, left_value : i64, right_value : i64) -> i64 { return match operator { '+' => left_value + right_value, '-' => left_value - right_value, '*' => left_value * right_value, '/' => left_value / right_value, '%' => left_value % right_value, '=' => if left_value == right_value {1} else {0}, '!' => if left_value != right_value {1} else {0}, '>' => if left_value > right_value {1} else {0}, 'g' => if left_value >= right_value {1} else {0}, '<' => if left_value < right_value {1} else {0}, 'l' => if left_value <= right_value {1} else {0}, _ => 0 }; } fn run_nodes(context : &mut Context, node_vector : &Vec<node::Node>) -> i64 { let mut result : i64 = 0; node_vector.iter().for_each(|node : &node::Node| { result = run_node(context, node.clone()); }); return result; } pub fn run(source : &str) -> i64 { let node_vector : Vec<node::Node> = parser::custom_language::parse(source).unwrap(); let mut context : Context = Context { variable_hashmap : collections::HashMap::new() }; return run_nodes(&mut context, &node_vector); } |
▶