Automatically generate the Java list of OBD2 sensor indices from the HIDL definition.

This commit introduces the hidl_parser library, which is capable of generating a parse tree from a set of HIDL enum definitions.
It then uses the output of hidl_parser to generate CarDiagnosticSensorIndices.java.

Test: update-obd2-sensors.py + rebuild
Change-Id: Ie368b616468b0a2b46da5aa92deb4617c78302e1
diff --git a/tools/hidl_parser/__init__.py b/tools/hidl_parser/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/hidl_parser/__init__.py
diff --git a/tools/hidl_parser/parser.py b/tools/hidl_parser/parser.py
new file mode 100644
index 0000000..b0349c0
--- /dev/null
+++ b/tools/hidl_parser/parser.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# A parser for enum types defined in HIDL.
+# This script can parse HIDL files and generate a parse tree.
+# To use, import and call parse("path/to/file.hal")
+# It will return a Python dictionary with two keys:
+#  - header: an instance of Header
+#  - enums: a dictionary of EnumDecl objects by name
+# This script cannot parse structs for now, but that would be easy to add.
+
+# It requires 'ply' (Python Lex/Yacc).
+
+import ply
+
+tokens = ('package', 'import', 'enum',
+    'COLON', 'IDENTIFIER', 'COMMENT', 'NUMBER', 'HEX', 'OR', 'EQUALS',
+    'LPAREN', 'RPAREN', 'LBRACE', 'RBRACE', 'DOT', 'SEMICOLON', 'VERSION',
+    'COMMA', 'SHIFT')
+
+t_COLON = r':'
+t_NUMBER = r'[0-9]+'
+t_HEX = r'0x[0-9A-Fa-f]+'
+t_OR = r'\|'
+t_EQUALS = r'='
+t_LPAREN = r'\('
+t_RPAREN = r'\)'
+t_SHIFT = r'<<'
+
+def t_COMMENT(t):
+    r'(/\*(.|\n)*?\*/)|(//.*)'
+    pass
+
+t_LBRACE = r'{'
+t_RBRACE = r'}'
+t_DOT = r'\.'
+t_SEMICOLON = r';'
+t_VERSION = r'@[0-9].[0-9]'
+t_COMMA = r','
+t_ignore = ' \n\t'
+
+def t_IDENTIFIER(t):
+    r'[a-zA-Z_][a-zA-Z_0-9]*'
+    if t.value == 'package':
+        t.type = 'package'
+    elif t.value == 'import':
+        t.type = 'import'
+    elif t.value == 'enum':
+        t.type = 'enum'
+    return t
+
+def t_error(t):
+    t.type = t.value[0]
+    t.value = t.value[0]
+    t.lexer.skip(1)
+    return t
+
+import ply.lex as lex
+lexer = lex.lex()
+
+class EnumHeader(object):
+    def __init__(self, name, base):
+        self.name = name
+        self.base = base
+
+    def __str__(self):
+        return '%s%s' % (self.name, ' %s' % self.base if self.base else '')
+
+class EnumDecl(object):
+    def __init__(self, header, cases):
+        self.header = header
+        self.cases = cases
+
+    def __str__(self):
+        return '%s {\n%s\n}' % (self.header,
+            '\n'.join(str(x) for x in self.cases))
+
+    def __repr__(self):
+        return self.__str__()
+
+class EnumCase(object):
+    def __init__(self, name, value):
+        self.name = name
+        self.value = value
+
+    def __str__(self):
+        return '%s = %s' % (self.name, self.value)
+
+class PackageID(object):
+    def __init__(self, name, version):
+        self.name = name
+        self.version = version
+
+    def __str__(self):
+        return '%s%s' % (self.name, self.version)
+
+class Package(object):
+    def __init__(self, package):
+        self.package = package
+
+    def __str__(self):
+        return 'package %s' % self.package
+
+class Import(object):
+    def __init__(self, package):
+        self.package = package
+
+    def __str__(self):
+        return 'import %s' % self.package
+
+class Header(object):
+    def __init__(self, package, imports):
+        self.package = package
+        self.imports = imports
+
+    def __str__(self):
+        return str(self.package) + "\n" + \
+            '\n'.join(str(x) for x in self.imports)
+
+# Error rule for syntax errors
+def p_error(p):
+    print("Syntax error in input: %s" % p)
+
+def p_document(t):
+    'document : header enum_decls'
+    enums = {}
+    for enum in t[2]:
+        enums[enum.header.name] = enum
+    t[0] = {'header' : t[1], 'enums' : enums}
+
+def p_enum_decls_1(t):
+    'enum_decls : enum_decl'
+    t[0] = [t[1]]
+def p_enum_decls_2(t):
+    'enum_decls : enum_decls enum_decl'
+    t[0] = t[1] + [t[2]]
+
+def p_enum_cases_1(t):
+    'enum_cases : enum_case'
+    t[0] = [t[1]]
+def p_enum_cases_2(t):
+    'enum_cases : enum_cases COMMA enum_case'
+    t[0] = t[1] + [t[3]]
+
+def p_enum_base_1(t):
+    'enum_base : VERSION COLON COLON IDENTIFIER'
+    t[0] = '%s::%s' % (t[1], t[4])
+def p_enum_base_2(t):
+    'enum_base : IDENTIFIER'
+    t[0] = t[1]
+
+def p_enum_header_1(t):
+    'enum_header : enum IDENTIFIER'
+    t[0] = EnumHeader(t[2], None)
+def p_enum_header_2(t):
+    'enum_header : enum IDENTIFIER COLON enum_base'
+    t[0] = EnumHeader(t[2], t[4])
+
+def p_enum_decl_1(t):
+    'enum_decl : enum_header LBRACE enum_cases RBRACE SEMICOLON'
+    t[0] = EnumDecl(t[1], t[3])
+def p_enum_decl_2(t):
+    'enum_decl : enum_header LBRACE enum_cases COMMA RBRACE SEMICOLON'
+    t[0] = EnumDecl(t[1], t[3])
+
+def p_enum_value_1(t):
+    '''enum_value : NUMBER
+                  | HEX
+                  | IDENTIFIER'''
+    t[0] = t[1]
+def p_enum_value_2(t):
+    'enum_value : enum_value SHIFT NUMBER'
+    t[0] = '%s << %s' % (t[1], t[3])
+def p_enum_value_3(t):
+    'enum_value : enum_value OR enum_value'
+    t[0] = "%s | %s" % (t[1], t[3])
+def p_enum_value_4(t):
+    'enum_value : LPAREN enum_value RPAREN'
+    t[0] = t[2]
+def p_enum_value_5(t):
+    'enum_value : IDENTIFIER COLON IDENTIFIER'
+    t[0] = '%s:%s' % (t[1],t[3])
+
+def p_enum_case(t):
+    'enum_case : IDENTIFIER EQUALS enum_value'
+    t[0] = EnumCase(t[1], t[3])
+
+def p_header_1(t):
+    'header : package_decl'
+    t[0] = Header(t[1], [])
+
+def p_header_2(t):
+    'header : package_decl import_decls'
+    t[0] = Header(t[1], t[2])
+
+def p_import_decls_1(t):
+    'import_decls : import_decl'
+    t[0] = [t[1]]
+
+def p_import_decls_2(t):
+    'import_decls : import_decls import_decl'
+    t[0] = t[1] + [t[2]]
+
+def p_package_decl(t):
+    'package_decl : package package_ID SEMICOLON'
+    t[0] = Package(t[2])
+
+def p_import_decl(t):
+    'import_decl : import package_ID SEMICOLON'
+    t[0] = Import(t[2])
+
+def p_package_ID(t):
+    'package_ID : dotted_identifier VERSION'
+    t[0] = PackageID(t[1], t[2])
+
+def p_dotted_identifier_1(t):
+    'dotted_identifier : IDENTIFIER'
+    t[0] = t[1]
+def p_dotted_identifier_2(t):
+    'dotted_identifier : dotted_identifier DOT IDENTIFIER'
+    t[0] = t[1] + '.' + t[3]
+
+class SilentLogger(object):
+    def warning(*args):
+        pass
+
+import ply.yacc as yacc
+parser = yacc.yacc(debug=False, write_tables=False, errorlog=SilentLogger())
+import sys
+
+def parse(filename):
+    return parser.parse(open(filename, 'r').read())
diff --git a/tools/update-obd2-sensors.py b/tools/update-obd2-sensors.py
index 1691c42..d0a89d4 100755
--- a/tools/update-obd2-sensors.py
+++ b/tools/update-obd2-sensors.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3.4
 #
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright (C) 2017 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,30 +15,16 @@
 # limitations under the License.
 #
 
-# This script generates useful representations of the current list of OBD2
-# diagnostic sensors we support. It is meant as an easy way to update the
-# list of diagnostic sensors and get all the required lists pretty-printed
-# and ready for use.
-# The script contains three parts:
-# 1) the part marked DO NOT MODIFY THIS. This defines a domain-specific language
-# that allows one to give a list of sensors;
-# 2) the part marked ACTUAL SENSOR DEFINITIONS HERE. This part gives the list
-# of diagnostic sensors, provided in the DSL defined above;
-# 3) the generate() call at the very end. This triggers the script to perform
-# its generation task.
-# To keep it simple, this script will produce its output
-# to stdout, also in three parts:
-# 1) Vehicle HAL enumerations;
-# 2) Java classes with a list of sensor identifiers;
-# 3) Java @interfaces with a list of sensor identifiers.
-# The several parts contain comments that indicate which files the content has
-# to be pasted into. Should there be a need, the script could be extended to
-# automatically insert the content in the files.
-# To run:
-# $ ./update-obd2-sensors.py
+# This script generates useful Java representations for the OBD2 sensors
+# defined in Vehicle HAL. It is meant to be as an easy way to update the
+# list of diagnostic sensors and get all downstream users of that information
+# updated in a consistent fashion.
 
-## DO NOT MODIFY THIS
-## This code is the machinery required to make the sensor generator DSL work
+import sys
+sys.dont_write_bytecode = True
+
+import hidl_parser.parser
+
 class SensorList(object):
     """A list of sensors ordered by a unique identifier."""
     def __init__(self, descriptor):
@@ -56,8 +42,6 @@
     def finalizeList(self):
         """Complete the list, adding well-known sensor information."""
         self.id -= 1
-        lastSystemSensor = self.sensorClass("LAST_SYSTEM_INDEX",
-            id=self.sensors[-1].name)
         vendorStartSensor = self.sensorClass("VENDOR_START_INDEX",
             id="LAST_SYSTEM_INDEX + 1")
         # make calling finalizeList idempotent
@@ -116,30 +100,6 @@
         s += self.suffix(theSensors) + "\n"
         return s
 
-class HalSensorPolicy(SensorPolicy):
-    """The sensor policy that emits Vehicle HAL sensor descriptions."""
-    def sensor(self, theSensor, theSensors):
-        s = ""
-        if theSensor.comment:
-            s = theSensor.comment + "\n"
-        s = s + theSensor.name + " = " + str(theSensor.id) + ","
-        return s
-
-    def prefix(self, theSensors):
-        return "enum Obd2%sSensorIndex : int32_t {" % (theSensors.descriptor)
-
-    def suffix(self, theSensors):
-        return "}"
-
-    def indent(self):
-        return 2
-
-    def separator(self):
-        return "\n"
-
-    def description(self):
-        return "/** this goes in types.hal **/"
-
 class JavaSensorPolicy(SensorPolicy):
     """The sensor policy that emits Java sensor descriptions."""
     def sensor(self, theSensor, theSensors):
@@ -149,18 +109,17 @@
             str(sensorId) + ";"
 
     def prefix(self, theSensors):
-        s = "public static final class Obd2%sSensorIndex {\n" % theSensors.descriptor
-        s += "    private Obd2%sSensorIndex() {}\n" % theSensors.descriptor
+        s = "    public static final class Obd2%sSensorIndex {\n" % \
+            theSensors.descriptor
+        s += "        private Obd2%sSensorIndex() {}\n" % \
+            theSensors.descriptor
         return s
 
     def suffix(self, theSensors):
-        return "}"
+        return "    }"
 
     def indent(self):
-        return 4
-
-    def description(self):
-        return "/** this goes in CarDiagnosticEvent.java **/"
+        return 8
 
 class IntDefSensorPolicy(SensorPolicy):
     """The sensor policy that emits @IntDef sensor descriptions."""
@@ -169,13 +128,14 @@
         return "Obd2%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
 
     def prefix(self, theSensors):
-        return "@Retention(RetentionPolicy.SOURCE)\n@IntDef({"
+        return "    @Retention(RetentionPolicy.SOURCE)\n    @IntDef({"
+
+    def indent(self):
+        return 8
 
     def suffix(self, theSensors):
-        return "})\npublic @interface %sSensorIndex {}" % theSensors.descriptor
-
-    def description(self):
-        return "/** this goes in CarDiagnosticEvent.java **/"
+        return "    })\n    public @interface %sSensorIndex {}" % \
+            theSensors.descriptor
 
 class SensorMeta(type):
     """Metaclass for sensor classes."""
@@ -207,138 +167,71 @@
 class floatSensor(metaclass=SensorMeta):
     sensorList = floatSensors
 
-def applyPolicy(policy):
+def applyPolicy(policy, destfile):
     """Given a sensor policy, apply it to all known sensor types"""
-    print(policy.description())
-    print(policy.sensors(intSensors))
-    print(policy.sensors(floatSensors))
+    print(policy.sensors(intSensors), file=destfile)
+    print(policy.sensors(floatSensors), file=destfile)
 
-def java():
-    applyPolicy(JavaSensorPolicy())
+def java(destfile):
+    applyPolicy(JavaSensorPolicy(), destfile)
 
-def hal():
-    applyPolicy(HalSensorPolicy())
+def intdef(destfile):
+    applyPolicy(IntDefSensorPolicy(), destfile)
 
-def intdef():
-    applyPolicy(IntDefSensorPolicy())
-
-def generate():
+def generate(filepath):
     """Generate data for all sensors."""
-    hal()
-    java()
-    intdef()
+    destfile = open(filepath, "w")
+    print("/*", file=destfile)
+    print(" * Copyright (C) 2017 The Android Open Source Project", file=destfile)
+    print(" *", file=destfile)
+    print(" * Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile)
+    print(" * you may not use this file except in compliance with the License.", file=destfile)
+    print(" * You may obtain a copy of the License at", file=destfile)
+    print(" *", file=destfile)
+    print(" *      http://www.apache.org/licenses/LICENSE-2.0", file=destfile)
+    print(" *", file=destfile)
+    print(" * Unless required by applicable law or agreed to in writing, software", file=destfile)
+    print(" * distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile)
+    print(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile)
+    print(" * See the License for the specific language governing permissions and", file=destfile)
+    print(" * limitations under the License.", file=destfile)
+    print("*/", file=destfile)
+    print("", file=destfile)
+    print("package android.car.hardware;", file=destfile)
+    print("", file=destfile)
+    print("import android.annotation.IntDef;", file=destfile)
+    print("import java.lang.annotation.Retention;", file=destfile)
+    print("import java.lang.annotation.RetentionPolicy;", file=destfile)
+    print("", file=destfile)
+    print("/**", file=destfile)
+    print(" * This class is a container for the indices of integer and float diagnostic sensors.", file=destfile)
+    print(" * These values are extracted from types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile)
+    print(" *", file=destfile)
+    print(" * DO NOT EDIT MANUALLY", file=destfile)
+    print(" *", file=destfile)
+    print(" * @hide", file=destfile)
+    print(" */", file=destfile)
+    print("public final class CarDiagnosticSensorIndices {", file=destfile)
+    java(destfile)
+    intdef(destfile)
+    print("}", file=destfile)
 
-## ACTUAL SENSOR DEFINITIONS HERE
-## Write sensor definitions here; terminate list with generate().
+def load(filepath):
+    """Load sensor data from Vehicle HAL."""
+    ast = hidl_parser.parser.parse(filepath)
+    integerSensors = ast['enums']['Obd2IntegerSensorIndex']
+    floatSensors = ast['enums']['Obd2FloatSensorIndex']
+    for case in integerSensors.cases:
+        intSensor(name=case.name, id=case.value)
+    for case in floatSensors.cases:
+        floatSensor(name=case.name, id=case.value)
 
-intSensor(name="FUEL_SYSTEM_STATUS", comment="/* refer to FuelSystemStatus for a description of this value. */")
-intSensor(name="MALFUNCTION_INDICATOR_LIGHT_ON")
-intSensor(name="IGNITION_MONITORS_SUPPORTED", comment="/* refer to IgnitionMonitorKind for a description of this value. */")
-intSensor(name="IGNITION_SPECIFIC_MONITORS", comment=r"""/*
- * The value of this sensor is a bitmask that specifies whether ignition-specific
- * tests are available and whether they are complete. The semantics of the individual
- * bits in this value are given by, respectively, SparkIgnitionMonitors and
- * CompressionIgnitionMonitors depending on the value of IGNITION_MONITORS_SUPPORTED.
- */""")
-intSensor(name="INTAKE_AIR_TEMPERATURE")
-intSensor(name="COMMANDED_SECONDARY_AIR_STATUS", comment="/* refer to SecondaryAirStatus for a description of this value. */")
-intSensor(name="NUM_OXYGEN_SENSORS_PRESENT")
-intSensor(name="RUNTIME_SINCE_ENGINE_START")
-intSensor(name="DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON")
-intSensor(name="WARMUPS_SINCE_CODES_CLEARED")
-intSensor(name="DISTANCE_TRAVELED_SINCE_CODES_CLEARED")
-intSensor(name="ABSOLUTE_BAROMETRIC_PRESSURE")
-intSensor(name="CONTROL_MODULE_VOLTAGE")
-intSensor(name="AMBIENT_AIR_TEMPERATURE")
-intSensor(name="TIME_WITH_MALFUNCTION_LIGHT_ON")
-intSensor(name="TIME_SINCE_TROUBLE_CODES_CLEARED")
-intSensor(name="MAX_FUEL_AIR_EQUIVALENCE_RATIO")
-intSensor(name="MAX_OXYGEN_SENSOR_VOLTAGE")
-intSensor(name="MAX_OXYGEN_SENSOR_CURRENT")
-intSensor(name="MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE")
-intSensor(name="MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR")
-intSensor(name="FUEL_TYPE", comment="/* refer to FuelType for a description of this value. */")
-intSensor(name="FUEL_RAIL_ABSOLUTE_PRESSURE")
-intSensor(name="ENGINE_OIL_TEMPERATURE")
-intSensor(name="DRIVER_DEMAND_PERCENT_TORQUE")
-intSensor(name="ENGINE_ACTUAL_PERCENT_TORQUE")
-intSensor(name="ENGINE_REFERENCE_PERCENT_TORQUE")
-intSensor(name="ENGINE_PERCENT_TORQUE_DATA_IDLE")
-intSensor(name="ENGINE_PERCENT_TORQUE_DATA_POINT1")
-intSensor(name="ENGINE_PERCENT_TORQUE_DATA_POINT2")
-intSensor(name="ENGINE_PERCENT_TORQUE_DATA_POINT3")
-intSensor(name="ENGINE_PERCENT_TORQUE_DATA_POINT4")
+import os
 
-floatSensor(name="CALCULATED_ENGINE_LOAD")
-floatSensor(name="ENGINE_COOLANT_TEMPERATURE")
-floatSensor(name="SHORT_TERM_FUEL_TRIM_BANK1")
-floatSensor(name="LONG_TERM_FUEL_TRIM_BANK1")
-floatSensor(name="SHORT_TERM_FUEL_TRIM_BANK2")
-floatSensor(name="LONG_TERM_FUEL_TRIM_BANK2")
-floatSensor(name="FUEL_PRESSURE")
-floatSensor(name="INTAKE_MANIFOLD_ABSOLUTE_PRESSURE")
-floatSensor(name="ENGINE_RPM")
-floatSensor(name="VEHICLE_SPEED")
-floatSensor(name="TIMING_ADVANCE")
-floatSensor(name="MAF_AIR_FLOW_RATE")
-floatSensor(name="THROTTLE_POSITION")
-floatSensor(name="OXYGEN_SENSOR1_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR2_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR3_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR4_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR5_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR6_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR7_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="OXYGEN_SENSOR8_VOLTAGE")
-floatSensor(name="OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM")
-floatSensor(name="OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO")
-floatSensor(name="FUEL_RAIL_PRESSURE")
-floatSensor(name="FUEL_RAIL_GAUGE_PRESSURE")
-floatSensor(name="COMMANDED_EXHAUST_GAS_RECIRCULATION")
-floatSensor(name="EXHAUST_GAS_RECIRCULATION_ERROR")
-floatSensor(name="COMMANDED_EVAPORATIVE_PURGE")
-floatSensor(name="FUEL_TANK_LEVEL_INPUT")
-floatSensor(name="EVAPORATION_SYSTEM_VAPOR_PRESSURE")
-floatSensor(name="CATALYST_TEMPERATURE_BANK1_SENSOR1")
-floatSensor(name="CATALYST_TEMPERATURE_BANK2_SENSOR1")
-floatSensor(name="CATALYST_TEMPERATURE_BANK1_SENSOR2")
-floatSensor(name="CATALYST_TEMPERATURE_BANK2_SENSOR2")
-floatSensor(name="ABSOLUTE_LOAD_VALUE")
-floatSensor(name="FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO")
-floatSensor(name="RELATIVE_THROTTLE_POSITION")
-floatSensor(name="ABSOLUTE_THROTTLE_POSITION_B")
-floatSensor(name="ABSOLUTE_THROTTLE_POSITION_C")
-floatSensor(name="ACCELERATOR_PEDAL_POSITION_D")
-floatSensor(name="ACCELERATOR_PEDAL_POSITION_E")
-floatSensor(name="ACCELERATOR_PEDAL_POSITION_F")
-floatSensor(name="COMMANDED_THROTTLE_ACTUATOR")
-floatSensor(name="ETHANOL_FUEL_PERCENTAGE")
-floatSensor(name="ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE")
-floatSensor(name="SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1")
-floatSensor(name="SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2")
-floatSensor(name="SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3")
-floatSensor(name="SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4")
-floatSensor(name="LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1")
-floatSensor(name="LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2")
-floatSensor(name="LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3")
-floatSensor(name="LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4")
-floatSensor(name="RELATIVE_ACCELERATOR_PEDAL_POSITION")
-floatSensor(name="HYBRID_BATTERY_PACK_REMAINING_LIFE")
-floatSensor(name="FUEL_INJECTION_TIMING")
-floatSensor(name="ENGINE_FUEL_RATE")
-
-generate()
+if len(sys.argv) != 3:
+    print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/CarDiagnosticSensorIndices.java>')
+    print('This scrippt will parse types.hal, and use the resulting', end='')
+    print('parse tree to generate CarDiagnosticSensorIndices.java.')
+    sys.exit(1)
+load(sys.argv[1])
+generate(sys.argv[2])