]> www.infradead.org Git - users/dhowells/kafs-utils.git/commitdiff
Parse error table
authorDavid Howells <dhowells@redhat.com>
Fri, 16 Oct 2020 08:14:51 +0000 (09:14 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 5 May 2023 10:53:26 +0000 (11:53 +0100)
comerr/comerr.py [new file with mode: 0755]

diff --git a/comerr/comerr.py b/comerr/comerr.py
new file mode 100755 (executable)
index 0000000..cd7c7f9
--- /dev/null
@@ -0,0 +1,283 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+#
+# Parse a comerr table into a series of throwable objects and a function that
+# throws the appropriate one for an abort code.
+#
+
+__copyright__ = """
+Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys
+import keyword
+import time
+import os
+
+#
+# Convert the error table name to the base value.  The name is a base-64
+# encoding, but without a 0.
+#
+def calc_base_from_name(name):
+    charmap = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
+    mod_base = 1000000
+    table_number = 0
+
+    for i in range(0, len(name)):
+        c = name[i]
+        v = charmap.find(c)
+        if v == -1:
+            print("Can't map name char ", c)
+            exit(1)
+        table_number *= 64
+        table_number += v
+    return table_number * 256
+
+
+class error:
+    def __init__(self, name, string):
+        self.source = None              # Current source file
+        self.lineno = None              # Current line number
+        self.symbol = name              # Symbolic name
+        self.text = string              # Display string
+        self.value = None               # Abort code
+
+class error_table:
+    def __init__(self, name, errors):
+        self.name = name                        # Name of error table
+        self.base = calc_base_from_name(name)   # Base error number
+        self.errors = errors                    # List of errors
+        val = self.base
+        for ec in errors:
+            ec.value = val
+            val += 1
+
+##########################################################################
+#                                                                        #
+#                            Lexical analysis                            #
+#                                                                        #
+##########################################################################
+import ply.lex as lex
+
+keywords = (
+    "error_table",
+    "ec",
+    "end",
+)
+
+# Required by lex.  Each token also allows a function t_<token>.
+tokens = tuple([t.upper() for t in keywords]) + (
+    "ID", "STRING",
+    # ,
+    "COMMA", "NEWFILE"
+)
+
+# t_<name> functions are used by lex.  They are called with t.value==<match
+# of rule in comment>, and t.type==<name>.  They expect a return value
+# with attribute type=<token>
+
+# Tell lexer to ignore Whitespace.
+t_ignore = " \t"
+
+def t_NEWFILE(t):
+    r'__NEWFILE__ [^\n]*'
+    t.lexer.source = t.value[12:]
+    t.lexer.lineno = 0
+    return t
+
+def t_ID(t):
+    r'[A-Za-z][A-Za-z0-9_]*'
+    if t.value in keywords:
+        t.type = t.value.upper()
+    return t
+
+def t_STRING(t):
+    r'\"(\\.|[^"])*\"'
+    return t
+
+# Tokens
+t_COMMA            = r','
+
+def t_newline(t):
+    r'\n+'
+    t.lexer.lineno += t.value.count("\n")
+
+def t_error(t):
+    print("Illegal character {:s} at {:d} type {:s}".format(repr(t.value[0]),
+                                                            t.lineno, t.type))
+    t.lexer.skip(1)
+
+# Build the lexer
+lex.lex(debug=0)
+
+##########################################################################
+#                                                                        #
+#                          Yacc Parsing Info                             #
+#                                                                        #
+##########################################################################
+
+def p_specification(t):
+    '''specification : NEWFILE definition'''
+    t[0] = t[2]
+
+def p_definition(t):
+    '''definition : ERROR_TABLE ID error_list END'''
+    t[0] = error_table(t[2], t[3])
+
+def p_error_list_1(t):
+    '''error_list : ec'''
+    t[0] = [ t[1] ]
+
+def p_error_list_2(t):
+    '''error_list : error_list ec'''
+    t[1].append(t[2])
+    t[0] = t[1]
+
+def p_ec(t):
+    '''ec : EC ID COMMA STRING'''
+    t[0] = error(t[2], t[4])
+
+def p_error(t):
+    global error_occurred
+    error_occurred = True
+    if t:
+        print(t)
+        print("Syntax error at '{:s}' (lineno {:d})".format(t.value, t.lineno))
+    else:
+        print("Syntax error: unexpectedly hit EOF")
+
+# Parse the input data with yacc
+import ply.yacc as yacc
+yacc.yacc(debug=0)
+
+##########################################################################
+#                                                                        #
+#                          Global Variables                              #
+#                                                                        #
+##########################################################################
+
+error_occurred = False # Parsing of infile status
+
+##########################################################################
+#                                                                        #
+#                         Preprocessor                                   #
+#                                                                        #
+##########################################################################
+def preprocess(data, filename):
+    lines = data.splitlines()
+
+    # Remove comments
+    for i in range(0, len(lines)):
+        l = lines[i]
+        p = l.find("#")
+        if p != -1:
+            lines[i] = l[:p]
+
+    lines.insert(0, "__NEWFILE__ {:s}".format(filename))
+    return "\n".join(lines)
+
+##########################################################################
+#                                                                        #
+#                          Main Loop                                     #
+#                                                                        #
+##########################################################################
+def parse(infile, debug=False):
+    f = open(infile, encoding="utf-8")
+    data = f.read()
+    f.close()
+
+    data = preprocess(data, infile)
+
+    print("Parsing", infile);
+    global yacc
+    p = yacc.parse(data, debug=debug)
+
+    if error_occurred:
+        print
+        print("Error occurred, did not write output files")
+        return False
+    return p
+
+#
+# Render the result
+#
+def rxhdr(*va):
+    for i in va:
+        sys.stdout.write(str(i))
+
+def rxsrc(*va):
+    for i in va:
+        sys.stdout.write(str(i))
+
+def rxsrcf(*va):
+    sys.stdout.write(fmt.format(*va))
+
+def render(tables):
+    rxhdr("namespace comerr {\n")
+    for et in tables:
+        rxhdr("\n")
+        rxhdr("namespace comerr::", et.name, " {\n")
+        for ec in et.errors:
+            ecclass = ec.symbol
+            rxhdr("class ", ecclass, " : public rxrpc::rx_abort {\n");
+            rxhdr("\tpublic: ", ecclass, "(int ac, const char *op)\n")
+            rxhdr("\t: rx_abort(", ec.value, ", \"", ec.symbol, "\", ", ec.text, ", op) {}\n")
+            rxhdr("};\n")
+        rxhdr("} /* namespace comerr::", et.name, " */\n")
+    rxhdr("extern void comerr_throw_abort(int ac, const char *op=NULL);\n");
+    rxhdr("} /* namespace comerr */\n")
+
+    # Emit an abort throwing function
+    rxsrc("\n")
+    rxsrc("namespace comerr {\n")
+    rxsrc("\n")
+    rxsrc("void comerr_throw_abort(int ac, const char *op)\n");
+    rxsrc("{\n")
+    rxsrc("\tswitch (ac) {\n")
+    for et in tables:
+        for ec in et.errors:
+            ecclass = ec.symbol
+            rxsrc("\tcase ", ec.symbol, ":\tthrow ", et.name, "::", ecclass, "(op);\n")
+
+    rxsrc("\tdefault: rxrpc::throw_abort(ac, op);\n")
+    rxsrc("\t}\n")
+    rxsrc("}\n")
+    rxsrc("\n")
+    rxsrc("} /* end namespace comerr */\n")
+
+#
+# Section: main
+#
+def run(files, out_file_prefix):
+    tables = []
+
+    for f in files:
+        et = parse(f)
+        if not et:
+            break
+        tables.append(et)
+    render(tables)
+
+#
+# Drive the program if executed as a standalone process
+#
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        print("Usage: {:s} <filename>*".format(sys.argv[0]))
+        sys.exit(1)
+
+    run(sys.argv[1:], "")