Packages

Cirru Parser in Elixir

Current section

Files

Jump to
cirru_parser lib cirru_parser.ex
Raw

lib/cirru_parser.ex

defmodule CirruParser do
require CirruParser.Tree
alias CirruParser.Tree
def parse(code, filename) do
buffer = nil
state = %{
:name => :indent,
:x => 1,
:y => 1,
:level => 1,
:indent => 0,
:indented => 0,
:nest => 0,
:path => filename
}
res = parseRunner [], buffer, state, code
res = Enum.map res, fn (item) -> Tree.resolveDollar item end
res = Enum.map res, fn (item) -> Tree.resolveComma item end
res
end
defp shorten(xs) when is_list xs do
Enum.map xs, fn (item) -> shorten item end
end
defp shorten(xs) do
xs.text
end
def pare(code, filename) do
res = parse code, filename
shorten res
end
# eof
defp escape_eof(_, _, _, _) do
raise "EOF in escape state"
end
defp string_eof(_, _, _, _) do
raise "EOF in string state"
end
def space_eof(xs, _, _, _) do
xs
end
defp token_eof(xs, buffer, state, _) do
buffer = %{buffer | :ex => state.x, :ey => state.y}
xs = Tree.appendItem xs, state.level, buffer
xs
end
defp indent_eof(xs, _, _, _) do
xs
end
# escape
defp escape_newline(_, _, _, _) do
raise "new line while space"
end
defp escape_n(xs, buffer, state, code) do
state = %{state | :x => state.x + 1, :name => :string}
buffer = %{buffer | :name => buffer.x <> "\n"}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp escape_t(xs, buffer, state, code) do
state = %{state | :x => state.x + 1, :name => :string}
buffer = %{buffer | :text => buffer.text <> "\t"}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp escape_else(xs, buffer, state, code) do
state = %{state | :x => state.x + 1, :name => :string}
buffer = %{buffer | :text => buffer.text <> (String.first code)}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
# string
defp string_backslash(xs, buffer, state, code) do
state = %{state | :name => :escape, :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp string_newline(_, _, _, _) do
raise "newline in a string"
end
defp string_quote(xs, buffer, state, code) do
state = %{state | :name => :token, :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp string_else(xs, buffer, state, code) do
state = %{state | :x => state.x + 1}
buffer = %{buffer | :text => buffer.text <> (String.first code)}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
# space
defp space_space(xs, buffer, state, code) do
state = %{state | :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp space_newline(xs, buffer, state, code) do
if state.nest != 0 do
raise "incorrect nesting"
end
state = %{state | :name => :indent, :x => 1,
:y => state.y + 1, :indented => 0
}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp space_open(xs, buffer, state, code) do
nesting = Tree.createNesting 1
xs = Tree.appendItem xs, state.level, nesting
state = %{state | :nest => state.nest + 1,
:level => state.level + 1, :x => state.x + 1
}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp space_close(xs, buffer, state, code) do
state = %{state | :nest => state.nest - 1,
:level => state.level - 1,
:x => state.x + 1
}
if state.nest < 0 do
raise "close at space"
end
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp space_quote(xs, _, state, code) do
buffer = %{:text => "",
:x => state.x, :y => state.y,
:ex => state.x, :ey => state.y,
:path => state.path
}
state = %{state | :name => :string, :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp space_else(xs, _, state, code) do
buffer = %{:text => (String.first code),
:x => state.x, :y => state.y,
:ex => state.x, :ey => state.y,
:path => state.path
}
state = %{state | :name => :token, :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
# token
defp token_space(xs, buffer, state, code) do
buffer = %{buffer | :ex => state.x, :ey => state.y}
xs = Tree.appendItem xs, state.level, buffer
state = %{state | :name => :space, :x => state.x + 1}
buffer = nil
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp token_newline(xs, buffer, state, code) do
buffer = %{buffer | :ex => state.x, :ey => state.y}
xs = Tree.appendItem xs, state.level, buffer
state = %{state | :name => :indent,
:indented => 0, :x => 1, :y => state.y + 1
}
buffer = nil
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp token_open(_, _, _, _) do
raise "open parenthesis in token"
end
defp token_close(xs, buffer, state, code) do
buffer = %{buffer | :ex => state.x, :ey => state.y}
xs = Tree.appendItem xs, state.level, buffer
state = %{state | :name => :space}
buffer = nil
parseRunner xs, buffer, state, code
end
defp token_quote(xs, buffer, state, code) do
state = %{state | :name => :string, :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp token_else(xs, buffer, state, code) do
buffer = %{buffer | :text => buffer.text <> (String.first code)}
state = %{state | :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
# indent
defp indent_space(xs, buffer, state, code) do
state = %{state | :indented => state.indented + 1, :x => state.x + 1}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp indent_newline(xs, buffer, state, code) do
state = %{state | :x => 1, :y => state.y + 1, :indented => 0}
parseRunner xs, buffer, state, (String.slice code, 1..-1)
end
defp indent_close(_, _, _, _) do
raise "close parenthesis at indent"
end
defp indent_else(xs, buffer, state, code) do
if (rem state.indented, 2) == 1 do
raise "odd indentation"
end
indented = state.indented / 2
diff = indented - state.indent
cond do
(diff <= 0) ->
nesting = Tree.createNesting 1
xs = Tree.appendItem xs, (state.level + diff - 1), nesting
(diff > 0) ->
nesting = Tree.createNesting diff
xs = Tree.appendItem xs, state.level, nesting
end
state = %{state | :name => :space,
:level => state.level + diff, :indent => indented
}
parseRunner xs, buffer, state, code
end
# parse
defp parseRunner(xs, buffer, state, code) do
# IO.inspect state
# IO.inspect code
# IO.inspect xs
# IO.puts "\n\n"
if code == "" do
eof = true
else
eof = false
char = String.first code
end
case state.name do
:escape ->
cond do
eof ->
escape_eof xs, buffer, state, code
(char == "\n") ->
escape_newline xs, buffer, state, code
(char == "n") ->
escape_n xs, buffer, state, code
(char == "t") ->
escape_t xs, buffer, state, code
true ->
escape_else xs, buffer, state, code
end
:string ->
cond do
eof ->
string_eof xs, buffer, state, code
(char == "\\") ->
string_backslash xs, buffer, state, code
(char == "\n") ->
string_newline xs, buffer, state, code
(char == "\"") ->
string_quote xs, buffer, state, code
true ->
string_else xs, buffer, state, code
end
:space ->
cond do
eof ->
space_eof xs, buffer, state, code
(char == " ") ->
space_space xs, buffer, state, code
(char == "\n") ->
space_newline xs, buffer, state, code
(char == "(") ->
space_open xs, buffer, state, code
(char == ")") ->
space_close xs, buffer, state, code
(char == "\"") ->
space_quote xs, buffer, state, code
true ->
space_else xs, buffer, state, code
end
:token ->
cond do
eof ->
token_eof xs, buffer, state, code
(char == " ") ->
token_space xs, buffer, state, code
(char == "\n") ->
token_newline xs, buffer, state, code
(char == "(") ->
token_open xs, buffer, state, code
(char == ")") ->
token_close xs, buffer, state, code
(char == "\"") ->
token_quote xs, buffer, state, code
true ->
token_else xs, buffer, state, code
end
:indent ->
cond do
eof ->
indent_eof xs, buffer, state, code
(char == " ") ->
indent_space xs, buffer, state, code
(char == "\n") ->
indent_newline xs, buffer, state, code
(char == ")") ->
indent_close xs, buffer, state, code
true ->
indent_else xs, buffer, state, code
end
end
end
end