diff options
| -rw-r--r-- | example/input.txt | 4 | ||||
| -rw-r--r-- | lib/elixir_math_parser.ex | 56 | ||||
| -rw-r--r-- | lib/main.ex | 26 | ||||
| -rw-r--r-- | mix.exs | 2 | ||||
| -rw-r--r-- | src/elixir_math_parser.yrl | 14 | ||||
| -rw-r--r-- | src/elixir_math_parser_lexer.xrl | 3 | ||||
| -rw-r--r-- | test/elixir_math_parser_test.exs | 24 |
7 files changed, 72 insertions, 57 deletions
diff --git a/example/input.txt b/example/input.txt index ec70a41..a530f47 100644 --- a/example/input.txt +++ b/example/input.txt @@ -1,8 +1,8 @@ :a = 7 :b = 4 -:result = (:a + :b) * 10 / 2 +:res = (:a + :b) * 10 / 2 :x = 20 :y = 3 :z = 6 -:value = 10 + :z / :y * :x / 4 +:val = 10 + :z / :y * :x / 4 diff --git a/lib/elixir_math_parser.ex b/lib/elixir_math_parser.ex index 1f147aa..91f67f9 100644 --- a/lib/elixir_math_parser.ex +++ b/lib/elixir_math_parser.ex @@ -4,46 +4,58 @@ defmodule ElixirMathParser do """ defp reduce_to_value({:int, _line, value}, _state) do - value + {:ok, value} + end + + defp reduce_to_value({:atom, _line, atom}, state) do + if !Map.has_key?(state, atom) do + {:error, "value not found for " <> to_string(atom)} + else + {:ok, state[atom]} + end end defp reduce_to_value({:add_op, lhs, rhs}, state) do - reduce_to_value(lhs, state) + reduce_to_value(rhs, state) + {:ok, op1} = reduce_to_value(lhs, state) + {:ok, op2} = reduce_to_value(rhs, state) + {:ok, op1 + op2} end defp reduce_to_value({:sub_op, lhs, rhs}, state) do - reduce_to_value(lhs, state) - reduce_to_value(rhs, state) + {:ok, op1} = reduce_to_value(lhs, state) + {:ok, op2} = reduce_to_value(rhs, state) + {:ok, op1 - op2} end defp reduce_to_value({:mul_op, lhs, rhs}, state) do - reduce_to_value(lhs, state) * reduce_to_value(rhs, state) + {:ok, op1} = reduce_to_value(lhs, state) + {:ok, op2} = reduce_to_value(rhs, state) + {:ok, op1 / op2} end defp reduce_to_value({:div_op, lhs, rhs}, state) do - reduce_to_value(lhs, state) / reduce_to_value(rhs, state) + {:ok, op1} = reduce_to_value(lhs, state) + {:ok, op2} = reduce_to_value(rhs, state) + {:ok, op1 / op2} end - defp reduce_to_value({:atom, _line, atom}, state) do - state[atom] - end - defp evaluate_tree([{:assign, {:atom, _line, lhs}, rhs} | tail], state) do - rhs_value = reduce_to_value(rhs, state) + {:ok, rhs_value} = reduce_to_value(rhs, state) evaluate_tree(tail, Map.merge(state, %{lhs => rhs_value})) end - defp evaluate_tree([], state) do - state - end + defp evaluate_tree([], state) do + state + end - def process_tree(tree) do - evaluate_tree(tree, %{}) - end + def process_tree(tree) do + evaluate_tree(tree, %{}) + end - def parse_file(filename) do - text = File.read!(filename) - {:ok, tokens, _line} = :elixir_math_parser_lexer.string(String.to_charlist(text)) - {:ok, tree} = :elixir_math_parser.parse(tokens) - process_tree(tree) - end + def parse_file(filename) do + text = File.read!(filename) + {:ok, tokens, _line} = :elixir_math_parser_lexer.string(String.to_charlist(text)) + {:ok, tree} = :elixir_math_parser.parse(tokens) + process_tree(tree) + end end diff --git a/lib/main.ex b/lib/main.ex index ca73cb5..f1f0a20 100644 --- a/lib/main.ex +++ b/lib/main.ex @@ -1,28 +1,28 @@ defmodule ElixirMathParser.Main do - def process_parse({:error, result}) do - IO.puts "\nParse error" - IO.inspect result + def process_parse({:error, result}) do + IO.puts("\nParse error") + IO.inspect(result) end def process_parse({:ok, tree}) do - IO.puts "\nParse tree" - IO.inspect tree, pretty: true + IO.puts("\nParse tree") + IO.inspect(tree, pretty: true) state = ElixirMathParser.process_tree(tree) - IO.puts "\nFinal state" - IO.inspect state, pretty: true + IO.puts("\nFinal state") + IO.inspect(state, pretty: true) end - + def main(args) do filename = Enum.fetch!(args, 0) - IO.puts "Parsing #{filename}" + IO.puts("Parsing #{filename}") text = File.read!(filename) {:ok, tokens, line} = :elixir_math_parser_lexer.string(String.to_charlist(text)) - IO.puts "Parsed #{filename}, stopped at line #{line}" - IO.puts "\nTokens:" - IO.inspect tokens, pretty: true + IO.puts("Parsed #{filename}, stopped at line #{line}") + IO.puts("\nTokens:") + IO.inspect(tokens, pretty: true) process_parse(:elixir_math_parser.parse(tokens)) - end + end end @@ -5,7 +5,7 @@ defmodule ElixirMathParser.MixProject do [ app: :elixir_math_parser, version: "0.1.0", - elixir: "~> 1.19", + elixir: "~> 1.18", start_permanent: Mix.env() == :prod, deps: deps(), escript: [main_module: ElixirMathParser.Main], diff --git a/src/elixir_math_parser.yrl b/src/elixir_math_parser.yrl index 762cf4b..3ec4946 100644 --- a/src/elixir_math_parser.yrl +++ b/src/elixir_math_parser.yrl @@ -1,7 +1,7 @@ Nonterminals root - assignment - assignments + statement + statements expr . @@ -15,6 +15,7 @@ Terminals '=' '(' ')' + ';;' . Rootsymbol @@ -28,12 +29,13 @@ Left 400 '*'. Left 400 '/'. Left 600 '('. -root -> assignments : '$1'. +root -> statements : '$1'. -assignments -> assignment : '$1'. -assignments -> assignment assignments : lists:merge('$1', '$2'). +statements -> statement : '$1'. +statements -> statement statements : lists:merge('$1', '$2'). +statements -> statement ';;' statements : lists:merge('$1', '$3'). -assignment -> atom '=' expr : [{assign, '$1', '$3'}]. +statement -> atom '=' expr : [{assign, '$1', '$3'}]. expr -> int : unwrap('$1'). expr -> atom : '$1'. diff --git a/src/elixir_math_parser_lexer.xrl b/src/elixir_math_parser_lexer.xrl index 8d7920a..7256090 100644 --- a/src/elixir_math_parser_lexer.xrl +++ b/src/elixir_math_parser_lexer.xrl @@ -1,7 +1,7 @@ Definitions. INT = [0-9]+ NAME = :[a-zA-Z_][a-zA-Z0-9_]* -WHITESPACE = [\s\t\n\r]|;{2} +WHITESPACE = [\s\t\n\r] Rules. \+ : {token, {'+', TokenLine}}. @@ -11,6 +11,7 @@ Rules. \= : {token, {'=', TokenLine}}. \( : {token, {'(', TokenLine}}. \) : {token, {')', TokenLine}}. +;; : {token, {';;', TokenLine}}. {NAME} : {token, {atom, TokenLine, to_atom(TokenChars)}}. {INT} : {token, {int, TokenLine, TokenChars}}. {WHITESPACE}+ : skip_token. diff --git a/test/elixir_math_parser_test.exs b/test/elixir_math_parser_test.exs index 3ccfb94..c71ee79 100644 --- a/test/elixir_math_parser_test.exs +++ b/test/elixir_math_parser_test.exs @@ -39,25 +39,25 @@ defmodule ElixirMathParserTest do test "variable reference" do assert parse_and_eval(""" - :a = 3 - :b = 2 - :result = :a + :b - """) == %{a: 3, b: 2, result: 5} + :a = 3 + :b = 2 + :result = :a + :b + """) == %{a: 3, b: 2, result: 5} end test "variable update" do assert parse_and_eval(""" - :a = 3 - :b = 2 - :a = :a + :b - """) == %{a: 5, b: 2} + :a = 3 + :b = 2 + :a = :a + :b + """) == %{a: 5, b: 2} end test "variables with numbers in name" do assert parse_and_eval(""" - :a1 = 3 - :a2 = 2 - :b1 = :a1 + :a2 - """) == %{a1: 3, a2: 2, b1: 5} + :a1 = 3 + :a2 = 2 + :b1 = :a1 + :a2 + """) == %{a1: 3, a2: 2, b1: 5} end end |
