Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
- { otp: "25", elixir: "1.14" }
- { otp: "26", elixir: "1.16" }
- { otp: "27", elixir: "1.18" }
- { otp: "28", elixir: "1.19" }

env:
MIX_ENV: test
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
elixir 1.18.2-otp-27
erlang 27.2.4
elixir 1.19.0-otp-28
erlang 28.1
29 changes: 17 additions & 12 deletions lib/codegen.ex
Original file line number Diff line number Diff line change
Expand Up @@ -175,18 +175,23 @@ defmodule Xpeg.Codegen do
end

{:code, code} ->
quote location: :keep do
def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do
{cap_stack, captures} = Xpeg.collect_captures(cap_stack, captures)
func = unquote(code)

{captures, ctx} =
case unquote(options[:userdata]) do
true -> func.(captures, ctx)
_ -> {func.(captures), ctx}
end

parse(unquote(ip + 1), s, si, ctx, back_stack, ret_stack, cap_stack, captures)
if options[:userdata] do
quote location: :keep do
def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do
{cap_stack, captures} = Xpeg.collect_captures(cap_stack, captures)
func = unquote(code)
{captures, ctx} = func.(captures, ctx)
parse(unquote(ip + 1), s, si, ctx, back_stack, ret_stack, cap_stack, captures)
end
end
else
quote location: :keep do
def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do
{cap_stack, captures} = Xpeg.collect_captures(cap_stack, captures)
func = unquote(code)
captures = func.(captures)
parse(unquote(ip + 1), s, si, ctx, back_stack, ret_stack, cap_stack, captures)
end
end
end

Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Xpeg.MixProject do
def project do
[
app: :xpeg,
version: "0.10.0",
version: "0.11.0",
description: "Native Elixir PEG (Parsing Expression Grammars) string matching library",
elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
Expand Down Expand Up @@ -36,7 +36,7 @@ defmodule Xpeg.MixProject do

defp deps do
[
{:ex_doc, "~> 0.14", only: [:dev], runtime: false}
{:ex_doc, "~> 0.38", only: [:dev], runtime: false}
# {:exprof, "~> 0.2.0", only: [:dev, :test], runtime: false},
# {:poison, "~> 5.0", only: [:dev, :test], runtime: false},
# {:jason, "~> 1.2", only: [:dev, :test], runtime: false},
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
%{
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
"ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"},
"ex_doc": {:hex, :ex_doc, "0.38.4", "ab48dff7a8af84226bf23baddcdda329f467255d924380a0cf0cee97bb9a9ede", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "f7b62346408a83911c2580154e35613eb314e0278aeea72ed7fedef9c1f165b2"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
Expand Down
32 changes: 16 additions & 16 deletions test/examples_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ defmodule ExamplesTest do
peg Dict do
Dict <- Pair * star("," * Pair) * !1
Pair <- Word * "=" * Number * fn [a, b | cs] -> [{b, a} | cs] end
Word <- str(+{'a'..'z'})
Number <- int(+{'0'..'9'})
Word <- str(+{~c"a"..~c"z"})
Number <- int(+{~c"0"..~c"9"})
end

r = match(p, "grass=4,horse=1,star=2")
Expand All @@ -26,9 +26,9 @@ defmodule ExamplesTest do
Exp <- Term * star(Exp_op)
Term <- Factor * star(Term_op)
Factor <- Number | "(" * Exp * ")"
Number <- int(+{'0'..'9'})
Term_op <- str({'*', '/'}) * Factor * fn [b, op, a | cs] -> [{op, a, b} | cs] end
Exp_op <- str({'+', '-'}) * Term * fn [b, op, a | cs] -> [{op, a, b} | cs] end
Number <- int(+{~c"0"..~c"9"})
Term_op <- str({~c"*", ~c"/"}) * Factor * fn [b, op, a | cs] -> [{op, a, b} | cs] end
Exp_op <- str({~c"+", ~c"-"}) * Term * fn [b, op, a | cs] -> [{op, a, b} | cs] end
end

cs = match(p, "1+(2-3*4)/5").captures
Expand All @@ -41,32 +41,32 @@ defmodule ExamplesTest do
p =
peg Json do
# White space
S <- star({' ', '\t', '\r', '\n'})
S <- star({" ", "\t", "\r", "\n"})

# Basic atoms
true <- "true" * fn cs -> [true | cs] end
false <- "false" * fn cs -> [false | cs] end
Null <- "null" * fn cs -> [nil | cs] end

# Strings
Xdigit <- {'0'..'9', 'a'..'f', 'A'..'F'}
Unicode_escape <- 'u' * Xdigit[4]
Escape <- '\\' * ({'"', '\\', '/', 'b', 'f', 'n', 'r', 't'} | Unicode_escape)
String_body <- star(Escape) * star(+({'\x20'..'\x7f'} - {'"'} - {'\\'}) * star(Escape))
String <- '"' * str(String_body) * '"'
Xdigit <- {~c"0"..~c"9", ~c"a"..~c"f", ~c"A"..~c"F"}
Unicode_escape <- "u" * Xdigit[4]
Escape <- "\\" * ({~c"\"", ~c"\\", ~c"/", ~c"b", ~c"f", ~c"n", ~c"r", ~c"t"} | Unicode_escape)
String_body <- star(Escape) * star(+({~c" "..~c"\x7f"} - {~c"\""} - {~c"\\"}) * star(Escape))
String <- "\"" * str(String_body) * "\""

# Numbers are converted to Elixir float
Minus <- '-'
Int_part <- '0' | {'1'..'9'} * star({'0'..'9'})
Fract_part <- "." * +{'0'..'9'}
Exp_part <- {'e', 'E'} * opt({'+', '-'}) * +{'0'..'9'}
Minus <- "-"
Int_part <- "0" | {~c"1"..~c"9"} * star({~c"0"..~c"9"})
Fract_part <- "." * +{~c"0"..~c"9"}
Exp_part <- {~c"e", ~c"E"} * opt({~c"+", ~c"-"}) * +{~c"0"..~c"9"}
Number <- float(opt(Minus) * Int_part * opt(Fract_part) * opt(Exp_part))

# Objects are represented by an Elixir map
Obj_pair <-
S * String * S * ":" * Value * fn [v, k, obj | cs] -> [Map.put(obj, k, v) | cs] end

Object <- '{' * fn cs -> [%{} | cs] end * (Obj_pair * star("," * Obj_pair) | S) * "}"
Object <- "{" * fn cs -> [%{} | cs] end * (Obj_pair * star("," * Obj_pair) | S) * "}"

# Arrays are represented by an Elixir list
Array_elem <- Value * fn [v, a | cs] -> [[v | a] | cs] end
Expand Down
136 changes: 68 additions & 68 deletions test/xpeg_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ defmodule XpegTest do
run(patt("a"), "a")
run(patt("a"), "b", :error)
run(patt("abc"), "abc")
run(patt('abc'), "abc")
run(patt(~c"abc"), "abc")
run(patt(~c"abc"), "abc")
run(patt("abc"), "-bcd", :error)
run(patt("abc"), "a-cd", :error)
Expand All @@ -29,38 +29,38 @@ defmodule XpegTest do
end

test "set" do
run(patt({'a'}), "a")
run(patt({'b'}), "a", :error)
run(patt({'a', 'b'}), "a")
run(patt({'a', 'b'}), "b")
run(patt({'a', 'b'}), "c", :error)
run(patt({'a', 'b', 'c'}), "a")
run(patt({'a', 'b', 'c'}), "b")
run(patt({'a', 'b', 'c'}), "c")
run(patt({'a', 'b', 'c'}), "d", :error)
run(patt({'a'..'c'}), "a")
run(patt({'a'..'c'}), "b")
run(patt({'a'..'c'}), "c")
run(patt({'a'..'c'}), "d", :error)
run(patt({'a'..'c', 'd'}), "a")
run(patt({'a'..'c', 'd'}), "b")
run(patt({'a'..'c', 'd'}), "c")
run(patt({'a'..'c', 'd'}), "d")
run(patt({'a', 'b'..'d'}), "a")
run(patt({'a', 'b'..'d'}), "b")
run(patt({'a', 'b'..'d'}), "c")
run(patt({'a', 'b'..'d'}), "d")
run(patt({'a', 'b'..'c', 'd'}), "a")
run(patt({'a', 'b'..'c', 'd'}), "b")
run(patt({'a', 'b'..'c', 'd'}), "c")
run(patt({'a', 'b'..'c', 'd'}), "d")
run(patt({'a'..'c', 'e'..'g'}), "a")
run(patt({'a'..'c', 'e'..'g'}), "b")
run(patt({'a'..'c', 'e'..'g'}), "c")
run(patt({'a'..'c', 'e'..'g'}), "d", :error)
run(patt({'a'..'c', 'e'..'g'}), "e")
run(patt({'a'..'c', 'e'..'g'}), "f")
run(patt({'a'..'c', 'e'..'g'}), "g")
run(patt({~c"a"}), "a")
run(patt({~c"b"}), "a", :error)
run(patt({~c"a", ~c"b"}), "a")
run(patt({~c"a", ~c"b"}), "b")
run(patt({~c"a", ~c"b"}), "c", :error)
run(patt({~c"a", ~c"b", ~c"c"}), "a")
run(patt({~c"a", ~c"b", ~c"c"}), "b")
run(patt({~c"a", ~c"b", ~c"c"}), "c")
run(patt({~c"a", ~c"b", ~c"c"}), "d", :error)
run(patt({~c"a"..~c"c"}), "a")
run(patt({~c"a"..~c"c"}), "b")
run(patt({~c"a"..~c"c"}), "c")
run(patt({~c"a"..~c"c"}), "d", :error)
run(patt({~c"a"..~c"c", ~c"d"}), "a")
run(patt({~c"a"..~c"c", ~c"d"}), "b")
run(patt({~c"a"..~c"c", ~c"d"}), "c")
run(patt({~c"a"..~c"c", ~c"d"}), "d")
run(patt({~c"a", ~c"b"..~c"d"}), "a")
run(patt({~c"a", ~c"b"..~c"d"}), "b")
run(patt({~c"a", ~c"b"..~c"d"}), "c")
run(patt({~c"a", ~c"b"..~c"d"}), "d")
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "a")
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "b")
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "c")
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "d")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "a")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "b")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "c")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "d", :error)
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "e")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "f")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "g")
end

test "set using sigil" do
Expand Down Expand Up @@ -90,13 +90,13 @@ defmodule XpegTest do
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "b")
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "c")
run(patt({~c"a", ~c"b"..~c"c", ~c"d"}), "d")
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "a")
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "b")
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "c")
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "d", :error)
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "e")
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "f")
run(patt({~c"a"..~c"c", 'e'..~c"g"}), "g")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "a")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "b")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "c")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "d", :error)
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "e")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "f")
run(patt({~c"a"..~c"c", ~c"e"..~c"g"}), "g")
end

test "zero-or-one" do
Expand All @@ -106,10 +106,10 @@ defmodule XpegTest do
end

test "zero-or-more" do
run(patt(star('a')), "aaaa")
run(patt(star('a') * 'b'), "aaaab")
run(patt(star('a') * 'b'), "bbbbb")
run(patt(star('a') * 'b'), "caaab", :error)
run(patt(star(~c"a")), "aaaa")
run(patt(star(~c"a") * ~c"b"), "aaaab")
run(patt(star(~c"a") * ~c"b"), "bbbbb")
run(patt(star(~c"a") * ~c"b"), "caaab", :error)
end

test "zero-or-more using sigils" do
Expand All @@ -120,9 +120,9 @@ defmodule XpegTest do
end

test "one-or-more" do
run(patt(+'a' * 'b'), "aaaab")
run(patt(+'a' * 'b'), "ab")
run(patt(+'a' * 'b'), "b", :error)
run(patt(+~c"a" * ~c"b"), "aaaab")
run(patt(+~c"a" * ~c"b"), "ab")
run(patt(+~c"a" * ~c"b"), "b", :error)
end

test "one-or-more using sigils" do
Expand All @@ -132,8 +132,8 @@ defmodule XpegTest do
end

test "not-predicate" do
run(patt('a' * !'b'), "ac")
run(patt('a' * !'b'), "ab", :error)
run(patt(~c"a" * !~c"b"), "ac")
run(patt(~c"a" * !~c"b"), "ab", :error)
end

test "not-predicate using sigils" do
Expand All @@ -156,15 +156,15 @@ defmodule XpegTest do
end

test "[m..n]: count" do
run(patt('a'[2..4] * !1), "", :error)
run(patt('a'[2..4] * !1), "a", :error)
run(patt('a'[2..4] * !1), "aa")
run(patt('a'[2..4] * !1), "aaa")
run(patt('a'[2..4] * !1), "aaaa")
run(patt('a'[2..4] * !1), "aaaaa", :error)
run(patt('a'[0..1] * !1), "")
run(patt('a'[0..1] * !1), "a")
run(patt('a'[0..1] * !1), "aa", :error)
run(patt(~c"a"[2..4] * !1), "", :error)
run(patt(~c"a"[2..4] * !1), "a", :error)
run(patt(~c"a"[2..4] * !1), "aa")
run(patt(~c"a"[2..4] * !1), "aaa")
run(patt(~c"a"[2..4] * !1), "aaaa")
run(patt(~c"a"[2..4] * !1), "aaaaa", :error)
run(patt(~c"a"[0..1] * !1), "")
run(patt(~c"a"[0..1] * !1), "a")
run(patt(~c"a"[0..1] * !1), "aa", :error)
end

test "[m..n]: count using sigils" do
Expand Down Expand Up @@ -194,24 +194,24 @@ defmodule XpegTest do
test "-: difference" do
run(patt("abcd" - "abcdef"), "abcdefgh", :error)
run(patt("abcd" - "abcdf"), "abcdefgh")
run(patt({'a', 'b', 'c'} - {'a'}), "a", :error)
run(patt({~c"a", ~c"b", ~c"c"} - {~c"a"}), "a", :error)
run(patt({~c"a", ~c"b", ~c"c"} - {~c"a"}), "a", :error)
end

test "Misc combos" do
run(patt('a' | 'b' * 'c'), "a")
run(patt('a' | 'b' * 'c' | 'd' * 'e' * 'f'), "a")
run(patt('a' | 'b' * 'c' | 'd' * 'e' * 'f'), "bc")
run(patt('a' | 'b' * 'c' | 'd' * 'e' * 'f'), "def")
run(patt({'a', 'b'} * 'c' | {'a', 'b'} * 'e'), "ac")
run(patt({'a', 'b'} * 'c' | {'a', 'b'} * 'e'), "ae")
run(patt(~c"a" | ~c"b" * ~c"c"), "a")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * ~c"f"), "a")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * ~c"f"), "bc")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * ~c"f"), "def")
run(patt({~c"a", ~c"b"} * ~c"c" | {~c"a", ~c"b"} * ~c"e"), "ac")
run(patt({~c"a", ~c"b"} * ~c"c" | {~c"a", ~c"b"} * ~c"e"), "ae")
end

test "Misc combos using sigils" do
run(patt(~c"a" | ~c"b" * ~c"c"), "a")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * 'f'), "a")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * 'f'), "bc")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * 'f'), "def")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * ~c"f"), "a")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * ~c"f"), "bc")
run(patt(~c"a" | ~c"b" * ~c"c" | ~c"d" * ~c"e" * ~c"f"), "def")
run(patt({~c"a", ~c"b"} * ~c"c" | {~c"a", ~c"b"} * ~c"e"), "ac")
run(patt({~c"a", ~c"b"} * ~c"c" | {~c"a", ~c"b"} * ~c"e"), "ae")
end
Expand Down