diff --git a/lib/slime.ex b/lib/slime.ex index 5097d40..a8c01ab 100644 --- a/lib/slime.ex +++ b/lib/slime.ex @@ -9,7 +9,16 @@ defmodule Slime do @moduledoc """ Syntax exception which may appear during parsing and compilation processes """ - defexception message: "Syntax error in slime file" + defexception [:line, :line_number, :column, message: "Syntax error", source: "INPUT"] + + def message(exception) do + """ + #{exception.message} + #{exception.source}, Line #{exception.line_number}, Column #{exception.column} + #{exception.line} + #{String.duplicate(" ", exception.column - 1)}^ + """ + end end defdelegate render(slime), to: Renderer diff --git a/lib/slime/parser.ex b/lib/slime/parser.ex index 4c9dbe8..e9650ff 100644 --- a/lib/slime/parser.ex +++ b/lib/slime/parser.ex @@ -13,13 +13,27 @@ defmodule Slime.Parser do def parse(input) do indented_input = Preprocessor.indent(input) case :slime_parser.parse(indented_input) do - # TODO: Map column index to original input, handle errors on indent and dedent - {:fail, error} -> - raise TemplateSyntaxError, "Parsing Error: #{inspect error}\n#{input}" + {:fail, error} -> handle_syntax_error(input, indented_input, error) tokens -> tokens end end + defp handle_syntax_error(input, indented_input, error) do + {_reason, error, {{:line, line}, {:column, column}}} = error + indented_line = indented_input |> String.split("\n") |> Enum.at(line - 1) + input_line = input |> String.split("\n") |> Enum.at(line - 1) + indent = Preprocessor.indent_meta_symbol + column = case indented_line do + <<^indent::binary-size(1), _::binary>> -> column - 1 + _ -> column + end + raise TemplateSyntaxError, + line: input_line, + message: inspect(error), + line_number: line, + column: column + end + @content "|" @comment "/" @html "<" diff --git a/lib/slime/parser/preprocessor.ex b/lib/slime/parser/preprocessor.ex index e63c313..7cafcb6 100644 --- a/lib/slime/parser/preprocessor.ex +++ b/lib/slime/parser/preprocessor.ex @@ -2,19 +2,7 @@ defmodule Slime.Parser.Preprocessor do @moduledoc """ This module helps to process input and insert indents and dedents to simplify parser design. """ - - defmodule IndentationError do - defexception [:line, :line_number, :indent, message: "Malformed indentation"] - - def message(exception) do - """ - #{exception.message} - INPUT, Line #{exception.line_number}, Column #{exception.indent} - #{exception.line} - #{String.duplicate(" ", exception.indent)}^ - """ - end - end + alias Slime.TemplateSyntaxError @indent "\x0E" @dedent "\x0F" @@ -62,7 +50,11 @@ defmodule Slime.Parser.Preprocessor do [prev_line | result] = result {stack, [line, prev_line <> dedents | result]} else - raise IndentationError, line: line, line_number: Enum.count(result) + 1, indent: indent + raise TemplateSyntaxError, + message: "Malformed indentation", + line: line, + line_number: Enum.count(result) + 1, + column: indent end end diff --git a/test/parser/preprocessor_test.exs b/test/parser/preprocessor_test.exs index 798e526..2ff9026 100644 --- a/test/parser/preprocessor_test.exs +++ b/test/parser/preprocessor_test.exs @@ -1,7 +1,7 @@ defmodule Slime.Parser.PreprocessorTest do use ExUnit.Case alias Slime.Parser.Preprocessor - alias Preprocessor.IndentationError + alias Slime.TemplateSyntaxError doctest Preprocessor @@ -16,7 +16,7 @@ defmodule Slime.Parser.PreprocessorTest do | test """ - assert_raise(IndentationError, fn -> Preprocessor.indent(slime) end) + assert_raise(TemplateSyntaxError, fn -> Preprocessor.indent(slime) end) end test "skip indents in embedded engine lines" do