From 643e15c2c8ffea20608afc2a1346eba3f14a9069 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 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 3986bbe..1dfbd70 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -62,6 +62,8 @@ end command_to_canonical[raw"\frac"] = TeXExpr(:argument_gatherer, [:frac, 2]) command_to_canonical[raw"\sqrt"] = TeXExpr(:argument_gatherer, [:sqrt, 1]) +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 @@ -118,6 +120,7 @@ for command in combining_accents command_to_canonical[command] = TeXExpr(:argument_gatherer, [:combining_accent, 2, symbol_expr]) end + for symbol in punctuation_symbols symbol = first(symbol) symbol_to_canonical[symbol] = TeXExpr(:punctuation, symbol) From 99d832fca4a24717986f7df15352d5de1402298e 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/commands_data.jl | 2 +- src/parser/commands_registration.jl | 1 + src/parser/parser.jl | 39 +++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/parser/commands_data.jl b/src/parser/commands_data.jl index 090abc1..a03364f 100644 --- a/src/parser/commands_data.jl +++ b/src/parser/commands_data.jl @@ -25,7 +25,7 @@ relation_commands = split(raw""" \preceq \succeq \simeq \mid \ll \gg \asymp \parallel \subset \supset \approx \bowtie - \subseteq \supseteq \cong \Join + \subseteq \supseteq \cong \join \sqsubset \sqsupset \neq \smile \sqsubseteq \sqsupseteq \doteq \frown \in \ni \propto \vdash diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 1dfbd70..55b8ac4 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -62,6 +62,7 @@ end command_to_canonical[raw"\frac"] = TeXExpr(:argument_gatherer, [:frac, 2]) command_to_canonical[raw"\sqrt"] = TeXExpr(:argument_gatherer, [:sqrt, 1]) + 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 80bda78..bea7ab0 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -148,28 +148,47 @@ 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] 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 90deff63c5434ea3b191309bd5118d5db8b3ffaa 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 bea7ab0..18eebe5 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -137,7 +137,7 @@ _begin_group!(stack, p, data) = push!(stack, TeXExpr(:group)) function _end_group!(stack, p, data) 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 @@ -163,11 +163,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 32a58cb350d33fb7dbf37c895ac90f3a67dc4c7e 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 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/parser/parser.jl b/src/parser/parser.jl index 18eebe5..d4e83a3 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 @@ -314,10 +314,9 @@ context = Automa.CodeGenContext() end if length(stack) > 1 - err = TeXParseError( + throw(TeXParseError( "end of string reached with unfinished $(current(stack).head)", - stack, p_eof, data) - throw(err) + stack, p_eof, data)) end final_expr = current(stack) From 3ffd0fc1b843e7954180c629cd8ddd7986715347 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 | 1 + src/parser/parser.jl | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 55b8ac4..79e3ec2 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -63,6 +63,7 @@ end command_to_canonical[raw"\frac"] = TeXExpr(:argument_gatherer, [:frac, 2]) command_to_canonical[raw"\sqrt"] = TeXExpr(:argument_gatherer, [:sqrt, 1]) +# 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 d4e83a3..3ce0850 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -161,18 +161,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) @@ -216,7 +222,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 @@ -244,6 +253,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] @@ -259,7 +278,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 b5ffefa59e1042c19556a4ddc91a5c59697cd1a4 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 3ce0850..1acfe07 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -177,8 +177,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 94f7e0ed972f28ee5c96fc036864c320f3922c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Richard?= Date: Sun, 20 Mar 2022 01:12:42 +0100 Subject: [PATCH 07/10] Ignore manifest.toml --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2251642 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Manifest.toml \ No newline at end of file From 2c25bb091206ff1ee7e56b465d3bcdc6f6547d53 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 08/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 4be350b..a0f9099 100644 --- a/src/parser/texexpr.jl +++ b/src/parser/texexpr.jl @@ -84,8 +84,53 @@ Base.copy(texexpr::TeXExpr) = TeXExpr(texexpr.head, deepcopy(texexpr.args)) AbstractTrees.children(texexpr::TeXExpr) = texexpr.args AbstractTrees.printnode(io::IO, texexpr::TeXExpr) = print(io, "TeXExpr :$(texexpr.head)") -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 077dd6c6a11ff24bd44f820370fabd78fcdca7cc 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 09/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 79e3ec2..09f0001 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 1acfe07..5e5b52e 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -160,6 +160,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)) @@ -168,7 +172,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 6a088c25fb609bd2ac11d513b740215d04673f1d 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 10/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 5e5b52e..f50273c 100644 --- a/src/parser/parser.jl +++ b/src/parser/parser.jl @@ -183,7 +183,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)