From 007aa86a96212832a106b11128399989b151ed31 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 4 Jan 2024 23:33:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E7=B3=BB=E5=88=97?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 8 +- Cargo.toml | 2 +- examples/README.md | 1 + examples/builtin_functions.mdtlbl | 46 ++++++ tools/display_source/Cargo.toml | 2 +- tools/display_source/src/impls.rs | 8 +- tools/parser/tests/Cargo.toml | 2 +- tools/parser/tests/src/lib.rs | 154 ++++++++++++++++++++ tools/syntax/Cargo.toml | 2 +- tools/syntax/src/builtins.rs | 235 ++++++++++++++++++++++++++++++ tools/syntax/src/lib.rs | 50 ++++++- 11 files changed, 497 insertions(+), 13 deletions(-) create mode 100644 examples/builtin_functions.mdtlbl create mode 100644 tools/syntax/src/builtins.rs diff --git a/Cargo.lock b/Cargo.lock index 057ad59..ba8963f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ dependencies = [ [[package]] name = "display_source" -version = "0.3.7" +version = "0.3.8" dependencies = [ "parser", "syntax", @@ -292,7 +292,7 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mindustry_logic_bang_lang" -version = "0.14.5" +version = "0.14.6" dependencies = [ "display_source", "parser", @@ -348,7 +348,7 @@ dependencies = [ [[package]] name = "parser-tests" -version = "0.1.3" +version = "0.1.4" dependencies = [ "parser", "syntax", @@ -525,7 +525,7 @@ dependencies = [ [[package]] name = "syntax" -version = "0.2.2" +version = "0.2.3" dependencies = [ "tag_code", "utils", diff --git a/Cargo.toml b/Cargo.toml index 09a3297..885e289 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mindustry_logic_bang_lang" -version = "0.14.5" +version = "0.14.6" edition = "2021" authors = ["A4-Tacks "] diff --git a/examples/README.md b/examples/README.md index e7d0213..3e98dc0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -34,6 +34,7 @@ > [`dexp_binder.mdtlbl`](./dexp_binder.mdtlbl)
> [`caller.mdtlbl`](./caller.mdtlbl)
> [`match.mdtlbl`](./match.mdtlbl)
+> [`builtin_functions.mdtlbl`](./builtin_functions.mdtlbl)
如果没有列出那请在看完上述后自行观看, 顺序可以参考文件创建顺序. diff --git a/examples/builtin_functions.mdtlbl b/examples/builtin_functions.mdtlbl new file mode 100644 index 0000000..1c183bc --- /dev/null +++ b/examples/builtin_functions.mdtlbl @@ -0,0 +1,46 @@ +#** +* 这是0.14.6添加的新功能, 在初始化时会增加一系列绑定, +* 其中绑定到的值类型为内置的函数. +* +* 以下为这些函数的作用和参数清单 +* +* * `Type[var]`: 获取右侧量的类型 +* * `Stringify[var]`: 将传入的Var全部转换成字符串格式的Var +* * `Concat[a b]`: 将传入的两个字符串连接 +* * `Info[var]`: 以info级日志形式将传入Var输出 +* * `Err[var]`: 以err级日志形式将传入Var输出 +* * `Unbind[var]`: 传入一个值绑定, 返回绑定者 +* * `Const[name value]`: 动态目标的进行一个const, 并泄露到上层 +* * `Binder[name value]`: 传入一个值绑定, 将其被绑定值const给给定名称 +* * `Debug[value]`: 以debug形式将传入值输出到日志 +* * `Exit[code]`: 直接使编译器以给定的退出码值退出 +* * `Status[]`: 获取上一个内置函数的退出代码, 通常情况下, 零代表正常, 非零即异常 +*# + +print Builtin.Type[x]; +print Builtin.Type[2]; +print Builtin.Type[()]; +print Builtin.Type[x.y]; +print Builtin.Type[..]; +print Builtin.Type[$]; +#* >>> +print var +print var +print dexp +print valuebind +print binder +print resulthandle +*# + + +take Builtin.Info["This is a info"]; +take Builtin.Err["This is a err"]; + + +const Name = `X`; +const Value = (m:); +take Builtin.Const[Name Value]; +print X; +#* >>> +print m +*# diff --git a/tools/display_source/Cargo.toml b/tools/display_source/Cargo.toml index 5ab6c44..4463695 100644 --- a/tools/display_source/Cargo.toml +++ b/tools/display_source/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "display_source" -version = "0.3.7" +version = "0.3.8" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/display_source/src/impls.rs b/tools/display_source/src/impls.rs index a5a07b6..8d16fef 100644 --- a/tools/display_source/src/impls.rs +++ b/tools/display_source/src/impls.rs @@ -29,7 +29,13 @@ impl DisplaySource for Value { meta.push("("); cmp.display_source(meta); meta.push(")"); - } + }, + Self::BuiltinFunc(builtin_func) => { + meta.push("(#*"); + meta.push("BuiltinFunc: "); + meta.push(builtin_func.name()); + meta.push("*#)"); + }, } } } diff --git a/tools/parser/tests/Cargo.toml b/tools/parser/tests/Cargo.toml index fa25a2a..4e4d9db 100644 --- a/tools/parser/tests/Cargo.toml +++ b/tools/parser/tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parser-tests" -version = "0.1.3" +version = "0.1.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/parser/tests/src/lib.rs b/tools/parser/tests/src/lib.rs index e77b4e8..3505921 100644 --- a/tools/parser/tests/src/lib.rs +++ b/tools/parser/tests/src/lib.rs @@ -4630,3 +4630,157 @@ fn dexp_expand_binder_test() { ], ); } + +#[test] +fn builtin_func_test() { + let parser = TopLevelParser::new(); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + print Builtin.Type[x]; + "#).unwrap()).compile().unwrap(), + vec![ + "print var", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + print Builtin.Stringify[2]; + print Builtin.Stringify[x]; + print Builtin.Stringify["x"]; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print "2""#, + r#"print "x""#, + r#"print "x""#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + print Builtin.Concat["abc" "def"]; + print Builtin.Status; + print Builtin.Concat["abc" def]; + print Builtin.Status; + "#).unwrap()).compile().unwrap(), + vec![ + r#"print "abcdef""#, + r#"print 0"#, + r#"print __"#, + r#"print 2"#, + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + print Builtin.Type[()]; + print Builtin.Type[`m`]; + "#).unwrap()).compile().unwrap(), + vec![ + "print dexp", + "print var", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const A = (); + const B = `emmm`; + const C = A.B; + const D = $; + const E = ..; + print Builtin.Type[A]; + print Builtin.Type[B]; + print Builtin.Type[C]; + print Builtin.Type[D]; + print Builtin.Type[E]; + "#).unwrap()).compile().unwrap(), + vec![ + "print dexp", + "print var", + "print valuebind", + "print resulthandle", + "print binder", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const A.B = 2; + print Builtin.Type[A.B]; + "#).unwrap()).compile().unwrap(), + vec![ + "print valuebind", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const A = x; + print Builtin.Info[A]; + print Builtin.Info[y]; + print Builtin.Err[A]; + print Builtin.Err[y]; + "#).unwrap()).compile().unwrap(), + vec![ + "print x", + "print y", + "print x", + "print y", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const A = x.y; + print Builtin.Unbind[A]; + print Builtin.Unbind[x.y]; + "#).unwrap()).compile().unwrap(), + vec![ + "print y", + "print y", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + { + const Name = `X`; + const Value = (h:); + take Builtin.Const[Name Value]; + print X; + } + + { + { # 击穿 + const Name = `Y`; + const Value = (i:); + match Name Value { @ {} } + take Builtin.Const; + } + print Y; + } + "#).unwrap()).compile().unwrap(), + vec![ + "print h", + "print i", + ], + ); + + assert_eq!( + CompileMeta::new().compile(parse!(parser, r#" + const Value = (x:); + const Binded = Value.x; + print Builtin.Type[Binded]; + take Builtin.Binder[Res Binded]; + print Builtin.Type[Res]; + print Res; + "#).unwrap()).compile().unwrap(), + vec![ + "print valuebind", + "print dexp", + "print x", + ], + ); +} diff --git a/tools/syntax/Cargo.toml b/tools/syntax/Cargo.toml index 5d81ebc..1a29daa 100644 --- a/tools/syntax/Cargo.toml +++ b/tools/syntax/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "syntax" -version = "0.2.2" +version = "0.2.3" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/syntax/src/builtins.rs b/tools/syntax/src/builtins.rs new file mode 100644 index 0000000..15ed835 --- /dev/null +++ b/tools/syntax/src/builtins.rs @@ -0,0 +1,235 @@ +use std::process; + +use crate::*; + +#[derive(Debug, PartialEq, Clone)] +pub struct BuiltinFunc { + name: &'static str, + func: fn(&Self, &mut CompileMeta) -> Var, +} + +impl BuiltinFunc { + pub fn name(&self) -> &str { + self.name + } + + pub fn func(&self) -> fn(&Self, &mut CompileMeta) -> Var { + self.func + } + + pub fn call(&self, meta: &mut CompileMeta) -> Var { + self.func()(self, meta) + } +} + +macro_rules! mutil { + (@ignore($($i:tt)*) $($t:tt)*) => { + $($t)* + }; + (@if($($t:tt)*) $($f:tt)*) => { + $($t)* + }; + (@if $($f:tt)*) => { + $($f)* + }; +} + +macro_rules! build_builtin_funcs { + { + $( + $(#[$attr:meta])* + fn $func_name:ident : $vfunc_name:ident + ($meta:ident) $([$($var:ident $(: $taked_var:ident)?)*])? + {$($t:tt)*} + )* + } => { + $( + $(#[$attr])* + fn $func_name(this: &BuiltinFunc, $meta: &mut CompileMeta) -> Var + { + fn f($meta: &mut CompileMeta) + -> Result { + $( + let args = $meta.get_env_args(); + let [$($var),*] = args else { + return Err((2, format!( + "args count error: argc := {}", + args.len() + ))); + }; + $($( + let $taked_var = $meta.get_const_value($var) + .unwrap(); + )?)* + )? + $($t)* + } + match f($meta) { + Ok(var) => { + $meta.set_last_builtin_exit_code(0); + var + }, + Err((code, e)) => { + $meta.log_err(format_args!( + "\ + Builtin Function Error:\n\ + name: {}, argc: {}\n\ + exit_code: {code}\n\ + msg: {}\ + ", + this.name(), + 0$($(+mutil!(@ignore($var) 1))*)?, + e, + )); + $meta.set_last_builtin_exit_code(code); + "__".into() + }, + } + } + )* + vec![$( + BuiltinFunc { + name: stringify!($vfunc_name), + func: $func_name, + } + ),*] + }; +} + +pub fn build_builtins() -> Vec { + fn value_type(value: &Value) -> &'static str { + match value { + Value::Var(_) => "var", + Value::DExp(_) => "dexp", + Value::ReprVar(_) => "reprvar", + Value::ResultHandle => "resulthandle", + Value::ValueBind(_) => "valuebind", + Value::Cmper(_) => "cmper", + Value::Binder => "binder", + Value::BuiltinFunc(_) => "builtinfunc", + } + } + macro_rules! check_type { + ($type:literal $pat:pat = $value:expr => $body:expr) => {{ + let value = $value; + if let $pat = value { + $body + } else { + return Err((1, format!( + "value type error:\n\ + expected: {}\n\ + found: {}\ + ", + $type, + value_type(value), + ))) + } + }}; + } + build_builtin_funcs! { + fn r#type:Type(meta) [var:data] { + Ok(value_type(data.value()).into()) + } + + fn stringify:Stringify(meta) [var:data] { + check_type!("var" Value::Var(var) = data.value() => { + Ok(if Value::is_string(var) { + var.into() + } else { + format!("\"{var}\"") + }) + }) + } + + fn status:Status(meta) [] { + Ok(meta.last_builtin_exit_code().to_string()) + } + + fn concat:Concat(meta) [va:a vb:b] { + check_type!("var" Value::Var(a) = a.value() => { + check_type!("var" Value::Var(b) = b.value() => { + if !Value::is_string(a) { + return Err((2, format!("{a} is not a string"))); + } + if !Value::is_string(b) { + return Err((2, format!("{b} is not a string"))); + } + Ok([&a[..a.len()-1], &b[1..]].concat()) + }) + }) + } + + fn info:Info(meta) [var:data] { + let value = data.value(); + check_type!("var" Value::Var(var) = value => { + let msg = String::from(var); + meta.log_info(msg.clone()); + Ok(msg) + }) + } + + fn err:Err(meta) [var:data] { + let value = data.value(); + check_type!("var" Value::Var(var) = value => { + let msg = String::from(var); + meta.log_err(msg.clone()); + Ok(msg) + }) + } + + fn unbind:Unbind(meta) [var:data] { + let value = data.value(); + check_type!("valuebind" Value::ValueBind(ValueBind(_, bind)) = value => { + Ok(bind.into()) + }) + } + + /// 动态名称的const + /// + /// 向上层泄露结果, 所以调用时距离目标层必须有且只有一个expand + /// 否则会击穿或者达不到目标层 + /// + /// 建议直接使用quick_dexp_take + fn r#const:Const(meta) [n:name v:value] { + check_type!("var" Value::Var(name) = name.value() => { + let name: String = name.into(); + Const(name.clone().into(), value.value().clone(), vec![]).compile(meta); + meta.add_const_value_leak(name); + Ok("__".into()) + }) + } + + fn binder:Binder(meta) [n:name v:value] { + check_type!("var" Value::Var(name) = name.value() => { + check_type!("valuebind" Value::ValueBind(ValueBind(binder, _)) = value.value() => { + let name: String = name.into(); + Const(name.clone().into(), binder.as_ref().clone(), vec![]) + .compile(meta); + meta.add_const_value_leak(name); + Ok("__".into()) + }) + }) + } + + fn exit:Exit(meta) [n:code] { + check_type!("var" Value::Var(code) = code.value() => { + let num_code = match code.parse() { + Ok(code) => code, + Err(e) => { + meta.log_err(format!( + "Invalid exit code: {code},\nerr: {e}", + )); + 128 + }, + }; + process::exit(num_code) + }) + } + + /// 以Debug形式显示一个值 + fn debug:Debug(meta) [v:value] { + meta.log_info(format!("Value Debug:\n{:#?}", value)); + Ok("__".into()) + } + } +} diff --git a/tools/syntax/src/lib.rs b/tools/syntax/src/lib.rs index ce01bda..a7bef6b 100644 --- a/tools/syntax/src/lib.rs +++ b/tools/syntax/src/lib.rs @@ -1,3 +1,5 @@ +mod builtins; + use std::{ num::ParseIntError, collections::{HashMap, HashSet}, @@ -10,6 +12,7 @@ use std::{ fmt::{Display, Debug}, convert::identity, borrow::Borrow, hash::Hash, }; +use builtins::{BuiltinFunc, build_builtins}; use tag_code::{ Jump, TagCodes, @@ -158,6 +161,7 @@ pub enum Value { Cmper(Box), /// 本层应该指向的绑定者, 也就是ValueBind的被绑定的值 Binder, + BuiltinFunc(BuiltinFunc), } impl Value { pub fn try_eval_const_num_to_var(&self, meta: &CompileMeta) -> Option { @@ -211,6 +215,7 @@ impl TakeHandle for Value { ); exit(6); } + Self::BuiltinFunc(func) => func.call(meta), } } } @@ -391,7 +396,7 @@ impl Value { Value::ValueBind(ValueBind(..)) => None, // NOTE: 这不能实现, 否则可能牵扯一些不希望的作用域问题 Value::ResultHandle => None, - Value::DExp(_) | Value::Cmper(_) => None, + Value::BuiltinFunc(_) | Value::DExp(_) | Value::Cmper(_) => None, } } } @@ -400,6 +405,7 @@ impl_enum_froms!(impl From for Value { Var => &str; DExp => DExp; ValueBind => ValueBind; + BuiltinFunc => BuiltinFunc; }); /// 带返回值的表达式 @@ -1094,6 +1100,7 @@ impl CmpTree { => Some(meta.get_dexp_expand_binder().map(|s| &**s).unwrap_or("__")), | V::ValueBind(_) | V::Cmper(_) + | V::BuiltinFunc(_) => None, } } @@ -1916,7 +1923,6 @@ pub enum ConstKey { Var(Var), ValueBind(ValueBind), } - impl ConstKey { /// Returns `true` if the const key is [`Var`]. /// @@ -1968,6 +1974,7 @@ impl TakeHandle for ConstKey { } impl_enum_froms!(impl From for ConstKey { Var => &str; + Var => &String; Var => Var; ValueBind => ValueBind; }); @@ -2689,6 +2696,7 @@ pub struct CompileMeta { value_binds: HashMap<(Var, Var), Var>, /// 值绑定全局常量表, 只有值绑定在使用它 value_bind_global_consts: HashMap, + last_builtin_exit_code: u8, } impl Debug for CompileMeta { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -2724,7 +2732,7 @@ impl CompileMeta { } pub fn with_tag_codes(tag_codes: TagCodes) -> Self { - Self { + let mut meta = Self { tags_map: HashMap::new(), tag_count: 0, tag_codes, @@ -2737,7 +2745,23 @@ impl CompileMeta { const_expand_tag_name_map: Vec::new(), value_binds: HashMap::new(), value_bind_global_consts: HashMap::new(), + last_builtin_exit_code: 0, + }; + let builtin = String::from("Builtin"); + for builtin_func in build_builtins() { + let handle = format!("__{builtin}__{}", builtin_func.name()); + let key = (builtin.clone(), builtin_func.name().into()); + meta.value_binds.insert(key, handle); + meta.add_const_value(Const( + ConstKey::ValueBind(ValueBind( + Box::new(builtin.clone().into()), + builtin_func.name().into() + )), + builtin_func.into(), + vec![], + )); } + meta } /// 获取一个标记的编号, 如果不存在则将其插入并返回新分配的编号. @@ -2851,7 +2875,7 @@ impl CompileMeta { // insert to prev block this.expand_env .last_mut() - .unwrap() + .expect("常量泄露击穿") .consts .insert(leak_const_name, value); } @@ -3096,6 +3120,24 @@ impl CompileMeta { let _ = f(self); self.env_args.pop().unwrap() } + + pub fn log_info(&mut self, s: impl std::fmt::Display) { + eprintln!("\x1b[1m[I] {}\x1b[0m", s.to_string() + .trim_end().replace('\n', "\n ")) + } + + pub fn log_err(&mut self, s: impl std::fmt::Display) { + eprintln!("\x1b[1;91m[E] {}\x1b[0m", s.to_string() + .trim_end().replace('\n', "\n ")) + } + + pub fn last_builtin_exit_code(&self) -> u8 { + self.last_builtin_exit_code + } + + pub fn set_last_builtin_exit_code(&mut self, new_code: u8) -> u8 { + replace(&mut self.last_builtin_exit_code, new_code) + } } pub fn line_first_add(lines: &mut Vec, insert: &str) {