create basic modification of arranger
Some checks are pending
CI / Julia 1.6 - ubuntu-latest - x64 (push) Waiting to run
CI / Julia 1.7 - ubuntu-latest - x64 (push) Waiting to run
CI / Julia pre - ubuntu-latest - x64 (push) Waiting to run

This commit is contained in:
Tan, Kian-ting 2025-01-30 00:39:34 +08:00
parent eb3f1967aa
commit 27d66b0e51
7 changed files with 248 additions and 59 deletions

View file

@ -1,8 +1,7 @@
{@lang|en} {@lang|en}
{@def|@font|{@quote|FreeSans|AR PL UKai TW}} {@def|@font|{@quote|FreeSans|AR PL UKai TW}}
{@def|@fontsize|12} {@def|@fontsize|12}
therapy of communication and pronounciation123%comment {@def|@linewidth|200} %in px%
{@def|@spacing|{@hglue|{@ex|1}|2}}
anotherline%{@set|@fontsize|10} processing {@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}}
11111 222 3333333 444444 555555 666666 77777 88888 99999 000000 11111 22222 33333 55555
of the problem

11
example/ex2.ug Normal file
View file

@ -0,0 +1,11 @@
{@lang|en}
{@def|@font|{@quote|FreeSans|AR PL UKai TW}}
{@def|@fontsize|12}
{@def|@linewidth|300} %in px%
{@def|@spacing|{@hglue|{@ex|1}|2}}
{@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}}
therapy of communication and pronounciation123%comment
anotherline%{@set|@fontsize|10} processing of
of the problem

37
src/arrange.jl Normal file
View file

@ -0,0 +1,37 @@
module Arrange
using Match
u = Main.uahgi
# the cost of make ith to jth char a line
function cost(items, i, j, linewidth)
slice = items[i:j]
cost_list = map(x -> item_cost(x), slice)
sum_of_cost_list = reduce((x, y)-> x+y, cost_list)
if sum_of_cost_list > linewidth
return Inf
else
return linewidth - sum_of_cost_list
end
end
function item_cost(i)
if typeof(i) == u.HGlue
return i.wd
elseif typeof(i) == u.ChBox
return i.wd
else
return 0
end
end
function arrange(vbox, env)
result_vbox_inner = []
linewidth = parse(Int, env["linewidth"])
print(cost(vbox.eles[1].eles, 1, 20, linewidth))
return vbox
end
end

View file

@ -8,6 +8,12 @@ using .PDFOperating
u = Main.uahgi u = Main.uahgi
c = Main.uahgi.Parsing.Passes.Classes c = Main.uahgi.Parsing.Passes.Classes
@enum PutChar begin
true_ = 0
false_ = 1
gen_chbox_ = 2
end
function interp_main(ast, env, res_box) function interp_main(ast, env, res_box)
ast_inner = ast.val ast_inner = ast.val
val = nothing val = nothing
@ -22,17 +28,33 @@ interp: the intepreter of the uahgi.
- ast: element or part of the ast - ast: element or part of the ast
- env: the variable storaging environment - env: the variable storaging environment
- res_box: the generated result box containing the content\ - res_box: the generated result box containing the content\
- put_char(bool. value): if the character should be put into the res_box - put_char(PutChar): if the character should be put into the res_box.
""" """
function interp(ast, env, res_box, put_char=true) function interp(ast, env, res_box, put_char=true_)
#println("INTERP", ast) #println("INTERP", ast)
@match ast begin @match ast begin
c.SEQ([c.ELE([c.ID("def")]), c.SEQ([c.ELE([c.ID("def")]),
c.ELE([c.ID(id)]),val]) => c.ELE([c.ID(id)]),val]) =>
begin begin
(val_evaled, env, res_box) = interp(val, env, res_box, false) (val_evaled, env, res_box) = interp(val, env, res_box, false_)
#println("ID~~~", id, " VAL~~~", val_evaled) #println("ID~~~", id, " VAL~~~", val_evaled)
if !haskey(env, id)
env[id] = val_evaled env[id] = val_evaled
else
throw("the variable $id has been defined.")
end
return (val_evaled, env, res_box)
end
c.SEQ([c.ELE([c.ID("set")]),
c.ELE([c.ID(id)]),val]) =>
begin
(val_evaled, env, res_box) = interp(val, env, res_box, false_)
#println("ID~~~", id, " VAL~~~", val_evaled)
if haskey(env, id)
env[id] = val_evaled
else
throw("the variable $id is not defined yet.")
end
return (val_evaled, env, res_box) return (val_evaled, env, res_box)
end end
c.SEQ([c.ELE([c.ID("quote")]), c.SEQ([c.ELE([c.ID("quote")]),
@ -45,49 +67,107 @@ function interp(ast, env, res_box, put_char=true)
c.ELE([v...]) => begin c.ELE([v...]) => begin
ele = reduce( (x, y) -> x*""*y, ele = reduce( (x, y) -> x*""*y,
map(i -> interp(i, env, res_box, false)[1], v)) map(i -> interp(i, env, res_box, false_)[1], v))
return (ele, env, res_box) return (ele, env, res_box)
end end
c.SEQ([c.ELE([c.ID("par")])]) =>begin
push!(res_box.eles, u.HBox([],
nothing, nothing, nothing, nothing, nothing))
return (ast, env, res_box)
end
c.SEQ([c.ELE([c.ID("ex")]), val]) =>begin
x = 'x'
font_family = select_font(x, env)
font_path = get_font_path(font_family)
font_size = parse(Int, env["fontsize"])
charmetrics = PDFOperating.check_char_size(
'x',
font_path,
font_size)
ex_in_px = charmetrics.wd
(val, _, _) = interp(val, env, res_box, false_)
val_ex_in_px = ex_in_px * parse(Int,val)
return (val_ex_in_px, env, res_box)
end
c.SEQ([c.ELE([c.ID("hglue")]),
width,stretch]) => begin
if put_char == true_
(width_evaled, _, _) = interp(width, env, res_box, false_)
(stretch_evaled, _, _) = interp(stretch, env, res_box, false_)
push!(res_box.eles[end].eles,
u.HGlue(width_evaled, parse(Int, stretch_evaled))
)
end
return (ast, env, res_box)
end
c.CHAR(ch) => begin c.CHAR(ch) => begin
atomic_ch = ch[1] atomic_ch = ch[1]
if put_char == true if put_char == false_
return (ch, env, res_box)
font_idx = 1
font_family = string(env["font"][font_idx])
font_family_length = length(font_family)
while !is_in_font(atomic_ch, font_family)
if font_idx <= font_family_length
font_idx += 1
font_family = string(env["font"][font_idx])
else
font_list = string(env["font"])
throw("the chars $atomic_ch is not contained in all the fonts
listed in listed fonts: $font_list")
end
end end
font_family = select_font(atomic_ch, env)
font_path = get_font_path(font_family) font_path = get_font_path(font_family)
font_size = parse(Int, env["fontsize"]) font_size = parse(Int, env["fontsize"])
glyph_metrics = PDFOperating.check_char_size( glyph_metrics = PDFOperating.check_char_size(
atomic_ch, font_path, font_size) atomic_ch, font_path, font_size)
chbox = u.ChBox(ch,
push!(res_box.eles[end].eles,
u.ChBox(ch,
font_path, font_path,
font_size, font_size,
glyph_metrics.ht, glyph_metrics.ht,
glyph_metrics.dp, glyph_metrics.dp,
glyph_metrics.wd)) glyph_metrics.wd,
end nothing, nothing)
return (ch, env, res_box)
end
c.NL(nl) => return (nl, env, res_box)
c.SPACE(sp) => return (sp, env, res_box)
if put_char == true_
push!(res_box.eles[end].eles, chbox)
return (ch, env, res_box)
else # put_char == gen_chbox_
return (chbox, env, res_box)
end
end
c.SEQ([c.ELE([c.ID("disc")]),
c.ELE(before),
c.ELE(after),
c.ELE(orig)])=> begin
before_item = length(before) == 0 ? [] : before[1]
after_item = length(after) == 0 ? [] : after[1]
orig_item = length(orig) == 0 ? [] : orig[1]
(before_evaled, _, _) = interp(before_item, env, res_box, gen_chbox_)
(after_evaled, _, _) = interp(after_item, env, res_box, gen_chbox_)
(orig_evaled, _, _) = interp(orig_item, env, res_box, gen_chbox_)
ret = u.Disc(before_evaled, after_evaled, orig_evaled)
println("RETURN", ret)
push!(res_box.eles[end].eles, ret)
return (ret, env, res_box)
end
c.NL(nl) => begin
#if spacing defined
if haskey(env, "spacing")
(spacing, _, _) = interp(env["spacing"], env, res_box, false_)
add_spacing = interp(spacing, env, res_box, true_)
end
return (nl, env, res_box)
end
c.SPACE(sp) => begin
#if spacing defined
if haskey(env, "spacing")
(spacing, _, _) = interp(env["spacing"], env, res_box, false_)
add_spacing = interp(spacing, env, res_box, true_)
end
return (sp, env, res_box)
end
# empty item
[] => return (ast, env, res_box)
_ => begin _ => begin
println("不知道") println("不知道")
val_evaled = "不知道" val_evaled = "不知道"
@ -144,6 +224,24 @@ function padding_zero(i)
return i return i
end end
"""
Select proper font having the glyph of char `ch`
"""
function select_font(ch, env)
font_idx = 1
font_family = string(env["font"][font_idx])
font_family_length = length(font_family)
while !is_in_font(ch, font_family)
if font_idx <= font_family_length
font_idx += 1
font_family = string(env["font"][font_idx])
else
font_list = string(env["font"])
throw("the chars $atomic_ch is not contained in all the fonts
listed in listed fonts: $font_list")
end
end
return font_family
end
end end

View file

@ -15,7 +15,7 @@ grammar rules of uahgi
comment = P"\%[^%]+\%" comment = P"\%[^%]+\%"
newline = P"(\r?\n)" |> Passes.Classes.NL newline = P"(\r?\n)" |> Passes.Classes.NL
space = p"[ \t]" > Passes.Classes.SPACE space = p"[ \t]+" > Passes.Classes.SPACE
id_name = p"[_a-zA-Z][_0-9a-zA-Z]*" > Passes.Classes.ID id_name = p"[_a-zA-Z][_0-9a-zA-Z]*" > Passes.Classes.ID
id = E"@" + id_name id = E"@" + id_name
@ -139,10 +139,4 @@ function ast_to_string(ast)
return str return str
end end
# Write your package code here.
#export cat
#cat = 1.2
end end

View file

@ -26,8 +26,7 @@ push!(processed_passes, two_nl_to_par_pass)
# in 2 hanzi add glue. # in 2 hanzi add glue.
function insert_hglue_in_adjacent_chinese(two_nl) function insert_hglue_in_adjacent_chinese(two_nl)
_0pt = Classes.SEQ([Classes.ID("pt"); Classes.CHAR(["0"])]) inner = Classes.ID("cjk_spacing")
inner = Classes.SEQ([Classes.ID("hglue"); _0pt])
return [two_nl[1]; inner; two_nl[2]] return [two_nl[1]; inner; two_nl[2]]
end end
adjacent_chinese_pattern = [Classes.CHAR(r"[\p{Han},。!?:「」『』…]"), adjacent_chinese_pattern = [Classes.CHAR(r"[\p{Han},。!?:「」『』…]"),

View file

@ -1,11 +1,28 @@
module uahgi module uahgi
include("parsing.jl") include("parsing.jl")
include("interp.jl") include("interp.jl")
include("arrange.jl")
using .Interp using .Interp
using .Parsing using .Parsing
using .Arrange
using ArgParse using ArgParse
export ChBox, HGlue
abstract type Box end abstract type Box end
"""
a like-breakable discrete point.
- before: the item before likebreaking
- after: the item after linebreaking
- orig: the status while not triggering like breaking
"""
mutable struct Disc<:Box
before
after
orig
end
""" """
Horizonal Box Horizonal Box
@ -19,8 +36,21 @@ mutable struct HBox<:Box
ht ht
dp dp
wd wd
x
y
end end
"""
Horizonal Glue (HGlue)
- wd: width
- str : stretch
"""
mutable struct HGlue<:Box
wd
str
end
""" """
Vertical Box Vertical Box
@ -34,6 +64,8 @@ mutable struct VBox<:Box
ht ht
dp dp
wd wd
x
y
end end
""" """
@ -53,6 +85,8 @@ mutable struct ChBox<:Box
ht ht
dp dp
wd wd
x
y
end end
function parse_commandline() function parse_commandline()
@ -64,7 +98,7 @@ function parse_commandline()
@add_arg_table! s begin @add_arg_table! s begin
"FILE" "FILE"
help = "the file path to be converted." help = "the file path to be converted."
required = true required = false
end end
return parse_args(s) return parse_args(s)
@ -74,6 +108,18 @@ end
function main() function main()
parsed_args = parse_commandline() parsed_args = parse_commandline()
file_path = parsed_args["FILE"] file_path = parsed_args["FILE"]
if file_path === nothing
#help string
help = "usage: uahgi.jl [-h] [FILE]
positional arguments:
FILE the file path to be converted.
optional arguments:
-h, --help show this help message and exit"
println(help)
return 0
end
# for test # for test
#if parsed_args["FILE"] === nothing #if parsed_args["FILE"] === nothing
# file_path = "./example/ex1.ug" # file_path = "./example/ex1.ug"
@ -85,11 +131,16 @@ function main()
default_env = Dict() # the default environment for the intepreter default_env = Dict() # the default environment for the intepreter
output_box_orig = VBox([HBox([], nothing, nothing, nothing)], output_box_orig = VBox([HBox([], nothing, nothing, nothing, nothing, nothing)],
nothing, nothing, nothing) nothing, nothing, nothing, nothing, nothing)
output_box_result = Interp.interp_main(ast, default_env, output_box_orig) output_box_result = Interp.interp_main(ast, default_env, output_box_orig)
print("Env", output_box_result[2]) env = output_box_result[2]
print("OutPutBox", output_box_result[3]) output_box = output_box_result[3]
print(output_box)
#TODO
arranged = Arrange.arrange(output_box, env)
end end