From 7477d8414523edc1087aaf4510912bff547957df Mon Sep 17 00:00:00 2001 From: Jacob Zelko Date: Sun, 16 Jan 2022 20:16:01 -0500 Subject: [PATCH 01/10] Registered functionalities --- src/parser/commands_registration.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 93e3f89..95ed1fe 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -65,6 +65,8 @@ command_to_canonical[raw"\sqrt"] = TeXExpr(:argument_gatherer, [:sqrt, 1]) command_to_canonical[raw"\overline"] = TeXExpr(:argument_gatherer, [:overline, 1]) command_to_canonical[raw"\{"] = TeXExpr(:delimiter, '{') command_to_canonical[raw"\}"] = TeXExpr(:delimiter, '}') +command_to_canonical[raw"\begin"] = TeXExpr(:argument_gatherer, [:begin_env, 1]) +command_to_canonical[raw"\end"] = TeXExpr(:argument_gatherer, [:end_env, 1]) ## ## Commands from the commands_data.jl file From 66a13f5fb2a9f0f44aa71a5a7790411ae7f5dd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 7 Mar 2022 04:08:09 +0100 Subject: [PATCH 02/10] Add parser special cases for begin-end of env --- src/parser/parser.jl | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 0062b04..21fc813 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -146,9 +146,9 @@ function _end_group!(stack, p, data) group = first(group.args) end - if requirement(stack) == :argument - push_to_current!(stack, group) + push_to_current!(stack, group) + if requirement(stack) == :argument command_builder = current(stack) head, required_n_args = command_builder.args[1:2] args = command_builder.args[3:end] @@ -156,19 +156,38 @@ function _end_group!(stack, p, data) # Check if the argument gatherer got all the needed arguments if required_n_args == length(args) pop!(stack) - command = TeXExpr(head, args) - push_to_current!(stack, command) + if head == :begin_env + # Transform the content of the argument back to a single string + env_name = String(Char.(first(args).args)) + push!(stack, TeXExpr(:env, Any[env_name])) + elseif head == :end_env + env_name = String(Char.(args[1].args)) + current_env_name = current(stack).args[1] + env_name != current_env_name && throw( + TeXParseError( + "Found an end for environnement '$env_name', but it is \ + not matching the currently open env '$current_env_name'", + stack, p, data)) + env = pop!(stack) + push_to_current!(stack, env) + else + command = TeXExpr(head, args) + push_to_current!(stack, command) + end end - else - push_to_current!(stack, group) end end function _push_char!(stack, p, data) - if current_head(stack) == :skip_char - pop!(stack) - elseif isvalid(data, p-1) - char = data[prevind(data, p)] + current_head(stack) == :skip_char && return pop!(stack) + !isvalid(data, p-1) && return + + char = data[prevind(data, p)] + + if current_head(stack) == :env_cell && char == '&' + push_to_current!(stack, pop!(stack)) + push!(stack, TeXExpr(:env_cell)) + else push_to_current!(stack, canonical_expr(char)) end end From aa2428b34855c25c6ab598d3050f562b8a995e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Sat, 12 Mar 2022 23:47:30 +0100 Subject: [PATCH 03/10] Error for closing env when none is open --- src/parser/parser.jl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 21fc813..308f91a 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -135,7 +135,7 @@ function _end_group!(stack, p, data) current_head(stack) == :skip_char && return current_head(stack) != :group && throw( - TeXParseError("Unexpected '}' at position $(p-1)", stack, p, data)) + TeXParseError("unexpected '}'", stack, p, data)) group = pop!(stack) # Remplace empty groups by a zero-width space @@ -162,11 +162,16 @@ function _end_group!(stack, p, data) push!(stack, TeXExpr(:env, Any[env_name])) elseif head == :end_env env_name = String(Char.(args[1].args)) - current_env_name = current(stack).args[1] - env_name != current_env_name && throw( + current(stack).head != :env && throw( TeXParseError( - "Found an end for environnement '$env_name', but it is \ - not matching the currently open env '$current_env_name'", + "unexpected end of environnement '$env_name'", + stack, p, data)) + + open_env_name = current(stack).args[1] + env_name != open_env_name && throw( + TeXParseError( + "found an end for environnement '$env_name', but it is \ + not matching the currently open env '$open_env_name'", stack, p, data)) env = pop!(stack) push_to_current!(stack, env) From bfbb268b46c835ec004164fd28a1615bbfaced1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Sun, 13 Mar 2022 00:50:44 +0100 Subject: [PATCH 04/10] Quickfix error whith incomplete commands --- src/parser/parser.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 308f91a..934a983 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -16,7 +16,7 @@ function show_state(io::IO, stack, position, data) if position > lastindex(data) println(io, "after the end of the data") println(io, data) - else + elseif position > 0 # Compute the index of the char from the byte index position_to_id = zeros(Int, lastindex(data)) id = 0 From 5d0d07a5ada7cc1f3732ecbe28ce73b0ae23e04e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Sun, 13 Mar 2022 01:47:11 +0100 Subject: [PATCH 05/10] Implement newline --- src/parser/commands_registration.jl | 2 ++ src/parser/parser.jl | 27 +++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 95ed1fe..5ef4301 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -65,6 +65,8 @@ command_to_canonical[raw"\sqrt"] = TeXExpr(:argument_gatherer, [:sqrt, 1]) command_to_canonical[raw"\overline"] = TeXExpr(:argument_gatherer, [:overline, 1]) command_to_canonical[raw"\{"] = TeXExpr(:delimiter, '{') command_to_canonical[raw"\}"] = TeXExpr(:delimiter, '}') + +# Commands for env command_to_canonical[raw"\begin"] = TeXExpr(:argument_gatherer, [:begin_env, 1]) command_to_canonical[raw"\end"] = TeXExpr(:argument_gatherer, [:end_env, 1]) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 934a983..729fd4c 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -160,18 +160,24 @@ function _end_group!(stack, p, data) # Transform the content of the argument back to a single string env_name = String(Char.(first(args).args)) push!(stack, TeXExpr(:env, Any[env_name])) + push!(stack, TeXExpr(:env_row)) + push!(stack, TeXExpr(:env_cell)) elseif head == :end_env env_name = String(Char.(args[1].args)) - current(stack).head != :env && throw( + current(stack).head != :env_cell && throw( TeXParseError( "unexpected end of environnement '$env_name'", stack, p, data)) + cell = pop!(stack) + push_to_current!(stack, cell) + row = pop!(stack) + push_to_current!(stack, row) open_env_name = current(stack).args[1] env_name != open_env_name && throw( TeXParseError( "found an end for environnement '$env_name', but it is \ - not matching the currently open env '$open_env_name'", + not matching the currently open env", stack, p, data)) env = pop!(stack) push_to_current!(stack, env) @@ -227,7 +233,10 @@ function _setup_decorated!(stack, p, data) end end -_begin_command_builder!(stack, p, data) = push!(stack, TeXExpr(:command_builder)) +function _begin_command_builder!(stack, p, data) + current_head(stack) == :skip_char && return pop!(stack) + push!(stack, TeXExpr(:command_builder)) +end function _end_command_builder!(stack, p, data) if current_head(stack) == :command_builder @@ -255,6 +264,16 @@ function _end_command_builder!(stack, p, data) TeXParseError("unexpected '\\right' at position $(p-1)", stack, p, data)) push!(stack, TeXExpr(:right_delimiter)) + elseif command_name == "\\" + current(stack).head != :env_cell && throw( + TeXParseError("'\\' for newline is only supported inside env", + stack, p, data)) + current_cell = pop!(stack) + push_to_current!(stack, current_cell) + current_row = pop!(stack) + push_to_current!(stack, current_row) + push!(stack, TeXExpr(:env_row)) + push!(stack, TeXExpr(:env_cell)) elseif haskey(command_to_canonical, command) expr = command_to_canonical[command] @@ -270,7 +289,7 @@ function _end_command_builder!(stack, p, data) stack, p, data)) end - if skip_char + if skip_char push!(stack, TeXExpr(:skip_char)) end end From f02a8eb71685197f316cc7a5510a3dbbc4bad7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Sun, 13 Mar 2022 02:37:14 +0100 Subject: [PATCH 06/10] Hotfix parser error when testing on remote --- src/parser/parser.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 729fd4c..cf1f636 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -176,8 +176,7 @@ function _end_group!(stack, p, data) open_env_name = current(stack).args[1] env_name != open_env_name && throw( TeXParseError( - "found an end for environnement '$env_name', but it is \ - not matching the currently open env", + "found an end for environnement '$env_name', but it is not matching the currently open env", stack, p, data)) env = pop!(stack) push_to_current!(stack, env) From 4aba85a59e4e861cbc3dd2c476d1f917206bb536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 28 Mar 2022 02:11:42 +0200 Subject: [PATCH 07/10] Compact show for TeXExpr --- src/parser/texexpr.jl | 49 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/parser/texexpr.jl b/src/parser/texexpr.jl index c41093d..483efb2 100644 --- a/src/parser/texexpr.jl +++ b/src/parser/texexpr.jl @@ -116,8 +116,53 @@ function AbstractTrees.printnode(io::IO, texexpr::TeXExpr) end end -function Base.show(io::IO, texexpr::TeXExpr) - print_tree(io, texexpr, maxdepth=10) +to_latex(::Nothing) = nothing + +function to_latex(texexpr::TeXExpr) + head = texexpr.head + args = texexpr.args + + head in [:char, :digit, :punctuation, :symbol] && return string(first(args)) + head == :function && return "\\$(first(args))" + head == :frac && return "\\frac{$(to_latex(args[1]))}{$(to_latex(args[2]))}" + head == :group && return join(to_latex.(args), " ") + head == :sqrt && return "\\sqrt{$(to_latex(first(args)))}" + head == :spaced && return string(first(first(args).args)) + head == :space && return "" + + if head in [:decorated, :integral, :underover] + core, sub, sup = to_latex.(args) + + if !isnothing(sub) + if length(sub) == 1 + core *= "_$sub" + else + core *= "_{$sub}" + end + end + + if !isnothing(sup) + if length(sup) == 1 + core *= "^$sup" + else + core *= "^{$sup}" + end + end + return core + end + + # TODO :accent, :space properly + + # Fallback + return "to_latex($texexpr)" +end + +function Base.show(io::IO, ::MIME"text/plain", texexpr::TeXExpr) + if haskey(io, :compact) && io[:compact] + print(io, "TeX\"$(to_latex(texexpr))\"") + else + return print_tree(io, texexpr, maxdepth=10) + end end function Base.:(==)(tex1::TeXExpr, tex2::TeXExpr) From e3e95f3a5b1d24b1c1faa397c53bd8ed5ff42bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 28 Mar 2022 22:00:22 +0200 Subject: [PATCH 08/10] Check env name --- src/parser/commands_data.jl | 7 +++++++ src/parser/commands_registration.jl | 2 ++ src/parser/parser.jl | 6 +++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/parser/commands_data.jl b/src/parser/commands_data.jl index a03364f..157c31a 100644 --- a/src/parser/commands_data.jl +++ b/src/parser/commands_data.jl @@ -110,5 +110,12 @@ punctuation_symbols = split(raw", ; . !") delimiter_symbols = split(raw"| / ( ) [ ] < >") font_names = split(raw"rm cal it tt sf bf default bb frak scr regular") +## +## Env +## + +supported_env = split("matrix pmatrix bmatrix") + + # TODO Add to the parser what come below, if needed wide_accent_commands = split(raw"\widehat \widetilde \widebar") \ No newline at end of file diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 5ef4301..fc8cc98 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -51,6 +51,8 @@ function get_symbol_char(command) return first(latex_symbols[command]) end +is_supported(env) = env in supported_env + # Numbers for char in join(0:9) symbol_to_canonical[char] = TeXExpr(:digit, char) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index cf1f636..206dd00 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -159,6 +159,10 @@ function _end_group!(stack, p, data) if head == :begin_env # Transform the content of the argument back to a single string env_name = String(Char.(first(args).args)) + !is_supported(env_name) && throw( + TeXParseError( + "env '$env_name' is not supported", + stack, p, data)) push!(stack, TeXExpr(:env, Any[env_name])) push!(stack, TeXExpr(:env_row)) push!(stack, TeXExpr(:env_cell)) @@ -167,7 +171,7 @@ function _end_group!(stack, p, data) current(stack).head != :env_cell && throw( TeXParseError( "unexpected end of environnement '$env_name'", - stack, p, data)) + stack, p, data)) cell = pop!(stack) push_to_current!(stack, cell) From d8896a7275344bbafd79724f3339fcf29fe3d3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Mon, 28 Mar 2022 22:15:20 +0200 Subject: [PATCH 09/10] Return args of env as a matrix --- src/parser/parser.jl | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 206dd00..09fd32a 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -182,7 +182,24 @@ function _end_group!(stack, p, data) TeXParseError( "found an end for environnement '$env_name', but it is not matching the currently open env", stack, p, data)) - env = pop!(stack) + + # Make a matrix of the arguments + env_data = pop!(stack) + rows = env_data.args[2:end] + n_cols = maximum([length(row.args) for row in rows]) + matrix = fill(TeXExpr(:space, 0), length(rows), n_cols) + for (i, row) in enumerate(rows) + for (j, cell) in enumerate(row.args) + # Remove the :env_cell wrapper + if length(cell.args) == 1 + matrix[i, j] = first(cell.args) + else + matrix[i, j] = TeXExpr(:group, cell.args) + end + end + end + + env = TeXExpr(:env, [env_name, matrix]) push_to_current!(stack, env) else command = TeXExpr(head, args) From 18a03e553f878485b91b5077c5138f19988d769b Mon Sep 17 00:00:00 2001 From: Luke Adams Date: Fri, 10 Nov 2023 07:00:10 -0700 Subject: [PATCH 10/10] initial layouting for matricies --- src/engine/layout.jl | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/engine/layout.jl b/src/engine/layout.jl index 4eafb07..ff835a9 100644 --- a/src/engine/layout.jl +++ b/src/engine/layout.jl @@ -219,6 +219,38 @@ function tex_layout(expr, state) ], [1, shrink, shrink] ) + elseif head == :env && args[1] == "matrix" + entries = permutedims(tex_layout.(args[2], state)) + entry_wdts = inkwidth.(entries) + entry_hgts = inkheight.(entries) + + wdts = vec(maximum(entry_wdts, dims=2)) + hgts = vec(maximum(entry_hgts, dims=1)) + + hor_sep = 2*xheight(font_family) + vert_sep = 0.5*xheight(font_family) + + padded_wdts = wdts .+ hor_sep + padded_hgts = hgts .+ vert_sep + + row_offsets = reverse(cumsum(reverse([padded_hgts..., 0]))) + column_offsets = cumsum([0, padded_wdts...]) + + linear_entries = TeXElement[] + offsets = Point2f[] + for I in eachindex(IndexCartesian(), entries) + offset = (column_offsets[I[1]], row_offsets[I[2]]) + + push!(linear_entries, entries[I]) + push!(offsets, offset) + + # Uncomment to see vertical extent of each matrix entry + push!(linear_entries, HLine(inkwidth(entries[I]), thickness(font_family))) + push!(offsets, offset) + push!(linear_entries, HLine(inkwidth(entries[I]), thickness(font_family))) + push!(offsets, offset .+ (0, inkheight(entries[I]))) + end + return Group(linear_entries, offsets) end catch # TODO Better error