From 9321cdda8f8b02831934e30dba73010709f3985e Mon Sep 17 00:00:00 2001 From: ARCJ137442 <61109168+ARCJ137442@users.noreply.github.com> Date: Sun, 27 Aug 2023 15:01:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=A7=A3=E6=9E=90=E5=99=A8):=20:art:=202.?= =?UTF-8?q?5.0=E2=80=94=E2=80=94=E6=9B=B4=E4=BE=BF=E6=8D=B7=E7=9A=84?= =?UTF-8?q?=E3=80=8C=E5=AD=97=E4=B8=B2=E8=A7=A3=E6=9E=90=E5=99=A8=E5=BF=AB?= =?UTF-8?q?=E6=8D=B7=E6=96=B9=E5=BC=8F=E5=AE=8F=E6=B3=A8=E5=86=8C=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E3=80=8D=EF=BC=9B=E4=BC=98=E5=8C=96=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 现在可使用`@register_parser_string_flag`宏自动添加`nse"XXX"flag` 2. 删除了「原生对象解析式」的字符串解析快捷方式 3. 优化测试集,使之覆盖更多代码 --- Project.toml | 2 +- src/Conversion/core/native.jl | 9 --- src/Conversion/core/string.jl | 2 +- src/Conversion/core/string/definitions.jl | 8 +-- src/Conversion/template.jl | 55 +++++++++++++++ src/Util.jl | 81 ++++++++++++----------- test/commons.jl | 2 +- test/runtests.jl | 1 + test/test_narsese.jl | 12 ++++ test/test_util.jl | 70 ++++++++++++++++++++ 10 files changed, 189 insertions(+), 53 deletions(-) create mode 100644 test/test_util.jl diff --git a/Project.toml b/Project.toml index f28b390..beea75b 100644 --- a/Project.toml +++ b/Project.toml @@ -3,7 +3,7 @@ uuid = "11330a76-bea1-45e0-8f80-7114e2f607b1" authors = ["ARCJ137442 <61109168+ARCJ137442@users.noreply.github.com>"] git-tree-sha1 = "308b5830bd3f1eb7f6bde386a3a23b3ce8b3365e" repo-url = "https://github.com/ARCJ137442/JuNarsese.jl" -version = "2.4.0" +version = "2.5.0" [deps] Reexport = "189a3867-3050-52da-a836-e630ba90ab69" diff --git a/src/Conversion/core/native.jl b/src/Conversion/core/native.jl index bc64135..0f8cf93 100644 --- a/src/Conversion/core/native.jl +++ b/src/Conversion/core/native.jl @@ -75,15 +75,6 @@ const NativeParser_vector::TNativeParser = NativeParser{Vector} "用于识别「作为字符串保存的数值」的前导标签" const NUMBER_PREFLAG::String = "_NUM_" -"重载「字符串宏の快捷方式」:native" -Conversion.get_parser_from_flag(::Val{:native})::TAbstractParser = NativeParser - -"重载「字符串宏の快捷方式」:native_vector" -Conversion.get_parser_from_flag(::Val{:native_vector})::TAbstractParser = NativeParser_vector - -"重载「字符串宏の快捷方式」:native_dict" -Conversion.get_parser_from_flag(::Val{:native_dict})::TAbstractParser = NativeParser_dict - """ 定义「Native转换」的「目标类型」 - 原生对象↔Narsese对象 diff --git a/src/Conversion/core/string.jl b/src/Conversion/core/string.jl index 3410729..dec2740 100644 --- a/src/Conversion/core/string.jl +++ b/src/Conversion/core/string.jl @@ -179,7 +179,7 @@ struct StringParser{Content} <: AbstractParser where {Content <: CONTENT} for copula in copulas startswith(s, copula) && return copula end - return empty(Content) # 【20230809 10:55:18】默认返回空文本(详见Util.jl扩展的方法) + return empty_content(Content) # 【20230809 10:55:18】默认返回空文本(详见Util.jl扩展的方法) end, tense_dict, Dict( # 自动反转字典: 标点 => 类型 diff --git a/src/Conversion/core/string/definitions.jl b/src/Conversion/core/string/definitions.jl index 8dabd6a..f0cd55f 100644 --- a/src/Conversion/core/string/definitions.jl +++ b/src/Conversion/core/string/definitions.jl @@ -280,18 +280,18 @@ const PRESET_STRING_PARSERS::Dict{Symbol,StringParser} = Dict( begin "字符串宏解析支持" "空字串⇒ASCII" - Conversion.get_parser_from_flag(::Val{Symbol()})::TAbstractParser = StringParser_ascii + @register_parser_string_flag Symbol() => StringParser_ascii # 遍历所有解析器,批量增加解析支持(如:StringParser_han ⇒ `han`) for (symbol::Symbol, parser::StringParser) in PRESET_STRING_PARSERS - Conversion.get_parser_from_flag(::Val{symbol})::TAbstractParser = parser + @register_parser_string_flag symbol => parser end # 漢文扩展的两个别名 ":汉" - Conversion.get_parser_from_flag(::Val{:汉})::TAbstractParser = StringParser_han + @register_parser_string_flag :汉 => StringParser_han ":漢" - Conversion.get_parser_from_flag(::Val{:漢})::TAbstractParser = StringParser_han + @register_parser_string_flag :漢 => StringParser_han end diff --git a/src/Conversion/template.jl b/src/Conversion/template.jl index be366c0..419e1b1 100644 --- a/src/Conversion/template.jl +++ b/src/Conversion/template.jl @@ -21,6 +21,7 @@ export DEFAULT_PARSE_TARGETS, TYPE_TERMS, TYPE_SENTENCES # API对接 export parse_target_types # API对接 export parse_type, pack_type_string, pack_type_symbol # API对接 export @narsese_str, @nse_str # 字符串宏(📌短缩写只要不盲目using就没有冲突问题) +export @register_parser_string_flag """ 陈述转换器的抽象类型模板 @@ -303,3 +304,57 @@ Expr(:(=), Symbol("@nse_str"), Symbol("@narsese_str")) |> eval - 默认⇒报错 """ get_parser_from_flag(::Val)::TAbstractParser = error("未定义的解析器符号!") + +""" +`@register_parser_string_flag`的表达式处理方法 +""" +function register_parser_string_flag_macro(expr::Expr) + + # 这两个「symbol」可能是封装Symbol字面量的QuoteNode,也可能是指定其它变量的变量名或其它待计算的表达式 + local flag_symbol, parser_symbol + + # 若只有一个 + if expr.head == :call + flag_symbol, parser_symbol = expr.args[2:end] + return :(Conversion.get_parser_from_flag(::Val{$flag_symbol})::TAbstractParser = $parser_symbol) + end + + # 若有多个:创建代码块 + blk::Expr = Expr(:block) + + for arg in expr.args + if arg isa Expr + flag_symbol, parser_symbol = arg.args[2:end] # 第一个是「=>」 + push!( + blk.args, + :(Conversion.get_parser_from_flag(::Val{$flag_symbol})::TAbstractParser = $parser_symbol) + ) + end + end + + return blk + +end + +""" +自动注册解析器在字符串上的flag +## 例 + +运行如下代码: +```julia +@register_parser_string_flag [ + :s_expr => SExprParser + :pika => PikaParser_alpha +] +``` + +此将扩展JuNarsese的字符串宏,使如下代码可用: + +```julia +nse"(Word A)"s_expr # 使用S-表达式解析器翻译成词语「A」 +nse"A"pika # 使用Pika解析器α翻译成词语「A」 +``` +""" +macro register_parser_string_flag(expr::Expr) + return register_parser_string_flag_macro(expr) |> esc +end diff --git a/src/Util.jl b/src/Util.jl index 8afb591..eb11f7c 100644 --- a/src/Util.jl +++ b/src/Util.jl @@ -5,7 +5,9 @@ module Util export UNothing export @reverse_dict_content, @redirect_SRS, @expectedError -export match_first, match_first_view, allproperties, allproperties_generator +export match_first, match_first_view +export allproperties, allproperties_generator, allproperties_named, allproperties_named_generator +export empty_content export get_pure_type_string, get_pure_type_symbol, verify_type_expr, assert_type_expr export @generate_ifelseif, @rand @@ -105,28 +107,46 @@ allproperties_generator(object::Any) = ( if isdefined(object, name) ) +""" +同`allproperties_generator`:获取对象的所有属性,并返回包含其所有`属性名=>属性值`的生成器 +""" +allproperties_named_generator(object::Any) = ( + name => getproperty(object, name) + for name::Symbol in propertynames(object) + if isdefined(object, name) +) + +""" +同`allproperties`:获取对象的所有属性,并返回形式为`属性名=属性值`的属性具名元组 +""" +allproperties_named(object::Any) = NamedTuple( + allproperties_named_generator(object) +) + raw""" -扩展字符串、字符、正则表达式、符号的empty方法 +字符串、字符、正则表达式、符号的`empty_content`方法 - ⚠【20230809 10:44:39】注意:实际上Char无「空字符」一说, - 为兼容起见,使用「\u200c」零宽无连接符作占位符 - 返回空字串,「空字符」(\u200c)、「空正则」 【20230815 16:19:31】现在加上括号,便可类型注释✅ - 参考链接:https://github.com/JuliaLang/julia/issues/21847#issuecomment-301263779 + +【20230827 13:55:39】为避免类型盗版,现使用独立的函数,而非扩展`Base`包 """ -(Base.empty(::Union{T, Type{T}})::AbstractString) where {T <: AbstractString} = +(empty_content(::Union{T, Type{T}})::AbstractString) where {T <: AbstractString} = "" -(Base.empty(::Union{T, Type{T}})::AbstractChar) where {T <: AbstractChar} = +(empty_content(::Union{T, Type{T}})::AbstractChar) where {T <: AbstractChar} = '\u200c' -(Base.empty(::Union{T, Type{T}})::Regex) where {T <: Regex} = +(empty_content(::Union{T, Type{T}})::Regex) where {T <: Regex} = r"" -(Base.empty(::Union{T, Type{T}})::Symbol) where {T <: Symbol} = +(empty_content(::Union{T, Type{T}})::Symbol) where {T <: Symbol} = Symbol() """ 删除「父模块路径」的正则替换对 """ -PURE_TYPE_NAME_REGEX::Pair{Regex, String} = r"([^.{}, ]+\.)+" => "" +const PURE_TYPE_NAME_REGEX::Pair{Regex, String} = r"([^.{}, ]+\.)+" => "" """ 获取「纯粹的类名」 @@ -179,20 +199,15 @@ assert_type_expr(symbol::Symbol)::Symbol = symbol 参数: - 元组:(条件, 内容) """ -function generate_ifelseif_macro(exprs::Vararg{Pair}) - return generate_ifelseif_macro(nothing, exprs...) -end - -"+默认情况" -function generate_ifelseif_macro(default, exprs::Vararg{Pair}) - blk::Expr = Expr(:block) - return generate_ifelseif_macro!(blk, default, exprs...) +function generate_ifelseif_macro(exprs::Vararg{Pair}; default=nothing) + return generate_ifelseif_macro!(Expr(:block), exprs...; default) end """ 基于已有的:block表达式,附带默认情况 +- 使用`nothing`开关默认情况 """ -function generate_ifelseif_macro!(parent::Expr, default, exprs::Vararg{Pair}) +function generate_ifelseif_macro!(parent::Expr, exprs::Vararg{Pair}; default=nothing) current_args::Vector = parent.args is_first::Bool = true @@ -220,32 +235,24 @@ function generate_ifelseif_macro!(parent::Expr, default, exprs::Vararg{Pair}) return parent end -""" -基于已有的:block表达式 -""" -function generate_ifelseif_macro!(parent::Expr, exprs::Vararg{Pair}) - generate_ifelseif_macro!(parent, nothing, exprs...) -end - """ 宏の形式 注意:传入的每个Pair表达式都是`Expr(:call, :(=>), 前, 后)`的形式 + +格式: + """ macro generate_ifelseif(default, exprs::Vararg{Expr}) # 直接获取第二、第三个参数 return generate_ifelseif_macro( - default, ( expr.args[2] => expr.args[3] for expr in exprs - )... + )...; + default ) |> esc end -d = Dict( - 1 => 1, 2 => 2, 3 => 3 -) - """ 宏的等价函数 用于自动 @@ -272,18 +279,18 @@ function rand_macro(exprs...)::Union{Symbol, Expr} rand_variable::Symbol = Symbol(":rand_n:") # 构造代码块 - blk::Expr = Expr( - :block, - :(local $rand_variable = rand(1:$n)) - ) - - return generate_ifelseif_expressions!( - blk, + blk::Expr = generate_ifelseif_macro( ( :($rand_variable == $i) => expr for (i, expr) in enumerate(exprs) - )... + )...; + default=nothing ) + + # 在最前方插入随机数代码,以便复用`generate_ifelseif_macro` + pushfirst!(blk.args, :(local $rand_variable = rand(1:$n))) + + return blk end """ diff --git a/test/commons.jl b/test/commons.jl index bbc5d00..2a2bd4e 100644 --- a/test/commons.jl +++ b/test/commons.jl @@ -4,7 +4,7 @@ if !(@isdefined JuNarsese) # 自动导入JuNarsese模块 using JuNarsese - using JuNarsese.Util # 【20230821 22:20:42】现在不用特别using其中的「Util」 + using JuNarsese.Util # 【20230827 13:47:27】使用Util模块是需要使用其中的「预期报错」`@expectedError`宏 # 启用「严格模式」 JuNarsese.Narsese.use_strict!() diff --git a/test/runtests.jl b/test/runtests.jl index 6081d79..bb0fe33 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,7 @@ push!(LOAD_PATH, "src") # 用于VSCode调试(项目根目录起) include("commons.jl") # 已在此中导入JuNarsese @testset "JuNarsese" begin + include("test_util.jl") include("test_narsese.jl") include("test_conversion.jl") end diff --git a/test/test_narsese.jl b/test/test_narsese.jl index 9cc50d4..8ddcaa4 100644 --- a/test/test_narsese.jl +++ b/test/test_narsese.jl @@ -4,6 +4,18 @@ A,B,C,D,E = "A B C D E" |> split .|> String .|> Symbol .|> Word @assert (∨(⩜(A→B, B→C, C→D), ⩚(A→B, B→C, C→D))) == (∨(⩜(A→B, B→C, C→D), ⩚(A→B, B→C, C→D))) @testset "Narsese" begin + + # 配置类参数 # + + # 默认精度数值解析 + @test Narsese.parse_default_float("12.2") isa Narsese.DEFAULT_FLOAT_PRECISION + @test Narsese.parse_default_float("12") == 12.0 + + @test Narsese.parse_default_int("42") isa Narsese.DEFAULT_INT_PRECISION + @test Narsese.parse_default_int("-1") == -1 + + @test Narsese.parse_default_uint("42") isa Narsese.DEFAULT_UINT_PRECISION + @test Narsese.parse_default_uint("0x1") == 0x0000000000000001 # 构造 # include("test_construction.jl") diff --git a/test/test_util.jl b/test/test_util.jl new file mode 100644 index 0000000..58962e7 --- /dev/null +++ b/test/test_util.jl @@ -0,0 +1,70 @@ +if !(@isdefined JuNarsese) + push!(LOAD_PATH, "../src") # 用于直接打开(..上一级目录) + push!(LOAD_PATH, "src") # 用于VSCode调试(项目根目录起) + + # 自动导入JuNarsese模块 + using JuNarsese +end + +# 测试Util库 # +using JuNarsese.Util +import JuNarsese.Util: @expectedError, check_tuple_equal, number_value_eq + +(@isdefined Test) || using Test + +@testset "Util" begin + + # Pair的属性:first、second + @test allproperties(1=>2) == (1, 2) + @test allproperties_named(1=>2) == (first=1, second=2) + + # 空字符/空字串/空符号 + @test empty_content("string") == empty_content(String) == "" + + # 类名提纯检验 + @test replace("JuNarsese.Narsese.Terms.Word", Util.PURE_TYPE_NAME_REGEX) == get_pure_type_string(Word) == "Word" + @test get_pure_type_string(w"word") == get_pure_type_string(JuNarsese.Narsese.Terms.Word) == "Word" + @test get_pure_type_symbol(Word) == :Word + @test verify_type_expr(:Word) + @test verify_type_expr(:(TermImage{Extension})) + @test !verify_type_expr(:(JuNarsese.Narsese.Terms.Word)) + @test assert_type_expr(:Word) == :Word + @test assert_type_expr(:(TermImage{Extension})) == :(TermImage{Extension}) + @expectedError assert_type_expr(:(JuNarsese.Narsese.Terms.Word)) + + # 随机选择宏 + i = @rand [ + 1 + 2 + 3 + ] + @test i in [1,2,3] + + @test ( + @generate_ifelseif( + nothing, + i==1 => 2, + i==2 => 3, + i==3 => 4, + ) + ) == i+1 + + # 判等 + @test check_tuple_equal( + (w"A", 2), + (w"A", 2); + is_commutative = false, + ) + @test check_tuple_equal( + (w"A", 3.0, 2), + (2, w"A", 3.0); + is_commutative = true, + ) + + @test number_value_eq(2.1, 2.1) + @test number_value_eq(Float16(2.1), Float16(2.1)) + @test !number_value_eq(Float16(2.1), 2.1) # "异类转换精度" 在`Narsese.jl`中实现,单用此方法时无效(转换成「共同默认精度」) + @test !number_value_eq(2.1, 2.0) + @test number_value_eq(2, 2.0) + +end