Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

readtags: S expression based filter engine like wireshark's display filter #791

Merged
merged 6 commits into from
Feb 18, 2016

Conversation

masatake
Copy link
Member

(Request for reviewer: I wonder which is better "filter" or "qualifier" for this feature. filter looks better but it misleads "pipe and filter" of command line interface. Give me advise if you like this feature.)

Examples of input

Make tags(*foo.tags*) with following command line

.. code-block:: console

    $ ./ctags --fields='*' --extra='*' -o foo.tags foo.py

for following input (*foo.py*)

.. code-block:: Python

    class Foo:
    def aq ():
        pass
    def aw ():
        pass
    def ae ():
        pass
    class A:
        pass
    class Bar (Foo):
    def bq ():
        pass
    def bw ():
        pass
    class B:
        pass

    class Baz (Foo):
    def bq ():
        pass
    def bw ():
        pass
    class C:
        pass

Examples of filter expressions
  • Print entries ended with "q"

    .. code-block:: console

    $ ./readtags -e -t foo.tags -Q '(suffix? $name "q")' -l
    Bar.bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Bar access:public signature:()
    Baz.bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Baz access:public signature:()
    Foo.aq foo.py /^ def aq ():$/;" kind:member language:Python scope:class:Foo access:public signature:()
    aq foo.py /^ def aq ():$/;" kind:member language:Python scope:class:Foo access:public signature:()
    bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Bar access:public signature:()
    bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Baz access:public signature:()

  • Print members of Baz

    .. code-block:: console

    $ ./readtags -e -t foo.tags -Q '(and (eq? $kind "member") (eq? "Baz" $scope-name))' -l
    Baz.bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Baz access:public signature:()
    Baz.bw foo.py /^ def bw ():$/;" kind:member language:Python scope:class:Baz access:public signature:()
    bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Baz access:public signature:()
    bw foo.py /^ def bw ():$/;" kind:member language:Python scope:class:Baz access:public signature:()

  • Print only full qualified entries (assuming "." is used as the separator)

    .. code-block:: console

    $ ./readtags -e -t foo.tags -Q '(and (eq? $kind "member") (substr? $name "."))' -l
    Bar.bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Bar access:public signature:()
    Bar.bw foo.py /^ def bw ():$/;" kind:member language:Python scope:class:Bar access:public signature:()
    Baz.bq foo.py /^ def bq ():$/;" kind:member language:Python scope:class:Baz access:public signature:()
    Baz.bw foo.py /^ def bw ():$/;" kind:member language:Python scope:class:Baz access:public signature:()
    Foo.ae foo.py /^ def ae ():$/;" kind:member language:Python scope:class:Foo access:public signature:()
    Foo.aq foo.py /^ def aq ():$/;" kind:member language:Python scope:class:Foo access:public signature:()
    Foo.aw foo.py /^ def aw ():$/;" kind:member language:Python scope:class:Foo access:public signature:()

  • Print only inheriting specified classes

    .. code-block:: console

    $ ./readtags -e -t foo.tags -Q '(and (member "Foo" $inherits) (eq? $kind "class"))' -l
    Bar foo.py /^class Bar (Foo):$/;" kind:class language:Python inherits:Foo access:public
    Baz foo.py /^class Baz (Foo): $/;" kind:class language:Python inherits:Foo access:public

@masatake
Copy link
Member Author

TODO: lambda and map are not there.

Eventually a real interpreter like lua or siod will be needed. I don't know lua so siod is better for me.
We can keep the current syntax proposed in this PR even if we introduce siod(scheme in one defun).

Some work about build script will be needed to introduce siod.
With build-in script language you can write parser, formatter, sorting and filtering rule.

The definition for READ_TEST macro used in roundtrip target was
broken. For running the target, this commit update the
definition.

Signed-off-by: Masatake YAMATO <[email protected]>
Filtering in readtags command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
readtags has ability to find tag entries by name.

The concept filtering is inspired from display filter of wireshark.
You can give more complex condition for searching. Currently this
feature is available only on platforms where `fmemopen` is available
as part of libc. Filtering in readtags command is an
experimental feature.

The syntax of filtering rule is based on scheme language, a variant
of lisp. The language has prefix notation and parenthesis.

Before printing an entry of tags file, readtags evaluates an
expression (S expression or sexp) given as an option argument for
``-Q`` option. As the result of the evaluation, readtags gets
an value. false represented as `#f` in S expression, means
rejection: readtags doesn't print it.

::

   SEXP =
	LIST
	INTEGER
	BOOLEAN
	STRING
	SYMBOL

	LIST = ( SEXP... ) | ()
	INTEGER = [0-9]+
	BOOLEAN = #t | #f
	STRING  = "..."
	SYMBOL  = null?
		    and
		     or
		    not
		    eq?
		      <
		      >
		     <=
		     >=
		prefix?
		suffix?
		substr?
		 member
		  $name
		 $input
		$access
		  $file
	      $language
	$implementation
		  $line
		  $kind
		  $role
	       $pattern
	      $inherits
	    $scope-kind
	    $scope-name

All symbols started from `$` represent a field of an entry which is
under judgment with the S expression. Most of all them are evaluated
as a string or `#f`. It is evaluated as `#f` when the field doesn't
exist. `$inherits` is evaluated to a list of strings if the entry has
`inherits` field. `scope` field holds structured data: the kind and
name of upper scope combined with `:`. The kind part goes
`$scope-kind`, and the name part goes `$scope-name`.

`$scope-kind` and `$scope-name` can be used only if the
input tags file is generated by ctags with ``--fields=+Z``.

All symbols not started from `$` are operators. When using, put them
at the head(car) of list. The rest(cdr) of list are passed to the
operator as arguments. Many of them are also available of scheme
language; see the other documents.

prefix?, suffix?, and substr? may be only available in this
implementation. All of them takes two strings. The first one
is called target.

::

	(prefix? "TARGET" "TA")
	=> #t

	(prefix? "TARGET" "RGET")
	=> #f

	(prefix? "TARGET" "RGE")
	=> #f

	(suffix? "TARGET" "TA")
	=> #f

	(suffix? "TARGET" "RGET")
	=> #t

	(suffix? "TARGET" "RGE")
	=> #f

	(substr? "TARGET" "TA")
	=> #t

	(suffix? "TARGET" "RGET")
	=> #t

	(suffix? "TARGET" "RGE")
	=> #t

	(and (suffix? "TARGET" "TARGET")
	     (prefix? "TARGET" "TARGET")
	     (substr? "TARGET" "TARGET")
	=> #t

Let's see examples.

Examples of input
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Make tags(*foo.tags*) with following command line

.. code-block:: console

	$ ./ctags --fields='*' --extra='*' -o foo.tags foo.py

for following input (*foo.py*)

.. code-block:: Python

    class Foo:
	def aq ():
	    pass
	def aw ():
	    pass
	def ae ():
	    pass
	class A:
	    pass
    class Bar (Foo):
	def bq ():
	    pass
	def bw ():
	    pass
	class B:
	    pass

    class Baz (Foo):
	def bq ():
	    pass
	def bw ():
	    pass
	class C:
	    pass

Examples of filter expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Print entries ended with "q"

  .. code-block:: console

	$ ./readtags -e -t foo.tags -Q '(suffix? $name "q")' -l
	Bar.bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Bar	access:public	signature:()
	Baz.bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()
	Foo.aq	foo.py	/^    def aq ():$/;"	kind:member	language:Python	scope:class:Foo	access:public	signature:()
	aq	foo.py	/^    def aq ():$/;"	kind:member	language:Python	scope:class:Foo	access:public	signature:()
	bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Bar	access:public	signature:()
	bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()

* Print members of Baz

  .. code-block:: console

	$ ./readtags -e -t foo.tags -Q '(and (eq? $kind "member") (eq? "Baz" $scope-name))' -l
	Baz.bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()
	Baz.bw	foo.py	/^    def bw ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()
	bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()
	bw	foo.py	/^    def bw ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()

* Print only full qualified entries (assuming "." is used as the separator)

  .. code-block:: console

	$ ./readtags -e -t foo.tags -Q '(and (eq? $kind "member") (substr? $name "."))' -l
	Bar.bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Bar	access:public	signature:()
	Bar.bw	foo.py	/^    def bw ():$/;"	kind:member	language:Python	scope:class:Bar	access:public	signature:()
	Baz.bq	foo.py	/^    def bq ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()
	Baz.bw	foo.py	/^    def bw ():$/;"	kind:member	language:Python	scope:class:Baz	access:public	signature:()
	Foo.ae	foo.py	/^    def ae ():$/;"	kind:member	language:Python	scope:class:Foo	access:public	signature:()
	Foo.aq	foo.py	/^    def aq ():$/;"	kind:member	language:Python	scope:class:Foo	access:public	signature:()
	Foo.aw	foo.py	/^    def aw ():$/;"	kind:member	language:Python	scope:class:Foo	access:public	signature:()

* Print only inheriting specified classes

  .. code-block:: console

	$ ./readtags  -e -t foo.tags -Q '(and (member "Foo" $inherits) (eq? $kind "class"))' -l
	Bar	foo.py	/^class Bar (Foo):$/;"	kind:class	language:Python	inherits:Foo	access:public
	Baz	foo.py	/^class Baz (Foo): $/;"	kind:class	language:Python	inherits:Foo	access:public

Signed-off-by: Masatake YAMATO <[email protected]>
For testing the behavior of readtags, this commits
extends tmain test harness.

With this change tmain passes the path for readtags
to a test case as the 4th argument.

Signed-off-by: Masatake YAMATO <[email protected]>
masatake added a commit that referenced this pull request Feb 18, 2016
readtags: S expression based filter engine like wireshark's display filter
@masatake masatake merged commit 1421dfa into universal-ctags:master Feb 18, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant