Skip to content

Commit

Permalink
Removes extraneous :expr nodes to only wrap parenthesized expressions…
Browse files Browse the repository at this point in the history
… in an :expr node
  • Loading branch information
Lucassifoni committed Feb 19, 2024
1 parent 7a08d15 commit 3996e37
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 182 deletions.
9 changes: 8 additions & 1 deletion ovo/lib/ovo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,21 @@ defmodule Ovo do
"""

@spec tokenize(String.t()) :: [Ovo.Token.t()]
@doc """
Tokenizes input (a string) to produce a list of 2-tuples as defined by Ovo.Token
"""
def tokenize(input), do: Ovo.Tokenizer.tokenize(input)

@doc """
Parses a list of tokens (not directly input), producing an Ovo.Ast
"""
def parse(tokens), do: Ovo.Parser.parse(tokens)

@doc ~S"""
Runs Ovo code through the interpreter
iex> {%Ovo.Ast{kind: :integer, nodes: [], value: 3}, _} = Ovo.run("addone = \\a -> add(1, a) end addone(2)")
iex> Ovo.run("addone = \\a -> add(1, a) end addone(2)")
iex> {%Ovo.Ast{kind: :integer, nodes: [], value: 3}}
"""
def run(code, input \\ %{}), do: Ovo.Interpreter.run(code, input)
Expand Down
16 changes: 8 additions & 8 deletions ovo/lib/ovo/ast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,27 @@ defmodule Ovo.Ast do
defstruct [:kind, :nodes, :value]

@spec make(kind(), term(), list(t())) :: t()
def make(kind \\ :root, value \\ nil, children \\ []),
def make(kind, value, children),
do: %__MODULE__{kind: kind, nodes: children, value: value}

def root(children), do: make(:root, nil, children)

def float(val), do: make(:float, val)
def float(val), do: make(:float, val, [])

def integer(val), do: make(:integer, val)
def integer(val), do: make(:integer, val, [])

def string(val), do: make(:string, val)
def string(val), do: make(:string, val, [])

def symbol(val), do: make(:symbol, val)
def symbol(val), do: make(:symbol, val, [])

def bool(val), do: make(:bool, val)
def bool(val), do: make(:bool, val, [])

def map(val), do: make(:map, val)
def map(val), do: make(:map, val, [])

def list(children), do: make(:list, nil, children)

def expr([val]), do: make(:expr, val, [])
def expr(val), do: make(:expr, val, [])
def expr(val), do: val

def assignment(symbol, expr), do: make(:assignment, symbol, expr)

Expand Down
4 changes: 2 additions & 2 deletions ovo/lib/ovo/builtins.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ defmodule Ovo.Builtins do

defp access(nodes, env) do
case map_nodes(nodes, env) do
[%{kind: :string, value: v}] = n ->
[%{kind: :string, value: v}] ->
data = Ovo.Env.find_value("data", env)
Map.get(data.value, v)

b ->
_ ->
:error
end
end
Expand Down
36 changes: 33 additions & 3 deletions ovo/lib/ovo/combinators.ex
Original file line number Diff line number Diff line change
@@ -1,19 +1,46 @@
defmodule Ovo.Combinators do
@moduledoc """
Parser combinators. Samples below operate on primitives instead of ovo tokens for clarity.
Parser combinators do not need to be tied to a particular data structure (tokens) because they
simply are higher-order functions over the idea of matching a pattern, or not.
Combinators are used to express concepts such as "a parser followed by another" (Ovo.Combinators.then/2),
"this particular parser, or this one" (Ovo.Combinators.either/2).
The available combinators in this file are :
- Ovo.Combinators.nothing/1 : always matches anything, does not emit.
- Ovo.Combinators.either/2 : ParserA or ParserB
- Ovo.Combinators.then/2 : ParserA then ParserB
- Ovo.Combinators.all/1 : All parsers in a list
- Ovo.Combinators.any/1 : First matching parser in a list
- Ovo.Combinators.repeat/1 : Emits while the parser matches
"""

@doc """
Sample parser that succeeds if the current token is 1, fails otherwise.
"""
def sample_one([1 | rest]), do: {:ok, [1], rest}
def sample_one(a), do: {:error, [], a}

@doc """
Sample parser that succeeds if the current token is ","
"""
def sample_comma(["," | rest]), do: {:ok, [","], rest}
def sample_comma(a), do: {:error, [], a}

@doc """
Sample parser that succeeds if the current token is "a" or "b".
"""
def sample_letter(["a" | rest]), do: {:ok, ["a"], rest}
def sample_letter(["b" | rest]), do: {:ok, ["b"], rest}
def sample_letter(a), do: {:error, [], a}

defp maybe_cons([], a), do: a
defp maybe_cons(a, b), do: [a | b]
@doc """
Utility to avoid adding empty list to the output node list.
"""
def maybe_cons([], a), do: a
def maybe_cons(a, b), do: [a | b]

@doc """
Succeeds if either parserA or parserB succeeds.
Expand All @@ -36,6 +63,9 @@ defmodule Ovo.Combinators do
end
end

@doc """
Always matches, but does not emit a node.
"""
def nothing(tokens) do
{:ok, [], tokens}
end
Expand Down Expand Up @@ -99,7 +129,7 @@ defmodule Ovo.Combinators do
end
end

defp decide(parser, res, rest, tokens) do
def decide(parser, res, rest, tokens) do
case parser.(rest) do
{:ok, resn, restn} -> {:ok, maybe_cons(resn, res), restn}
_ -> {:error, [], tokens}
Expand Down
1 change: 0 additions & 1 deletion ovo/lib/ovo/converter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ defmodule Ovo.Converter do
def ovo_to_elixir(%Ast{kind: :integer, value: v}), do: v
def ovo_to_elixir(%Ast{kind: :string, value: v}), do: v
def ovo_to_elixir(%Ast{kind: :symbol, value: v}), do: v
def ovo_to_elixir(%Ast{kind: :bool, value: v}), do: v
def ovo_to_elixir(%Ast{kind: :expr, value: v}), do: ovo_to_elixir(v)

def ovo_to_elixir(%Ast{kind: :map, value: v}),
Expand Down
4 changes: 2 additions & 2 deletions ovo/lib/ovo/env.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ defmodule Ovo.Env do
end

def user_bindings(env) do
Agent.get(env, &(&1.user))
Agent.get(env, & &1.user)
end

def update_captures(env, bindings) do
Agent.update(env, &(Map.put(&1, :user, Map.merge(&1.user, bindings))))
Agent.update(env, &Map.put(&1, :user, Map.merge(&1.user, bindings)))
end

@spec bind_input(pid(), map()) :: map()
Expand Down
Loading

0 comments on commit 3996e37

Please sign in to comment.