mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Gabriele Monaco <gmonaco@redhat.com>
To: Nam Cao <namcao@linutronix.de>
Cc: Wander Lairson Costa <wander@redhat.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	linux-trace-kernel@vger.kernel.org,
	 linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 01/13] verification/rvgen: Switch LTL parser to Lark
Date: Wed, 03 Jun 2026 16:49:10 +0200	[thread overview]
Message-ID: <9e8309eda671e19579397ac14762f72f2c33466c.camel@redhat.com> (raw)
In-Reply-To: <e1736ef618e32712eeb65d1714a2fea76298057b.1779956342.git.namcao@linutronix.de>

On Thu, 2026-05-28 at 10:27 +0200, Nam Cao wrote:
> The LTL parser is built using Ply. However, Ply is no longer
> maintained [1].
> 
> Switch to use Lark instead. In addition to being actively maintained,
> Lark
> also offers additional features (namely, automatically creating the
> abstract syntax tree) which make the parser simpler.
> 
> Link:
> https://github.com/dabeaz/ply/commit/9d7c40099e23ff78f9d86ef69a26c1e8a83e706a
>  [1]
> Signed-off-by: Nam Cao <namcao@linutronix.de>
> ---

Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>

> v2:
>   - fix identifier starting with a digit is allowed [Wander]
>   - fixup ast node uid [Gabriele]
>   - Fix up Literal AST node construction [Wander, Sashiko]
>   - FIx up unary op error message [Sashiko]
>   - Add nice exception handling [Gabriele]
> ---
>  tools/verification/rvgen/__main__.py     |   5 +-
>  tools/verification/rvgen/rvgen/ltl2ba.py | 202 +++++++++------------
> --
>  2 files changed, 82 insertions(+), 125 deletions(-)
> 
> diff --git a/tools/verification/rvgen/__main__.py
> b/tools/verification/rvgen/__main__.py
> index 5c923dc10d0f..0915cf86e43b 100644
> --- a/tools/verification/rvgen/__main__.py
> +++ b/tools/verification/rvgen/__main__.py
> @@ -14,6 +14,7 @@ if __name__ == '__main__':
>      from rvgen.container import Container
>      from rvgen.ltl2k import ltl2k
>      from rvgen.automata import AutomataError
> +    from rvgen.ltl2ba import LTLError
>      import argparse
>      import sys
>  
> @@ -57,8 +58,8 @@ if __name__ == '__main__':
>                  sys.exit(1)
>          else:
>              monitor = Container(vars(params))
> -    except AutomataError as e:
> -        print(f"There was an error processing {params.spec}: {e}",
> file=sys.stderr)
> +    except (AutomataError, LTLError) as e:
> +        print(f"There was an error processing {params.spec}:\n{e}",
> file=sys.stderr)
>          sys.exit(1)
>  
>      print(f"Writing the monitor into the directory {monitor.name}")
> diff --git a/tools/verification/rvgen/rvgen/ltl2ba.py
> b/tools/verification/rvgen/rvgen/ltl2ba.py
> index 016e7cf93bbb..7cebda61bce8 100644
> --- a/tools/verification/rvgen/rvgen/ltl2ba.py
> +++ b/tools/verification/rvgen/rvgen/ltl2ba.py
> @@ -7,9 +7,7 @@
>  # https://doi.org/10.1007/978-0-387-34892-6_1
>  # With extra optimizations
>  
> -from ply.lex import lex
> -from ply.yacc import yacc
> -from .automata import AutomataError
> +import lark
>  
>  # Grammar:
>  # 	ltl ::= opd | ( ltl ) | ltl binop ltl | unop ltl
> @@ -30,42 +28,41 @@ from .automata import AutomataError
>  #       imply
>  #       equivalent
>  
> -tokens = (
> -   'AND',
> -   'OR',
> -   'IMPLY',
> -   'UNTIL',
> -   'ALWAYS',
> -   'EVENTUALLY',
> -   'NEXT',
> -   'VARIABLE',
> -   'LITERAL',
> -   'NOT',
> -   'LPAREN',
> -   'RPAREN',
> -   'ASSIGN',
> -)
> -
> -t_AND = r'and'
> -t_OR = r'or'
> -t_IMPLY = r'imply'
> -t_UNTIL = r'until'
> -t_ALWAYS = r'always'
> -t_NEXT = r'next'
> -t_EVENTUALLY = r'eventually'
> -t_VARIABLE = r'[A-Z_0-9]+'
> -t_LITERAL = r'true|false'
> -t_NOT = r'not'
> -t_LPAREN = r'\('
> -t_RPAREN = r'\)'
> -t_ASSIGN = r'='
> -t_ignore_COMMENT = r'\#.*'
> -t_ignore = ' \t\n'
> -
> -def t_error(t):
> -    raise AutomataError(f"Illegal character '{t.value[0]}'")
> -
> -lexer = lex()
> +GRAMMAR = r'''
> +start: assign+
> +
> +assign: VARIABLE "=" _ltl
> +
> +_ltl: _opd | binop | unop
> +
> +_opd : VARIABLE
> +     | LITERAL
> +     | "(" _ltl ")"
> +
> +unop: UNOP _ltl
> +UNOP: "always"
> +     | "eventually"
> +     | "next"
> +     | "not"
> +
> +binop: _opd BINOP _ltl
> +BINOP: "until"
> +      | "and"
> +      | "or"
> +      | "imply"
> +
> +VARIABLE: /[A-Z_][A-Z0-9_]*/
> +LITERAL: "true" | "false"
> +
> +COMMENT: "#" /.*/ "\n"
> +%ignore COMMENT
> +
> +%import common.WS
> +%ignore WS
> +'''
> +
> +class LTLError(Exception):
> +    "Exception raised for malformed linear temporal logic"
>  
>  class GraphNode:
>      uid = 0
> @@ -97,7 +94,7 @@ class GraphNode:
>          return self.id < other.id
>  
>  class ASTNode:
> -    uid = 1
> +    uid = 0
>  
>      def __init__(self, op):
>          self.op = op
> @@ -433,90 +430,49 @@ class Literal:
>          node.old |= {n}
>          return node.expand(node_set)
>  
> -def p_spec(p):
> -    '''
> -    spec : assign
> -         | assign spec
> -    '''
> -    if len(p) == 3:
> -        p[2].append(p[1])
> -        p[0] = p[2]
> -    else:
> -        p[0] = [p[1]]
> -
> -def p_assign(p):
> -    '''
> -    assign : VARIABLE ASSIGN ltl
> -    '''
> -    p[0] = (p[1], p[3])
> -
> -def p_ltl(p):
> -    '''
> -    ltl : opd
> -        | binop
> -        | unop
> -    '''
> -    p[0] = p[1]
> -
> -def p_opd(p):
> -    '''
> -    opd : VARIABLE
> -        | LITERAL
> -        | LPAREN ltl RPAREN
> -    '''
> -    if p[1] == "true":
> -        p[0] = ASTNode(Literal(True))
> -    elif p[1] == "false":
> -        p[0] = ASTNode(Literal(False))
> -    elif p[1] == '(':
> -        p[0] = p[2]
> -    else:
> -        p[0] = ASTNode(Variable(p[1]))
> -
> -def p_unop(p):
> -    '''
> -    unop : ALWAYS ltl
> -         | EVENTUALLY ltl
> -         | NEXT ltl
> -         | NOT ltl
> -    '''
> -    if p[1] == "always":
> -        op = AlwaysOp(p[2])
> -    elif p[1] == "eventually":
> -        op = EventuallyOp(p[2])
> -    elif p[1] == "next":
> -        op = NextOp(p[2])
> -    elif p[1] == "not":
> -        op = NotOp(p[2])
> -    else:
> -        raise AutomataError(f"Invalid unary operator {p[1]}")
> -
> -    p[0] = ASTNode(op)
> -
> -def p_binop(p):
> -    '''
> -    binop : opd UNTIL ltl
> -          | opd AND ltl
> -          | opd OR ltl
> -          | opd IMPLY ltl
> -    '''
> -    if p[2] == "and":
> -        op = AndOp(p[1], p[3])
> -    elif p[2] == "until":
> -        op = UntilOp(p[1], p[3])
> -    elif p[2] == "or":
> -        op = OrOp(p[1], p[3])
> -    elif p[2] == "imply":
> -        op = ImplyOp(p[1], p[3])
> -    else:
> -        raise AutomataError(f"Invalid binary operator {p[2]}")
> -
> -    p[0] = ASTNode(op)
> -
> -parser = yacc()
> +class Transform(lark.visitors.Transformer):
> +    def unop(self, node):
> +        if node[0] == "always":
> +            return ASTNode(AlwaysOp(node[1]))
> +        if node[0] == "eventually":
> +            return ASTNode(EventuallyOp(node[1]))
> +        if node[0] == "next":
> +            return ASTNode(NextOp(node[1]))
> +        if node[0] == "not":
> +            return ASTNode(NotOp(node[1]))
> +        raise ValueError("Unknown operator %s" % node[0])
> +
> +    def binop(self, node):
> +        if node[1] == "until":
> +            return ASTNode(UntilOp(node[0], node[2]))
> +        if node[1] == "and":
> +            return ASTNode(AndOp(node[0], node[2]))
> +        if node[1] == "or":
> +            return ASTNode(OrOp(node[0], node[2]))
> +        if node[1] == "imply":
> +            return ASTNode(ImplyOp(node[0], node[2]))
> +        raise ValueError("Unknown operator %s" % node[1])
> +
> +    def VARIABLE(self, args):
> +        return ASTNode(Variable(args))
> +
> +    def LITERAL(self, args):
> +        return ASTNode(Literal(args == "true"))
> +
> +    def start(self, node):
> +        return node
> +
> +    def assign(self, node):
> +        return node[0].op.name, node[1]
> +
> +parser = lark.Lark(GRAMMAR)
>  
>  def parse_ltl(s: str) -> ASTNode:
> -    spec = parser.parse(s)
> +    try:
> +        spec = parser.parse(s)
> +    except lark.exceptions.UnexpectedInput as e:
> +        raise LTLError(str(e))
> +    spec = Transform().transform(spec)
>  
>      rule = None
>      subexpr = {}
> @@ -528,7 +484,7 @@ def parse_ltl(s: str) -> ASTNode:
>              subexpr[assign[0]] = assign[1]
>  
>      if rule is None:
> -        raise AutomataError("Please define your specification in the
> \"RULE = <LTL spec>\" format")
> +        raise LTLError("Please define your specification in the
> \"RULE = <LTL spec>\" format")
>  
>      for node in rule:
>          if not isinstance(node.op, Variable):

  reply	other threads:[~2026-06-03 14:49 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-28  8:27 [PATCH v2 00/13] rv: Convert rvgen " Nam Cao
2026-05-28  8:27 ` [PATCH v2 01/13] verification/rvgen: Switch LTL parser " Nam Cao
2026-06-03 14:49   ` Gabriele Monaco [this message]
2026-05-28  8:27 ` [PATCH v2 02/13] verification/rvgen: Introduce a parse tree for automata using Lark Nam Cao
2026-06-03 14:55   ` Gabriele Monaco
2026-05-28  8:27 ` [PATCH v2 03/13] verification/rvgen: Implement state and transition parser based on Lark Nam Cao
2026-06-03 14:55   ` Gabriele Monaco
2026-05-28  8:27 ` [PATCH v2 04/13] verification/rvgen: Convert __fill_verify_invariants_func() to Lark Nam Cao
2026-06-03 14:56   ` Gabriele Monaco
2026-05-28  8:27 ` [PATCH v2 05/13] verification/rvgen: Convert __fill_setup_invariants_func() " Nam Cao
2026-06-03 15:24   ` Gabriele Monaco
2026-05-28  8:27 ` [PATCH v2 06/13] verification/rvgen: Convert __fill_verify_guards_func() " Nam Cao
2026-06-03 16:00   ` Gabriele Monaco
2026-05-28  8:27 ` [PATCH v2 07/13] rv: Simply hybrid automata monitors's clock variables Nam Cao
2026-06-03  9:27   ` Gabriele Monaco
2026-05-28  8:27 ` [PATCH v2 08/13] verification/rvgen: Simplify the generation for " Nam Cao
2026-05-28  8:27 ` [PATCH v2 09/13] verification/rvgen: Delete __parse_constraint() Nam Cao
2026-05-28  8:27 ` [PATCH v2 10/13] verification/rvgen: Switch __get_event_variables() to Lark Nam Cao
2026-05-28  8:27 ` [PATCH v2 11/13] verification/rvgen: Switch __create_matrix() " Nam Cao
2026-05-28  8:28 ` [PATCH v2 12/13] verification/rvgen: Remove the old state variables Nam Cao
2026-05-28  8:28 ` [PATCH v2 13/13] verification/rvgen: Remove dead code Nam Cao
2026-06-03 15:36   ` Gabriele Monaco
2026-06-08  8:29     ` Nam Cao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9e8309eda671e19579397ac14762f72f2c33466c.camel@redhat.com \
    --to=gmonaco@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=namcao@linutronix.de \
    --cc=rostedt@goodmis.org \
    --cc=wander@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®