Skip to content

Commit

Permalink
feat(解析器): 🎨 2.5.0——更便捷的「字串解析器快捷方式宏注册方法」;优化测试
Browse files Browse the repository at this point in the history
1. 现在可使用`@register_parser_string_flag`宏自动添加`nse"XXX"flag`
2. 删除了「原生对象解析式」的字符串解析快捷方式
3. 优化测试集,使之覆盖更多代码
  • Loading branch information
ARCJ137442 committed Aug 27, 2023
1 parent 280fac7 commit 9321cdd
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 0 additions & 9 deletions src/Conversion/core/native.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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对象
Expand Down
2 changes: 1 addition & 1 deletion src/Conversion/core/string.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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( # 自动反转字典: 标点 => 类型
Expand Down
8 changes: 4 additions & 4 deletions src/Conversion/core/string/definitions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
55 changes: 55 additions & 0 deletions src/Conversion/template.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

"""
陈述转换器的抽象类型模板
Expand Down Expand Up @@ -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
81 changes: 44 additions & 37 deletions src/Util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"([^.{}, ]+\.)+" => ""

"""
获取「纯粹的类名」
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
)

"""
宏的等价函数
用于自动
Expand All @@ -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

"""
Expand Down
2 changes: 1 addition & 1 deletion test/commons.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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!()
Expand Down
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions test/test_narsese.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ A,B,C,D,E = "A B C D E" |> split .|> String .|> Symbol .|> Word
@assert (((AB, BC, CD), (AB, BC, CD))) == (((AB, BC, CD), (AB, BC, CD)))

@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")
Expand Down
70 changes: 70 additions & 0 deletions test/test_util.jl
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 9321cdd

Please sign in to comment.