From adf6cdcdc7114193cb0ecbab338b307983a4acfd Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Mon, 30 Jul 2018 12:00:27 +0900 Subject: [PATCH 01/12] Elixir: new parser Imported from https://github.com/mmorearty/elixir-ctags. Close #1758. See also https://github.com/mmorearty/elixir-ctags/issues/5. The change from the original .ctags. * Put the original LICENSE file as the header of the .ctags. * Use --map= option instead of --langmap because optlib2c doesn't handle --langmap option. * Define kinds explicitly with --kinddef- option. * Remove backslashes before double quotes chars in the regex pattern for "test". * Use singular forms for kind names. TODO: Test cases for above kinds are needed. c callbacks (defcallback ...) d delegates (defdelegate ...) e exceptions (defexception ...) i implementations (defimpl ...) a macros (defmacro ...) o operators (e.g. "defmacro a <<< b") p protocols (defprotocol...) r records (defrecord...) Signed-off-by: Masatake YAMATO --- Units/simple-elixir.d/args.ctags | 2 + Units/simple-elixir.d/expected.tags | 3 + Units/simple-elixir.d/input.ex | 18 +++++ docs/news.rst | 1 + main/parsers_p.h | 1 + makefiles/optlib2c_input.mak | 3 +- optlib/elixir.c | 104 ++++++++++++++++++++++++++++ optlib/elixir.ctags | 77 ++++++++++++++++++++ win32/ctags_vs2013.vcxproj | 1 + win32/ctags_vs2013.vcxproj.filters | 3 + 10 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 Units/simple-elixir.d/args.ctags create mode 100644 Units/simple-elixir.d/expected.tags create mode 100644 Units/simple-elixir.d/input.ex create mode 100644 optlib/elixir.c create mode 100644 optlib/elixir.ctags diff --git a/Units/simple-elixir.d/args.ctags b/Units/simple-elixir.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/simple-elixir.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/simple-elixir.d/expected.tags b/Units/simple-elixir.d/expected.tags new file mode 100644 index 0000000000..b4fc330375 --- /dev/null +++ b/Units/simple-elixir.d/expected.tags @@ -0,0 +1,3 @@ +drive input.ex /^def drive(%User{age: age}) when age >= 16 do$/;" function +MathTest input.ex /^defmodule MathTest do$/;" module +can add two numbers input.ex /^ test "can add two numbers" do$/;" test diff --git a/Units/simple-elixir.d/input.ex b/Units/simple-elixir.d/input.ex new file mode 100644 index 0000000000..3bf3272596 --- /dev/null +++ b/Units/simple-elixir.d/input.ex @@ -0,0 +1,18 @@ +# +# Taken from https://elixir-lang.org/ +# + +def drive(%User{age: age}) when age >= 16 do + # Code that drives a car +end + +drive(User.get("John Doe")) +#=> Fails if the user is under 16 + +defmodule MathTest do + use ExUnit.Case, async: true + + test "can add two numbers" do + assert 1 + 1 == 2 + end +end diff --git a/docs/news.rst b/docs/news.rst index 0cb951eeb3..480c9964a9 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -52,6 +52,7 @@ The following parsers have been added: * Diff * DTD * DTS +* Elixir *optlib* * Elm *optlib* * Falcon * Gdbinit script *optlib* diff --git a/main/parsers_p.h b/main/parsers_p.h index 289875ab59..7e86b25c2c 100644 --- a/main/parsers_p.h +++ b/main/parsers_p.h @@ -70,6 +70,7 @@ DTSParser, \ DosBatchParser, \ EiffelParser, \ + ElixirParser, \ ElmParser, \ ErlangParser, \ FalconParser, \ diff --git a/makefiles/optlib2c_input.mak b/makefiles/optlib2c_input.mak index 11b01e827d..84c7507f57 100644 --- a/makefiles/optlib2c_input.mak +++ b/makefiles/optlib2c_input.mak @@ -3,7 +3,8 @@ OPTLIB2C_INPUT = \ optlib/RSpec.ctags \ optlib/cmake.ctags \ optlib/ctags-optlib.ctags \ - optlib/elm.ctags \ + optlib/elixir.ctags \ + optlib/elm.ctags \ optlib/gdbinit.ctags \ optlib/man.ctags \ optlib/markdown.ctags \ diff --git a/optlib/elixir.c b/optlib/elixir.c new file mode 100644 index 0000000000..128e82f868 --- /dev/null +++ b/optlib/elixir.c @@ -0,0 +1,104 @@ +/* + * Generated by ./misc/optlib2c from optlib/elixir.ctags, Don't edit this manually. + */ +#include "general.h" +#include "parse.h" +#include "routines.h" + + +static void initializeElixirParser (const langType language CTAGS_ATTR_UNUSED) +{ +} + +extern parserDefinition* ElixirParser (void) +{ + static const char *const extensions [] = { + "ex", + "exs", + NULL + }; + + static const char *const aliases [] = { + NULL + }; + + static const char *const patterns [] = { + NULL + }; + + static kindDefinition ElixirKindTable [] = { + { + true, 'f', "function", "functions (def ...)", + }, + { + true, 'c', "callback", "callbacks (defcallback ...)", + }, + { + true, 'd', "delegate", "delegates (defdelegate ...)", + }, + { + true, 'e', "exception", "exceptions (defexception ...)", + }, + { + true, 'i', "implementation", "implementations (defimpl ...)", + }, + { + true, 'a', "macro", "macros (defmacro ...)", + }, + { + true, 'o', "operator", "operators (e.g. \"defmacro a <<< b\")", + }, + { + true, 'm', "module", "modules (defmodule ...)", + }, + { + true, 'p', "protocol", "protocols (defprotocol...)", + }, + { + true, 'r', "record", "records (defrecord...)", + }, + { + true, 't', "test", "tests (test ...)", + }, + }; + static tagRegexTable ElixirTagRegexTable [] = { + {"^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\2", + "f", NULL, NULL, false}, + {"^[ \t]*defcallback[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\1", + "c", NULL, NULL, false}, + {"^[ \t]*defdelegate[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\1", + "d", NULL, NULL, false}, + {"^[ \t]*defexception[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", + "e", NULL, NULL, false}, + {"^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", + "i", NULL, NULL, false}, + {"^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)\\(", "\\2", + "a", NULL, NULL, false}, + {"^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)?[ \t]+([^ \tA-Za-z0-9_]+)[ \t]*[a-zA-Z0-9_!?!]", "\\3", + "o", NULL, NULL, false}, + {"^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", + "m", NULL, NULL, false}, + {"^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", + "p", NULL, NULL, false}, + {"^[ \t]*Record\\.defrecord[ \t]+:([a-zA-Z0-9_]+)", "\\1", + "r", NULL, NULL, false}, + {"^[ \t]*test[ \t]+\"([a-z_][a-zA-Z0-9_?! ]*)\"*", "\\1", + "t", NULL, NULL, false}, + }; + + + parserDefinition* const def = parserNew ("Elixir"); + + def->enabled = true; + def->extensions = extensions; + def->patterns = patterns; + def->aliases = aliases; + def->method = METHOD_NOT_CRAFTED|METHOD_REGEX; + def->kindTable = ElixirKindTable; + def->kindCount = ARRAY_SIZE(ElixirKindTable); + def->tagRegexTable = ElixirTagRegexTable; + def->tagRegexCount = ARRAY_SIZE(ElixirTagRegexTable); + def->initialize = initializeElixirParser; + + return def; +} diff --git a/optlib/elixir.ctags b/optlib/elixir.ctags new file mode 100644 index 0000000000..3f3fdc5c66 --- /dev/null +++ b/optlib/elixir.ctags @@ -0,0 +1,77 @@ +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +# For more information, please refer to +# + +# +# WHEN FIXING A BUG OF THIS PARSER, WORK WITH THE UPSTREAM PROJECT. +# + +# +# This file is imported from https://github.com/mmorearty/elixir-ctags +# by Masatake YAMATO with some modifications. +# See https://github.com/mmorearty/elixir-ctags/issues/5 and +# https://github.com/universal-ctags/ctags/issues/1758#issuecomment-391692762. +# @dylan-chong and @FredrikAugust realized this u-ctags integration. +# + +# +# * Put the original LICENSE file as the header of the .ctags. +# * Use --map= option instead of --langmap because optlib2c doesn't handle +# --langmap option. +# * Define kinds explicitly with --kinddef- option. +# * Remove backslashes before double quotes chars in the regex pattern for +# "test". +# * Use singular forms for kind names. +# + +--langdef=Elixir + +--map-Elixir=+.ex +--map-Elixir=+.exs + +--kinddef-Elixir=f,function,functions (def ...) +--kinddef-Elixir=c,callback,callbacks (defcallback ...) +--kinddef-Elixir=d,delegate,delegates (defdelegate ...) +--kinddef-Elixir=e,exception,exceptions (defexception ...) +--kinddef-Elixir=i,implementation,implementations (defimpl ...) +--kinddef-Elixir=a,macro,macros (defmacro ...) +--kinddef-Elixir=o,operator,operators (e.g. "defmacro a <<< b") +--kinddef-Elixir=m,module,modules (defmodule ...) +--kinddef-Elixir=p,protocol,protocols (defprotocol...) +--kinddef-Elixir=r,record,records (defrecord...) +--kinddef-Elixir=t,test,tests (test ...) + +--regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\2/f/ +--regex-Elixir=/^[ \t]*defcallback[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/c/ +--regex-Elixir=/^[ \t]*defdelegate[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/d/ +--regex-Elixir=/^[ \t]*defexception[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/e/ +--regex-Elixir=/^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/i/ +--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)\(/\2/a/ +--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)?[ \t]+([^ \tA-Za-z0-9_]+)[ \t]*[a-zA-Z0-9_!?!]/\3/o/ +--regex-Elixir=/^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/m/ +--regex-Elixir=/^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/p/ +--regex-Elixir=/^[ \t]*Record\.defrecord[ \t]+:([a-zA-Z0-9_]+)/\1/r/ +--regex-Elixir=/^[ \t]*test[ \t]+"([a-z_][a-zA-Z0-9_?! ]*)"*/\1/t/ diff --git a/win32/ctags_vs2013.vcxproj b/win32/ctags_vs2013.vcxproj index bb9f9d9cd6..9884852206 100644 --- a/win32/ctags_vs2013.vcxproj +++ b/win32/ctags_vs2013.vcxproj @@ -142,6 +142,7 @@ + diff --git a/win32/ctags_vs2013.vcxproj.filters b/win32/ctags_vs2013.vcxproj.filters index 19eb148175..fda10a27e6 100644 --- a/win32/ctags_vs2013.vcxproj.filters +++ b/win32/ctags_vs2013.vcxproj.filters @@ -180,6 +180,9 @@ Source Files\optlib + + Source Files\optlib + Source Files\optlib From ffd1e98e2f2abda35b0787e9c97b81db6b036b31 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Sat, 2 Mar 2019 07:02:40 -0400 Subject: [PATCH 02/12] optlib2c: Change some indentation for aesthetics purposes --- misc/optlib2c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/misc/optlib2c b/misc/optlib2c index db2a86fec7..c802e61656 100755 --- a/misc/optlib2c +++ b/misc/optlib2c @@ -826,22 +826,22 @@ EOF } if (@{$opts->{'kinddefs'}}) { print <kindTable = $opts->{'Clangdef'}KindTable; - def->kindCount = ARRAY_SIZE($opts->{'Clangdef'}KindTable); + def->kindTable = $opts->{'Clangdef'}KindTable; + def->kindCount = ARRAY_SIZE($opts->{'Clangdef'}KindTable); EOF } if (@{$opts->{'fielddefs'}}) { print <fieldTable = $opts->{'Clangdef'}FieldTable; - def->fieldCount = ARRAY_SIZE($opts->{'Clangdef'}FieldTable); + def->fieldTable = $opts->{'Clangdef'}FieldTable; + def->fieldCount = ARRAY_SIZE($opts->{'Clangdef'}FieldTable); EOF } if (@{$opts->{'extradefs'}}) { print <xtagTable = $opts->{'Clangdef'}XtagTable; - def->xtagCount = ARRAY_SIZE($opts->{'Clangdef'}XtagTable); + def->xtagTable = $opts->{'Clangdef'}XtagTable; + def->xtagCount = ARRAY_SIZE($opts->{'Clangdef'}XtagTable); EOF } @@ -854,7 +854,7 @@ EOF if (defined $opts->{'base'}) { print <dependencies = $opts->{'Clangdef'}Dependencies; + def->dependencies = $opts->{'Clangdef'}Dependencies; def->dependencyCount = ARRAY_SIZE($opts->{'Clangdef'}Dependencies); EOF } From e06ed3a18c56ea151e75bd77e196d19f05c4b1a6 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Sat, 2 Mar 2019 07:04:49 -0400 Subject: [PATCH 03/12] Update elixir.c to lastest optlib2c --- optlib/elixir.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/optlib/elixir.c b/optlib/elixir.c index 128e82f868..c67371baa1 100644 --- a/optlib/elixir.c +++ b/optlib/elixir.c @@ -4,6 +4,8 @@ #include "general.h" #include "parse.h" #include "routines.h" +#include "field.h" +#include "xtag.h" static void initializeElixirParser (const langType language CTAGS_ATTR_UNUSED) @@ -94,8 +96,8 @@ extern parserDefinition* ElixirParser (void) def->patterns = patterns; def->aliases = aliases; def->method = METHOD_NOT_CRAFTED|METHOD_REGEX; - def->kindTable = ElixirKindTable; - def->kindCount = ARRAY_SIZE(ElixirKindTable); + def->kindTable = ElixirKindTable; + def->kindCount = ARRAY_SIZE(ElixirKindTable); def->tagRegexTable = ElixirTagRegexTable; def->tagRegexCount = ARRAY_SIZE(ElixirTagRegexTable); def->initialize = initializeElixirParser; From 37bb27edbc9b94bf864966356befad8943004d5e Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Sun, 3 Mar 2019 10:02:54 -0400 Subject: [PATCH 04/12] Add a more exhaustive Elixir test case --- Units/simple-elixir.d/expected.tags | 58 ++++++++- Units/simple-elixir.d/input.ex | 186 ++++++++++++++++++++++++++-- 2 files changed, 234 insertions(+), 10 deletions(-) diff --git a/Units/simple-elixir.d/expected.tags b/Units/simple-elixir.d/expected.tags index b4fc330375..4154db14bc 100644 --- a/Units/simple-elixir.d/expected.tags +++ b/Units/simple-elixir.d/expected.tags @@ -1,3 +1,55 @@ -drive input.ex /^def drive(%User{age: age}) when age >= 16 do$/;" function -MathTest input.ex /^defmodule MathTest do$/;" module -can add two numbers input.ex /^ test "can add two numbers" do$/;" test +MyString input.ex /^defmodule MyString do$/;" module +one_liner_func input.ex /^ def one_liner_func, do: :baz$/;" function +func_no_params input.ex /^ def func_no_params do$/;" function +join input.ex /^ def join(string1, string2 \\\\ nil, separator \\\\ " ")$/;" function +join input.ex /^ def join(string1, nil, _separator) do$/;" function +join input.ex /^ def join(string1, string2, separator) do$/;" function +private_func input.ex /^ defp private_func(a), do: a <> " alone"$/;" function +private_func_no_params input.ex /^ defp private_func_no_params do$/;" function +MyParser input.ex /^defmodule URI.MyParser do$/;" module +default_port input.ex /^ @callback default_port() :: integer$/;" callback +parse input.ex /^ defcallback parse(uri_info :: URI.t()) :: URI.t()$/;" callback +MyList input.ex /^defmodule MyList do$/;" module +reverse input.ex /^ defdelegate reverse(list), to: Enum$/;" delegate +other_reverse input.ex /^ defdelegate other_reverse(list), to: Enum, as: :reverse$/;" delegate +Size input.ex /^defprotocol Size do$/;" protocol +size input.ex /^ def size(data)$/;" function +MyGuards input.ex /^defmodule Integer.MyGuards do$/;" module +is_even input.ex /^ defguard is_even(value) when is_integer(value) and rem(value, 2) == 0$/;" macro +is_odd input.ex /^ defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0$/;" macro +Size input.ex /^defimpl Size, for: BitString do$/;" implementation +size input.ex /^ def size(binary), do: byte_size(binary)$/;" function +MyLogic input.ex /^defmodule MyLogic do$/;" module +macro input.ex /^ defmacro macro(expr, opts) do$/;" macro +macro_no_params input.ex /^ defmacro macro_no_params do$/;" macro +private_macro input.ex /^ defmacrop private_macro(expr, opts) do$/;" macro +private_macro_no_params input.ex /^ defmacrop private_macro_no_params do$/;" macro +MyOperators input.ex /^defmodule MyOperators do$/;" module ++ input.ex /^ def a + b, do: max(a, b)$/;" operator +- input.ex /^ def a - b, do: max(a, b)$/;" operator +* input.ex /^ def a * b, do: max(a, b)$/;" operator +/ input.ex /^ def a \/ b, do: max(a, b)$/;" operator += input.ex /^ def a = b, do: max(a, b)$/;" operator +. input.ex /^ def a . b, do: max(a, b)$/;" operator +| input.ex /^ def a | b, do: max(a, b)$/;" operator +||| input.ex /^ def _ ||| b, do: max(a, b)$/;" operator +&&& input.ex /^ def a &&& _, do: max(a, b)$/;" operator +<<< input.ex /^ def a <<< b, do: max(a, b)$/;" operator +>>> input.ex /^ defp a >>> b, do: max(a, b)$/;" operator +<<~ input.ex /^ defp _ <<~ _, do: max(a, b)$/;" operator +~>> input.ex /^ defp a ~>> b, do: max(a, b)$/;" operator +<~ input.ex /^ defmacro a <~ b, do: max(a, b)$/;" operator +~> input.ex /^ defmacro _ ~> b, do: max(a, b)$/;" operator +<~> input.ex /^ defmacro a <~> _, do: max(a, b)$/;" operator +<|> input.ex /^ defmacrop a <|> b, do: max(a, b)$/;" operator +^^^ input.ex /^ defmacrop _ ^^^ _, do: max(a, b)$/;" operator +~~~ input.ex /^ defmacrop a ~~~ b, do: max(a, b)$/;" operator +Nest input.ex /^defmodule Nest do$/;" module +Of input.ex /^ defmodule Of do$/;" module +Modules input.ex /^ defmodule Modules do$/;" module +MyRecords input.ex /^defmodule MyRecords do$/;" module +user1 input.ex /^ Record.defrecord(:user1, name: "megan", age: "25")$/;" record +user2 input.ex /^ Record.defrecordp :user2, name: "ivan", age: "23"$/;" record +CallbackModule input.ex /^defmodule CallbackModule do$/;" module +with spaces input.ex /^ test "with spaces" do$/;" test +with parens input.ex /^ test("with parens") do$/;" test diff --git a/Units/simple-elixir.d/input.ex b/Units/simple-elixir.d/input.ex index 3bf3272596..33728c9217 100644 --- a/Units/simple-elixir.d/input.ex +++ b/Units/simple-elixir.d/input.ex @@ -1,18 +1,190 @@ +# Most of this code is taken from the official Elixir documentation. +# With comments and modifications +# by Ivan Gonzalez Polanco # -# Taken from https://elixir-lang.org/ +# This code doesn't compile, since it's supposed to test de ctags elixir +# parser, not the elixir elixir parser. + +# +# f functions (def ...) +# +defmodule MyString do + def one_liner_func, do: :baz + + def func_no_params do + # + end + + # Function head + def join(string1, string2 \\ nil, separator \\ " ") + + # Function with 1 arity + def join(string1, nil, _separator) do + private_function(string1) + end + + # Normal function + def join(string1, string2, separator) do + string1 <> separator <> string2 + end + + # Private function + defp private_func(a), do: a <> " alone" + + defp private_func_no_params do + # + end +end + +# +# c callbacks (defcallback ...) +# +defmodule URI.MyParser do + use Behaviour + # This is the new callback syntax + @callback default_port() :: integer + + # This is the old (deprecated) callback syntax + defcallback parse(uri_info :: URI.t()) :: URI.t() +end + +# +# d delegates (defdelegate ...) +# +defmodule MyList do + defdelegate reverse(list), to: Enum + defdelegate other_reverse(list), to: Enum, as: :reverse +end + +# TODO: I don't know how to identificate/differenciate an exception since they +# don't _actually_ have name. Instead are defined like a struct. +# +# e exceptions (defexception ...) +# +# defmodule MyAppError do +# defexception [:message] +# +# @impl true +# def exception(value) do +# msg = "did not get what was expected, got: #{inspect(value)}" +# %MyAppError{message: msg} +# end +# end + +# +# p protocols (defprotocol ...) +# +defprotocol Size do + @doc "Calculates the size (and not the length!) of a data structure" + def size(data) +end + +# +# g guards (defguard ...) +# +defmodule Integer.MyGuards do + defguard is_even(value) when is_integer(value) and rem(value, 2) == 0 + defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0 +end + +# +# i implementations (defimpl ...) +# +defimpl Size, for: BitString do + def size(binary), do: byte_size(binary) +end + +# +# a macros (defmacro ...) +# +defmodule MyLogic do + defmacro macro(expr, opts) do + # + end + + defmacro macro_no_params do + # + end + + defmacrop private_macro(expr, opts) do + # + end + + defmacrop private_macro_no_params do + # + end +end + # +# o operators (e.g. "defmacro a <<< b") +# +defmodule MyOperators do + def a + b, do: max(a, b) + def a - b, do: max(a, b) + def a * b, do: max(a, b) + def a / b, do: max(a, b) + def a = b, do: max(a, b) + def a . b, do: max(a, b) + + # TODO: Fix the operators regex and then test these definitions + # def a and b, do: max(a, b) + # def a or b, do: max(a, b) + # def a not b, do: max(a, b) + # def a in b, do: max(a, b) + # def a not in b, do: max(a, b) -def drive(%User{age: age}) when age >= 16 do - # Code that drives a car + # The 13 operators bellow are ALL the operators that Elixir is CAPABLE of + # parsing and are not used by default, so the user can + def a | b, do: max(a, b) + def _ ||| b, do: max(a, b) + def a &&& _, do: max(a, b) + def a <<< b, do: max(a, b) + defp a >>> b, do: max(a, b) + defp _ <<~ _, do: max(a, b) + defp a ~>> b, do: max(a, b) + defmacro a <~ b, do: max(a, b) + defmacro _ ~> b, do: max(a, b) + defmacro a <~> _, do: max(a, b) + defmacrop a <|> b, do: max(a, b) + defmacrop _ ^^^ _, do: max(a, b) + defmacrop a ~~~ b, do: max(a, b) end -drive(User.get("John Doe")) -#=> Fails if the user is under 16 +# +# m modules (defmodule ...) +# +defmodule Nest do + defmodule Of do + defmodule Modules do + end + end +end -defmodule MathTest do +# +# r records (defrecord ...) +# +defmodule MyRecords do + require Record + Record.defrecord(:user1, name: "megan", age: "25") + Record.defrecordp :user2, name: "ivan", age: "23" + # This is not a typo but an intentional bad test, used to test the parser + Record.defrecordp:bad, name: "ivan", age: "23" +end + +# +# t tests (test ...) +# +defmodule CallbackModule do use ExUnit.Case, async: true - test "can add two numbers" do + test "with spaces" do + assert 1 + 1 == 2 + end + + test("with parens") do + assert 1 + 1 == 2 + end + test"with bad spaces" do assert 1 + 1 == 2 end end From 1ad9b8dbe1cbf4052abed5917e9c303fd2ecfdc2 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Sun, 3 Mar 2019 10:04:31 -0400 Subject: [PATCH 05/12] Improve the Elixir parser I already made a pull request with *detailed commits* to the upstream of the elixir.ctags parser. Said pull request and commits can be found in https://github.com/mmorearty/elixir-ctags/pull/8 This parser is missing basically two things which are annotated in the input.ex test with a TODO keyword, plus an explanation of _why_ they're missing. Said missing features are listed bellow: * exceptions: I don't know how to identificate or differenciate exceptions from one another because they do not have a name. Instead, they resemble an struct. For example, if I have: ``` defmodule MyAppError1 do defexception [:message] # code end defmodule MyAppError2 do defexception [:message] # code end ``` How would/should ctags differenciate between these two exceptions? * word-defined logical operators (`and, or, not`): The elixir parser in this commit already has the regex atom including these logical operators, but since the regex engine used by universal-ctags does not have lookahead, I couldn't think of a way to add them or negate them. The test for them is commented in, for the time someone is brave enough to add them. --- optlib/elixir.c | 19 ++++++++++++------ optlib/elixir.ctags | 48 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/optlib/elixir.c b/optlib/elixir.c index c67371baa1..01c15bc039 100644 --- a/optlib/elixir.c +++ b/optlib/elixir.c @@ -41,6 +41,9 @@ extern parserDefinition* ElixirParser (void) { true, 'e', "exception", "exceptions (defexception ...)", }, + { + true, 'g', "guard", "guards (defguard ...)", + }, { true, 'i', "implementation", "implementations (defimpl ...)", }, @@ -64,27 +67,31 @@ extern parserDefinition* ElixirParser (void) }, }; static tagRegexTable ElixirTagRegexTable [] = { - {"^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\2", + {"^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\\|\\^/&<>~.=!*+-]+)", "\\2", "f", NULL, NULL, false}, - {"^[ \t]*defcallback[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\1", + {"^[ \t]*(@|def)callback[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\2", "c", NULL, NULL, false}, {"^[ \t]*defdelegate[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\1", "d", NULL, NULL, false}, {"^[ \t]*defexception[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "e", NULL, NULL, false}, + {"^[ \t]*defguard(p?)[ \t]+(is_[a-zA-Z0-9_?!]+)", "\\2", + "a", NULL, NULL, false}, {"^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "i", NULL, NULL, false}, - {"^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)\\(", "\\2", + {"^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\\|\\^/&<>~.=!*+-]+)", "\\2", "a", NULL, NULL, false}, - {"^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)?[ \t]+([^ \tA-Za-z0-9_]+)[ \t]*[a-zA-Z0-9_!?!]", "\\3", + {"^[ \t]*def(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]", "\\3", + "o", NULL, NULL, false}, + {"^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]", "\\3", "o", NULL, NULL, false}, {"^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "m", NULL, NULL, false}, {"^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "p", NULL, NULL, false}, - {"^[ \t]*Record\\.defrecord[ \t]+:([a-zA-Z0-9_]+)", "\\1", + {"^[ \t]*Record\\.defrecord(p?)[ \t(]+:([a-zA-Z0-9_]+)(\\)?)", "\\2", "r", NULL, NULL, false}, - {"^[ \t]*test[ \t]+\"([a-z_][a-zA-Z0-9_?! ]*)\"*", "\\1", + {"^[ \t]*test[ \t(]+\"([a-z_][a-zA-Z0-9_?! ]*)\"*(\\)?)", "\\1", "t", NULL, NULL, false}, }; diff --git a/optlib/elixir.ctags b/optlib/elixir.ctags index 3f3fdc5c66..2a94a87a35 100644 --- a/optlib/elixir.ctags +++ b/optlib/elixir.ctags @@ -47,6 +47,36 @@ # * Use singular forms for kind names. # +# +# from https://hexdocs.pm/elixir/operators.html: +# +# Elixir is capable of parsing a predefined set of operators; this means that +# it's not possible to define new operators (like one could do in Haskell, for +# example). However, not all operators that Elixir can parse are *used* by +# Elixir: for example, `+` and `||` are used by Elixir for addition and boolean +# *or*, but `<~>` is not used (but valid). +# + +# +# The following is a list of all the operators that Elixir is capable of +# parsing, but that are not used by default (separated by a comma): +# +# |, |||, &&&, <<<, >>>, <<~, ~>>, <~, ~>, <~>, <|>, ^^^, ~~~ +# +# They could be expressed in a regex atom `(&&&|<~|<~>|etc...)` but I think +# it can be reduced (I don't know the difference in efficiency) to: +# +# [\|\^&<>~]{1,3} +# +# Adding to that bracket expression the rest of the characters and words used +# for the defined operators (which can be overriden): +# +# ([\|\^\/&<>~.=!*+-]{1,3}|and|or|not|in|not in) +# +# But since the regex engine used in universal-ctags can't do lookahead, I'm +# not gonna bother to try and make the operator regex exhaustive... plus it's +# also bad practice to override the default operators. + --langdef=Elixir --map-Elixir=+.ex @@ -56,6 +86,7 @@ --kinddef-Elixir=c,callback,callbacks (defcallback ...) --kinddef-Elixir=d,delegate,delegates (defdelegate ...) --kinddef-Elixir=e,exception,exceptions (defexception ...) +--kinddef-Elixir=g,guard,guards (defguard ...) --kinddef-Elixir=i,implementation,implementations (defimpl ...) --kinddef-Elixir=a,macro,macros (defmacro ...) --kinddef-Elixir=o,operator,operators (e.g. "defmacro a <<< b") @@ -64,14 +95,19 @@ --kinddef-Elixir=r,record,records (defrecord...) --kinddef-Elixir=t,test,tests (test ...) ---regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\2/f/ ---regex-Elixir=/^[ \t]*defcallback[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/c/ +--regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/f/ +--regex-Elixir=/^[ \t]*(@|def)callback[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\2/c/ --regex-Elixir=/^[ \t]*defdelegate[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/d/ --regex-Elixir=/^[ \t]*defexception[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/e/ +--regex-Elixir=/^[ \t]*defguard(p?)[ \t]+(is_[a-zA-Z0-9_?!]+)/\2/g/ --regex-Elixir=/^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/i/ ---regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)\(/\2/a/ ---regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)?[ \t]+([^ \tA-Za-z0-9_]+)[ \t]*[a-zA-Z0-9_!?!]/\3/o/ +--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/a/ + +# These are the operators (which can be defined with def(p) and defmacro(p)) +--regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]/\3/o/ +--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]/\3/o/ + --regex-Elixir=/^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/m/ --regex-Elixir=/^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/p/ ---regex-Elixir=/^[ \t]*Record\.defrecord[ \t]+:([a-zA-Z0-9_]+)/\1/r/ ---regex-Elixir=/^[ \t]*test[ \t]+"([a-z_][a-zA-Z0-9_?! ]*)"*/\1/t/ +--regex-Elixir=/^[ \t]*Record\.defrecord(p?)[ \t(]+:([a-zA-Z0-9_]+)(\)?)/\2/r/ +--regex-Elixir=/^[ \t]*test[ \t(]+"([a-z_][a-zA-Z0-9_?! ]*)"*(\)?)/\1/t/ From 5aec4f812fd2bf4685b6eee8117b882b3c8ea61f Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Sun, 3 Mar 2019 11:33:44 -0400 Subject: [PATCH 06/12] Fix expected.ctags assigning wrong kind --- Units/simple-elixir.d/expected.tags | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Units/simple-elixir.d/expected.tags b/Units/simple-elixir.d/expected.tags index 4154db14bc..ce2538aba9 100644 --- a/Units/simple-elixir.d/expected.tags +++ b/Units/simple-elixir.d/expected.tags @@ -15,8 +15,8 @@ other_reverse input.ex /^ defdelegate other_reverse(list), to: Enum, as: :rever Size input.ex /^defprotocol Size do$/;" protocol size input.ex /^ def size(data)$/;" function MyGuards input.ex /^defmodule Integer.MyGuards do$/;" module -is_even input.ex /^ defguard is_even(value) when is_integer(value) and rem(value, 2) == 0$/;" macro -is_odd input.ex /^ defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0$/;" macro +is_even input.ex /^ defguard is_even(value) when is_integer(value) and rem(value, 2) == 0$/;" guard +is_odd input.ex /^ defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0$/;" guard Size input.ex /^defimpl Size, for: BitString do$/;" implementation size input.ex /^ def size(binary), do: byte_size(binary)$/;" function MyLogic input.ex /^defmodule MyLogic do$/;" module From 3161d4fc6d2e5ab4a77662b9d5b8c483231a8f09 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Sun, 3 Mar 2019 11:34:58 -0400 Subject: [PATCH 07/12] Reduce regex of operators to just one regex --- optlib/elixir.c | 6 ++---- optlib/elixir.ctags | 6 +----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/optlib/elixir.c b/optlib/elixir.c index 01c15bc039..189271daf5 100644 --- a/optlib/elixir.c +++ b/optlib/elixir.c @@ -76,14 +76,12 @@ extern parserDefinition* ElixirParser (void) {"^[ \t]*defexception[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "e", NULL, NULL, false}, {"^[ \t]*defguard(p?)[ \t]+(is_[a-zA-Z0-9_?!]+)", "\\2", - "a", NULL, NULL, false}, + "g", NULL, NULL, false}, {"^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "i", NULL, NULL, false}, {"^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\\|\\^/&<>~.=!*+-]+)", "\\2", "a", NULL, NULL, false}, - {"^[ \t]*def(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]", "\\3", - "o", NULL, NULL, false}, - {"^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]", "\\3", + {"^[ \t]*def((p?)|macro(p?))[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]", "\\5", "o", NULL, NULL, false}, {"^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "m", NULL, NULL, false}, diff --git a/optlib/elixir.ctags b/optlib/elixir.ctags index 2a94a87a35..83c8d20009 100644 --- a/optlib/elixir.ctags +++ b/optlib/elixir.ctags @@ -102,11 +102,7 @@ --regex-Elixir=/^[ \t]*defguard(p?)[ \t]+(is_[a-zA-Z0-9_?!]+)/\2/g/ --regex-Elixir=/^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/i/ --regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/a/ - -# These are the operators (which can be defined with def(p) and defmacro(p)) ---regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]/\3/o/ ---regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]/\3/o/ - +--regex-Elixir=/^[ \t]*def((p?)|macro(p?))[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]/\5/o/ --regex-Elixir=/^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/m/ --regex-Elixir=/^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/p/ --regex-Elixir=/^[ \t]*Record\.defrecord(p?)[ \t(]+:([a-zA-Z0-9_]+)(\)?)/\2/r/ From c0aca49b02bf372e7dbffc845effe02ec6b4e8b8 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Wed, 6 Mar 2019 09:19:16 -0400 Subject: [PATCH 08/12] Move elixir's test to a category and divide to smaller cases --- .../elixir-callbacks.d}/args.ctags | 0 .../elixir-callbacks.d/expected.tags | 3 + .../elixir-callbacks.d/input.ex | 8 + .../elixir-functions.d/args.ctags | 2 + .../elixir-functions.d/expected.tags | 8 + .../elixir-functions.d/input.ex | 27 +++ .../elixir-macros.d/args.ctags | 2 + .../elixir-macros.d/expected.tags | 5 + .../parser-elixir.r/elixir-macros.d/input.ex | 17 ++ .../elixir-modules.d/args.ctags | 2 + .../elixir-modules.d/expected.tags | 3 + .../parser-elixir.r/elixir-modules.d/input.ex | 7 + .../elixir-records.d/args.ctags | 2 + .../elixir-records.d/expected.tags | 3 + .../parser-elixir.r/elixir-records.d/input.ex | 7 + .../elixir-sign-operators.d/args.ctags | 2 + .../elixir-sign-operators.d/expected.tags | 20 ++ .../elixir-sign-operators.d/input.ex | 24 +++ .../elixir-simple.d/args.ctags | 2 + .../elixir-simple.d/expected.tags | 10 + .../parser-elixir.r/elixir-simple.d/input.ex | 37 ++++ .../parser-elixir.r/elixir-tests.d/args.ctags | 2 + .../elixir-tests.d/expected.tags | 3 + Units/parser-elixir.r/elixir-tests.d/input.ex | 22 ++ Units/simple-elixir.d/expected.tags | 55 ----- Units/simple-elixir.d/input.ex | 190 ------------------ 26 files changed, 218 insertions(+), 245 deletions(-) rename Units/{simple-elixir.d => parser-elixir.r/elixir-callbacks.d}/args.ctags (100%) create mode 100644 Units/parser-elixir.r/elixir-callbacks.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-callbacks.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-functions.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-functions.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-functions.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-macros.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-macros.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-macros.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-modules.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-modules.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-modules.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-records.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-records.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-records.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-sign-operators.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-sign-operators.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-sign-operators.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-simple.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-simple.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-simple.d/input.ex create mode 100644 Units/parser-elixir.r/elixir-tests.d/args.ctags create mode 100644 Units/parser-elixir.r/elixir-tests.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-tests.d/input.ex delete mode 100644 Units/simple-elixir.d/expected.tags delete mode 100644 Units/simple-elixir.d/input.ex diff --git a/Units/simple-elixir.d/args.ctags b/Units/parser-elixir.r/elixir-callbacks.d/args.ctags similarity index 100% rename from Units/simple-elixir.d/args.ctags rename to Units/parser-elixir.r/elixir-callbacks.d/args.ctags diff --git a/Units/parser-elixir.r/elixir-callbacks.d/expected.tags b/Units/parser-elixir.r/elixir-callbacks.d/expected.tags new file mode 100644 index 0000000000..b82ece7998 --- /dev/null +++ b/Units/parser-elixir.r/elixir-callbacks.d/expected.tags @@ -0,0 +1,3 @@ +CallbackModule input.ex /^defmodule CallbackModule do$/;" module +new_callback input.ex /^ @callback new_callback() :: integer$/;" callback +old_callback input.ex /^ defcallback old_callback(info :: integer) :: integer$/;" callback diff --git a/Units/parser-elixir.r/elixir-callbacks.d/input.ex b/Units/parser-elixir.r/elixir-callbacks.d/input.ex new file mode 100644 index 0000000000..b554a761f7 --- /dev/null +++ b/Units/parser-elixir.r/elixir-callbacks.d/input.ex @@ -0,0 +1,8 @@ +defmodule CallbackModule do + use Behaviour + # This is the new callback syntax + @callback new_callback() :: integer + + # This is the old (deprecated) callback syntax + defcallback old_callback(info :: integer) :: integer +end diff --git a/Units/parser-elixir.r/elixir-functions.d/args.ctags b/Units/parser-elixir.r/elixir-functions.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-functions.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-functions.d/expected.tags b/Units/parser-elixir.r/elixir-functions.d/expected.tags new file mode 100644 index 0000000000..0d05d65b97 --- /dev/null +++ b/Units/parser-elixir.r/elixir-functions.d/expected.tags @@ -0,0 +1,8 @@ +FunctionModule input.ex /^defmodule FunctionModule do$/;" module +one_liner_func input.ex /^ def one_liner_func, do: :baz$/;" function +func_no_params input.ex /^ def func_no_params do$/;" function +func_head input.ex /^ def func_head(string1, string2 \\\\ nil, separator \\\\ " ")$/;" function +func_one_arity input.ex /^ def func_one_arity(string1, nil, _separator) do$/;" function +normal_func input.ex /^ def normal_func(string1, string2, separator) do$/;" function +private_func input.ex /^ defp private_func(a), do: a <> " alone"$/;" function +private_func_no_params input.ex /^ defp private_func_no_params do$/;" function diff --git a/Units/parser-elixir.r/elixir-functions.d/input.ex b/Units/parser-elixir.r/elixir-functions.d/input.ex new file mode 100644 index 0000000000..90ab224409 --- /dev/null +++ b/Units/parser-elixir.r/elixir-functions.d/input.ex @@ -0,0 +1,27 @@ +defmodule FunctionModule do + def one_liner_func, do: :baz + + def func_no_params do + # + end + + # Function head + def func_head(string1, string2 \\ nil, separator \\ " ") + + # Function with 1 arity + def func_one_arity(string1, nil, _separator) do + private_function(string1) + end + + # Normal function + def normal_func(string1, string2, separator) do + string1 <> separator <> string2 + end + + # Private function + defp private_func(a), do: a <> " alone" + + defp private_func_no_params do + # + end +end diff --git a/Units/parser-elixir.r/elixir-macros.d/args.ctags b/Units/parser-elixir.r/elixir-macros.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-macros.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-macros.d/expected.tags b/Units/parser-elixir.r/elixir-macros.d/expected.tags new file mode 100644 index 0000000000..c01172751d --- /dev/null +++ b/Units/parser-elixir.r/elixir-macros.d/expected.tags @@ -0,0 +1,5 @@ +MacroModule input.ex /^defmodule MacroModule do$/;" module +macro input.ex /^ defmacro macro(expr, opts) do$/;" macro +macro_no_params input.ex /^ defmacro macro_no_params do$/;" macro +private_macro input.ex /^ defmacrop private_macro(expr, opts) do$/;" macro +private_macro_no_params input.ex /^ defmacrop private_macro_no_params do$/;" macro diff --git a/Units/parser-elixir.r/elixir-macros.d/input.ex b/Units/parser-elixir.r/elixir-macros.d/input.ex new file mode 100644 index 0000000000..2c308ad94d --- /dev/null +++ b/Units/parser-elixir.r/elixir-macros.d/input.ex @@ -0,0 +1,17 @@ +defmodule MacroModule do + defmacro macro(expr, opts) do + # + end + + defmacro macro_no_params do + # + end + + defmacrop private_macro(expr, opts) do + # + end + + defmacrop private_macro_no_params do + # + end +end diff --git a/Units/parser-elixir.r/elixir-modules.d/args.ctags b/Units/parser-elixir.r/elixir-modules.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-modules.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-modules.d/expected.tags b/Units/parser-elixir.r/elixir-modules.d/expected.tags new file mode 100644 index 0000000000..cd7f43fcd0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-modules.d/expected.tags @@ -0,0 +1,3 @@ +Foo input.ex /^defmodule Foo do$/;" module +Bar input.ex /^defmodule Foo.Bar do$/;" module +Baz input.ex /^ defmodule Baz do$/;" module diff --git a/Units/parser-elixir.r/elixir-modules.d/input.ex b/Units/parser-elixir.r/elixir-modules.d/input.ex new file mode 100644 index 0000000000..5fb1a9a137 --- /dev/null +++ b/Units/parser-elixir.r/elixir-modules.d/input.ex @@ -0,0 +1,7 @@ +defmodule Foo do +end + +defmodule Foo.Bar do + defmodule Baz do + end +end diff --git a/Units/parser-elixir.r/elixir-records.d/args.ctags b/Units/parser-elixir.r/elixir-records.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-records.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-records.d/expected.tags b/Units/parser-elixir.r/elixir-records.d/expected.tags new file mode 100644 index 0000000000..170553a640 --- /dev/null +++ b/Units/parser-elixir.r/elixir-records.d/expected.tags @@ -0,0 +1,3 @@ +RecordsModule input.ex /^defmodule RecordsModule do$/;" module +user1 input.ex /^ Record.defrecord(:user1, name: "megan", age: "25")$/;" record +user2 input.ex /^ Record.defrecordp :user2, name: "ivan", age: "23"$/;" record diff --git a/Units/parser-elixir.r/elixir-records.d/input.ex b/Units/parser-elixir.r/elixir-records.d/input.ex new file mode 100644 index 0000000000..c5c146fe0a --- /dev/null +++ b/Units/parser-elixir.r/elixir-records.d/input.ex @@ -0,0 +1,7 @@ +defmodule RecordsModule do + require Record + Record.defrecord(:user1, name: "megan", age: "25") + Record.defrecordp :user2, name: "ivan", age: "23" + # This is not a typo but an intentional bad test, used to test the parser + Record.defrecordp:bad, name: "ivan", age: "23" +end diff --git a/Units/parser-elixir.r/elixir-sign-operators.d/args.ctags b/Units/parser-elixir.r/elixir-sign-operators.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-sign-operators.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-sign-operators.d/expected.tags b/Units/parser-elixir.r/elixir-sign-operators.d/expected.tags new file mode 100644 index 0000000000..20f6ea940b --- /dev/null +++ b/Units/parser-elixir.r/elixir-sign-operators.d/expected.tags @@ -0,0 +1,20 @@ +OperatorModule input.ex /^defmodule OperatorModule do$/;" module ++ input.ex /^ def a + b, do: max(a, b)$/;" operator +- input.ex /^ def a - b, do: max(a, b)$/;" operator +* input.ex /^ def a * b, do: max(a, b)$/;" operator +/ input.ex /^ def a \/ b, do: max(a, b)$/;" operator += input.ex /^ def a = b, do: max(a, b)$/;" operator +. input.ex /^ def a . b, do: max(a, b)$/;" operator +| input.ex /^ def a | b, do: max(a, b)$/;" operator +||| input.ex /^ def _ ||| b, do: max(a, b)$/;" operator +&&& input.ex /^ def a &&& _, do: max(a, b)$/;" operator +<<< input.ex /^ def a <<< b, do: max(a, b)$/;" operator +>>> input.ex /^ defp a >>> b, do: max(a, b)$/;" operator +<<~ input.ex /^ defp _ <<~ _, do: max(a, b)$/;" operator +~>> input.ex /^ defp a ~>> b, do: max(a, b)$/;" operator +<~ input.ex /^ defmacro a <~ b, do: max(a, b)$/;" operator +~> input.ex /^ defmacro _ ~> b, do: max(a, b)$/;" operator +<~> input.ex /^ defmacro a <~> _, do: max(a, b)$/;" operator +<|> input.ex /^ defmacrop a <|> b, do: max(a, b)$/;" operator +^^^ input.ex /^ defmacrop _ ^^^ _, do: max(a, b)$/;" operator +~~~ input.ex /^ defmacrop a ~~~ b, do: max(a, b)$/;" operator diff --git a/Units/parser-elixir.r/elixir-sign-operators.d/input.ex b/Units/parser-elixir.r/elixir-sign-operators.d/input.ex new file mode 100644 index 0000000000..18655ca314 --- /dev/null +++ b/Units/parser-elixir.r/elixir-sign-operators.d/input.ex @@ -0,0 +1,24 @@ +defmodule OperatorModule do + def a + b, do: max(a, b) + def a - b, do: max(a, b) + def a * b, do: max(a, b) + def a / b, do: max(a, b) + def a = b, do: max(a, b) + def a . b, do: max(a, b) + + # The 13 operators bellow are ALL the operators that Elixir is CAPABLE of + # parsing and are not used by default, so the user can + def a | b, do: max(a, b) + def _ ||| b, do: max(a, b) + def a &&& _, do: max(a, b) + def a <<< b, do: max(a, b) + defp a >>> b, do: max(a, b) + defp _ <<~ _, do: max(a, b) + defp a ~>> b, do: max(a, b) + defmacro a <~ b, do: max(a, b) + defmacro _ ~> b, do: max(a, b) + defmacro a <~> _, do: max(a, b) + defmacrop a <|> b, do: max(a, b) + defmacrop _ ^^^ _, do: max(a, b) + defmacrop a ~~~ b, do: max(a, b) +end diff --git a/Units/parser-elixir.r/elixir-simple.d/args.ctags b/Units/parser-elixir.r/elixir-simple.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-simple.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-simple.d/expected.tags b/Units/parser-elixir.r/elixir-simple.d/expected.tags new file mode 100644 index 0000000000..3e64129479 --- /dev/null +++ b/Units/parser-elixir.r/elixir-simple.d/expected.tags @@ -0,0 +1,10 @@ +MyList input.ex /^defmodule MyList do$/;" module +reverse input.ex /^ defdelegate reverse(list), to: Enum$/;" delegate +other_reverse input.ex /^ defdelegate other_reverse(list), to: Enum, as: :reverse$/;" delegate +Size input.ex /^defprotocol Size do$/;" protocol +size input.ex /^ def size(data)$/;" function +MyGuards input.ex /^defmodule Integer.MyGuards do$/;" module +is_even input.ex /^ defguard is_even(value) when is_integer(value) and rem(value, 2) == 0$/;" guard +is_odd input.ex /^ defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0$/;" guard +Size input.ex /^defimpl Size, for: BitString do$/;" implementation +size input.ex /^ def size(binary), do: byte_size(binary)$/;" function diff --git a/Units/parser-elixir.r/elixir-simple.d/input.ex b/Units/parser-elixir.r/elixir-simple.d/input.ex new file mode 100644 index 0000000000..a35d652d41 --- /dev/null +++ b/Units/parser-elixir.r/elixir-simple.d/input.ex @@ -0,0 +1,37 @@ +# Most of this code is taken from the official Elixir documentation. +# With comments and modifications +# by Ivan Gonzalez Polanco +# +# This code doesn't compile, since it's supposed to test de ctags elixir +# parser, not the elixir elixir parser. + +# +# d delegates (defdelegate ...) +# +defmodule MyList do + defdelegate reverse(list), to: Enum + defdelegate other_reverse(list), to: Enum, as: :reverse +end + +# +# p protocols (defprotocol ...) +# +defprotocol Size do + @doc "Calculates the size (and not the length!) of a data structure" + def size(data) +end + +# +# g guards (defguard ...) +# +defmodule Integer.MyGuards do + defguard is_even(value) when is_integer(value) and rem(value, 2) == 0 + defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0 +end + +# +# i implementations (defimpl ...) +# +defimpl Size, for: BitString do + def size(binary), do: byte_size(binary) +end diff --git a/Units/parser-elixir.r/elixir-tests.d/args.ctags b/Units/parser-elixir.r/elixir-tests.d/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-tests.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-tests.d/expected.tags b/Units/parser-elixir.r/elixir-tests.d/expected.tags new file mode 100644 index 0000000000..38d4814740 --- /dev/null +++ b/Units/parser-elixir.r/elixir-tests.d/expected.tags @@ -0,0 +1,3 @@ +TestModule input.ex /^defmodule TestModule do$/;" module +good with spaces input.ex /^ test "good with spaces" do$/;" test +good with parens input.ex /^ test("good with parens") do$/;" test diff --git a/Units/parser-elixir.r/elixir-tests.d/input.ex b/Units/parser-elixir.r/elixir-tests.d/input.ex new file mode 100644 index 0000000000..75929f5e0f --- /dev/null +++ b/Units/parser-elixir.r/elixir-tests.d/input.ex @@ -0,0 +1,22 @@ +defmodule TestModule do + use ExUnit.Case, async: true + + test "good with spaces" do + assert 1 + 1 == 2 + end + + test("good with parens") do + assert 1 + 1 == 2 + end + test"bad without spaces" do + assert 1 + 1 == 2 + end + + test "bad without 'do' word" + assert 1 + 1 == 2 + end + + test 'bad with single quotes' do + assert 1 + 1 == 2 + end +end diff --git a/Units/simple-elixir.d/expected.tags b/Units/simple-elixir.d/expected.tags deleted file mode 100644 index ce2538aba9..0000000000 --- a/Units/simple-elixir.d/expected.tags +++ /dev/null @@ -1,55 +0,0 @@ -MyString input.ex /^defmodule MyString do$/;" module -one_liner_func input.ex /^ def one_liner_func, do: :baz$/;" function -func_no_params input.ex /^ def func_no_params do$/;" function -join input.ex /^ def join(string1, string2 \\\\ nil, separator \\\\ " ")$/;" function -join input.ex /^ def join(string1, nil, _separator) do$/;" function -join input.ex /^ def join(string1, string2, separator) do$/;" function -private_func input.ex /^ defp private_func(a), do: a <> " alone"$/;" function -private_func_no_params input.ex /^ defp private_func_no_params do$/;" function -MyParser input.ex /^defmodule URI.MyParser do$/;" module -default_port input.ex /^ @callback default_port() :: integer$/;" callback -parse input.ex /^ defcallback parse(uri_info :: URI.t()) :: URI.t()$/;" callback -MyList input.ex /^defmodule MyList do$/;" module -reverse input.ex /^ defdelegate reverse(list), to: Enum$/;" delegate -other_reverse input.ex /^ defdelegate other_reverse(list), to: Enum, as: :reverse$/;" delegate -Size input.ex /^defprotocol Size do$/;" protocol -size input.ex /^ def size(data)$/;" function -MyGuards input.ex /^defmodule Integer.MyGuards do$/;" module -is_even input.ex /^ defguard is_even(value) when is_integer(value) and rem(value, 2) == 0$/;" guard -is_odd input.ex /^ defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0$/;" guard -Size input.ex /^defimpl Size, for: BitString do$/;" implementation -size input.ex /^ def size(binary), do: byte_size(binary)$/;" function -MyLogic input.ex /^defmodule MyLogic do$/;" module -macro input.ex /^ defmacro macro(expr, opts) do$/;" macro -macro_no_params input.ex /^ defmacro macro_no_params do$/;" macro -private_macro input.ex /^ defmacrop private_macro(expr, opts) do$/;" macro -private_macro_no_params input.ex /^ defmacrop private_macro_no_params do$/;" macro -MyOperators input.ex /^defmodule MyOperators do$/;" module -+ input.ex /^ def a + b, do: max(a, b)$/;" operator -- input.ex /^ def a - b, do: max(a, b)$/;" operator -* input.ex /^ def a * b, do: max(a, b)$/;" operator -/ input.ex /^ def a \/ b, do: max(a, b)$/;" operator -= input.ex /^ def a = b, do: max(a, b)$/;" operator -. input.ex /^ def a . b, do: max(a, b)$/;" operator -| input.ex /^ def a | b, do: max(a, b)$/;" operator -||| input.ex /^ def _ ||| b, do: max(a, b)$/;" operator -&&& input.ex /^ def a &&& _, do: max(a, b)$/;" operator -<<< input.ex /^ def a <<< b, do: max(a, b)$/;" operator ->>> input.ex /^ defp a >>> b, do: max(a, b)$/;" operator -<<~ input.ex /^ defp _ <<~ _, do: max(a, b)$/;" operator -~>> input.ex /^ defp a ~>> b, do: max(a, b)$/;" operator -<~ input.ex /^ defmacro a <~ b, do: max(a, b)$/;" operator -~> input.ex /^ defmacro _ ~> b, do: max(a, b)$/;" operator -<~> input.ex /^ defmacro a <~> _, do: max(a, b)$/;" operator -<|> input.ex /^ defmacrop a <|> b, do: max(a, b)$/;" operator -^^^ input.ex /^ defmacrop _ ^^^ _, do: max(a, b)$/;" operator -~~~ input.ex /^ defmacrop a ~~~ b, do: max(a, b)$/;" operator -Nest input.ex /^defmodule Nest do$/;" module -Of input.ex /^ defmodule Of do$/;" module -Modules input.ex /^ defmodule Modules do$/;" module -MyRecords input.ex /^defmodule MyRecords do$/;" module -user1 input.ex /^ Record.defrecord(:user1, name: "megan", age: "25")$/;" record -user2 input.ex /^ Record.defrecordp :user2, name: "ivan", age: "23"$/;" record -CallbackModule input.ex /^defmodule CallbackModule do$/;" module -with spaces input.ex /^ test "with spaces" do$/;" test -with parens input.ex /^ test("with parens") do$/;" test diff --git a/Units/simple-elixir.d/input.ex b/Units/simple-elixir.d/input.ex deleted file mode 100644 index 33728c9217..0000000000 --- a/Units/simple-elixir.d/input.ex +++ /dev/null @@ -1,190 +0,0 @@ -# Most of this code is taken from the official Elixir documentation. -# With comments and modifications -# by Ivan Gonzalez Polanco -# -# This code doesn't compile, since it's supposed to test de ctags elixir -# parser, not the elixir elixir parser. - -# -# f functions (def ...) -# -defmodule MyString do - def one_liner_func, do: :baz - - def func_no_params do - # - end - - # Function head - def join(string1, string2 \\ nil, separator \\ " ") - - # Function with 1 arity - def join(string1, nil, _separator) do - private_function(string1) - end - - # Normal function - def join(string1, string2, separator) do - string1 <> separator <> string2 - end - - # Private function - defp private_func(a), do: a <> " alone" - - defp private_func_no_params do - # - end -end - -# -# c callbacks (defcallback ...) -# -defmodule URI.MyParser do - use Behaviour - # This is the new callback syntax - @callback default_port() :: integer - - # This is the old (deprecated) callback syntax - defcallback parse(uri_info :: URI.t()) :: URI.t() -end - -# -# d delegates (defdelegate ...) -# -defmodule MyList do - defdelegate reverse(list), to: Enum - defdelegate other_reverse(list), to: Enum, as: :reverse -end - -# TODO: I don't know how to identificate/differenciate an exception since they -# don't _actually_ have name. Instead are defined like a struct. -# -# e exceptions (defexception ...) -# -# defmodule MyAppError do -# defexception [:message] -# -# @impl true -# def exception(value) do -# msg = "did not get what was expected, got: #{inspect(value)}" -# %MyAppError{message: msg} -# end -# end - -# -# p protocols (defprotocol ...) -# -defprotocol Size do - @doc "Calculates the size (and not the length!) of a data structure" - def size(data) -end - -# -# g guards (defguard ...) -# -defmodule Integer.MyGuards do - defguard is_even(value) when is_integer(value) and rem(value, 2) == 0 - defguardp is_odd(value) when is_integer(value) and rem(value, 2) != 0 -end - -# -# i implementations (defimpl ...) -# -defimpl Size, for: BitString do - def size(binary), do: byte_size(binary) -end - -# -# a macros (defmacro ...) -# -defmodule MyLogic do - defmacro macro(expr, opts) do - # - end - - defmacro macro_no_params do - # - end - - defmacrop private_macro(expr, opts) do - # - end - - defmacrop private_macro_no_params do - # - end -end - -# -# o operators (e.g. "defmacro a <<< b") -# -defmodule MyOperators do - def a + b, do: max(a, b) - def a - b, do: max(a, b) - def a * b, do: max(a, b) - def a / b, do: max(a, b) - def a = b, do: max(a, b) - def a . b, do: max(a, b) - - # TODO: Fix the operators regex and then test these definitions - # def a and b, do: max(a, b) - # def a or b, do: max(a, b) - # def a not b, do: max(a, b) - # def a in b, do: max(a, b) - # def a not in b, do: max(a, b) - - # The 13 operators bellow are ALL the operators that Elixir is CAPABLE of - # parsing and are not used by default, so the user can - def a | b, do: max(a, b) - def _ ||| b, do: max(a, b) - def a &&& _, do: max(a, b) - def a <<< b, do: max(a, b) - defp a >>> b, do: max(a, b) - defp _ <<~ _, do: max(a, b) - defp a ~>> b, do: max(a, b) - defmacro a <~ b, do: max(a, b) - defmacro _ ~> b, do: max(a, b) - defmacro a <~> _, do: max(a, b) - defmacrop a <|> b, do: max(a, b) - defmacrop _ ^^^ _, do: max(a, b) - defmacrop a ~~~ b, do: max(a, b) -end - -# -# m modules (defmodule ...) -# -defmodule Nest do - defmodule Of do - defmodule Modules do - end - end -end - -# -# r records (defrecord ...) -# -defmodule MyRecords do - require Record - Record.defrecord(:user1, name: "megan", age: "25") - Record.defrecordp :user2, name: "ivan", age: "23" - # This is not a typo but an intentional bad test, used to test the parser - Record.defrecordp:bad, name: "ivan", age: "23" -end - -# -# t tests (test ...) -# -defmodule CallbackModule do - use ExUnit.Case, async: true - - test "with spaces" do - assert 1 + 1 == 2 - end - - test("with parens") do - assert 1 + 1 == 2 - end - test"with bad spaces" do - assert 1 + 1 == 2 - end -end From eff912a4f1b87f30e62da69cc2dae5088bba8cfe Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Wed, 6 Mar 2019 09:22:59 -0400 Subject: [PATCH 09/12] Add two test cases with bugs More info on the bugs can be found in the test cases' `README.md`. --- .../elixir-exceptions.b/README.md | 5 ++ .../elixir-exceptions.b/expected.tags | 2 + .../elixir-exceptions.b/input.ex | 9 +++ .../elixir-word-operators.b/README.md | 70 +++++++++++++++++++ .../elixir-word-operators.b/args.ctags | 2 + .../elixir-word-operators.b/expected.tags | 6 ++ .../elixir-word-operators.b/input.ex | 7 ++ 7 files changed, 101 insertions(+) create mode 100644 Units/parser-elixir.r/elixir-exceptions.b/README.md create mode 100644 Units/parser-elixir.r/elixir-exceptions.b/expected.tags create mode 100644 Units/parser-elixir.r/elixir-exceptions.b/input.ex create mode 100644 Units/parser-elixir.r/elixir-word-operators.b/README.md create mode 100644 Units/parser-elixir.r/elixir-word-operators.b/args.ctags create mode 100644 Units/parser-elixir.r/elixir-word-operators.b/expected.tags create mode 100644 Units/parser-elixir.r/elixir-word-operators.b/input.ex diff --git a/Units/parser-elixir.r/elixir-exceptions.b/README.md b/Units/parser-elixir.r/elixir-exceptions.b/README.md new file mode 100644 index 0000000000..6d13e3b982 --- /dev/null +++ b/Units/parser-elixir.r/elixir-exceptions.b/README.md @@ -0,0 +1,5 @@ +Exceptions are defined like structs but their name is the name of module they +are defined in. For this I'll have to use a multiline regex +(`--mline-regex-`) which I don't know yet how to use. + +This is more a TODO than a bug. diff --git a/Units/parser-elixir.r/elixir-exceptions.b/expected.tags b/Units/parser-elixir.r/elixir-exceptions.b/expected.tags new file mode 100644 index 0000000000..963b628e95 --- /dev/null +++ b/Units/parser-elixir.r/elixir-exceptions.b/expected.tags @@ -0,0 +1,2 @@ +ModuleError input.ex /^defmodule ModuleError do$/;" module +ModuleError input.ex /^ defexception [:message]$/;" delegate diff --git a/Units/parser-elixir.r/elixir-exceptions.b/input.ex b/Units/parser-elixir.r/elixir-exceptions.b/input.ex new file mode 100644 index 0000000000..604cb80ab4 --- /dev/null +++ b/Units/parser-elixir.r/elixir-exceptions.b/input.ex @@ -0,0 +1,9 @@ +defmodule ErrorModule do + defexception [:message] + + @impl true + def exception(value) do + msg = "did not get what was expected, got: #{inspect(value)}" + %ErrorModule{message: msg} + end +end diff --git a/Units/parser-elixir.r/elixir-word-operators.b/README.md b/Units/parser-elixir.r/elixir-word-operators.b/README.md new file mode 100644 index 0000000000..cf733d436a --- /dev/null +++ b/Units/parser-elixir.r/elixir-word-operators.b/README.md @@ -0,0 +1,70 @@ +When defining a *function* or macro, Elixir uses the following syntax: + +```elixir +def do + +end + +def sum a b do + a + b +end +``` + +But when defining (or overriding) an *operator*, Elixir follows this syntax: + +```elixir +def do + +end + +def a < b do + :erlang.<(a,b) +end +``` + +This makes creating a regex that differenciates between function and operator +particulary annoying, specially when you consider on the edge cases (a function +with zero params, or a name with special characters, like `assert!`) + +The solution my uneducated mind could think of was to determine all the +operators that Elixir could parse, and then reduce them to their basic +characters: + +The following is a list of all the operators that Elixir is capable of +parsing, but that are not used by default (separated by a comma): + +`|, |||, &&&, <<<, >>>, <<~, ~>>, <~, ~>, <~>, <|>, ^^^, \~\~\~` + +They could be expressed in a regex atom `(&&&|<~|<~>|etc...)` but I think +it can be reduced (I don't know the difference in efficiency) to: + +`[\|\^&<>~]{1,3}` + +If we add the characters for the regular operators that can be overriden, we're +left with: `[\|\^\/&<>~.=!*+-]{1,3}` + +Now we're left with these regex: + +```perl +--regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/f/ +--regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/a/ +``` + +Which aren't so bad but are not fine grained enough, because they're missing +these important operators: + +* and +* or +* not +* in +* when + +My solution isn't good enough to notice the keywords above are actually +operators (and even more: default operators). + +I tried with a negative lookahead: `(?![\|\^\/&<>~.=!*+-]{1,3}|and|or|not|in|when)` + +But since the regex engine used in universal-ctags can't do lookahead, I'm +not gonna bother to try and make the operator regex exhaustive... + +Good luck and godspeed to the person brave enough to tackle this. diff --git a/Units/parser-elixir.r/elixir-word-operators.b/args.ctags b/Units/parser-elixir.r/elixir-word-operators.b/args.ctags new file mode 100644 index 0000000000..39d2c946a0 --- /dev/null +++ b/Units/parser-elixir.r/elixir-word-operators.b/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+K diff --git a/Units/parser-elixir.r/elixir-word-operators.b/expected.tags b/Units/parser-elixir.r/elixir-word-operators.b/expected.tags new file mode 100644 index 0000000000..ad9bb480d7 --- /dev/null +++ b/Units/parser-elixir.r/elixir-word-operators.b/expected.tags @@ -0,0 +1,6 @@ +OperatorModule input.ex /^defmodule OperatorModule do$/;" module +and input.ex /^ def a and b, do: max(a, b)$/;" operator +or input.ex /^ def a or b, do: max(a, b)$/;" operator +not input.ex /^ def a not b, do: max(a, b)$/;" operator +in input.ex /^ def a in b, do: max(a, b)$/;" operator +when input.ex /^ def a when b, do: max(a, b)$/;" operator diff --git a/Units/parser-elixir.r/elixir-word-operators.b/input.ex b/Units/parser-elixir.r/elixir-word-operators.b/input.ex new file mode 100644 index 0000000000..4511273c8d --- /dev/null +++ b/Units/parser-elixir.r/elixir-word-operators.b/input.ex @@ -0,0 +1,7 @@ +defmodule OperatorModule do + def a and b, do: max(a, b) + def a or b, do: max(a, b) + def a not b, do: max(a, b) + def a in b, do: max(a, b) + def a when b, do: max(a, b) +end From 88a6546f01ddd41ccb1ae62616e0bcb6eb404ff9 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Wed, 6 Mar 2019 09:33:36 -0400 Subject: [PATCH 10/12] Improve regex for tests and remove operators comment Moved that comment to the README.md of a bugged test case in the Elixir directory. --- optlib/elixir.c | 2 +- optlib/elixir.ctags | 32 +------------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/optlib/elixir.c b/optlib/elixir.c index 189271daf5..b432f61174 100644 --- a/optlib/elixir.c +++ b/optlib/elixir.c @@ -89,7 +89,7 @@ extern parserDefinition* ElixirParser (void) "p", NULL, NULL, false}, {"^[ \t]*Record\\.defrecord(p?)[ \t(]+:([a-zA-Z0-9_]+)(\\)?)", "\\2", "r", NULL, NULL, false}, - {"^[ \t]*test[ \t(]+\"([a-z_][a-zA-Z0-9_?! ]*)\"*(\\)?)", "\\1", + {"^[ \t]*test[ \t(]+\"([a-z_][a-zA-Z0-9_?! ]*)\"*(\\)?)[ \t]*do", "\\1", "t", NULL, NULL, false}, }; diff --git a/optlib/elixir.ctags b/optlib/elixir.ctags index 83c8d20009..0cdcad6f9b 100644 --- a/optlib/elixir.ctags +++ b/optlib/elixir.ctags @@ -47,36 +47,6 @@ # * Use singular forms for kind names. # -# -# from https://hexdocs.pm/elixir/operators.html: -# -# Elixir is capable of parsing a predefined set of operators; this means that -# it's not possible to define new operators (like one could do in Haskell, for -# example). However, not all operators that Elixir can parse are *used* by -# Elixir: for example, `+` and `||` are used by Elixir for addition and boolean -# *or*, but `<~>` is not used (but valid). -# - -# -# The following is a list of all the operators that Elixir is capable of -# parsing, but that are not used by default (separated by a comma): -# -# |, |||, &&&, <<<, >>>, <<~, ~>>, <~, ~>, <~>, <|>, ^^^, ~~~ -# -# They could be expressed in a regex atom `(&&&|<~|<~>|etc...)` but I think -# it can be reduced (I don't know the difference in efficiency) to: -# -# [\|\^&<>~]{1,3} -# -# Adding to that bracket expression the rest of the characters and words used -# for the defined operators (which can be overriden): -# -# ([\|\^\/&<>~.=!*+-]{1,3}|and|or|not|in|not in) -# -# But since the regex engine used in universal-ctags can't do lookahead, I'm -# not gonna bother to try and make the operator regex exhaustive... plus it's -# also bad practice to override the default operators. - --langdef=Elixir --map-Elixir=+.ex @@ -106,4 +76,4 @@ --regex-Elixir=/^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/m/ --regex-Elixir=/^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/p/ --regex-Elixir=/^[ \t]*Record\.defrecord(p?)[ \t(]+:([a-zA-Z0-9_]+)(\)?)/\2/r/ ---regex-Elixir=/^[ \t]*test[ \t(]+"([a-z_][a-zA-Z0-9_?! ]*)"*(\)?)/\1/t/ +--regex-Elixir=/^[ \t]*test[ \t(]+"([a-z_][a-zA-Z0-9_?! ]*)"*(\)?)[ \t]*do/\1/t/ From ef545aee3a3425e569ff71735892aa788a1a5749 Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Wed, 6 Mar 2019 18:00:17 -0400 Subject: [PATCH 11/12] Add 'exclusive' tag to operator regex Fixes the bug where the parser wouldn't correctly generate tags for the 'word' operators (and, or, not, etc...) --- optlib/elixir.c | 4 ++-- optlib/elixir.ctags | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/optlib/elixir.c b/optlib/elixir.c index b432f61174..e1838b0549 100644 --- a/optlib/elixir.c +++ b/optlib/elixir.c @@ -67,6 +67,8 @@ extern parserDefinition* ElixirParser (void) }, }; static tagRegexTable ElixirTagRegexTable [] = { + {"^[ \t]*def((p?)|macro(p?))[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3}|and|or|in|not|when|not in)[ \t]+[a-zA-Z0-9_?!]", "\\5", + "o", "{exclusive}", NULL, false}, {"^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\\|\\^/&<>~.=!*+-]+)", "\\2", "f", NULL, NULL, false}, {"^[ \t]*(@|def)callback[ \t]+([a-z_][a-zA-Z0-9_?!]*)", "\\2", @@ -81,8 +83,6 @@ extern parserDefinition* ElixirParser (void) "i", NULL, NULL, false}, {"^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\\|\\^/&<>~.=!*+-]+)", "\\2", "a", NULL, NULL, false}, - {"^[ \t]*def((p?)|macro(p?))[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\\|\\^/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]", "\\5", - "o", NULL, NULL, false}, {"^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", "m", NULL, NULL, false}, {"^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\\.)*([A-Z][a-zA-Z0-9_?!]*)", "\\2", diff --git a/optlib/elixir.ctags b/optlib/elixir.ctags index 0cdcad6f9b..6a57a817cf 100644 --- a/optlib/elixir.ctags +++ b/optlib/elixir.ctags @@ -65,6 +65,7 @@ --kinddef-Elixir=r,record,records (defrecord...) --kinddef-Elixir=t,test,tests (test ...) +--regex-Elixir=/^[ \t]*def((p?)|macro(p?))[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3}|and|or|in|not|when|not in)[ \t]+[a-zA-Z0-9_?!]/\5/o/{exclusive} --regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/f/ --regex-Elixir=/^[ \t]*(@|def)callback[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\2/c/ --regex-Elixir=/^[ \t]*defdelegate[ \t]+([a-z_][a-zA-Z0-9_?!]*)/\1/d/ @@ -72,7 +73,6 @@ --regex-Elixir=/^[ \t]*defguard(p?)[ \t]+(is_[a-zA-Z0-9_?!]+)/\2/g/ --regex-Elixir=/^[ \t]*defimpl[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/i/ --regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/a/ ---regex-Elixir=/^[ \t]*def((p?)|macro(p?))[ \t]+([a-zA-Z0-9_?!]+)[ \t]+([\|\^\/&<>~.=!*+-]{1,3})[ \t]+[a-zA-Z0-9_!?!]/\5/o/ --regex-Elixir=/^[ \t]*defmodule[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/m/ --regex-Elixir=/^[ \t]*defprotocol[ \t]+([A-Z][a-zA-Z0-9_]*\.)*([A-Z][a-zA-Z0-9_?!]*)/\2/p/ --regex-Elixir=/^[ \t]*Record\.defrecord(p?)[ \t(]+:([a-zA-Z0-9_]+)(\)?)/\2/r/ From 750386c74fd1028e062020f809fc1a4c5fd658ff Mon Sep 17 00:00:00 2001 From: Ivan Gonzalez Polanco Date: Wed, 6 Mar 2019 18:02:50 -0400 Subject: [PATCH 12/12] Move test case from a bugged one to a correct one --- .../elixir-word-operators.b/README.md | 70 ------------------- .../elixir-word-operators.b/expected.tags | 6 -- .../elixir-word-operators.b/input.ex | 7 -- .../args.ctags | 0 .../elixir-word-operators.d/expected.tags | 7 ++ .../elixir-word-operators.d/input.ex | 8 +++ 6 files changed, 15 insertions(+), 83 deletions(-) delete mode 100644 Units/parser-elixir.r/elixir-word-operators.b/README.md delete mode 100644 Units/parser-elixir.r/elixir-word-operators.b/expected.tags delete mode 100644 Units/parser-elixir.r/elixir-word-operators.b/input.ex rename Units/parser-elixir.r/{elixir-word-operators.b => elixir-word-operators.d}/args.ctags (100%) create mode 100644 Units/parser-elixir.r/elixir-word-operators.d/expected.tags create mode 100644 Units/parser-elixir.r/elixir-word-operators.d/input.ex diff --git a/Units/parser-elixir.r/elixir-word-operators.b/README.md b/Units/parser-elixir.r/elixir-word-operators.b/README.md deleted file mode 100644 index cf733d436a..0000000000 --- a/Units/parser-elixir.r/elixir-word-operators.b/README.md +++ /dev/null @@ -1,70 +0,0 @@ -When defining a *function* or macro, Elixir uses the following syntax: - -```elixir -def do - -end - -def sum a b do - a + b -end -``` - -But when defining (or overriding) an *operator*, Elixir follows this syntax: - -```elixir -def do - -end - -def a < b do - :erlang.<(a,b) -end -``` - -This makes creating a regex that differenciates between function and operator -particulary annoying, specially when you consider on the edge cases (a function -with zero params, or a name with special characters, like `assert!`) - -The solution my uneducated mind could think of was to determine all the -operators that Elixir could parse, and then reduce them to their basic -characters: - -The following is a list of all the operators that Elixir is capable of -parsing, but that are not used by default (separated by a comma): - -`|, |||, &&&, <<<, >>>, <<~, ~>>, <~, ~>, <~>, <|>, ^^^, \~\~\~` - -They could be expressed in a regex atom `(&&&|<~|<~>|etc...)` but I think -it can be reduced (I don't know the difference in efficiency) to: - -`[\|\^&<>~]{1,3}` - -If we add the characters for the regular operators that can be overriden, we're -left with: `[\|\^\/&<>~.=!*+-]{1,3}` - -Now we're left with these regex: - -```perl ---regex-Elixir=/^[ \t]*def(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/f/ ---regex-Elixir=/^[ \t]*defmacro(p?)[ \t]+([a-z_][a-zA-Z0-9_?!]*)(.[^\|\^\/&<>~.=!*+-]+)/\2/a/ -``` - -Which aren't so bad but are not fine grained enough, because they're missing -these important operators: - -* and -* or -* not -* in -* when - -My solution isn't good enough to notice the keywords above are actually -operators (and even more: default operators). - -I tried with a negative lookahead: `(?![\|\^\/&<>~.=!*+-]{1,3}|and|or|not|in|when)` - -But since the regex engine used in universal-ctags can't do lookahead, I'm -not gonna bother to try and make the operator regex exhaustive... - -Good luck and godspeed to the person brave enough to tackle this. diff --git a/Units/parser-elixir.r/elixir-word-operators.b/expected.tags b/Units/parser-elixir.r/elixir-word-operators.b/expected.tags deleted file mode 100644 index ad9bb480d7..0000000000 --- a/Units/parser-elixir.r/elixir-word-operators.b/expected.tags +++ /dev/null @@ -1,6 +0,0 @@ -OperatorModule input.ex /^defmodule OperatorModule do$/;" module -and input.ex /^ def a and b, do: max(a, b)$/;" operator -or input.ex /^ def a or b, do: max(a, b)$/;" operator -not input.ex /^ def a not b, do: max(a, b)$/;" operator -in input.ex /^ def a in b, do: max(a, b)$/;" operator -when input.ex /^ def a when b, do: max(a, b)$/;" operator diff --git a/Units/parser-elixir.r/elixir-word-operators.b/input.ex b/Units/parser-elixir.r/elixir-word-operators.b/input.ex deleted file mode 100644 index 4511273c8d..0000000000 --- a/Units/parser-elixir.r/elixir-word-operators.b/input.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule OperatorModule do - def a and b, do: max(a, b) - def a or b, do: max(a, b) - def a not b, do: max(a, b) - def a in b, do: max(a, b) - def a when b, do: max(a, b) -end diff --git a/Units/parser-elixir.r/elixir-word-operators.b/args.ctags b/Units/parser-elixir.r/elixir-word-operators.d/args.ctags similarity index 100% rename from Units/parser-elixir.r/elixir-word-operators.b/args.ctags rename to Units/parser-elixir.r/elixir-word-operators.d/args.ctags diff --git a/Units/parser-elixir.r/elixir-word-operators.d/expected.tags b/Units/parser-elixir.r/elixir-word-operators.d/expected.tags new file mode 100644 index 0000000000..b41592a4e1 --- /dev/null +++ b/Units/parser-elixir.r/elixir-word-operators.d/expected.tags @@ -0,0 +1,7 @@ +OperatorModule input.ex /^defmodule OperatorModule do$/;" module +and input.ex /^ def a and b, do: max(a, b)$/;" operator +or input.ex /^ def a or b, do: max(a, b)$/;" operator +not input.ex /^ defp a not b, do: max(a, b)$/;" operator +in input.ex /^ defmacro a in b, do: max(a, b)$/;" operator +not in input.ex /^ defmacrop a not in b, do: max(a, b)$/;" operator +when input.ex /^ defmacrop a when b, do: max(a, b)$/;" operator diff --git a/Units/parser-elixir.r/elixir-word-operators.d/input.ex b/Units/parser-elixir.r/elixir-word-operators.d/input.ex new file mode 100644 index 0000000000..c52945b96e --- /dev/null +++ b/Units/parser-elixir.r/elixir-word-operators.d/input.ex @@ -0,0 +1,8 @@ +defmodule OperatorModule do + def a and b, do: max(a, b) + def a or b, do: max(a, b) + defp a not b, do: max(a, b) + defmacro a in b, do: max(a, b) + defmacrop a not in b, do: max(a, b) + defmacrop a when b, do: max(a, b) +end