diff --git a/Cargo.lock b/Cargo.lock index 7768a14..dd06f09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,10 @@ dependencies = [ "copager_lex_regex", "copager_parse", "copager_parse_lr1", + "example_lang_arithmetic", + "example_lang_json", + "example_lang_pl0", + "example_lang_xml", "serde", "serde_json", ] @@ -210,7 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] -name = "example_oneshot" +name = "example_build_oneshot" version = "0.1.0" dependencies = [ "anyhow", @@ -219,7 +223,7 @@ dependencies = [ ] [[package]] -name = "example_prebuild" +name = "example_build_prebuild" version = "0.1.0" dependencies = [ "anyhow", @@ -229,6 +233,42 @@ dependencies = [ "thiserror", ] +[[package]] +name = "example_lang_arithmetic" +version = "0.1.0" +dependencies = [ + "anyhow", + "copager", + "thiserror", +] + +[[package]] +name = "example_lang_json" +version = "0.1.0" +dependencies = [ + "anyhow", + "copager", + "thiserror", +] + +[[package]] +name = "example_lang_pl0" +version = "0.1.0" +dependencies = [ + "anyhow", + "copager", + "thiserror", +] + +[[package]] +name = "example_lang_xml" +version = "0.1.0" +dependencies = [ + "anyhow", + "copager", + "thiserror", +] + [[package]] name = "example_prebuild_grammar" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 61469d6..928bba1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,10 @@ anyhow = { workspace = true } serde = { workspace = true } serde_json = "1.0.117" copager = { path = ".", features = ["all"] } +example_lang_arithmetic = { path = "./examples/lang_arithmetic" } +example_lang_json = { path = "./examples/lang_json" } +example_lang_pl0 = { path = "./examples/lang_pl0" } +example_lang_xml = { path = "./examples/lang_xml" } [features] # common @@ -60,8 +64,12 @@ members = [ "./crates/utils", # Examples - "./examples/oneshot", - "./examples/prebuild", + "./examples/build_oneshot", + "./examples/build_prebuild", + "./examples/lang_arithmetic", + "./examples/lang_json", + "./examples/lang_pl0", + "./examples/lang_xml", ] exclude = [] @@ -69,3 +77,19 @@ exclude = [] anyhow = "1.0.82" thiserror = "1.0.58" serde = { version = "1.0.197", features = ["derive"] } + +[[test]] +name = "test_by_arithmetic" +path = "./tests/arithmetic/test.rs" + +[[test]] +name = "test_by_json" +path = "./tests/json/test.rs" + +[[test]] +name = "test_by_pl0" +path = "./tests/pl0/test.rs" + +[[test]] +name = "test_by_xml" +path = "./tests/xml/test.rs" diff --git a/README.md b/README.md index e2e6379..2a6ade1 100644 --- a/README.md +++ b/README.md @@ -25,28 +25,22 @@ Rust製パーサジェネレータ ## Examples -### One-shot - -[examples/oneshot](examples/oneshot) +- [example_build_oneshot](examples/build_oneshot) +- [example_build_prebuild](examples/build_prebuild) +- [example_lang_arithmetic](examples/lang_arithmetic) +- [example_lang_json](examples/lang_json) +- [example_lang_pl0](examples/lang_pl0) +- [example_lang_xml](examples/lang_xml) ``` -$ echo "10 * (20 + 30)" | cargo run -p example_oneshot -Success : (Expr (Term (Term (Num "10")) "*" (Num "(" (Expr (Expr (Term (Num "20"))) "+" (Term (Num "30"))) ")"))) +$ cargo run -p example_build_oneshot +Example +Input: 10 * 20 + 30 +Success: (Expr (Expr (Term (Term (Num "10")) "*" (Num "20"))) "+" (Term (Num "30"))) ``` -### Pre-build - -[examples/prebuild](examples/prebuild) +## Test ``` -$ echo "10 * (20 + 30)" | cargo run -p example_prebuild -Success : (Expr (Term (Term (Num "10")) "*" (Num "(" (Expr (Expr (Term (Num "20"))) "+" (Term (Num "30"))) ")"))) +$ cargo test ``` - -## Docs - -``` -$ make -C docs run -``` - -⇒ http://localhost:1313 diff --git a/examples/build_oneshot/Cargo.toml b/examples/build_oneshot/Cargo.toml new file mode 100644 index 0000000..3fbf60a --- /dev/null +++ b/examples/build_oneshot/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "example_build_oneshot" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +thiserror = { workspace = true } +copager = { path = "../..", features = ["derive", "regexlex", "lr1", "sexp"] } diff --git a/examples/oneshot/src/main.rs b/examples/build_oneshot/src/main.rs similarity index 90% rename from examples/oneshot/src/main.rs rename to examples/build_oneshot/src/main.rs index 8971e61..ba27d14 100644 --- a/examples/oneshot/src/main.rs +++ b/examples/build_oneshot/src/main.rs @@ -1,4 +1,4 @@ -use std::io::stdin; +use std::io::{stdin, stdout, Write}; use copager::lex::{LexSource, RegexLexer}; use copager::parse::{ParseSource, LR1}; @@ -49,6 +49,10 @@ type MyParser = LR1; type MyProcessor = Processor; fn main() -> anyhow::Result<()> { + println!("Example "); + print!("Input: "); + stdout().flush()?; + let mut input = String::new(); stdin().read_line(&mut input)?; @@ -56,7 +60,7 @@ fn main() -> anyhow::Result<()> { .build_lexer()? .build_parser()? .process::>(&input)?; - println!("Success : {}", sexp); + println!("Success: {}", sexp); Ok(()) } diff --git a/examples/prebuild/Cargo.toml b/examples/build_prebuild/Cargo.toml similarity index 93% rename from examples/prebuild/Cargo.toml rename to examples/build_prebuild/Cargo.toml index c3ffa74..cd548cb 100644 --- a/examples/prebuild/Cargo.toml +++ b/examples/build_prebuild/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "example_prebuild" +name = "example_build_prebuild" version = "0.1.0" edition = "2021" diff --git a/examples/prebuild/build.rs b/examples/build_prebuild/build.rs similarity index 100% rename from examples/prebuild/build.rs rename to examples/build_prebuild/build.rs diff --git a/examples/prebuild/grammar/Cargo.toml b/examples/build_prebuild/grammar/Cargo.toml similarity index 100% rename from examples/prebuild/grammar/Cargo.toml rename to examples/build_prebuild/grammar/Cargo.toml diff --git a/examples/prebuild/grammar/src/lib.rs b/examples/build_prebuild/grammar/src/lib.rs similarity index 100% rename from examples/prebuild/grammar/src/lib.rs rename to examples/build_prebuild/grammar/src/lib.rs diff --git a/examples/prebuild/src/main.rs b/examples/build_prebuild/src/main.rs similarity index 68% rename from examples/prebuild/src/main.rs rename to examples/build_prebuild/src/main.rs index ccb8ee7..ab09d78 100644 --- a/examples/prebuild/src/main.rs +++ b/examples/build_prebuild/src/main.rs @@ -1,4 +1,4 @@ -use std::io::stdin; +use std::io::{stdin, stdout, Write}; use copager::ir::SExp; @@ -6,6 +6,10 @@ use grammar::MyProcessor; #[copager::load] fn main(processor: MyProcessor) -> anyhow::Result<()> { + println!("Example "); + print!("Input: "); + stdout().flush()?; + let mut input = String::new(); stdin().read_line(&mut input)?; @@ -13,7 +17,7 @@ fn main(processor: MyProcessor) -> anyhow::Result<()> { .build_lexer()? .build_parser_by_cache() .process::>(&input)?; - println!("Success : {}", sexp); + println!("Success: {}", sexp); Ok(()) } diff --git a/examples/lang_arithmetic/Cargo.toml b/examples/lang_arithmetic/Cargo.toml new file mode 100644 index 0000000..032efc3 --- /dev/null +++ b/examples/lang_arithmetic/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "example_lang_arithmetic" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +thiserror = { workspace = true } +copager = { path = "../..", features = ["derive", "regexlex", "lr1", "sexp"] } diff --git a/examples/lang_arithmetic/src/lib.rs b/examples/lang_arithmetic/src/lib.rs new file mode 100644 index 0000000..6b4a994 --- /dev/null +++ b/examples/lang_arithmetic/src/lib.rs @@ -0,0 +1,43 @@ +use copager::lex::LexSource; +use copager::parse::ParseSource; +use copager::prelude::*; +use copager::Grammar; + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, LexSource)] +pub enum ArithmeticToken { + #[default] + #[token(text = r"\+")] + Plus, + #[token(text = r"-")] + Minus, + #[token(text = r"\*")] + Mul, + #[token(text = r"/")] + Div, + #[token(text = r"\(")] + BracketL, + #[token(text = r"\)")] + BracketR, + #[token(text = r"[1-9][0-9]*")] + Num, + #[token(text = r"[ \t\n]+", ignored)] + _Whitespace, +} + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, ParseSource)] +pub enum ArithmeticRule { + #[default] + #[rule(" ::= Plus ")] + #[rule(" ::= Minus ")] + #[rule(" ::= ")] + Expr, + #[rule(" ::= Mul ")] + #[rule(" ::= Div ")] + #[rule(" ::= ")] + Term, + #[rule(" ::= BracketL BracketR")] + #[rule(" ::= Num")] + Num, +} + +pub type Arithmetic = Grammar; diff --git a/examples/lang_arithmetic/src/main.rs b/examples/lang_arithmetic/src/main.rs new file mode 100644 index 0000000..50f70cd --- /dev/null +++ b/examples/lang_arithmetic/src/main.rs @@ -0,0 +1,29 @@ +use std::io::{stdin, stdout, Write}; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::SExp; +use copager::Processor; + +use example_lang_arithmetic::*; + +type MyLexer = RegexLexer; +type MyParser = LR1; +type MyProcessor = Processor; + +fn main() -> anyhow::Result<()> { + println!("Example "); + print!("Input: "); + stdout().flush()?; + + let mut input = String::new(); + stdin().read_line(&mut input)?; + + let sexp = MyProcessor::new() + .build_lexer()? + .build_parser()? + .process::>(&input)?; + println!("Success: {}", sexp); + + Ok(()) +} diff --git a/examples/lang_json/Cargo.toml b/examples/lang_json/Cargo.toml new file mode 100644 index 0000000..2bdc8a7 --- /dev/null +++ b/examples/lang_json/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "example_lang_json" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +thiserror = { workspace = true } +copager = { path = "../..", features = ["derive", "regexlex", "lr1", "sexp"] } diff --git a/examples/lang_json/src/lib.rs b/examples/lang_json/src/lib.rs new file mode 100644 index 0000000..a18cb97 --- /dev/null +++ b/examples/lang_json/src/lib.rs @@ -0,0 +1,92 @@ +use copager::lex::LexSource; +use copager::parse::ParseSource; +use copager::prelude::*; +use copager::Grammar; + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, LexSource)] +pub enum JsonToken { + // 記号 + #[token(text = r"\:")] + Colon, + #[token(text = r"\,")] + Comma, + + // キーワード + #[token(text = r"true")] + True, + #[token(text = r"false")] + False, + #[token(text = r"null")] + Null, + + // 識別子 & 数値 + #[token(text = r#""[a-zA-Z_][a-zA-Z0-9_]*""#)] + String, + #[token(text = r"\d+")] + Number, + + // オブジェクト用括弧 + #[default] + #[token(text = r"\{")] + CurlyBracketL, + #[token(text = r"\}")] + CurlyBracketR, + + // 配列用括弧 + #[token(text = r"\[")] + SquareBracketL, + #[token(text = r"\]")] + SquareBracketR, + + // 空白文字 + #[token(text = r"[ \t\n]+", ignored)] + _Whitespace, +} + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, ParseSource)] +pub enum JsonRule { + // JSON本体 + #[default] + #[rule(" ::= ")] + #[rule(" ::= ")] + Json, + + #[rule(" ::= ")] + #[rule(" ::= ")] + Item, + + // 配列 + #[rule(" ::= SquareBracketL SquareBracketR")] + Array, + + #[rule(" ::= Comma ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + ValueList, + + // オブジェクト + #[rule(" ::= CurlyBracketL CurlyBracketR")] + Object, + + #[rule(" ::= Comma ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + KeyValueList, + + #[rule(" ::= Colon ")] + KeyValue, + + #[rule(" ::= String")] + Key, + + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= String")] + #[rule(" ::= Number")] + #[rule(" ::= True")] + #[rule(" ::= False")] + #[rule(" ::= Null")] + Value, +} + +pub type Json = Grammar; diff --git a/examples/lang_json/src/main.rs b/examples/lang_json/src/main.rs new file mode 100644 index 0000000..10b7906 --- /dev/null +++ b/examples/lang_json/src/main.rs @@ -0,0 +1,29 @@ +use std::io::{stdin, stdout, Write}; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::SExp; +use copager::Processor; + +use example_lang_json::*; + +type MyLexer = RegexLexer; +type MyParser = LR1; +type MyProcessor = Processor; + +fn main() -> anyhow::Result<()> { + println!("Example "); + print!("Input: "); + stdout().flush()?; + + let mut input = String::new(); + stdin().read_line(&mut input)?; + + let sexp = MyProcessor::new() + .build_lexer()? + .build_parser()? + .process::>(&input)?; + println!("Success: {}", sexp); + + Ok(()) +} diff --git a/examples/oneshot/Cargo.toml b/examples/lang_pl0/Cargo.toml similarity index 88% rename from examples/oneshot/Cargo.toml rename to examples/lang_pl0/Cargo.toml index b38a4b8..fec3ee4 100644 --- a/examples/oneshot/Cargo.toml +++ b/examples/lang_pl0/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "example_oneshot" +name = "example_lang_pl0" version = "0.1.0" edition = "2021" diff --git a/examples/lang_pl0/src/lib.rs b/examples/lang_pl0/src/lib.rs new file mode 100644 index 0000000..fdd1d53 --- /dev/null +++ b/examples/lang_pl0/src/lib.rs @@ -0,0 +1,9 @@ +mod token; +mod rule; + +use copager::Grammar; + +pub use token::Pl0Token; +pub use rule::Pl0Rule; + +pub type Pl0 = Grammar; diff --git a/examples/lang_pl0/src/main.rs b/examples/lang_pl0/src/main.rs new file mode 100644 index 0000000..7eb376f --- /dev/null +++ b/examples/lang_pl0/src/main.rs @@ -0,0 +1,29 @@ +use std::io::{stdin, stdout, Write}; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::SExp; +use copager::Processor; + +use example_lang_pl0::*; + +type MyLexer = RegexLexer; +type MyParser = LR1; +type MyProcessor = Processor; + +fn main() -> anyhow::Result<()> { + println!("Example "); + print!("Input: "); + stdout().flush()?; + + let mut input = String::new(); + stdin().read_line(&mut input)?; + + let sexp = MyProcessor::new() + .build_lexer()? + .build_parser()? + .process::>(&input)?; + println!("Success: {}", sexp); + + Ok(()) +} diff --git a/examples/lang_pl0/src/rule.rs b/examples/lang_pl0/src/rule.rs new file mode 100644 index 0000000..b8cae11 --- /dev/null +++ b/examples/lang_pl0/src/rule.rs @@ -0,0 +1,110 @@ +use copager::parse::ParseSource; +use copager::prelude::*; + +use crate::token::Pl0Token; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, ParseSource)] +pub enum Pl0Rule { + // プログラム本体 + #[default] + #[rule(" ::= Period")] + Program, + + // ブロック + #[rule(" ::= ")] + Block, + + // 定数宣言 + #[rule(" ::= Const Semicolon")] + #[rule(" ::= ")] + ConstDecl, + + #[rule(" ::= Comma ")] + #[rule(" ::= ")] + ConstDefList, + + #[rule(" ::= Ident Eql Number")] + ConstDef, + + // 変数宣言 + #[rule(" ::= Var Semicolon")] + #[rule(" ::= ")] + VarDecl, + + #[rule(" ::= Comma Ident")] + #[rule(" ::= Ident")] + IdentList, + + // 手続き宣言 + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + ProcDeclList, + + #[rule(" ::= Procedure Ident Semicolon Semicolon")] + // #[rule(" ::= Procedure Ident Semicolon Semicolon")] + ProcDecl, + + // 文 + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + Stmt, + + #[rule(" ::= Ident Becomes ")] + AssignStmt, + + #[rule(" ::= Call Ident")] + CallStmt, + + #[rule(" ::= Begin Semicolon End")] + BeginStmt, + + #[rule(" ::= Semicolon ")] + #[rule(" ::= ")] + StmtList, + + #[rule(" ::= If Then ")] + IfStmt, + + #[rule(" ::= While Do ")] + WhileStmt, + + #[rule(" ::= Read ParenL Ident ParenR")] + ReadStmt, + + #[rule(" ::= Write ParenL ParenR")] + WriteStmt, + + // 式 + #[rule(" ::= Odd ")] + #[rule(" ::= ")] + Condition, + + #[rule(" ::= Eql")] + #[rule(" ::= Neq")] + #[rule(" ::= Lss")] + #[rule(" ::= Leq")] + #[rule(" ::= Gtr")] + #[rule(" ::= Geq")] + RelOp, + + #[rule(" ::= Plus ")] + #[rule(" ::= Minus ")] + #[rule(" ::= ")] + Expr, + + #[rule(" ::= Times ")] + #[rule(" ::= Slash ")] + #[rule(" ::= ")] + Term, + + #[rule(" ::= Ident")] + #[rule(" ::= Number")] + #[rule(" ::= ParenL ParenR")] + Factor, +} diff --git a/examples/lang_pl0/src/token.rs b/examples/lang_pl0/src/token.rs new file mode 100644 index 0000000..6b4a156 --- /dev/null +++ b/examples/lang_pl0/src/token.rs @@ -0,0 +1,78 @@ +use copager::lex::LexSource; +use copager::prelude::*; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, LexSource)] +pub enum Pl0Token { + // キーワード + #[default] + #[token(text = r"const")] + Const, + #[token(text = r"var")] + Var, + #[token(text = r"procedure")] + Procedure, + #[token(text = r"call")] + Call, + #[token(text = r"begin")] + Begin, + #[token(text = r"end")] + End, + #[token(text = r"if")] + If, + #[token(text = r"then")] + Then, + #[token(text = r"while")] + While, + #[token(text = r"do")] + Do, + #[token(text = r"odd")] + Odd, + #[token(text = r"write")] + Write, + #[token(text = r"read")] + Read, + + // 識別子と数値 + #[token(text = r"[a-zA-Z_][a-zA-Z0-9_]*")] + Ident, + #[token(text = r"\d+")] + Number, + + // 演算子と記号 + #[token(text = r"\+")] + Plus, + #[token(text = r"-")] + Minus, + #[token(text = r"\*")] + Times, + #[token(text = r"/")] + Slash, + #[token(text = r"=")] + Eql, + #[token(text = r"#")] + Neq, + #[token(text = r"<=")] + Leq, + #[token(text = r"<")] + Lss, + #[token(text = r">=")] + Geq, + #[token(text = r">")] + Gtr, + #[token(text = r"\(")] + ParenL, + #[token(text = r"\)")] + ParenR, + #[token(text = r",")] + Comma, + #[token(text = r"\.")] + Period, + #[token(text = r";")] + Semicolon, + #[token(text = r":=")] + Becomes, + + // 空白 + #[token(text = r"[ \t\n\r]+", ignored)] + _Whitespace, +} diff --git a/examples/lang_xml/Cargo.toml b/examples/lang_xml/Cargo.toml new file mode 100644 index 0000000..132787a --- /dev/null +++ b/examples/lang_xml/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "example_lang_xml" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +thiserror = { workspace = true } +copager = { path = "../..", features = ["derive", "regexlex", "lr1", "sexp"] } diff --git a/examples/lang_xml/src/lib.rs b/examples/lang_xml/src/lib.rs new file mode 100644 index 0000000..940d921 --- /dev/null +++ b/examples/lang_xml/src/lib.rs @@ -0,0 +1,70 @@ +use copager::lex::LexSource; +use copager::parse::ParseSource; +use copager::prelude::*; +use copager::Grammar; + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, LexSource)] +pub enum XmlToken { + // 記号 + #[default] + #[token(text = r"<")] + TagL, + #[token(text = r">")] + TagR, + #[token(text = r"/")] + Slash, + #[token(text = r"=")] + Equal, + + // 文字列 & 識別子 + #[token(text = r"[a-zA-Z_][a-zA-Z0-9_]*")] + String, + #[token(text = r"'[a-zA-Z_][a-zA-Z0-9_]*'")] + QuotedString, + #[token(text = r#""[a-zA-Z_][a-zA-Z0-9_]*""#)] + WQuotedString, + + // 空白文字 + #[token(text = r"[ \t\n]+", ignored)] + _Whitespace, +} + +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, ParseSource)] +pub enum XmlRule { + // XML本体 + #[default] + #[rule(" ::= ")] + #[rule(" ::= ")] + Xml, + + // タグ + #[rule(" ::= ")] + #[rule(" ::= ")] + Tag, + + #[rule(" ::= TagL String Slash TagR")] + Single, + + #[rule(" ::= TagL String TagR")] + Begin, + + #[rule(" ::= TagL Slash String TagR")] + End, + + // 属性 + #[rule(" ::= ")] + #[rule(" ::= ")] + #[rule(" ::= ")] + AttrList, + + #[rule(" ::= String Equal QuotedString")] + #[rule(" ::= String Equal WQuotedString")] + Attr, + + // 値 + #[rule(" ::= ")] + #[rule(" ::= String")] + Value, +} + +pub type Xml = Grammar; diff --git a/examples/lang_xml/src/main.rs b/examples/lang_xml/src/main.rs new file mode 100644 index 0000000..b71b977 --- /dev/null +++ b/examples/lang_xml/src/main.rs @@ -0,0 +1,29 @@ +use std::io::{stdin, stdout, Write}; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::SExp; +use copager::Processor; + +use example_lang_xml::*; + +type MyLexer = RegexLexer; +type MyParser = LR1; +type MyProcessor = Processor; + +fn main() -> anyhow::Result<()> { + println!("Example "); + print!("Input: "); + stdout().flush()?; + + let mut input = String::new(); + stdin().read_line(&mut input)?; + + let sexp = MyProcessor::new() + .build_lexer()? + .build_parser()? + .process::>(&input)?; + println!("Success: {}", sexp); + + Ok(()) +} diff --git a/tests/arithmetic/fail/testcase_1.txt b/tests/arithmetic/fail/testcase_1.txt new file mode 100644 index 0000000..6a452c1 --- /dev/null +++ b/tests/arithmetic/fail/testcase_1.txt @@ -0,0 +1 @@ +() diff --git a/tests/arithmetic/fail/testcase_2.txt b/tests/arithmetic/fail/testcase_2.txt new file mode 100644 index 0000000..bf8d49e --- /dev/null +++ b/tests/arithmetic/fail/testcase_2.txt @@ -0,0 +1 @@ +(10 - diff --git a/tests/arithmetic/fail/testcase_3.txt b/tests/arithmetic/fail/testcase_3.txt new file mode 100644 index 0000000..5555c87 --- /dev/null +++ b/tests/arithmetic/fail/testcase_3.txt @@ -0,0 +1 @@ +10 + diff --git a/tests/arithmetic/fail/testcase_4.txt b/tests/arithmetic/fail/testcase_4.txt new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/tests/arithmetic/fail/testcase_4.txt @@ -0,0 +1 @@ +* diff --git a/tests/arithmetic/fail/testcase_5.txt b/tests/arithmetic/fail/testcase_5.txt new file mode 100644 index 0000000..76badf8 --- /dev/null +++ b/tests/arithmetic/fail/testcase_5.txt @@ -0,0 +1 @@ +10 20 + 30 diff --git a/tests/arithmetic/fail/testcase_6.txt b/tests/arithmetic/fail/testcase_6.txt new file mode 100644 index 0000000..167c36b --- /dev/null +++ b/tests/arithmetic/fail/testcase_6.txt @@ -0,0 +1 @@ +10 + 20 * 30 / 40 ( diff --git a/tests/arithmetic/fail/testcase_7.txt b/tests/arithmetic/fail/testcase_7.txt new file mode 100644 index 0000000..162acdf --- /dev/null +++ b/tests/arithmetic/fail/testcase_7.txt @@ -0,0 +1 @@ +(((10)) diff --git a/tests/arithmetic/success/testcase_1.txt b/tests/arithmetic/success/testcase_1.txt new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/tests/arithmetic/success/testcase_1.txt @@ -0,0 +1 @@ +10 diff --git a/tests/arithmetic/success/testcase_10.txt b/tests/arithmetic/success/testcase_10.txt new file mode 100644 index 0000000..4e08e69 --- /dev/null +++ b/tests/arithmetic/success/testcase_10.txt @@ -0,0 +1 @@ +((10 + 20) * (30 / 40)) - 50 diff --git a/tests/arithmetic/success/testcase_2.txt b/tests/arithmetic/success/testcase_2.txt new file mode 100644 index 0000000..f0da93f --- /dev/null +++ b/tests/arithmetic/success/testcase_2.txt @@ -0,0 +1 @@ +10 + 20 diff --git a/tests/arithmetic/success/testcase_3.txt b/tests/arithmetic/success/testcase_3.txt new file mode 100644 index 0000000..fe24c3e --- /dev/null +++ b/tests/arithmetic/success/testcase_3.txt @@ -0,0 +1 @@ +10 - 20 diff --git a/tests/arithmetic/success/testcase_4.txt b/tests/arithmetic/success/testcase_4.txt new file mode 100644 index 0000000..2f5116a --- /dev/null +++ b/tests/arithmetic/success/testcase_4.txt @@ -0,0 +1 @@ +10 * 20 diff --git a/tests/arithmetic/success/testcase_5.txt b/tests/arithmetic/success/testcase_5.txt new file mode 100644 index 0000000..88d40be --- /dev/null +++ b/tests/arithmetic/success/testcase_5.txt @@ -0,0 +1 @@ +10 / 20 diff --git a/tests/arithmetic/success/testcase_6.txt b/tests/arithmetic/success/testcase_6.txt new file mode 100644 index 0000000..3e9d1a6 --- /dev/null +++ b/tests/arithmetic/success/testcase_6.txt @@ -0,0 +1 @@ +10 + 20 * 30 - 40 diff --git a/tests/arithmetic/success/testcase_7.txt b/tests/arithmetic/success/testcase_7.txt new file mode 100644 index 0000000..44e7963 --- /dev/null +++ b/tests/arithmetic/success/testcase_7.txt @@ -0,0 +1 @@ +(10) diff --git a/tests/arithmetic/success/testcase_8.txt b/tests/arithmetic/success/testcase_8.txt new file mode 100644 index 0000000..7b5a24a --- /dev/null +++ b/tests/arithmetic/success/testcase_8.txt @@ -0,0 +1 @@ +((((10)))) diff --git a/tests/arithmetic/success/testcase_9.txt b/tests/arithmetic/success/testcase_9.txt new file mode 100644 index 0000000..877fba7 --- /dev/null +++ b/tests/arithmetic/success/testcase_9.txt @@ -0,0 +1 @@ +10 * (20 - 30) diff --git a/tests/arithmetic/test.rs b/tests/arithmetic/test.rs new file mode 100644 index 0000000..24d146b --- /dev/null +++ b/tests/arithmetic/test.rs @@ -0,0 +1,33 @@ +mod utils; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::Void; +use copager::Processor; + +use utils::{Expect, test_dir}; + +use example_lang_arithmetic::*; + +#[test] +fn success() { + test_dir("tests/arithmetic/success", Expect::Ok, &parse); +} + +#[test] +fn fail() { + test_dir("tests/arithmetic/fail", Expect::Err, &parse); +} + +fn parse(input: &str) -> anyhow::Result<()> { + type TestLexer = RegexLexer; + type TestParser = LR1; + type TestProcessor = Processor; + + TestProcessor::new() + .build_lexer()? + .build_parser()? + .process::(input)?; + + Ok(()) +} diff --git a/tests/arithmetic/utils.rs b/tests/arithmetic/utils.rs new file mode 100644 index 0000000..ac683ff --- /dev/null +++ b/tests/arithmetic/utils.rs @@ -0,0 +1,41 @@ +use std::fs; +use std::panic; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Expect { + Ok, + Err, +} + +pub fn test_dir(dir: &str, expect: Expect, test_fn: &T) +where + T: Fn(&str) -> anyhow::Result<()> + panic::RefUnwindSafe, +{ + let mut entries = fs::read_dir(dir) + .unwrap() + .map(|entry| entry.unwrap().path()) + .filter(|path| { path.is_file() }) + .map(|path| { + let body = fs::read_to_string(&path).unwrap(); + (path, body) + }) + .collect::>(); + entries.sort(); + + for (path, body) in entries { + print!("Testing {:?} ... ", path); + let result = panic::catch_unwind(|| test_fn(&body).unwrap()); + match result { + Ok(_) if expect == Expect::Err => { + println!("Failed (expected Error, but got Ok)"); + panic!(""); + + } + Err(e) if expect == Expect::Ok => { + println!("expected Ok, but got Error."); + panic!("{}", e.downcast_ref::().unwrap()); + } + _ => println!("Ok"), + } + } +} diff --git a/tests/json/fail/testcase_01.txt b/tests/json/fail/testcase_01.txt new file mode 100644 index 0000000..98232c6 --- /dev/null +++ b/tests/json/fail/testcase_01.txt @@ -0,0 +1 @@ +{ diff --git a/tests/json/fail/testcase_02.txt b/tests/json/fail/testcase_02.txt new file mode 100644 index 0000000..e072a79 --- /dev/null +++ b/tests/json/fail/testcase_02.txt @@ -0,0 +1 @@ +{"key: "value"} diff --git a/tests/json/fail/testcase_03.txt b/tests/json/fail/testcase_03.txt new file mode 100644 index 0000000..12c5185 --- /dev/null +++ b/tests/json/fail/testcase_03.txt @@ -0,0 +1 @@ +{"key": True} diff --git a/tests/json/fail/testcase_04.txt b/tests/json/fail/testcase_04.txt new file mode 100644 index 0000000..1424259 --- /dev/null +++ b/tests/json/fail/testcase_04.txt @@ -0,0 +1,4 @@ +{ + "key1": "value1", + "key2": "value2", +} diff --git a/tests/json/success/testcase_01.txt b/tests/json/success/testcase_01.txt new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/json/success/testcase_01.txt @@ -0,0 +1 @@ +{} diff --git a/tests/json/success/testcase_02.txt b/tests/json/success/testcase_02.txt new file mode 100644 index 0000000..76519fa --- /dev/null +++ b/tests/json/success/testcase_02.txt @@ -0,0 +1 @@ +{"key": "value"} diff --git a/tests/json/success/testcase_03.txt b/tests/json/success/testcase_03.txt new file mode 100644 index 0000000..e78fb99 --- /dev/null +++ b/tests/json/success/testcase_03.txt @@ -0,0 +1 @@ +{"key1": "value1", "key2": "value2"} diff --git a/tests/json/success/testcase_04.txt b/tests/json/success/testcase_04.txt new file mode 100644 index 0000000..2c3dcde --- /dev/null +++ b/tests/json/success/testcase_04.txt @@ -0,0 +1 @@ +{"key": 10} diff --git a/tests/json/success/testcase_05.txt b/tests/json/success/testcase_05.txt new file mode 100644 index 0000000..95ff2f8 --- /dev/null +++ b/tests/json/success/testcase_05.txt @@ -0,0 +1 @@ +{"key": true} diff --git a/tests/json/success/testcase_06.txt b/tests/json/success/testcase_06.txt new file mode 100644 index 0000000..60424ef --- /dev/null +++ b/tests/json/success/testcase_06.txt @@ -0,0 +1 @@ +{"key": []} diff --git a/tests/json/success/testcase_07.txt b/tests/json/success/testcase_07.txt new file mode 100644 index 0000000..ae4e490 --- /dev/null +++ b/tests/json/success/testcase_07.txt @@ -0,0 +1 @@ +{"key": [10, 20]} diff --git a/tests/json/success/testcase_08.txt b/tests/json/success/testcase_08.txt new file mode 100644 index 0000000..fc52e6d --- /dev/null +++ b/tests/json/success/testcase_08.txt @@ -0,0 +1,6 @@ +{ + "key1": { + "key2": 10, + "key3": 20 + } +} diff --git a/tests/json/success/testcase_09.txt b/tests/json/success/testcase_09.txt new file mode 100644 index 0000000..28d3056 --- /dev/null +++ b/tests/json/success/testcase_09.txt @@ -0,0 +1,10 @@ +{ + "key1": { + "key2": 10, + "key3": 20 + }, + "key4": "value", + "key5": { + "key6": [10, 20, 30] + } +} diff --git a/tests/json/success/testcase_10.txt b/tests/json/success/testcase_10.txt new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/tests/json/success/testcase_10.txt @@ -0,0 +1 @@ +[] diff --git a/tests/json/success/testcase_11.txt b/tests/json/success/testcase_11.txt new file mode 100644 index 0000000..2a421cc --- /dev/null +++ b/tests/json/success/testcase_11.txt @@ -0,0 +1 @@ +[10] diff --git a/tests/json/success/testcase_12.txt b/tests/json/success/testcase_12.txt new file mode 100644 index 0000000..b6ad0ff --- /dev/null +++ b/tests/json/success/testcase_12.txt @@ -0,0 +1 @@ +[10, 20, 30] diff --git a/tests/json/success/testcase_13.txt b/tests/json/success/testcase_13.txt new file mode 100644 index 0000000..c762def --- /dev/null +++ b/tests/json/success/testcase_13.txt @@ -0,0 +1,8 @@ +[ + 10, + "value", + true, + false, + null, + {"key": "value"} +] diff --git a/tests/json/test.rs b/tests/json/test.rs new file mode 100644 index 0000000..77ee1bd --- /dev/null +++ b/tests/json/test.rs @@ -0,0 +1,33 @@ +mod utils; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::Void; +use copager::Processor; + +use utils::{Expect, test_dir}; + +use example_lang_json::*; + +#[test] +fn success() { + test_dir("tests/json/success", Expect::Ok, &parse); +} + +#[test] +fn fail() { + test_dir("tests/json/fail", Expect::Err, &parse); +} + +fn parse(input: &str) -> anyhow::Result<()> { + type TestLexer = RegexLexer; + type TestParser = LR1; + type TestProcessor = Processor; + + TestProcessor::new() + .build_lexer()? + .build_parser()? + .process::(input)?; + + Ok(()) +} diff --git a/tests/json/utils.rs b/tests/json/utils.rs new file mode 100644 index 0000000..ac683ff --- /dev/null +++ b/tests/json/utils.rs @@ -0,0 +1,41 @@ +use std::fs; +use std::panic; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Expect { + Ok, + Err, +} + +pub fn test_dir(dir: &str, expect: Expect, test_fn: &T) +where + T: Fn(&str) -> anyhow::Result<()> + panic::RefUnwindSafe, +{ + let mut entries = fs::read_dir(dir) + .unwrap() + .map(|entry| entry.unwrap().path()) + .filter(|path| { path.is_file() }) + .map(|path| { + let body = fs::read_to_string(&path).unwrap(); + (path, body) + }) + .collect::>(); + entries.sort(); + + for (path, body) in entries { + print!("Testing {:?} ... ", path); + let result = panic::catch_unwind(|| test_fn(&body).unwrap()); + match result { + Ok(_) if expect == Expect::Err => { + println!("Failed (expected Error, but got Ok)"); + panic!(""); + + } + Err(e) if expect == Expect::Ok => { + println!("expected Ok, but got Error."); + panic!("{}", e.downcast_ref::().unwrap()); + } + _ => println!("Ok"), + } + } +} diff --git a/tests/pl0/fail/testcase_1.txt b/tests/pl0/fail/testcase_1.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/pl0/success/testcase_1.txt b/tests/pl0/success/testcase_1.txt new file mode 100644 index 0000000..4ee4453 --- /dev/null +++ b/tests/pl0/success/testcase_1.txt @@ -0,0 +1,17 @@ +const max = 10; + +var x, y; + +procedure find_max; +begin + if x > y then + write(x); + if x <= y then + write(y); +end; + +begin + read(x); + read(y); + call find_max; +end. diff --git a/tests/pl0/test.rs b/tests/pl0/test.rs new file mode 100644 index 0000000..b5a1daa --- /dev/null +++ b/tests/pl0/test.rs @@ -0,0 +1,33 @@ +mod utils; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::Void; +use copager::Processor; + +use utils::{Expect, test_dir}; + +use example_lang_pl0::*; + +#[test] +fn success() { + test_dir("tests/pl0/success", Expect::Ok, &parse); +} + +#[test] +fn fail() { + test_dir("tests/pl0/fail", Expect::Err, &parse); +} + +fn parse(input: &str) -> anyhow::Result<()> { + type TestLexer = RegexLexer; + type TestParser = LR1; + type TestProcessor = Processor; + + TestProcessor::new() + .build_lexer()? + .build_parser()? + .process::(input)?; + + Ok(()) +} diff --git a/tests/pl0/utils.rs b/tests/pl0/utils.rs new file mode 100644 index 0000000..ac683ff --- /dev/null +++ b/tests/pl0/utils.rs @@ -0,0 +1,41 @@ +use std::fs; +use std::panic; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Expect { + Ok, + Err, +} + +pub fn test_dir(dir: &str, expect: Expect, test_fn: &T) +where + T: Fn(&str) -> anyhow::Result<()> + panic::RefUnwindSafe, +{ + let mut entries = fs::read_dir(dir) + .unwrap() + .map(|entry| entry.unwrap().path()) + .filter(|path| { path.is_file() }) + .map(|path| { + let body = fs::read_to_string(&path).unwrap(); + (path, body) + }) + .collect::>(); + entries.sort(); + + for (path, body) in entries { + print!("Testing {:?} ... ", path); + let result = panic::catch_unwind(|| test_fn(&body).unwrap()); + match result { + Ok(_) if expect == Expect::Err => { + println!("Failed (expected Error, but got Ok)"); + panic!(""); + + } + Err(e) if expect == Expect::Ok => { + println!("expected Ok, but got Error."); + panic!("{}", e.downcast_ref::().unwrap()); + } + _ => println!("Ok"), + } + } +} diff --git a/tests/xml/fail/testcase_01.txt b/tests/xml/fail/testcase_01.txt new file mode 100644 index 0000000..1b232f5 --- /dev/null +++ b/tests/xml/fail/testcase_01.txt @@ -0,0 +1 @@ + diff --git a/tests/xml/fail/testcase_02.txt b/tests/xml/fail/testcase_02.txt new file mode 100644 index 0000000..0142cf3 --- /dev/null +++ b/tests/xml/fail/testcase_02.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/fail/testcase_03.txt b/tests/xml/fail/testcase_03.txt new file mode 100644 index 0000000..050a905 --- /dev/null +++ b/tests/xml/fail/testcase_03.txt @@ -0,0 +1,2 @@ +value + diff --git a/tests/xml/fail/testcase_04.txt b/tests/xml/fail/testcase_04.txt new file mode 100644 index 0000000..6ab5ce3 --- /dev/null +++ b/tests/xml/fail/testcase_04.txt @@ -0,0 +1,2 @@ +value + diff --git a/tests/xml/fail/testcase_05.txt b/tests/xml/fail/testcase_05.txt new file mode 100644 index 0000000..7b8fd44 --- /dev/null +++ b/tests/xml/fail/testcase_05.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/success/testcase_01.txt b/tests/xml/success/testcase_01.txt new file mode 100644 index 0000000..6e34491 --- /dev/null +++ b/tests/xml/success/testcase_01.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/success/testcase_02.txt b/tests/xml/success/testcase_02.txt new file mode 100644 index 0000000..f7ad3e3 --- /dev/null +++ b/tests/xml/success/testcase_02.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/success/testcase_03.txt b/tests/xml/success/testcase_03.txt new file mode 100644 index 0000000..6e34491 --- /dev/null +++ b/tests/xml/success/testcase_03.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/success/testcase_04.txt b/tests/xml/success/testcase_04.txt new file mode 100644 index 0000000..81858b6 --- /dev/null +++ b/tests/xml/success/testcase_04.txt @@ -0,0 +1 @@ + diff --git a/tests/xml/success/testcase_05.txt b/tests/xml/success/testcase_05.txt new file mode 100644 index 0000000..8474b01 --- /dev/null +++ b/tests/xml/success/testcase_05.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/success/testcase_06.txt b/tests/xml/success/testcase_06.txt new file mode 100644 index 0000000..6dea9cd --- /dev/null +++ b/tests/xml/success/testcase_06.txt @@ -0,0 +1 @@ +value diff --git a/tests/xml/success/testcase_07.txt b/tests/xml/success/testcase_07.txt new file mode 100644 index 0000000..a7cc1c9 --- /dev/null +++ b/tests/xml/success/testcase_07.txt @@ -0,0 +1,3 @@ +value +value +value diff --git a/tests/xml/success/testcase_08.txt b/tests/xml/success/testcase_08.txt new file mode 100644 index 0000000..1e74546 --- /dev/null +++ b/tests/xml/success/testcase_08.txt @@ -0,0 +1,3 @@ + + value + diff --git a/tests/xml/success/testcase_09.txt b/tests/xml/success/testcase_09.txt new file mode 100644 index 0000000..71d23fd --- /dev/null +++ b/tests/xml/success/testcase_09.txt @@ -0,0 +1,4 @@ + + value + value + diff --git a/tests/xml/test.rs b/tests/xml/test.rs new file mode 100644 index 0000000..cc9d879 --- /dev/null +++ b/tests/xml/test.rs @@ -0,0 +1,33 @@ +mod utils; + +use copager::lex::RegexLexer; +use copager::parse::LR1; +use copager::ir::Void; +use copager::Processor; + +use utils::{Expect, test_dir}; + +use example_lang_xml::*; + +#[test] +fn success() { + test_dir("tests/xml/success", Expect::Ok, &parse); +} + +#[test] +fn fail() { + test_dir("tests/xml/fail", Expect::Err, &parse); +} + +fn parse(input: &str) -> anyhow::Result<()> { + type TestLexer = RegexLexer; + type TestParser = LR1; + type TestProcessor = Processor; + + TestProcessor::new() + .build_lexer()? + .build_parser()? + .process::(input)?; + + Ok(()) +} diff --git a/tests/xml/utils.rs b/tests/xml/utils.rs new file mode 100644 index 0000000..ac683ff --- /dev/null +++ b/tests/xml/utils.rs @@ -0,0 +1,41 @@ +use std::fs; +use std::panic; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Expect { + Ok, + Err, +} + +pub fn test_dir(dir: &str, expect: Expect, test_fn: &T) +where + T: Fn(&str) -> anyhow::Result<()> + panic::RefUnwindSafe, +{ + let mut entries = fs::read_dir(dir) + .unwrap() + .map(|entry| entry.unwrap().path()) + .filter(|path| { path.is_file() }) + .map(|path| { + let body = fs::read_to_string(&path).unwrap(); + (path, body) + }) + .collect::>(); + entries.sort(); + + for (path, body) in entries { + print!("Testing {:?} ... ", path); + let result = panic::catch_unwind(|| test_fn(&body).unwrap()); + match result { + Ok(_) if expect == Expect::Err => { + println!("Failed (expected Error, but got Ok)"); + panic!(""); + + } + Err(e) if expect == Expect::Ok => { + println!("expected Ok, but got Error."); + panic!("{}", e.downcast_ref::().unwrap()); + } + _ => println!("Ok"), + } + } +}