From 27d66b0e51b085030901d6dd149c39440cbf31c1 Mon Sep 17 00:00:00 2001 From: Tan Kian-ting Date: Thu, 30 Jan 2025 00:39:34 +0800 Subject: [PATCH] create basic modification of arranger --- example/ex1.ug | 9 ++- example/ex2.ug | 11 +++ src/arrange.jl | 37 ++++++++++ src/interp.jl | 178 ++++++++++++++++++++++++++++++++++++++----------- src/parsing.jl | 8 +-- src/passes.jl | 3 +- src/uahgi.jl | 61 +++++++++++++++-- 7 files changed, 248 insertions(+), 59 deletions(-) create mode 100644 example/ex2.ug create mode 100644 src/arrange.jl diff --git a/example/ex1.ug b/example/ex1.ug index 9c54c74..c158703 100644 --- a/example/ex1.ug +++ b/example/ex1.ug @@ -1,8 +1,7 @@ {@lang|en} {@def|@font|{@quote|FreeSans|AR PL UKai TW}} {@def|@fontsize|12} -therapy of communication and pronounciation123%comment - -anotherline%{@set|@fontsize|10} processing - -of the problem \ No newline at end of file +{@def|@linewidth|200} %in px% +{@def|@spacing|{@hglue|{@ex|1}|2}} +{@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}} +11111 222 3333333 444444 555555 666666 77777 88888 99999 000000 11111 22222 33333 55555 \ No newline at end of file diff --git a/example/ex2.ug b/example/ex2.ug new file mode 100644 index 0000000..32addac --- /dev/null +++ b/example/ex2.ug @@ -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 \ No newline at end of file diff --git a/src/arrange.jl b/src/arrange.jl new file mode 100644 index 0000000..58c6ac1 --- /dev/null +++ b/src/arrange.jl @@ -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 \ No newline at end of file diff --git a/src/interp.jl b/src/interp.jl index b418600..b542395 100644 --- a/src/interp.jl +++ b/src/interp.jl @@ -8,6 +8,12 @@ using .PDFOperating u = Main.uahgi c = Main.uahgi.Parsing.Passes.Classes +@enum PutChar begin + true_ = 0 + false_ = 1 + gen_chbox_ = 2 +end + function interp_main(ast, env, res_box) ast_inner = ast.val val = nothing @@ -22,19 +28,35 @@ interp: the intepreter of the uahgi. - ast: element or part of the ast - env: the variable storaging environment - 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) @match ast begin c.SEQ([c.ELE([c.ID("def")]), c.ELE([c.ID(id)]),val]) => 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) - env[id] = val_evaled + if !haskey(env, id) + 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) + end c.SEQ([c.ELE([c.ID("quote")]), y...]) => begin list = map(x -> x[1], @@ -45,49 +67,107 @@ function interp(ast, env, res_box, put_char=true) c.ELE([v...]) => begin 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) 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 atomic_ch = ch[1] - - if put_char == true - 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 - - - font_path = get_font_path(font_family) - font_size = parse(Int, env["fontsize"]) - glyph_metrics = PDFOperating.check_char_size( - atomic_ch, font_path, font_size) - - - push!(res_box.eles[end].eles, - u.ChBox(ch, - font_path, - font_size, - glyph_metrics.ht, - glyph_metrics.dp, - glyph_metrics.wd)) - end + if put_char == false_ return (ch, env, res_box) - end - c.NL(nl) => return (nl, env, res_box) - c.SPACE(sp) => return (sp, env, res_box) + end + font_family = select_font(atomic_ch, env) + + font_path = get_font_path(font_family) + font_size = parse(Int, env["fontsize"]) + glyph_metrics = PDFOperating.check_char_size( + atomic_ch, font_path, font_size) + chbox = u.ChBox(ch, + font_path, + font_size, + glyph_metrics.ht, + glyph_metrics.dp, + glyph_metrics.wd, + nothing, nothing) + + 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 println("不知道") val_evaled = "不知道" @@ -144,6 +224,24 @@ function padding_zero(i) return i 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 \ No newline at end of file diff --git a/src/parsing.jl b/src/parsing.jl index b523639..151aa6f 100644 --- a/src/parsing.jl +++ b/src/parsing.jl @@ -15,7 +15,7 @@ grammar rules of uahgi comment = P"\%[^%]+\%" 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 = E"@" + id_name @@ -139,10 +139,4 @@ function ast_to_string(ast) return str end -# Write your package code here. -#export cat - -#cat = 1.2 - - end \ No newline at end of file diff --git a/src/passes.jl b/src/passes.jl index 1d08561..a885884 100644 --- a/src/passes.jl +++ b/src/passes.jl @@ -26,8 +26,7 @@ push!(processed_passes, two_nl_to_par_pass) # in 2 hanzi add glue. function insert_hglue_in_adjacent_chinese(two_nl) - _0pt = Classes.SEQ([Classes.ID("pt"); Classes.CHAR(["0"])]) - inner = Classes.SEQ([Classes.ID("hglue"); _0pt]) + inner = Classes.ID("cjk_spacing") return [two_nl[1]; inner; two_nl[2]] end adjacent_chinese_pattern = [Classes.CHAR(r"[\p{Han},。!?:「」『』…]"), diff --git a/src/uahgi.jl b/src/uahgi.jl index 1a22e3b..c9d99d7 100644 --- a/src/uahgi.jl +++ b/src/uahgi.jl @@ -1,11 +1,28 @@ module uahgi include("parsing.jl") include("interp.jl") +include("arrange.jl") + using .Interp using .Parsing +using .Arrange using ArgParse +export ChBox, HGlue 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 @@ -19,8 +36,21 @@ mutable struct HBox<:Box ht dp wd + x + y end +""" +Horizonal Glue (HGlue) +- wd: width +- str : stretch +""" +mutable struct HGlue<:Box + wd + str +end + + """ Vertical Box @@ -34,6 +64,8 @@ mutable struct VBox<:Box ht dp wd + x + y end """ @@ -53,6 +85,8 @@ mutable struct ChBox<:Box ht dp wd + x + y end function parse_commandline() @@ -64,7 +98,7 @@ function parse_commandline() @add_arg_table! s begin "FILE" help = "the file path to be converted." - required = true + required = false end return parse_args(s) @@ -74,6 +108,18 @@ end function main() parsed_args = parse_commandline() 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 #if parsed_args["FILE"] === nothing # file_path = "./example/ex1.ug" @@ -85,11 +131,16 @@ function main() default_env = Dict() # the default environment for the intepreter - output_box_orig = VBox([HBox([], nothing, nothing, nothing)], - nothing, nothing, nothing) + output_box_orig = VBox([HBox([], nothing, nothing, nothing, nothing, nothing)], + nothing, nothing, nothing, nothing, nothing) output_box_result = Interp.interp_main(ast, default_env, output_box_orig) - print("Env", output_box_result[2]) - print("OutPutBox", output_box_result[3]) + env = output_box_result[2] + output_box = output_box_result[3] + print(output_box) + #TODO + arranged = Arrange.arrange(output_box, env) + + end