Current section

Files

Jump to
minipeg lib minipeg parsers lispy_parser.ex
Raw

lib/minipeg/parsers/lispy_parser.ex

defmodule Minipeg.Parsers.LispyParser do
use Minipeg.Types
alias Minipeg.Parser
import Minipeg.{Combinators, Parsers}
@moduledoc ~S"""
Parses s-expressions (lispy)
The syntax is very simple
s_exp:
----> ( `opening_literal`) ---------------------------------------------+-------------> ( `closing_literal` ) ------>
^ |
| |
| +---------+ |
+--- (whitespace) <--- | element | <-------+
+---------+
element:
string | integer | name | s_exp
`opening_literal` defaults to `(` and closing literal defaults to `)`, of course
"""
@doc ~S"""
Parses an s-expression according to the schema above
"""
@spec s_exp_parser(binary?(), binary(), binary()) :: Parser.t
def s_exp_parser(name \\ nil, opening_literal \\ "(", closing_literal \\ ")") do
sequence([
literal_parser(opening_literal),
list_parser(element_parser(), ws_parser(true, 1), "s_exp_parser"),
literal_parser(closing_literal),
])
|> map(&Enum.at(&1, 1), name || "s_exp_parser")
end
@doc ~S"""
parses a string, integer, name (atom) or s-exp
"""
@spec element_parser(binary?()) :: Parser.t
def element_parser(name \\ nil) do
name1 = name || "element_parser"
select([
int_parser(),
name_parser(),
string_parser(),
lazy(fn -> s_exp_parser() end)
], name1)
end
@doc ~S"""
Parses a name which is actually any non digit starting printable character sequence
"""
@spec name_parser(binary?()) :: Parser.t
def name_parser(name \\ nil) do
name1 = name || "name_parser"
sequence([
start_char_parser(),
many(inner_char_parser())
], name1)
|> map(fn ast -> ast |> IO.chardata_to_string |> String.to_atom end)
end
@symbols "&!-*^$:/,.;=+<>_"
@spec inner_char_parser() :: Parser.t
defp inner_char_parser do
select([
char_parser(:alnum),
char_parser(@symbols)
], "inner_char_parser")
end
@spec start_char_parser() :: Parser.t
defp start_char_parser do
select([
char_parser(:alpha),
char_parser(@symbols)
], "start_char_parser")
end
end
# SPDX-License-Identifier: Apache-2.0