[C#/WINUI3/.NET8] 소스 코드 HTML 문자열 만들기
■ 소스 코드를 HTML 문자열로 만드는 방법을 보여준다. ※ 비주얼 스튜디오에서 TestProject(Unpackaged) 모드로 빌드한다. ※ TestProject.csproj 프로젝트 파일에서 WindowsPackageType 태그를 None으로 추가했다.
■ 소스 코드를 HTML 문자열로 만드는 방법을 보여준다. ※ 비주얼 스튜디오에서 TestProject(Unpackaged) 모드로 빌드한다. ※ TestProject.csproj 프로젝트 파일에서 WindowsPackageType 태그를 None으로 추가했다.
■ 미니 언어를 실행하는 웹 어셈블리를 만드는 방법을 보여준다. ▶ Cargo.toml
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[package] name = "test_library" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib", "rlib"] [dependencies] peg = "0.7" wasm-bindgen = "0.2" |
▶ 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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
use std::collections; use crate::node; use crate::parser; struct Context { variable_hashmap : collections::HashMap<String, i64>, output : String } 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) => { context.output += &format!("{}\n", value); return 0; }, node::Node::Print(node_box) => { let value : i64 = run_node(context, *node_box); context.output += &format!("{}\n", 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) -> String { let node_vector : Vec<node::Node> = match parser::custom_language::parse(source) { Ok(res) => res, Err(e) => return e.to_string() }; let mut context : Context = Context { variable_hashmap : collections::HashMap::new(), output : String::new() }; let result = run_nodes(&mut context, &node_vector); if context.output == "" { return format!("{}", result); } else { return context.output.clone(); } } |
▶
■ 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); } |
▶
■ 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/main.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 |
peg::parser! ( grammar calc() for str { pub rule eval() -> i64 = expr() rule expr() -> i64 = left_value : term() "+" right_value : expr() { left_value + right_value } / left_value : term() "-" right_value : expr() { left_value - right_value } / term() rule term() -> i64 = left_value : value() "*" right_value : term() { left_value * right_value } / left_value : value() "/" right_value : term() { left_value / right_value } / value : value() rule value() -> i64 = number() / "(" value : expr() ")" { value } rule number() -> i64 = value : $(['0'..='9']+) { value.parse().unwrap() } } ); fn main() { println!("{}", calc::eval("10+20*30").unwrap()); println!("{}", calc::eval("(10+20)*30").unwrap()); println!("{}", calc::eval("10/20-30").unwrap()); } |
test_project.zip
■ 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/main.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 |
peg::parser! ( grammar calc() for str { pub rule eval() -> i64 = term() rule term() -> i64 = v1 : num() "+" v2 : num() { v1 + v2 } rule num() -> i64 = value : $(['0'..='9']+) { value.parse().unwrap() } } ); fn main() { println!("2+5={}", calc::eval("2+5").unwrap()); println!("8+2={}", calc::eval("8+2").unwrap()); println!("200+50={}", calc::eval("200+50").unwrap()); } /* 2+5=7 8+2=10 200+50=250 */ |
test_project.zip
■ peg 크레이트를 설치하는 방법을 보여준다. ▶ Cargo.toml
1 2 3 4 5 6 |
... [dependencies] peg = "0.8" |