Current section
Files
Jump to
Current section
Files
lib/cliex_map/compiler.ex
defmodule CliexMap.Compiler do
use CliexMap.Types
alias Minipeg.Parser
import Minipeg.{Combinators, Mappers, Parsers}
import CliexMap.PipelineFunctions
alias CliexMap.{Context, Fn, FunctionList}
@moduledoc false
@spec compile(binary()) :: Parser.result_tuple_t(ast())
def compile(input) do
Parser.parse_string(pattern_compiler(), input)
end
@spec compileok(binary()) :: ast()
def compileok(input) do
with {:ok, compiled} <- compile(input), do: compiled
end
@spec pattern_compiler() :: Parser.t()
defp pattern_compiler do
many(chunk_parser(), "pattern_compiler")
|> map(&List.flatten/1)
end
@spec chunk_parser() :: Parser.t()
defp chunk_parser do
select(
[
literal_parser("%%") |> map(fn _ -> FunctionList.new(literal_function("%")) end),
many(not_char_parser("%"), nil, 1)
|> map(fn ast -> FunctionList.new(literal_function(IO.chardata_to_string(ast))) end),
many(pattern_parser(), nil, 1) #|> debug()
],
"chunk_parser"
)
end
@spec inner_format_parser() :: Parser.t
defp inner_format_parser do
sequence([
int_parser(),
maybe(char_parser("x")),
many(ignore_ws(maybe_escaped_char_parser(">", "%")))
], "inner format parser")
end
@spec field_parser() :: Parser.t()
defp field_parser do
maybe(int_parser())
|> map(&_make_field/1, "field_parser")
end
@spec format_parser() :: Parser.t()
defp format_parser do
maybe(
in_between_parser(
char_parser("<"),
inner_format_parser(),
char_parser(">")
)
)
end
@spec lnb_parser() :: Parser.t()
defp lnb_parser do
sequence([
char_parser(["n"]),
maybe(
in_between_parser(
char_parser(":"),
list_parser(tokenize(int_parser()), char_parser(",")),
char_parser(":")
)
)
])
|> map(&_make_lnb/1, "lnb_parser")
end
@spec load_variable_parser() :: Parser.t()
defp load_variable_parser() do
sequence([
char_parser("L"),
ident_parser()
])
|> map(&_make_load_variable/1, "load_variable_parser")
end
@spec pattern_parser() :: Parser.t()
defp pattern_parser do
sequence(
[
char_parser("%"),
sequence([
sequence([
select(
[
lnb_parser(),
ts_parser(),
load_variable_parser(),
store_variable_parser(),
field_parser()
],
"patterns"
),
format_parser()
], "maybe with format")
|> map(&_add_formats_to_pipeline/1),
many(s_expression_parser())#|>debug()
])
|> map(&_unify_function_pipeline/1)
],
"pattern_parser"
)
|> map(&ignore_first_and_flatten/1)
end
@spec s_expression_parser() :: Parser.t()
defp s_expression_parser do
Minipeg.Parsers.LispyParser.s_exp_parser()
end
@spec store_variable_parser() :: Parser.t()
defp store_variable_parser do
sequence(
[
char_parser("S"),
ident_parser(),
char_parser("<"),
upto_parser_parser(char_parser(">"), "parse to end of var decl", :discard)
],
"store variable parser"
)
|> map(
fn [_, name, _, ast] -> _make_store_variable_function(name, ast) end,
"store variable map"
)
end
@spec ts_parser() :: Parser.t()
defp ts_parser do
keywords_parser(["ts", "tms", "tmics", "xs", "xms", "xmics"])
|> map(fn kwd ->
FunctionList.new(
Fn.new(fn _, ctxt -> Context.format_ts_fn(ctxt, kwd) end, "timestamp #{kwd}")
)
end)
end
@spec _add_formats_to_pipeline(any()) :: FunctionList.t
defp _add_formats_to_pipeline(ast)
defp _add_formats_to_pipeline([flist, nil]), do: flist
defp _add_formats_to_pipeline([flist, format]) do
_add_sexps_from_format(flist, format)
end
@spec _add_sexps_from_format(FunctionList.t, list()) :: FunctionList.t
defp _add_sexps_from_format(flist, format)
defp _add_sexps_from_format(flist, [size, nil, filler]) do
cmd = if size < 0, do: :rpad, else: :lpad
flist
|> FunctionList.add_sexp([cmd, abs(size) | filler])
end
defp _add_sexps_from_format(flist, [size, "x", filler]) do
cmd = if size < 0, do: :rpad, else: :lpad
flist
|> FunctionList.add_sexp([:to_s, 16])
|> FunctionList.add_sexp([cmd, abs(size) | filler])
end
@spec _make_store_variable_function(binary(), binary()) :: FunctionList.t()
defp _make_store_variable_function(name, ast) do
with {:ok, pattern} <- compile(ast) do
FunctionList.new(
Fn.new(
fn line, ctxt ->
result = CliexMap.Mapper.transform_line(line, pattern, ctxt)
{Context.store_variable(ctxt, {name, result}), ""}
end,
"#{name} = #{ast}"
)
)
end
end
@spec _make_field(ast()) :: FunctionList.t()
defp _make_field(ast)
defp _make_field(nil), do: FunctionList.new(field_function(0))
defp _make_field(n), do: FunctionList.new(field_function(n))
@spec _make_lnb(ast()) :: FunctionList.t()
defp _make_lnb(ast) do
function_list =
FunctionList.new(Fn.new(fn {lnb, _}, _context -> lnb end, "lnb #{inspect(ast)}"))
case ast do
[_, nil] -> function_list
[_, values] -> FunctionList.add_function(function_list, _make_lnb_increments(values))
end
end
@spec _make_lnb_increments(list()) :: Fn.t
defp _make_lnb_increments(values) do
{start, step} =
case values do
[strt] -> {strt, 1}
[strt, stp] -> {strt, stp}
end
Fn.new(fn lnb, _context -> start + lnb * step end, "lnb inc start: #{start}, step: #{step}")
end
@spec _make_load_variable(list()) :: FunctionList.t()
defp _make_load_variable(["L", name]) do
FunctionList.new(
Fn.new(
fn _, ctxt ->
Context.load_variable(ctxt, name)
end,
"load variable #{name}"
)
)
end
@spec _unify_function_pipeline(list()) :: FunctionList.t()
defp _unify_function_pipeline([function_list | [functions]]) do
# IO.inspect(functions, label: :result_from_lispy)
functions
|> Enum.reduce(function_list, fn fun, fun_list ->
FunctionList.add_sexp(fun_list, fun)
end)
end
end
# SPDX-License-Identifier: Apache-2.0