diff --git a/.gitignore b/.gitignore index ea8c4bf..eb5a316 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +target diff --git a/Cargo.lock b/Cargo.lock index e0b9d40..3ce20d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "ena" @@ -192,6 +192,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itermaps" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8d95feba9d17d04af7e885aa60ee9721bcd3c770f287dbd4ff494b720e2c5a" + [[package]] name = "itertools" version = "0.10.5" @@ -301,7 +307,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.16.22" +version = "0.17.0" dependencies = [ "display_source", "logic_lint", @@ -358,13 +364,41 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.34" +version = "0.1.35" dependencies = [ + "either", "parser", "syntax", "tag_code", ] +[[package]] +name = "peg" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a" + [[package]] name = "petgraph" version = "0.6.3" @@ -535,9 +569,10 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.38" +version = "0.2.39" dependencies = [ "either", + "itermaps", "tag_code", "utils", "var_utils", @@ -545,7 +580,12 @@ dependencies = [ [[package]] name = "tag_code" -version = "0.1.7" +version = "0.2.0" +dependencies = [ + "either", + "peg", + "var_utils", +] [[package]] name = "term" @@ -605,7 +645,7 @@ version = "0.1.2" [[package]] name = "var_utils" -version = "0.5.3" +version = "0.5.4" dependencies = [ "lazy-regex", ] diff --git a/Cargo.toml b/Cargo.toml index 7eae31f..442549b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.16.22" +version = "0.17.0" edition = "2021" authors = ["A4-Tacks "] diff --git a/src/main.rs b/src/main.rs index 0ba11ec..ae8540d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,8 @@ use std::{ stdin, Read }, + mem, process::exit, - str::FromStr, collections::HashMap, ops::Deref, fmt::Display, @@ -23,7 +23,7 @@ use syntax::{ Errors, Expand, Meta, - line_first_add, CompileMetaExtends, + CompileMetaExtends, }; use parser::{ TopLevelParser, @@ -32,7 +32,9 @@ use parser::{ ParseError }, }; -use tag_code::TagCodes; +use tag_code::{ + logic_parser::{parser as tparser, ParseLines}, TagCodes, +}; use logic_lint::Source; /// 带有错误前缀, 并且文本为红色的eprintln @@ -53,29 +55,6 @@ macro_rules! concat_lines { }; } -pub const HELP_MSG: &str = concat_lines! { - ""; - "Author: A4-Tacks A4的钉子"; - "Version: ", env!("CARGO_PKG_VERSION"); - "https://github.com/A4-Tacks/mindustry_logic_bang_lang"; - "MODE:"; - "\t", "c: compile MdtBangLang to MdtLogicCode"; - "\t", "a: compile MdtBangLang to AST Debug"; - "\t", "A: compile MdtBangLang to MdtBangLang"; - "\t", "t: compile MdtBangLang to MdtTagCode"; - "\t", "T: compile MdtBangLang to MdtTagCode (Builded TagDown)"; - "\t", "f: compile MdtLogicCode to MdtTagCode"; - "\t", "F: compile MdtLogicCode to MdtTagCode (Builded TagDown)"; - "\t", "r: compile MdtLogicCode to MdtBangLang"; - "\t", "R: compile MdtLogicCode to MdtBangLang (Builded TagDown)"; - "\t", "C: compile MdtTagCode to MdtLogicCode"; - "\t", "l: lint MdtLogicCode"; - ; - "input from stdin"; - "output to stdout"; - "error to stderr"; -}; - const MAX_INVALID_TOKEN_VIEW: usize = 5; fn help() { @@ -117,18 +96,22 @@ enum CompileMode { BangToASTDisplay, BangToMdtTagCode { tag_down: bool }, MdtLogicToMdtTagCode { tag_down: bool }, - MdtLogicToBang { tag_down: bool }, + MdtLogicToBang, MdtTagCodeToMdtLogic, LintLogic, + IndentLogic, + BangToMdtLabel, } impl CompileMode { fn compile(&self, src: String) -> String { match *self { Self::BangToMdtLogic => { let ast = build_ast(&src); - let mut meta = compile_ast(ast, src); - build_tag_down(&mut meta); - let logic_lines = meta.tag_codes_mut().compile().unwrap(); + let mut meta = compile_ast(ast, src.clone()); + let logic_codes = mem::take(meta.parse_lines_mut()); + let mut tag_codes = logic_to_tagcode(logic_codes, &src); + build_tag_down(&mut tag_codes); + let logic_lines = tag_codes.compile().unwrap(); logic_lines.join("\n") }, Self::BangToASTDebug => { @@ -141,61 +124,33 @@ impl CompileMode { }, Self::BangToMdtTagCode { tag_down } => { let ast = build_ast(&src); - let mut meta = compile_ast(ast, src); - if tag_down { build_tag_down(&mut meta); } - meta.tag_codes().to_string() + let mut meta = compile_ast(ast, src.clone()); + let mut tag_codes = logic_to_tagcode(mem::take(meta.parse_lines_mut()), &src); + if tag_down { build_tag_down(&mut tag_codes); } + tag_codes.to_string() }, Self::MdtLogicToMdtTagCode { tag_down } => { - match TagCodes::from_str(&src) { - Ok(mut lines) => { - if tag_down { - lines.build_tagdown().unwrap(); - lines.tag_up(); - } - lines.to_string() - }, - Err((line, e)) => { - err!("line: {}, {:?}", line, e); - exit(4); - }, + let mut lines = logic_src_to_tagcode(&src); + if tag_down { + lines.build_tagdown().unwrap(); + lines.tag_up(); } + lines.to_string() }, - Self::MdtLogicToBang { tag_down } => { - match TagCodes::from_str(&src) { - Ok(mut lines) => { - if tag_down { - lines.build_tagdown().unwrap(); - lines.tag_up(); - } - let ast = Expand::try_from(&lines) - .unwrap_or_else(|(idx, e)| { - let mut lines_str = lines.iter() - .map(ToString::to_string) - .collect::>(); - line_first_add(&mut lines_str, " "); - err!( - "在构建第{}行时出错: {}\n\ - 已构建的行:\n\ - {}", - lines_str.join("\n"), - idx + 1, - e - ); - exit(4); - }); - display_ast(&ast) - }, - Err((line, e)) => { - err!("line: {}, {:?}", line, e); - exit(4); - }, - } + Self::MdtLogicToBang => { + let logic_lines = logic_parse(&src); + let ast = Expand::try_from(logic_lines) + .unwrap_or_else(|e| { + let (line, col) = e.location(&src); + err!("MdtLogicToBang {line}:{col} {}", e.value); + exit(4) + }); + display_ast(&ast) }, Self::MdtTagCodeToMdtLogic => { - let tag_codes = TagCodes::from_tag_lines(&src); - let mut meta = CompileMeta::with_tag_codes(tag_codes); - build_tag_down(&mut meta); - let logic_lines = meta.tag_codes_mut().compile().unwrap(); + let mut tag_codes = logic_src_to_tagcode(&src); + build_tag_down(&mut tag_codes); + let logic_lines = tag_codes.compile().unwrap(); logic_lines.join("\n") }, Self::LintLogic => { @@ -203,9 +158,75 @@ impl CompileMode { linter.show_lints(); src }, + Self::IndentLogic => { + let mut logic_lines = logic_parse(&src); + logic_lines.index_label_popup(); + format!("{logic_lines:#}") + }, + Self::BangToMdtLabel => { + let ast = build_ast(&src); + let mut meta = compile_ast(ast, src.clone()); + meta.parse_lines_mut().index_label_popup(); + format!("{}", meta.parse_lines()) + }, } } } + +fn logic_to_tagcode<'a>(lines: ParseLines<'a>, src: &str) -> TagCodes { + let tagcodes = match TagCodes::try_from(lines) { + Ok(tagcode) => tagcode, + Err(e) => { + let (line, column) = e.location(src); + let prefix = format!("ParseTagCode {line}:{column}"); + err!("{prefix} {e}"); + exit(10) + }, + }; + tagcodes +} + +fn logic_parse(src: &str) -> ParseLines<'_> { + match tparser::lines(src) { + Ok(lines) => lines, + Err(e) => { + err!("ParseLogicCode {}:{} expected {}", + e.location.line, + e.location.column, + e.expected, + ); + exit(9) + }, + } +} + +fn logic_src_to_tagcode(src: &str) -> TagCodes { + let lines = logic_parse(src); + logic_to_tagcode(lines, src) +} +pub const HELP_MSG: &str = concat_lines! { + ""; + "Author: A4-Tacks A4的钉子"; + "Version: ", env!("CARGO_PKG_VERSION"); + "https://github.com/A4-Tacks/mindustry_logic_bang_lang"; + "MODE:"; + "\t", "c: compile MdtBangLang to MdtLogicCode"; + "\t", "a: compile MdtBangLang to AST Debug"; + "\t", "A: compile MdtBangLang to MdtBangLang"; + "\t", "t: compile MdtBangLang to MdtTagCode"; + "\t", "T: compile MdtBangLang to MdtTagCode (Builded TagDown)"; + "\t", "f: compile MdtLogicCode to MdtTagCode"; + "\t", "F: compile MdtLogicCode to MdtTagCode (Builded TagDown)"; + "\t", "r: compile MdtLogicCode to MdtBangLang"; + "\t", "C: compile MdtTagCode to MdtLogicCode"; + "\t", "l: lint MdtLogicCode"; + "\t", "i: indent MdtLogicCode"; + "\t", "L: compile MdtBangLang to MdtLabelCode"; + ; + "input from stdin"; + "output to stdout"; + "error to stderr"; +}; impl TryFrom for CompileMode { type Error = char; @@ -218,10 +239,11 @@ impl TryFrom for CompileMode { 'T' => Self::BangToMdtTagCode { tag_down: true }, 'f' => Self::MdtLogicToMdtTagCode { tag_down: false }, 'F' => Self::MdtLogicToMdtTagCode { tag_down: true }, - 'r' => Self::MdtLogicToBang { tag_down: false }, - 'R' => Self::MdtLogicToBang { tag_down: true }, + 'r' => Self::MdtLogicToBang, 'C' => Self::MdtTagCodeToMdtLogic, 'l' => Self::LintLogic, + 'i' => Self::IndentLogic, + 'L' => Self::BangToMdtLabel, mode => return Err(mode), }) } @@ -234,29 +256,10 @@ fn display_ast(ast: &Expand) -> String { meta.buffer().into() } -fn build_tag_down(meta: &mut CompileMeta) { - let result = meta.tag_codes_mut().build_tagdown(); - result.unwrap_or_else(|(_line, tag)| { - let (tag_str, _id) = meta.tags_map() - .iter() - .filter(|&(_k, v)| *v == tag) - .take(1) - .last() - .unwrap(); - let mut tags_map = meta.debug_tags_map(); - let mut tag_codes = meta.tag_codes().iter().map(ToString::to_string).collect(); - line_first_add(&mut tags_map, " "); - line_first_add(&mut tag_codes, " "); - err!( - "重复的标记: {:?}\n\ - TagCode:\n\ - {}\n\ - TagsMap:\n\ - {}", - tag_str, - tag_codes.join("\n"), - tags_map.join("\n"), - ); +fn build_tag_down(tag_codes: &mut TagCodes) { + let result = tag_codes.build_tagdown(); + result.unwrap_or_else(|(line, tag)| { + err!("重复的标记: {tag:?} (line {line})"); exit(4) }) } diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index f133f98..f68ae6c 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.34" +version = "0.1.35" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,3 +9,6 @@ edition = "2021" parser = { path = "..", version = "*" } tag_code = { path = "../../tag_code", version = "*" } syntax = { path = "../../syntax", version = "*" } + +[dependencies] +either = "1.13.0" diff --git a/tools/parser/tests/src/lib.rs b/tools/parser/tests/src/lib.rs index bff7219..4abb995 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -1,8 +1,9 @@ #![cfg(test)] -use std::str::FromStr; use ::parser::*; use ::syntax::*; use ::tag_code::*; +use ::either::Either::{self, Left, Right}; +use logic_parser::{ParseLine, IdxBox}; /// 快捷的创建一个新的`Meta`并且`parse` macro_rules! parse { @@ -11,6 +12,18 @@ macro_rules! parse { }; } +trait PCompile { + type E; + fn compile(self) -> Result, Self::E>; +} +impl<'a> PCompile for tag_code::logic_parser::ParseLines<'a> { + type E = Either, (usize, Tag)>; + fn compile(self) -> Result, Self::E> { + let mut tagcodes = TagCodes::try_from(self).map_err(Left)?; + tagcodes.compile().map_err(Right) + } +} + #[test] fn var_test() { let parser = VarParser::new(); @@ -509,7 +522,7 @@ fn switch_test() { ]) ).into() ); - let mut tag_codes = CompileMeta::new() + let tag_codes = CompileMeta::new() .compile(Expand(vec![ast]).into()); let lines = tag_codes .compile() @@ -629,7 +642,7 @@ fn comments_test() { fn op_generate_test() { assert_eq!( Op::Add("x".into(), "y".into(), "z".into()).generate_args(&mut Default::default()), - vec!["op", "add", "x", "y", "z"], + args!["op", "add", "x", "y", "z"], ); } @@ -648,7 +661,7 @@ fn compile_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, [ r#"op add x 1 2"#, @@ -671,16 +684,16 @@ fn compile_take_test() { let parser = LogicLineParser::new(); let ast = parse!(parser, "op x ({}op $ 1 + 2;) + 3;").unwrap(); let mut meta = CompileMeta::new(); - meta.push(TagLine::Line("noop".to_string().into())); + meta.push(ParseLine::Args(args!("noop"))); assert_eq!( ast.compile_take(&mut meta), vec![ - TagLine::Line("op add __0 1 2".to_string().into()), - TagLine::Line("op add x __0 3".to_string().into()), + ParseLine::Args(args!("op", "add", "__0", "1", "2")), + ParseLine::Args(args!("op", "add", "x", "__0", "3")), ] ); - assert_eq!(meta.tag_codes().len(), 1); - assert_eq!(meta.tag_codes().lines(), &vec![TagLine::Line("noop".to_string().into())]); + assert_eq!(meta.parse_lines().len(), 1); + assert_eq!(meta.parse_lines().lines(), &vec![ParseLine::Args(args!("noop"))]); } #[test] @@ -694,7 +707,7 @@ fn const_value_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "set x C", @@ -709,7 +722,7 @@ fn const_value_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "set x C", @@ -724,7 +737,7 @@ fn const_value_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "set x C", @@ -739,7 +752,7 @@ fn const_value_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "read m cell1 0", @@ -752,7 +765,7 @@ fn const_value_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "read i cell2 0", @@ -781,7 +794,7 @@ fn const_value_block_range_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "set x C", @@ -812,7 +825,7 @@ fn take_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print start", @@ -831,7 +844,7 @@ fn take_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "read m cell1 0", @@ -846,7 +859,7 @@ fn take_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print X", @@ -861,7 +874,7 @@ fn take_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print 2", @@ -877,7 +890,7 @@ fn take_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print 2", @@ -908,7 +921,7 @@ fn print_test() { "#; let ast = parse!(parser, src).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ r#"print "abc""#, @@ -968,7 +981,7 @@ fn const_expand_label_rename_test() { take __ = X; "#).unwrap(); let compile_meta = CompileMeta::new(); - let mut tag_codes = compile_meta.compile(ast); + let tag_codes = compile_meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!( logic_lines, @@ -1006,7 +1019,7 @@ fn const_expand_label_rename_test() { take __ = A; "#).unwrap(); let compile_meta = CompileMeta::new(); - let mut tag_codes = compile_meta.compile(ast); + let tag_codes = compile_meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!( logic_lines, @@ -1035,7 +1048,7 @@ fn dexp_result_handle_use_const_test() { print (R: $ = 2;); "#).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "set R 2", @@ -1066,7 +1079,7 @@ fn in_const_const_label_rename_test() { take __ = X; "#).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let _logic_lines = tag_codes.compile().unwrap(); } @@ -1089,7 +1102,7 @@ fn const_value_leak_test() { LogicLine::Other(vec!["print".into(), "N".into()].into()), ].into(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print N", @@ -1107,7 +1120,7 @@ fn const_value_leak_test() { LogicLine::Other(vec!["print".into(), "N".into()].into()), ].into(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print N", @@ -1160,7 +1173,7 @@ fn take_args_test() { print R; "#).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "print 1", @@ -1194,7 +1207,7 @@ fn take_args_test() { take["loop" F] DO; "#).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ r#"print "loop""#, @@ -1223,7 +1236,7 @@ fn sets_test() { a b c = 1 2 ({}op $ 2 + 1;); "#).unwrap(); let meta = CompileMeta::new(); - let mut tag_codes = meta.compile(ast); + let tag_codes = meta.compile(ast); let logic_lines = tag_codes.compile().unwrap(); assert_eq!(logic_lines, vec![ "set a 1", @@ -2287,23 +2300,23 @@ fn no_string_var_test() { #[test] fn jumpcmp_from_str_test() { let datas = [ - ("always", Err(JumpCmpRParseError::ArgsCountError( + (args!("always"), Err(JumpCmpRParseError::ArgsCountError( vec!["always".into()] ).into())), - ("always 0", Err(JumpCmpRParseError::ArgsCountError( + (args!("always", "0"), Err(JumpCmpRParseError::ArgsCountError( vec!["always".into(), "0".into()] ).into())), - ("add 1 2", Err(JumpCmpRParseError::UnknownComparer( + (args!("add", "1", "2"), Err(JumpCmpRParseError::UnknownComparer( "add".into(), ["1".into(), "2".into()] ).into())), - ("equal a b", Ok(JumpCmp::Equal("a".into(), "b".into()))), - ("lessThan a b", Ok(JumpCmp::LessThan("a".into(), "b".into()))), - ("always 0 0", Ok(JumpCmp::Always)), + (args!("equal", "a", "b"), Ok(JumpCmp::Equal("a".into(), "b".into()))), + (args!("lessThan", "a", "b"), Ok(JumpCmp::LessThan("a".into(), "b".into()))), + (args!("always", "0", "0"), Ok(JumpCmp::Always)), ]; for (src, expect) in datas { - assert_eq!(JumpCmp::from_mdt_args(&mdt_logic_split(src).unwrap()), expect) + assert_eq!(JumpCmp::from_mdt_args(src), expect) } } @@ -2327,11 +2340,9 @@ fn logic_line_from() { ), ]; for (src, lines2) in datas { - let mut tag_codes = TagCodes::from_str(src).unwrap(); - tag_codes.build_tagdown().unwrap(); - tag_codes.tag_up(); + let logic_lines = logic_parser::parser::lines(src).unwrap(); assert_eq!( - (&tag_codes).try_into(), + Expand::try_from(logic_lines).map_err(|e| (e.index, e.value)), lines2.map(Expand) ); } @@ -2749,6 +2760,7 @@ fn consted_dexp() { ].into()), ]).into() ); + dbg!(1); let logic_lines = CompileMeta::new().compile(parse!(parser, r#" const Do2 = ( @@ -2776,6 +2788,7 @@ fn consted_dexp() { "jump 0 always 0 0", "print 1", ]); + dbg!(2); let logic_lines = CompileMeta::new().compile(parse!(parser, r#" const Do2 = ( @@ -2803,6 +2816,7 @@ fn consted_dexp() { "jump 0 always 0 0", "print 1", ]); + dbg!(3); assert!(CompileMeta::new().compile(parse!(parser, r#" const Do2 = ( @@ -2820,6 +2834,7 @@ fn consted_dexp() { ) ] Do2; "#).unwrap()).compile().is_err()); + dbg!(4); assert_eq!( parse!(parser, r#" @@ -7114,3 +7129,38 @@ fn non_take_result_handle_dexp_test() { ], ); } + +#[test] +fn to_label_code_test() { + let parser = TopLevelParser::new(); + + let mut lines = CompileMeta::new().compile(parse!(parser, r#" + :2 + goto :2; + goto :end; + :end + :end1 + "#).unwrap()); + + assert_eq!( + lines.to_string().lines().collect::>(), + vec![ + r#":2:"#, + r#"jump :2 always 0 0"#, + r#"jump end always 0 0"#, + r#"end:"#, + r#"end1:"#, + ], + ); + lines.index_label_popup(); + assert_eq!( + lines.to_string().lines().collect::>(), + vec![ + r#"end1:"#, + r#"end:"#, + r#":2:"#, + r#"jump :2 always 0 0"#, + r#"jump end always 0 0"#, + ], + ); +} diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index 9a99205..92e8b45 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.38" +version = "0.2.39" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,3 +10,4 @@ tag_code = { path = "../tag_code", version = "*" } var_utils = { path = "../var_utils", version = "*"} utils = { path = "../utils", version = "*" } either = "1.10.0" +itermaps = "0.2.8" diff --git a/tools/syntax/src/lib.rs b/tools/syntax/src/lib.rs index f01773b..f9b987f 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -4,26 +4,30 @@ mod tests; use std::{ borrow::{Borrow, Cow}, + cell::Cell, collections::{HashMap, HashSet}, convert::identity, fmt::{self, Debug, Display}, hash::Hash, - iter::{repeat_with, zip, once}, + iter::{once, repeat_with, zip}, mem::{self, replace}, num::ParseIntError, - ops::{self, Deref}, + ops, process::exit, - rc::Rc, - cell::Cell, }; use builtins::{build_builtins, BuiltinFunc}; use either::Either; -use tag_code::{Jump, TagCodes, TagLine, mdt_logic_split}; +use itermaps::{fields, MapExt, Unpack}; +use tag_code::{ + args, + logic_parser::{Args as LArgs, IdxBox, ParseLine, ParseLines}, +}; use utils::counter::Counter; use var_utils::{string_unescape, AsVarType}; pub use either; +pub use var_utils::Var; macro_rules! impl_enum_froms { (impl From for $ty:ty { $( @@ -157,114 +161,9 @@ macro_rules! err { }; } -#[derive(Debug, Default, Clone)] -pub struct Var { - value: Rc, -} -impl Var { - pub fn new() -> Self { - Self::default() - } - - pub fn display_src<'a>(&'a self, meta: &'a CompileMeta) -> Cow<'a, str> { - meta.extender.as_ref() - .map(|ext| ext - .display_value(&Value::Var(self.clone()))) - .unwrap_or(self.as_str().into()) - } -} -impl FromIterator for Var { - fn from_iter>(iter: T) -> Self { - String::from_iter(iter).into() - } -} -impl From<&'_ Var> for Var { - fn from(value: &'_ Var) -> Self { - value.clone() - } -} -impl From for String { - fn from(mut value: Var) -> Self { - Rc::make_mut(&mut value.value); - Rc::try_unwrap(value.value).unwrap() - } -} -impl From for Rc { - fn from(value: Var) -> Self { - value.value - } -} -impl Display for Var { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.value, f) - } -} -impl Hash for Var { - fn hash(&self, state: &mut H) { - self.value.hash(state) - } -} -impl Borrow for Var { - fn borrow(&self) -> &String { - &self.value - } -} -impl Borrow for Var { - fn borrow(&self) -> &str { - &***self - } -} -impl AsRef for Var { - fn as_ref(&self) -> &String { - self.borrow() - } -} -impl AsRef for Var { - fn as_ref(&self) -> &str { - self.borrow() - } -} -impl From for Var { - fn from(value: String) -> Self { - Self { value: value.into() } - } -} -impl From<&'_ str> for Var { - fn from(value: &'_ str) -> Self { - Self { value: Rc::new(value.into()) } - } -} -impl Deref for Var { - type Target = String; - - fn deref(&self) -> &Self::Target { - &*self.value - } -} -impl Eq for Var { } -impl PartialEq for Var { - fn eq(&self, other: &Self) -> bool { - self.value == other.value - } -} -impl PartialEq for Var { - fn eq(&self, other: &String) -> bool { - &**self == other - } -} -impl PartialEq for Var { - fn eq(&self, other: &str) -> bool { - **self == other - } -} -impl PartialEq<&str> for Var { - fn eq(&self, other: &&str) -> bool { - **self == *other - } -} - pub type Location = usize; pub type Float = f64; +type Line = IdxBox>; fn default() -> T { T::default() @@ -276,6 +175,15 @@ pub const ZERO_VAR: &str = "0"; pub const UNUSED_VAR: &str = "0"; pub const UNNAMED_VAR: &str = "__"; +trait DisplaySrc { + fn display_src<'a>(&self, meta: &'a CompileMeta) -> Cow<'a, str>; +} +impl DisplaySrc for Var { + fn display_src<'a>(&self, meta: &'a CompileMeta) -> Cow<'a, str> { + Value::Var(self.clone()).display_src(meta) + } +} + pub trait TakeHandle: Sized { /// 编译依赖并返回句柄 fn take_handle(self, meta: &mut CompileMeta) -> Var; @@ -573,7 +481,7 @@ impl Value { let Value::ReprVar(cmd) = &args[0] else { return None; }; - match &***cmd { + match &**cmd { "set" if args.len() == 3 && args[1].is_result_handle() @@ -854,7 +762,7 @@ impl ClosuredValue { .collect(); let bindh = meta.get_tmp_var(); for catch in catch_values { - catch.do_catch(meta, &**bindh); + catch.do_catch(meta, &*bindh); } let key = Self::make_valkey(bindh.clone()); @@ -999,7 +907,7 @@ impl TakeHandle for DExp { ), meta.err_info().join("\n"), value.display_src(meta), - result.display_src(meta), + Value::Var(result).display_src(meta), ); exit(5); } @@ -1239,13 +1147,11 @@ impl Meta { } } -pub trait FromMdtArgs -where Self: Sized -{ +pub trait FromMdtArgs<'a>: Sized { type Err; /// 从逻辑参数构建 - fn from_mdt_args(args: &[&str]) -> Result; + fn from_mdt_args(args: LArgs<'a>) -> Result; } /// `jump`可用判断条件枚举 @@ -1459,15 +1365,20 @@ impl Display for JumpCmpRParseError { } } } -impl FromMdtArgs for JumpCmp { +impl FromMdtArgs<'_> for JumpCmp { type Err = LogicLineFromTagError; - fn from_mdt_args(args: &[&str]) -> Result { - let &[oper, a, b] = args else { + fn from_mdt_args(args: LArgs<'_>) -> Result + { + let Ok(args): Result<&[_; 3], _> = args[..].try_into() else { return Err(JumpCmpRParseError::ArgsCountError( - args.iter().cloned().map(Into::into).collect() + args.iter() + .map_as_ref::() + .map_into() + .collect() ).into()); }; + let [oper, a, b] = args.unpack().map(AsRef::as_ref); macro_rules! build_match { ( $( $name:ident , $str:pat ),* $(,)? ) => { @@ -1543,10 +1454,6 @@ impl Display for OpRParseError { pub enum LogicLineFromTagError { JumpCmpRParseError(JumpCmpRParseError), OpRParseError(OpRParseError), - StringNoStop { - str: String, - char_num: usize, - }, } impl Display for LogicLineFromTagError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -1555,14 +1462,6 @@ impl Display for LogicLineFromTagError { Display::fmt(&e, f), Self::OpRParseError(e) => Display::fmt(&e, f), - Self::StringNoStop { str, char_num } => { - write!( - f, - "未闭合的字符串, 在第{}个字符处起始, 行:[{}]", - char_num, - str - ) - }, } } } @@ -1640,7 +1539,7 @@ impl CmpTree { lines }), .. - }) => take_result.then_some(&***s) + }) => take_result.then_some(&**s) .or_else(|| lines.is_empty() .and_then(|| f(meta, s))), Some(_) => None, @@ -1651,7 +1550,7 @@ impl CmpTree { take_result, result: s, lines, - }) => take_result.then_some(&***s) + }) => take_result.then_some(&**s) .or_else(|| lines.is_empty() .and_then(|| f(meta, s))), | V::ReprVar(s) @@ -1659,7 +1558,7 @@ impl CmpTree { | V::ResultHandle => Some(&**meta.dexp_handle()), | V::Binder - => Some(meta.get_dexp_expand_binder().map(|s| &***s).unwrap_or("__")), + => Some(meta.get_dexp_expand_binder().map(|s| &**s).unwrap_or("__")), | V::ValueBind(_) | V::ValueBindRef(_) | V::Cmper(_) @@ -1759,8 +1658,7 @@ impl CmpTree { let end = meta.get_tmp_tag(); a.reverse().build(meta, end.clone()); b.build(meta, do_tag_expanded); - let tag_id = meta.get_tag(end); - meta.push(TagLine::TagDown(tag_id)); + meta.push(end.to_string().into()); }, Expand(handle, cmp) => { meta.const_expand_enter(&handle); @@ -1775,11 +1673,10 @@ impl CmpTree { let cmp_str = cmp.cmp_str(); let (a, b) = cmp.build_value(meta); - let jump = Jump( - meta.get_tag(do_tag_expanded).into(), - format!("{} {} {}", cmp_str, a, b) - ); - meta.push(jump.into()) + meta.push(ParseLine::Jump( + do_tag_expanded.to_string().into(), + args!(cmp_str, &a, &b).into_owned(), + )) }, } } @@ -2016,7 +1913,7 @@ impl Op { info.oper_sym.unwrap_or(info.oper_str) } - pub fn generate_args(self, meta: &mut CompileMeta) -> Vec { + pub fn generate_args(self, meta: &mut CompileMeta) -> LArgs<'static> { let info = self.into_info(); let mut args: Vec = Vec::with_capacity(5); @@ -2024,15 +1921,14 @@ impl Op { args.push(info.oper_str.into()); args.push(info.result.take_handle(meta).into()); args.push(info.arg1.take_handle(meta).into()); - args.push( - info.arg2 + args.push(info.arg2 .map(|arg| arg.take_handle(meta)) .map(Into::into) .unwrap_or(UNUSED_VAR.into()) ); debug_assert!(args.len() == 5); - args + args.try_into().unwrap() } /// 根据自身运算类型尝试获取一个比较器 @@ -2137,18 +2033,24 @@ impl Op { impl Compile for Op { fn compile(self, meta: &mut CompileMeta) { let args = self.generate_args(meta); - meta.tag_codes.push(args.join(" ").into()) + meta.parse_lines.push((0, args.into()).into()) } } -impl FromMdtArgs for Op { +impl FromMdtArgs<'_> for Op { type Err = OpRParseError; - fn from_mdt_args(args: &[&str]) -> Result { - let &[oper, res, a, b] = args else { + fn from_mdt_args(args: LArgs<'_>) -> Result + { + let Ok(args): Result<&[_; 5], _> = args[..].try_into() else { return Err(OpRParseError::ArgsCountError( - args.iter().cloned().map(Into::into).collect() + args.iter() + .map_as_ref::() + .map_into() + .collect() )); }; + let [_, oper, res, a, b] = args.unpack() + .map(AsRef::as_ref); macro_rules! build_match { { @@ -2275,15 +2177,17 @@ impl From<[LogicLine; N]> for Expand { Self(value.into()) } } -impl TryFrom<&TagCodes> for Expand { - type Error = (usize, LogicLineFromTagError); +impl TryFrom> for Expand { + type Error = IdxBox; - fn try_from(codes: &TagCodes) -> Result { - let mut lines = Vec::with_capacity(codes.lines().len()); - for (idx, code) in codes.lines().iter().enumerate() { - lines.push(code.try_into().map_err(|e| (idx, e))?) - } - Ok(Self(lines)) + fn try_from(mut codes: ParseLines<'_>) -> Result { + codes.index_label_popup(); + codes.into_iter() + .map(fields!(index, value.try_into())) + .map(|(i, res)| + res.map_err(|e| (i, e))) + .map(|res| res.map_err(Into::into)) + .collect() } } impl FromIterator for Expand { @@ -2316,7 +2220,7 @@ impl_derefs!(impl for InlineBlock => (self: self.0): Vec); pub struct Select(pub Value, pub Expand); impl Compile for Select { fn compile(self, meta: &mut CompileMeta) { - let mut cases: Vec> = self.1.0 + let mut cases: Vec>> = self.1.0 .into_iter() .map( |line| line.compile_take(meta) @@ -2324,7 +2228,7 @@ impl Compile for Select { let lens: Vec = cases.iter() .map(|lines| { lines.iter() - .filter(|line| line.can_generate_line()) + .filter(|line| line.is_solid()) .count() }).collect(); let max_len = lens.iter().copied().max().unwrap_or_default(); @@ -2333,7 +2237,7 @@ impl Compile for Select { if let 0 | 1 = cases.len() { Take("__".into(), target).compile(meta); if let Some(case) = cases.pop() { - meta.tag_codes_mut().extend(case) + meta.parse_lines_mut().extend(case) } assert!(cases.is_empty(), "{}", cases.len()); return @@ -2354,36 +2258,37 @@ impl Compile for Select { }; #[cfg(debug_assertions)] - let old_tag_codes_len = meta.tag_codes.count_no_tag(); + let old_tag_codes_len = meta.parse_lines.solid_count(); // 因为跳转表式的穿透更加高效, 所以长度相同时优先选择 if simple_select_len < goto_table_select_len { Self::build_simple_select(target, max_len, meta, lens, cases); #[cfg(debug_assertions)] assert_eq!( - meta.tag_codes.count_no_tag(), + meta.parse_lines.solid_count(), old_tag_codes_len + simple_select_len, - "预期长度公式错误\n{}", - meta.tag_codes, + "预期长度公式错误\n{:#?}", + meta.parse_lines, ); } else { Self::build_goto_table_select(target, max_len, meta, lens, cases); #[cfg(debug_assertions)] assert_eq!( - meta.tag_codes.count_no_tag(), + meta.parse_lines.solid_count(), old_tag_codes_len + goto_table_select_len, - "预期长度公式错误\n{}", - meta.tag_codes, + "预期长度公式错误\n{:#?}", + meta.parse_lines, ); } } } + impl Select { fn build_goto_table_select( target: Value, max_len: usize, meta: &mut CompileMeta, lens: Vec, - cases: Vec>, + cases: Vec>, ) { let counter = Value::ReprVar(COUNTER.into()); @@ -2410,7 +2315,7 @@ impl Select { for case in cases { let tag = tags_iter.next().unwrap(); LogicLine::Label(tag).compile(meta); - meta.tag_codes.lines_mut().extend(case); + meta.parse_lines.lines_mut().extend(case); } } @@ -2419,7 +2324,7 @@ impl Select { max_len: usize, meta: &mut CompileMeta, lens: Vec, - mut cases: Vec>, + mut cases: Vec>, ) { let counter = Value::ReprVar(COUNTER.into()); @@ -2466,10 +2371,9 @@ impl Select { 0 => continue, insert_counts => { let end_tag = meta.get_tmp_tag(); - let end_tag = meta.get_tag(end_tag); - case.push(TagLine::Jump( - tag_code::Jump::new_always(end_tag).into() - )); + case.push(Line::new(0, ParseLine::new_always( + end_tag.to_string().into(), + ))); case.extend( repeat_with(|| LogicLine::NoOp @@ -2477,12 +2381,12 @@ impl Select { .take(insert_counts - 1) .flatten() ); - case.push(TagLine::TagDown(end_tag)); + case.push((0, end_tag.to_string().into()).into()); }, } } - let lines = meta.tag_codes.lines_mut(); + let lines = meta.parse_lines.lines_mut(); lines.extend(cases.into_iter().flatten()); } } @@ -2743,6 +2647,15 @@ pub enum Args { /// 夹杂一个展开的参数 Expanded(Vec, Vec), } +impl From> for Args { + fn from(value: tag_code::logic_parser::Args<'_>) -> Self { + let args = value.into_iter() + .map_into::() + .map_into() + .collect(); + Self::Normal(args) + } +} impl Args { pub const GLOB_ONLY: Self = Self::Expanded(vec![], vec![]); @@ -3554,16 +3467,21 @@ pub enum LogicLine { impl Compile for LogicLine { fn compile(self, meta: &mut CompileMeta) { match self { - Self::NoOp => meta.push(meta.noop_line.clone().into()), + Self::NoOp => meta.push(args!(&*meta.noop_line).into()), Self::Label(mut lab) => { // 如果在常量展开中, 尝试将这个标记替换 lab = meta.get_in_const_label(lab); - let data = TagLine::TagDown(meta.get_tag(lab)); - meta.push(data) + meta.push(lab.to_string().into()) }, Self::Other(args) => { let handles: Vec = args.into_taked_args_handle(meta); - meta.push(TagLine::Line(handles.join(" ").into())); + let args = handles.into_iter() + .map_to_string() + .map_into() + .collect::>() + .try_into() + .unwrap(); + meta.push(args); }, Self::SetResultHandle(value) => { let new_dexp_handle = value.take_handle(meta); @@ -3723,50 +3641,28 @@ impl_enum_froms!(impl From for LogicLine { Match => Match; ConstMatch => ConstMatch; }); -impl TryFrom<&TagLine> for LogicLine { +impl TryFrom> for LogicLine { type Error = LogicLineFromTagError; - fn try_from(line: &TagLine) -> Result { - type Error = LogicLineFromTagError; - fn mdt_logic_split_2(s: &str) -> Result, Error> { - mdt_logic_split(s) - .map_err(|char_num| Error::StringNoStop { - str: s.into(), - char_num - }) + fn try_from(line: ParseLine<'_>) -> Result { + fn run_lab(mut s: String) -> String { + if s.starts_with(':') { s.remove(0); } + s } match line { - TagLine::Jump(jump) => { - assert!(jump.tag().is_none()); - let jump = jump.data(); - let str = &jump.1; - let (to_tag, args) = ( - jump.0, - mdt_logic_split_2(&str)? - ); - Ok(Goto( - to_tag.to_string().into(), - match JumpCmp::from_mdt_args(&args) { - Ok(cmp) => cmp.into(), - Err(e) => return Err(e.into()), - } - ).into()) + ParseLine::Label(lab) => { + Ok(Self::Label(run_lab(lab.into_owned()).into())) }, - TagLine::TagDown(tag) => Ok(Self::Label(tag.to_string().into())), - TagLine::Line(line) => { - assert!(line.tag().is_none()); - let line = line.data(); - let args = mdt_logic_split_2(line)?; - match args[0] { - "op" => Op::from_mdt_args(&args[1..]) - .map(Into::into) - .map_err(Into::into), - _ => { - let mut args_value = Vec::with_capacity(args.len()); - args_value.extend(args.into_iter().map(Into::into)); - Ok(Self::Other(Args::Normal(args_value))) - }, - } + ParseLine::Jump(target, args) => { + let target = run_lab(target.into_owned()).into(); + let cmp = JumpCmp::from_mdt_args(args)?; + Ok(Goto(target, cmp.into()).into()) + }, + ParseLine::Args(args) => { + Ok(match args.first() { + "op" => Op::from_mdt_args(args)?.into(), + _ => Self::Other(args.into()), + }) }, } } @@ -3781,12 +3677,12 @@ pub trait Compile { /// 使用时需要考虑其副作用, 例如`compile`并不止做了`push`至尾部, /// 它还可能做了其他事 #[must_use] - fn compile_take(self, meta: &mut CompileMeta) -> Vec + fn compile_take(self, meta: &mut CompileMeta) -> Vec where Self: Sized { - let start = meta.tag_codes.len(); + let start = meta.parse_lines.len(); self.compile(meta); - meta.tag_codes.lines_mut().split_off(start) + meta.parse_lines.lines_mut().split_off(start) } } @@ -3915,10 +3811,7 @@ pub trait CompileMetaExtends { pub struct CompileMeta { extender: Option>, - /// 标记与`id`的映射关系表 - tags_map: HashMap, - tag_count: usize, - tag_codes: TagCodes, + parse_lines: ParseLines<'static>, tmp_var_count: Counter Var>, /// 每层Expand的环境, 也包括顶层 expand_env: Vec, @@ -3950,9 +3843,7 @@ impl Debug for CompileMeta { } } f.debug_struct("CompileMeta") - .field("tags_map", &self.tags_map) - .field("tag_count", &self.tag_count) - .field("tag_codes", &self.tag_codes) + .field("parse_lines", &self.parse_lines) .field("tmp_var_count", &self.tmp_var_count.counter()) .field("expand_env", &self.expand_env) .field("env_args", &self.env_args) @@ -3969,7 +3860,7 @@ impl Debug for CompileMeta { } impl Default for CompileMeta { fn default() -> Self { - Self::with_tag_codes(TagCodes::new()) + Self::with_tag_codes(ParseLines::new(vec![])) } } impl CompileMeta { @@ -3979,12 +3870,10 @@ impl CompileMeta { Self::default() } - pub fn with_tag_codes(tag_codes: TagCodes) -> Self { + pub fn with_tag_codes(tag_codes: ParseLines<'static>) -> Self { let mut meta = Self { extender: None, - tags_map: HashMap::new(), - tag_count: 0, - tag_codes, + parse_lines: tag_codes, tmp_var_count: Counter::new(Self::tmp_var_getter), expand_env: vec![ExpandEnv::new(vec![], HashMap::new())], env_args: Vec::new(), @@ -4019,16 +3908,6 @@ impl CompileMeta { meta } - /// 获取一个标记的编号, 如果不存在则将其插入并返回新分配的编号. - /// 注: `Tag`与`Label`是混用的, 表示同一个意思 - pub fn get_tag(&mut self, label: Var) -> usize { - *self.tags_map.entry(label.to_string()).or_insert_with(|| { - let id = self.tag_count; - self.tag_count += 1; - id - }) - } - fn tmp_tag_getter(id: &mut usize) -> Var { let old = *id; *id += 1; @@ -4083,46 +3962,43 @@ impl CompileMeta { } /// 向已生成代码`push` - pub fn push(&mut self, data: TagLine) { - self.tag_codes.push(data) + pub fn push(&mut self, mut data: ParseLine<'static>) { + match data { + ParseLine::Label(ref mut lab) + if matches!(lab.chars().next(), Some('0'..='9')) + => lab.to_mut().insert(0, ':'), + ParseLine::Jump(ref mut lab, _) + if matches!(lab.chars().next(), Some('0'..='9')) + => lab.to_mut().insert(0, ':'), + ParseLine::Label(_) + | ParseLine::Jump(_, _) + | ParseLine::Args(_) => (), + } + self.parse_lines.push((0, data).into()) } /// 向已生成代码`pop` - pub fn pop(&mut self) -> Option { - self.tag_codes.pop() - } - - /// 获取已生成的代码条数 - pub fn tag_code_count(&self) -> usize { - self.tag_codes.len() - } - - /// 获取已生成的非`TagDown`代码条数 - pub fn tag_code_count_no_tag(&self) -> usize { - self.tag_codes.count_no_tag() + pub fn pop(&mut self) -> Option> { + self.parse_lines.pop().map(fields!(value)) } - pub fn compile(self, lines: Expand) -> TagCodes { - self.compile_res_self(lines).tag_codes + pub fn compile(self, lines: Expand) -> ParseLines<'static> { + self.compile_res_self(lines).parse_lines } pub fn compile_res_self(mut self, lines: Expand) -> Self { - self.tag_codes.clear(); + self.parse_lines.clear(); lines.compile(&mut self); self } - pub fn tag_codes(&self) -> &TagCodes { - &self.tag_codes + pub fn parse_lines(&self) -> &ParseLines<'static> { + &self.parse_lines } - pub fn tag_codes_mut(&mut self) -> &mut TagCodes { - &mut self.tag_codes - } - - pub fn tags_map(&self) -> &HashMap { - &self.tags_map + pub fn parse_lines_mut(&mut self) -> &mut ParseLines<'static> { + &mut self.parse_lines } /// 进入一个拥有子命名空间的子块 @@ -4287,23 +4163,11 @@ impl CompileMeta { } pub fn debug_tag_codes(&self) -> Vec { - self.tag_codes().lines().iter() + self.parse_lines().lines().iter() .map(ToString::to_string) .collect() } - pub fn debug_tags_map(&self) -> Vec { - let mut tags_map: Vec<_> = self - .tags_map() - .iter() - .collect(); - tags_map.sort_unstable_by_key(|(_, &k)| k); - tags_map - .into_iter() - .map(|(tag, id)| format!("{} \t-> {}", id, tag)) - .collect() - } - /// 尝试获取当前DExp返回句柄, 没有DExp的话返回空 pub fn try_get_dexp_handle(&self) -> Option<&Var> { self.dexp_result_handles.last() @@ -4415,13 +4279,9 @@ impl CompileMeta { pub fn err_info(&self) -> Vec { let mut res = Vec::new(); - let mut tags_map = self.debug_tags_map(); let mut tag_lines = self.debug_tag_codes(); - line_first_add(&mut tags_map, "\t"); line_first_add(&mut tag_lines, "\t"); - res.push("Id映射Tag:".into()); - res.extend(tags_map); res.push("已生成代码:".into()); res.extend(tag_lines); res diff --git a/tools/tag_code/Cargo.toml b/tools/tag_code/Cargo.toml index f38ac43..88aa9d5 100644 --- a/tools/tag_code/Cargo.toml +++ b/tools/tag_code/Cargo.toml @@ -1,8 +1,11 @@ [package] name = "tag_code" -version = "0.1.7" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +either = "1.13.0" +peg = "0.8.4" +var_utils = { path = "../var_utils", version = "*" } diff --git a/tools/tag_code/src/lib.rs b/tools/tag_code/src/lib.rs index 88ba4c1..e8e5399 100644 --- a/tools/tag_code/src/lib.rs +++ b/tools/tag_code/src/lib.rs @@ -1,20 +1,16 @@ use std::{ - collections::{ - HashMap, - HashSet - }, - mem::{swap, replace}, - ops::{ - Deref, - DerefMut - }, - str::FromStr, - num::ParseIntError, + collections::HashMap, fmt::Display, + mem::{replace, swap}, + ops::{Deref, DerefMut}, panic::catch_unwind, process::exit, }; +use logic_parser::{IdxBox, ParseLine, ParseLines}; + +pub mod logic_parser; + pub type Tag = usize; pub type TagsTable = Vec; pub const UNINIT_TAG_TARGET: usize = usize::MAX; @@ -26,7 +22,6 @@ macro_rules! err { }; } - /// 传入`TagsTable`, 生成逻辑代码 pub trait Compile { fn compile(&self, tags_table: &TagsTable) -> String; @@ -460,69 +455,75 @@ impl Compile for TagLine { #[derive(Debug, PartialEq, Eq, Clone)] pub enum ParseTagCodesError { - ParseIntError(ParseIntError), + RepeatLabel(String), + MissedLabel(String), +} +impl Display for ParseTagCodesError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParseTagCodesError::RepeatLabel(lab) => { + write!(f, "重复的标签 `{lab}`") + }, + ParseTagCodesError::MissedLabel(lab) => { + write!(f, "未命中的标签 `{lab}`") + }, + } + } } #[derive(Debug, PartialEq, Eq, Clone)] pub struct TagCodes { lines: Vec, } -impl FromStr for TagCodes { - type Err = (usize, ParseTagCodesError); - - /// 从逻辑语言中构建 - fn from_str(s: &str) -> Result { - // K: jump所在行, jump所用tag为第n个jump - let mut jumps: HashSet = HashSet::new(); - // K: 被跳转行, V: tag - let mut tags: HashMap> = HashMap::new(); - let lines = - || s.lines().filter(|s| !s.trim().is_empty()); - - for (i, line) in lines().enumerate() { - if line.starts_with("jump") { - let target = take_jump_target(line); - if target == "-1" { - // no target jump - continue; - } - let tag = jumps.len(); - jumps.insert(i); - let target_idx = target - .parse::() - .map_err(|e| ( - i, - ParseTagCodesError::ParseIntError(e) - ))?; - tags.entry(target_idx).or_default().push(tag); +impl<'a> TryFrom> for TagCodes { + type Error = IdxBox; + + fn try_from(mut codes: ParseLines<'a>) -> Result { + codes.index_label_popup(); + + let mut lab2idx = HashMap::with_capacity(codes.modif_count()); + codes.iter() + .filter_map(|x| x.as_ref() + .and_then(|x| x.as_label())) + .try_for_each(|label| + { + if lab2idx.insert(*label, lab2idx.len()).is_some() { + return Err(label.as_ref().map(|_| { + ParseTagCodesError::RepeatLabel( + label.to_string() + ) + })); } - } + Ok(()) + })?; + + let get = |lab: IdxBox<&str>| { + lab2idx.get(*lab) + .copied() + .ok_or_else(|| { + lab.as_ref().map(|_| ParseTagCodesError::MissedLabel( + lab.to_string() + )) + }) + }; - let mut res_lines: Vec = Vec::new(); - let mut jump_count = 0; - for (i, line) in lines().enumerate() { - if let Some(self_tags) = tags.get(&i) { - for &tag in self_tags { - res_lines.push(TagLine::TagDown(tag)) - } - } - if jumps.get(&i).is_some() { - // 该行为一个jump - let jump_body = take_jump_body(line); - res_lines.push(TagLine::Jump( - Jump(jump_count, jump_body).into() - )); - jump_count += 1; - } else { - res_lines.push(TagLine::Line(line.to_string().into())) + let lines = codes.iter().map(|line| { + match *line.as_ref() { + ParseLine::Label(lab) => { + let tag = get(line.as_ref().map(|_| lab.as_ref()))?; + Ok(TagLine::TagDown(tag)) + }, + ParseLine::Jump(tgt, args) => { + let tag = get(line.as_ref().map(|_| tgt.as_ref()))?; + Ok(TagLine::Jump(Jump(tag, args.join(" ")).into())) + }, + ParseLine::Args(args) => { + Ok(TagLine::Line(args.join(" ").into())) + }, } - } - // 需要先给jump建立索引 - // 记录每个被跳转点, 记录每个jump及其跳转目标标记 - // - // 遍历每行, 如果是一个被记录的jump行则构建jump - // 同时, 如果该行为一个被跳转点, 那么构建时在其上增加一个`TagDown` - Ok(res_lines.into()) + }).collect::>>()?; + + Ok(Self { lines }) } } impl From> for TagCodes { @@ -581,7 +582,7 @@ impl TagCodes { self.lines.clear() } - /// 构建, 将[`TagDown`]消除, 如果是最后一行裸`Tag`则被丢弃 + /// 构建, 将[`TagDown`]消除, /// 如果目标`Tag`重复则将其返回 /// /// > jump :a @@ -888,7 +889,6 @@ impl Display for TagCodes { } } - fn take_jump_target(line: &str) -> String { debug_assert!(line.starts_with("jump")); @@ -1088,10 +1088,9 @@ mod tests { #[test] fn from_str_test() { - let mut tag_lines: TagCodes = MY_INSERT_SORT_LOGIC_LINES - .join("\n") - .parse() - .unwrap(); + let src = MY_INSERT_SORT_LOGIC_LINES.join("\n"); + let logic_lines = logic_parser::parser::lines(&src).unwrap(); + let mut tag_lines: TagCodes = logic_lines.try_into().unwrap(); let lines = tag_lines.compile().unwrap(); assert_eq!(lines, &MY_INSERT_SORT_LOGIC_LINES); } diff --git a/tools/tag_code/src/logic_parser.rs b/tools/tag_code/src/logic_parser.rs new file mode 100644 index 0000000..6aae517 --- /dev/null +++ b/tools/tag_code/src/logic_parser.rs @@ -0,0 +1,845 @@ +use std::{ + borrow::Cow, collections::HashSet, fmt::Display, iter::{self, once}, mem, ops::{Deref, DerefMut}, slice, vec +}; +use either::Either::{self, Left, Right}; + +pub use var_utils::Var; + +peg::parser!(pub grammar parser() for str { + rule _() = quiet!{ [' ' | '\t']+ } / expected!("whitespace") + rule newline_raw() = quiet!{ "\r"? "\n" } / expected!("newline") + rule comment() = quiet!{ "#" [^'\r' | '\n']* } / expected!("comment") + pub(crate) rule nl() + = ![_] + / _? ( + ";" + / newline_raw() + / comment() + ) nl()? _? + + rule string() -> &'input str + = quiet!{ $("\"" [^'\r' | '\n' | '"']* "\"") } + / expected!("string") + + rule norm_arg_ch() = [^' ' | '\t' | '\r' | '\n' | '"' | '#' | ';'] + rule norm_arg_inner() + = norm_arg_ch() norm_arg_inner() + / !":" norm_arg_ch() + rule norm_arg() -> &'input str + = quiet!{ $(norm_arg_inner()) } + / expected!("norm-arg") + + pub rule arg() -> &'input str + = norm_arg() + / string() + + pub rule args() -> Args<'input> + = args:(arg:arg() { Var::from(arg) }) ++ _ { args.try_into().unwrap() } + + pub rule label() -> &'input str + = quiet!{ s:arg() ":" { s } / $(":" arg()) } + / expected!("label") + + pub rule line() -> ParseLine<'input> + = l:label() { ParseLine::Label(l.into()) } + / "jump" _ !"-1" target:arg() _ args:args() + { ParseLine::Jump(target.into(), args) } + / args:args() { args.into() } + + pub rule lines() -> ParseLines<'input> + = nl()? lines:( + pos:position!() + l:line() nl() { (pos, l).into() } + )* { lines.into() } +}); + +/// # Examples +/// ``` +/// # use tag_code::{args, logic_parser::Args}; +/// # use std::borrow::Cow; +/// assert_eq!( +/// args!("a", "b"), +/// Args::try_from( +/// Cow::Owned(vec!["a".into(), "b".into()]) +/// ).unwrap(), +/// ); +/// ``` +#[macro_export] +macro_rules! args { + ($($arg:expr),+ $(,)?) => { + $crate::logic_parser::Args::try_from(::std::vec![ + $( + $crate::logic_parser::Var::from($arg), + )+ + ]).unwrap() + }; +} + +macro_rules! i { + (*$i:ident++) => {{ + let __res = *$i; + *$i += 1; + __res + }}; +} + +/// `Vec>` wrapper, but not empty +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Args<'a>(Cow<'a, [Var]>); +impl<'a> Args<'a> { + pub fn first(&self) -> &str { + self.0.first().unwrap() + } + + pub fn into_owned(self) -> Args<'static> { + Args(self.0.into_owned().into()) + } +} +impl<'a> IntoIterator for Args<'a> { + type Item = Var; + type IntoIter = Either< + iter::Cloned>, + vec::IntoIter + >; + + fn into_iter(self) -> Self::IntoIter { + match self.0 { + Cow::Borrowed(slice) => { + Left(slice.iter().cloned()) + }, + Cow::Owned(vec) => { + Right(vec.into_iter()) + }, + } + } +} +impl<'a> Deref for Args<'a> { + type Target = [Var]; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} +impl<'a> From> for Vec { + fn from(value: Args<'a>) -> Self { + value.0.into_owned() + } +} +impl<'a> TryFrom> for Args<'a> { + type Error = (); + + fn try_from(value: Vec) -> Result { + if value.is_empty() { + return Err(()); + } + Ok(Self(value.into())) + } +} +impl<'a> TryFrom> for Args<'a> { + type Error = (); + + fn try_from(value: Vec<&'a str>) -> Result { + if value.is_empty() { + return Err(()); + } + let args = value.into_iter() + .map(Into::into) + .collect(); + Ok(Self(Cow::Owned(args))) + } +} +impl<'a> TryFrom> for Args<'a> { + type Error = (); + + fn try_from(value: Cow<'a, [Var]>) -> Result { + if value.is_empty() { + return Err(()); + } + Ok(Self(value)) + } +} +impl TryFrom> for Args<'static> { + type Error = (); + + fn try_from(value: Vec) -> Result { + if value.is_empty() { + return Err(()); + } + let args = value.into_iter() + .map(Into::into) + .collect(); + Ok(Self(Cow::Owned(args))) + } +} +impl<'a> Display for Args<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Self(args) = self; + + f.write_str(args.first().unwrap())?; + for arg in args.iter().skip(1) { + f.write_str(" ")?; + f.write_str(&arg)?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ParseLine<'a> { + Label(Cow<'a, str>), + Jump(Cow<'a, str>, Args<'a>), + Args(Args<'a>), +} +impl<'a> ParseLine<'a> { + /// new always jump + pub fn new_always(target: Cow<'a, str>) -> Self { + Self::Jump(target, args!("always", "0", "0")) + } + + pub fn into_owned(self) -> ParseLine<'static> { + match self { + ParseLine::Label(lab) => { + ParseLine::Label(lab.into_owned().into()) + }, + ParseLine::Jump(tgt, args) => { + ParseLine::Jump(tgt.into_owned().into(), args.into_owned()) + }, + ParseLine::Args(args) => { + args.into_owned().into() + }, + } + } + + pub fn is_solid(&self) -> bool { + matches!(self, Self::Args(_) | Self::Jump(_, _)) + } + + /// Returns `true` if the parse line is [`Args`]. + /// + /// [`Args`]: ParseLine::Args + #[must_use] + pub fn is_args(&self) -> bool { + matches!(self, Self::Args(..)) + } + + pub fn as_args(&self) -> Option<&Args<'a>> { + if let Self::Args(v) = self { + Some(v) + } else { + None + } + } + + /// Returns `true` if the parse line is [`Label`]. + /// + /// [`Label`]: ParseLine::Label + #[must_use] + pub fn is_label(&self) -> bool { + matches!(self, Self::Label(..)) + } + + pub fn as_label(&self) -> Option<&str> { + if let Self::Label(v) = self { + Some(v) + } else { + None + } + } + + /// Returns `true` if the parse line is [`Jump`]. + /// + /// [`Jump`]: ParseLine::Jump + #[must_use] + pub fn is_jump(&self) -> bool { + matches!(self, Self::Jump(..)) + } + + pub fn as_jump_target(&self) -> Option<&str> { + if let Self::Jump(target, _) = self { + Some(target) + } else { + None + } + } + + pub fn as_jump_idx(&self) -> Option { + self.as_jump_target() + .map(str::parse)? + .ok() + } + + pub fn as_jump_args(&self) -> Option<&Args<'a>> { + if let Self::Jump(_, args) = self { + Some(args) + } else { + None + } + } +} +impl<'a> TryFrom> for ParseLine<'a> { + type Error = as TryFrom>>::Error; + + fn try_from(value: Vec) -> Result { + Ok(Self::Args(value.try_into()?)) + } +} +impl<'a> From<&'a str> for ParseLine<'a> { + fn from(v: &'a str) -> Self { + Self::Label(v.into()) + } +} +impl<'a> From for ParseLine<'a> { + fn from(v: String) -> Self { + Self::Label(v.into()) + } +} +impl<'a> From> for ParseLine<'a> { + fn from(v: Cow<'a, str>) -> Self { + Self::Label(v) + } +} +impl<'a> From> for ParseLine<'a> { + fn from(v: Args<'a>) -> Self { + Self::Args(v) + } +} +impl<'a> Display for ParseLine<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParseLine::Label(lab) => { + f.write_str(&*lab)?; + f.write_str(":") + }, + ParseLine::Jump(target, args) => { + if f.alternate() { f.write_str(" ")? } + f.write_fmt(format_args!("jump {target} {args}")) + }, + ParseLine::Args(args) => { + if f.alternate() { f.write_str(" ")? } + args.fmt(f) + }, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct IdxBox { + pub index: usize, + pub value: T, +} +impl PartialEq for IdxBox { + fn eq(&self, other: &T) -> bool { + **self == *other + } + fn ne(&self, other: &T) -> bool { + **self != *other + } +} +impl Display for IdxBox { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.value.fmt(f) + } +} +impl From<(usize, T)> for IdxBox { + fn from((index, value): (usize, T)) -> Self { + Self::new(index, value) + } +} +impl IdxBox { + pub fn new(index: usize, value: T) -> Self { + Self { index, value } + } + + /// Get line and column + /// + /// - `index >= s.len()` return last char next char location + /// + /// start line and column is 1 + /// + /// newline char is LF (`\n` 0x0A), and next line after the newline char, + /// e.g `("a\n", 1)` location is `(1, 2)` + pub fn location(&self, s: &str) -> (u32, u32) { + if self.index >= s.len() { + let nl = s.chars().filter(|&ch| ch == '\n').count(); + let ch = s.chars() + .rev() + .take_while(|&ch| ch != '\n') + .count(); + return ((nl + 1).try_into().unwrap(), (ch + 1).try_into().unwrap()); + } + let mut cur = 0; + let mut lnum = 1; + for line in s.split_inclusive('\n') { + if cur + line.len() > self.index { + let lidx = self.index - cur; + let col = line + .char_indices() + .filter(|&(i, _)| i <= lidx) + .count(); + return (lnum, col.try_into().unwrap()); + } + cur += line.len(); + lnum += 1; + } + unreachable!() + } + + pub fn as_ref(&self) -> IdxBox<&T> { + let Self { index, value } = self; + IdxBox::new(*index, value) + } + + pub fn as_mut(&mut self) -> IdxBox<&mut T> { + let Self { index, value } = self; + IdxBox::new(*index, value) + } + + pub fn map(self, f: F) -> IdxBox + where F: FnOnce(T) -> U, + { + let Self { index, value } = self; + IdxBox { index, value: f(value) } + } + + pub fn and_then(self, f: F) -> Option> + where F: FnOnce(T) -> Option, + { + let Self { index, value } = self; + Some(IdxBox { index, value: f(value)? }) + } +} +impl Deref for IdxBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} +impl DerefMut for IdxBox { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct ParseLines<'a> { + lines: Vec>>, +} +impl<'a> Display for ParseLines<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut iter = self.iter(); + if let Some(line) = iter.next() { + line.fmt(f)?; + } + for line in iter { + writeln!(f)?; + line.fmt(f)?; + } + Ok(()) + } +} +impl<'a> FromIterator>> for ParseLines<'a> { + fn from_iter>>>(iter: T) -> Self { + Self::new(iter.into_iter().collect()) + } +} +impl<'a> IntoIterator for ParseLines<'a> { + type Item = IdxBox>; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.lines.into_iter() + } +} +impl<'a> ParseLines<'a> { + pub fn new(lines: Vec>>) -> Self { + Self { lines } + } + + pub fn solid_count(&self) -> usize { + self.lines + .iter() + .filter(|line| line.is_solid()) + .count() + } + + pub fn modif_count(&self) -> usize { + self.len() - self.solid_count() + } + + pub fn into_owned(self) -> ParseLines<'static> { + self.into_iter() + .map(|line| line + .map(|line| line.into_owned())) + .collect() + } + + pub fn lines(&self) -> &[IdxBox>] { + &self.lines + } + + pub fn lines_mut(&mut self) -> &mut Vec>> { + &mut self.lines + } + +pub fn index_label_popup(&mut self) { + let mut lines = mem::take(self.lines_mut()); + let poped: HashSet = lines.iter() + .filter_map(|x| x.as_label()) + .filter_map(|x| x.parse().ok()) + .collect(); + let indexs: HashSet = lines.iter() + .filter_map(|x| x.as_jump_idx()) + .collect(); + let pop_idxs = &indexs - &poped; + while Some(true) == lines.last() + .map(|x| x.is_label()) // move last label to first + { + self.lines.push(lines.pop().unwrap()); + } + self.lines.extend(lines.into_iter() + .scan(0, |i, line| { + Some((line.is_solid().then(|| i!(*i++)), line)) + }) + .flat_map(|(i, line)| { + i.and_then(|i| { + pop_idxs.contains(&i).then(|| { + IdxBox::new( + line.index, + ParseLine::Label(i.to_string().into()), + ) + }) + }).into_iter().chain(once(line)) + }) + ); +} +} +impl<'a> From>>> for ParseLines<'a> { + fn from(lines: Vec>>) -> Self { + Self::new(lines) + } +} +impl<'a> From> for Vec>> { + fn from(value: ParseLines<'a>) -> Self { + value.lines + } +} +impl<'a> Deref for ParseLines<'a> { + type Target = Vec>>; + + fn deref(&self) -> &Self::Target { + &self.lines + } +} +impl<'a> DerefMut for ParseLines<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.lines + } +} + +#[cfg(test)] +mod tests { + use crate::TagCodes; + use super::*; + + #[test] + fn newline_test() { + let datas = [ + "", + "\n", + "\r\n", + "#foo\n", + "#foo\r\n", + "# foo\n", + "# foo\r\n", + " # foo\n", + " # foo\r\n", + " # foo\n", + " # foo\r\n", + " \t # foo\n", + " \t # foo\r\n", + " \t # foo\r\n", + "\n ", + "\n ", + " \n ", + " \r\n ", + " \r\n ", + ";", + " ;", + "; ", + " ; ", + " ; ;", + " ; ; ", + "\n\n", + " \n\n", + "\n \n", + "\n\n ", + " \n\n ", + "\n \n ", + " \n \n ", + " #foo\n \n ", + " #foo\n #bar\n ", + " #foo\n #bar\n #baz", + " ; #foo\n #bar\n #baz\n", + ]; + assert!(parser::nl(" ; #foo\n #bar\n #baz\nm").is_err()); + + for src in datas { + assert_eq!(parser::nl(&src), Ok(()), "src: {src}"); + } + } + + #[test] + fn arg_test() { + let datas = [ + "a", + "'", + "a'b", + "ab", + "x", + "A", + "\"\"", + "\"x\"", + "\"foo\"", + "\"f oo\"", + "\"#\"", + "\";\"", + ]; + let fails = [ + " a", + "a ", + "a#", + "a;", + "a;", + "", + "\"", + "\"\n\"", + "\"\"\"", + ";", + " ", + "foo:", + "o:", + ":", + ]; + + for src in datas { + assert_eq!(parser::arg(src), Ok(src)); + } + for src in fails { + assert!(parser::arg(src).is_err()); + } + } + + #[test] + fn label_test() { + let datas = [ + ("foo:", "foo"), + ("x:", "x"), + (":x", ":x"), + (":foo", ":foo"), + (":f:oo", ":f:oo"), + ("fo:o:", "fo:o"), + (":a:", ":a"), + ]; + let fails = [ + ":", + "::", + ":::", + ]; + + for (src, dst) in datas { + assert_eq!(parser::label(src), Ok(dst), "{src}"); + } + for src in fails { + assert!(parser::label(src).is_err()); + } + } + + #[test] + fn all_parse() { + let src = r#" + # find player unit + set id 0 + loop: + lookup unit unit_ty id + restart: + ubind unit_ty + jump skip strictEqual @unit null + set first @unit + jump do_bind_loop_cond always 0 0 + # 这里会将该种单位全部绑定一遍, 直到找到玩家控制的那个 + do_bind_loop: + sensor is_dead first @dead + jump restart notEqual is_dead false # 头单位已经死亡, 我们永远无法到达, 所以我们需要重新开始 + do_bind_loop_cond: + sensor ctrl_type @unit @controlled + jump finded equal ctrl_type @ctrlPlayer # 绑定到的是玩家 + ubind unit_ty + jump do_bind_loop notEqual @unit first + skip: + op add id id 1 + jump loop lessThan id @unitCount + end + finded: + set player @unit + follow_loop: + sensor x player @x + sensor y player @y + sensor name player @name # 如果有, 则可以显示玩家名 + # 取两位小数省的小数部分太长 + op idiv x x 0.01 # 通过整除省去乘再向下取整运算 + op idiv y y 0.01 + op div x x 100 + op div y y 100 + print "player "; print name + print "[]: "; print unit_ty + print " -> "; print x; print ", "; print y + printflush message1 + sensor ctrl_type player @controlled + jump follow_loop equal ctrl_type @ctrlPlayer # 仅在玩家控制期间进行执行, 玩家解除控制重新寻找 + printflush message1 # clear message + "#; + let lines = parser::lines(&src).unwrap(); + for line in lines { + println!("{line:#}"); + } + } + + #[test] + fn line_fmt_test() { + let datas: [(ParseLine, _); 5] = [ + (args!("read").into(), "read"), + (args!["read", "foo"].into(), "read foo"), + (args!["read", "foo", "bar"].into(), "read foo bar"), + (args!["read", "\"foo\"", "bar"].into(), "read \"foo\" bar"), + ("label".into(), "label:"), + ]; + for (src, dst) in datas { + assert_eq!(src.to_string(), format!("{dst}")); + } + } + + #[test] + fn location_eof_test() { + let datas = [ + ("", (1, 1)), + ("a", (1, 2)), + ("a\n", (2, 1)), + ("a\nx", (2, 2)), + ("a\n\n", (3, 1)), + ]; + for (src, loc) in datas { + let box_ = IdxBox::new(src.len(), ()); + assert_eq!(box_.location(src), loc); + } + } + + #[test] + fn location_test() { + let datas = [ + ("", 0, (1, 1)), + ("\n", 0, (1, 1)), + ("\n", 1, (2, 1)), + ("a", 0, (1, 1)), + ("ab", 0, (1, 1)), + ("ab", 1, (1, 2)), + ("a\n", 1, (1, 2)), + ("a\nb", 1, (1, 2)), + ("a\nb", 2, (2, 1)), + ("a\nb", 3, (2, 2)), + ("a\nbc", 3, (2, 2)), + ("a\n\nc", 2, (2, 1)), + ("a\n\nc", 3, (3, 1)), + ("你", 0, (1, 1)), + ("你", 1, (1, 1)), + ("你", 2, (1, 1)), + ("你", 3, (1, 2)), + ("你x", 0, (1, 1)), + ("你x", 1, (1, 1)), + ("你x", 2, (1, 1)), + ("你x", 3, (1, 2)), + ]; + for (src, idx, loc) in datas { + let box_ = IdxBox::new(idx, ()); + assert_eq!(box_.location(src), loc, "{src:?}[{idx}]"); + } + } + + #[test] + fn lines_parse_index_test() { + let s = "a\nb\n c"; + let lines = parser::lines(s).unwrap(); + assert_eq!(lines[0].index, 0); + assert_eq!(lines[1].index, 2); + assert_eq!(lines[2].index, 5); + assert_eq!(lines[0].location(s), (1, 1)); + assert_eq!(lines[1].location(s), (2, 1)); + assert_eq!(lines[2].location(s), (3, 2)); + } + + #[test] + fn jump_target_test() { + let s = r#" + jump -1 x + jump :-1 y + jump 0 z + jump :0 a + :-1 + :0 + end + "#; + let lines = parser::lines(s).unwrap(); + assert!(lines[0].is_args()); + assert!(lines[1].is_jump()); + assert!(lines[2].is_jump()); + assert!(lines[3].is_jump()); + let mut tagcodes = TagCodes::try_from(lines).unwrap(); + tagcodes.build_tagdown().unwrap(); + assert!(tagcodes.lines[0].is_line()); + assert!(tagcodes.lines[1].is_jump()); + assert!(tagcodes.lines[2].is_jump()); + assert!(tagcodes.lines[3].is_jump()); + } + + #[test] + fn jump_target_popup_test() { + let s = r#" + jump 0 always 0 0 + 2: + jump 1 always 0 0 + jump 2 always 0 0 + end + jump 1 always 0 0 + "#; + + let mut lines = parser::lines(&s).unwrap(); + + let inner_lines = lines.iter() + .map(|line| line.value.clone()) + .collect::>(); + assert_eq!(inner_lines, vec![ + ParseLine::Jump("0".into(), args!("always", "0", "0")), + ParseLine::Label("2".into()), + ParseLine::Jump("1".into(), args!("always", "0", "0")), + ParseLine::Jump("2".into(), args!("always", "0", "0")), + ParseLine::Args(args!("end")), + ParseLine::Jump("1".into(), args!("always", "0", "0")), + ]); + + lines.index_label_popup(); + + let inner_lines = lines.iter() + .map(|line| line.value.clone()) + .collect::>(); + assert_eq!(inner_lines, vec![ + ParseLine::Label("0".into()), + ParseLine::Jump("0".into(), args!("always", "0", "0")), + ParseLine::Label("2".into()), + ParseLine::Label("1".into()), + ParseLine::Jump("1".into(), args!("always", "0", "0")), + ParseLine::Jump("2".into(), args!("always", "0", "0")), + ParseLine::Args(args!("end")), + ParseLine::Jump("1".into(), args!("always", "0", "0")), + ]); + } +} diff --git a/tools/utils/src/tcow.rs b/tools/utils/src/tcow.rs new file mode 100644 index 0000000..3d0b596 --- /dev/null +++ b/tools/utils/src/tcow.rs @@ -0,0 +1,132 @@ +use std::{ + borrow::{Borrow, Cow}, + ops::Deref, +}; + +#[derive(Debug, Copy)] +pub enum TCow<'a, B: 'a + ?Sized, O> { + Borrowed(&'a B), + Owned(O), +} +impl<'a, B> TCow<'a, B, B::Owned> +where B: 'a + ?Sized + ToOwned, +{ + pub fn from_cow(cow: Cow<'a, B>) -> Self { + match cow { + Cow::Borrowed(b) => Self::Borrowed(b), + Cow::Owned(owned) => Self::Owned(owned), + } + } + + pub fn into_owned(self) -> B::Owned { + match self { + TCow::Borrowed(b) => b.to_owned(), + TCow::Owned(owned) => owned, + } + } + + pub fn make_mut(&mut self) -> &mut B::Owned { + match self { + TCow::Borrowed(b) => { + *self = Self::Owned(b.to_owned()); + self.make_mut() + }, + TCow::Owned(owned) => owned, + } + } +} +impl<'a, B: 'a + ?Sized, O> From for TCow<'a, B, O> { + fn from(value: O) -> Self { + Self::Owned(value) + } +} +impl<'a, B: 'a + ?Sized, O: Clone> Clone for TCow<'a, B, O> { + fn clone(&self) -> Self { + match self { + Self::Borrowed(b) => Self::Borrowed(b), + Self::Owned(owned) => Self::Owned(owned.clone()), + } + } +} +impl<'a, B, O> std::hash::Hash for TCow<'a, B, O> +where B: 'a + ?Sized + std::hash::Hash, + O: Deref + std::hash::Hash, +{ + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} +impl<'a, B, O> Eq for TCow<'a, B, O> +where B: 'a + Eq + ?Sized, + O: Deref, +{ +} +impl<'a, B, O> PartialEq for TCow<'a, B, O> +where B: 'a + PartialEq + ?Sized, + O: Deref, +{ + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} +impl<'a, B, O> Deref for TCow<'a, B, O> +where B: 'a + ?Sized, + O: Deref, +{ + type Target = B; + + fn deref(&self) -> &Self::Target { + match self { + TCow::Borrowed(b) => *b, + TCow::Owned(owned) => &**owned, + } + } +} +impl<'a, B, O> Borrow for TCow<'a, B, O> +where B: 'a + ?Sized, + O: Borrow, +{ + fn borrow(&self) -> &B { + match self { + TCow::Borrowed(b) => *b, + TCow::Owned(owned) => owned.borrow(), + } + } +} +impl<'a, B, O> AsRef for TCow<'a, B, O> +where B: 'a + ?Sized, + O: AsRef, +{ + fn as_ref(&self) -> &B { + match self { + TCow::Borrowed(b) => *b, + TCow::Owned(owned) => owned.as_ref(), + } + } +} +impl<'a, T, B, O> FromIterator for TCow<'a, B, O> +where B: 'a + ?Sized, + O: FromIterator, +{ + fn from_iter>(iter: I) -> Self { + Self::Owned(iter.into_iter().collect()) + } +} +impl<'a, T, B> Extend for TCow<'a, B, B::Owned> +where B: 'a + ?Sized + ToOwned, + B::Owned: Extend, +{ + fn extend>(&mut self, iter: I) { + self.make_mut() + .extend(iter) + } +} +impl<'a, B, O> ToString for TCow<'a, B, O> +where B: 'a + ?Sized + ToString, + O: Borrow, +{ + fn to_string(&self) -> String { + let borrowed: &B = &self.borrow(); + borrowed.to_string() + } +} diff --git a/tools/var_utils/Cargo.toml b/tools/var_utils/Cargo.toml index 65dde11..2e11177 100644 --- a/tools/var_utils/Cargo.toml +++ b/tools/var_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "var_utils" -version = "0.5.3" +version = "0.5.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/var_utils/src/lib.rs b/tools/var_utils/src/lib.rs index 325aee7..ea57736 100644 --- a/tools/var_utils/src/lib.rs +++ b/tools/var_utils/src/lib.rs @@ -1,3 +1,5 @@ +mod var; + use lazy_regex::{regex, Lazy, Regex}; use std::{ collections::HashSet, @@ -5,6 +7,8 @@ use std::{ thread_local, }; +pub use var::Var; + /// 判断是否是一个标识符(包括数字) pub fn is_ident(s: &str) -> bool { static REGEX: &Lazy = regex!( diff --git a/tools/var_utils/src/var.rs b/tools/var_utils/src/var.rs new file mode 100644 index 0000000..6320910 --- /dev/null +++ b/tools/var_utils/src/var.rs @@ -0,0 +1,130 @@ +use std::{ + borrow::Borrow, + fmt::{self, Display}, + hash::Hash, + mem, + ops::Deref, + rc, +}; + +type Rc = rc::Rc; + +#[derive(Default, Clone)] +pub struct Var { + value: Rc, +} +impl fmt::Debug for Var { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::Debug::fmt(&*self.value, f) + } +} +impl Var { + pub fn new() -> Self { + Self::default() + } + + pub fn to_mut(&mut self) -> &mut String { + Rc::make_mut(&mut self.value) + } + + pub fn into_owned(&mut self) -> String { + mem::take(Rc::make_mut(&mut self.value)) + } + + pub fn as_str(&self) -> &str { + &**self + } +} +impl FromIterator for Var { + fn from_iter>(iter: T) -> Self { + String::from_iter(iter).into() + } +} +impl From<&'_ Var> for Var { + fn from(value: &'_ Var) -> Self { + value.clone() + } +} +impl From for String { + fn from(mut value: Var) -> Self { + value.into_owned() + } +} +impl From for Rc { + fn from(value: Var) -> Self { + value.value + } +} +impl Display for Var { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.value, f) + } +} +impl Hash for Var { + fn hash(&self, state: &mut H) { + self.value.hash(state) + } +} +impl Borrow for Var { + fn borrow(&self) -> &String { + &self.value + } +} +impl Borrow for Var { + fn borrow(&self) -> &str { + &**self + } +} +impl AsRef for Var { + fn as_ref(&self) -> &String { + self.borrow() + } +} +impl AsRef for Var { + fn as_ref(&self) -> &str { + self.borrow() + } +} +impl From for Var { + fn from(value: String) -> Self { + Self { value: value.into() } + } +} +impl From<&'_ String> for Var { + fn from(value: &'_ String) -> Self { + value.as_str().into() + } +} +impl From<&'_ str> for Var { + fn from(value: &'_ str) -> Self { + Self { value: Rc::new(value.into()) } + } +} +impl Deref for Var { + type Target = str; + + fn deref(&self) -> &Self::Target { + &**self.value + } +} +impl Eq for Var { } +impl PartialEq for Var { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} +impl PartialEq for Var { + fn eq(&self, other: &String) -> bool { + &**self == other + } +} +impl PartialEq for Var { + fn eq(&self, other: &str) -> bool { + *self == other + } +} +impl PartialEq<&str> for Var { + fn eq(&self, other: &&str) -> bool { + **self == **other + } +}