Skip to content

Commit

Permalink
[JsonGen] Add interface-global @text:keep tag to use original C++ nam…
Browse files Browse the repository at this point in the history
…es in JSON objects
  • Loading branch information
sebaszm authored and MFransen69 committed Jul 4, 2024
1 parent dd743eb commit f4ac307
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 22 deletions.
48 changes: 34 additions & 14 deletions JsonGenerator/source/header_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def _EvaluateRpcFormat(obj):

return rpc_format

log.Info("Parsing interface %s in file %s" % (face.obj.full_name, file))

schema = OrderedDict()
methods = OrderedDict()
properties = OrderedDict()
Expand All @@ -84,12 +86,29 @@ def _EvaluateRpcFormat(obj):

verify = face.obj.is_json or face.obj.is_event

_case_format = face.obj.meta.text if face.obj.meta.text else "lower"

def compute_name(obj, relay = None):
if not relay:
relay = obj

_default_name = relay.name if _case_format == "keep" else relay.name.lower()

if obj.meta.text == _default_name:
log.WarnLine(method, "'%s': overriden name is same as default ('%s')" % (obj.meta.text, _default_name))

_name = obj.meta.text if obj.meta.text else _default_name

return (_name)

schema["@interfaceonly"] = True
schema["configuration"] = { "nodefault" : True }

info = dict()
info["format"] = rpc_format.value

log.Info("JSON-RPC format is %s" % rpc_format.value)

if not face.obj.parent.full_name.endswith(ns):
info["namespace"] = face.obj.parent.name[1:] if (face.obj.parent.name[0] == "I" and face.obj.parent.name[1].isupper()) else face.obj.parent.name

Expand All @@ -110,6 +129,11 @@ def _EvaluateRpcFormat(obj):

info["title"] = info["class"] + " API"
info["description"] = info["class"] + " JSON-RPC interface"

if _case_format == "keep":
info["legacy"] = True # suppress case warnings
log.Info("@text:keep is used!")

schema["info"] = info

clash_msg = "JSON-RPC name clash detected"
Expand Down Expand Up @@ -217,7 +241,10 @@ def ConvertType(var):
#if autos != 0 and (autos != len(cppType.items)):
# raise CppParseError(var, "enumerator values in an enum must all be explicit or all be implied")

enum_spec = { "enum": [e.meta.text if e.meta.text else e.name.replace("_"," ").title().replace(" ","") for e in cppType.items], "scoped": var_type.Type().scoped }
if _case_format == "keep":
enum_spec = { "enum": [e.meta.text if e.meta.text else e.name for e in cppType.items] }
else:
enum_spec = { "enum": [e.meta.text if e.meta.text else e.name.replace("_"," ").title().replace(" ","") for e in cppType.items] }
enum_spec["ids"] = [e.name for e in cppType.items]
enum_spec["hint"] = var.type.Type().name

Expand Down Expand Up @@ -250,7 +277,7 @@ def GenerateObject(ctype, was_typdef):
required = []

for p in kind.vars:
name = p.meta.text if p.meta.text else p.name.lower()
name = compute_name(p)

if isinstance(p.type, list):
raise CppParseError(p, "%s: undefined type" % " ".join(p.type))
Expand Down Expand Up @@ -368,7 +395,7 @@ def BuildParameters(vars, rpc_format, is_property=False, test=False):
elif not var.meta.output:
log.WarnLine(var, "'%s': non-const parameter marked with @in tag (forgot 'const'?)" % var.name)

var_name = "value" if is_property else (var.meta.text if var.meta.text else var.name.lower())
var_name = "value" if (is_property and _case_format == "lower") else compute_name(var)

if var_name.startswith("__unnamed") and not test:
raise CppParseError(var, "unnamed parameter, can't deduce parameter name (*1)")
Expand Down Expand Up @@ -426,7 +453,7 @@ def BuildResult(vars, is_property=False):
if var_type.IsConst():
raise CppParseError(var, "parameter marked with @out tag must not be const")

var_name = "value" if is_property else (var.meta.text if var.meta.text else var.name.lower())
var_name = "value" if (is_property and _case_format == "lower") else compute_name(var)

if var_name.startswith("__unnamed"):
raise CppParseError(var, "unnamed parameter, can't deduce parameter name (*2)")
Expand Down Expand Up @@ -470,9 +497,6 @@ def BuildResult(vars, is_property=False):

continue

if method.retval.meta.text == method.name.lower():
log.WarnLine(method, "'%s': overriden method name is same as default ('%s')" % (method.name, method.retval.meta.text))

# Copy over @text tag to the other method of a property
if method.retval.meta.text and method.retval.meta.is_property:
for mm in face.obj.methods:
Expand All @@ -487,7 +511,7 @@ def BuildResult(vars, is_property=False):
mm.retval.meta.alt = method.retval.meta.alt
break

method_name = method.retval.meta.text if method.retval.meta.text else method.name.lower()
method_name = compute_name(method.retval, method)

if method.retval.meta.alt == method_name:
log.WarnLine(method, "%s': alternative name is same as original name ('%s')" % (method.name, method.retval.meta.text))
Expand Down Expand Up @@ -546,8 +570,7 @@ def BuildResult(vars, is_property=False):
obj["index"] = BuildIndex(method.vars[0])

if obj["index"]:
obj["index"]["name"] = method.vars[0].name

obj["index"]["name"] = (method.vars[0].name if _case_format == "keep" else method.vars[0].name.capitalize())
if "enum" in obj["index"]:
obj["index"]["example"] = obj["index"]["enum"][0]

Expand Down Expand Up @@ -782,10 +805,7 @@ def BuildResult(vars, is_property=False):
if params:
obj["params"] = params

if method.retval.meta.text == method.name.lower():
log.WarnLine(method, "'%s': overriden notification name is same as default ('%s')" % (method.name, method.retval.meta.text))

method_name = method.retval.meta.text if method.retval.meta.text else method.name.lower()
method_name = compute_name(method.retval, method)

if method.parent.is_event: # excludes .json inlcusion of C++ headers
for mm in events:
Expand Down
20 changes: 16 additions & 4 deletions ProxyStubGenerator/CppParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def ASSERT_ISVALID(token):

def ASSERT_ISEXPECTED(token, list):
if token not in list:
raise ParserError("unexpected identifier: '" + token + "', expected one of " + str(list))
raise ParserError("unexpected identifier: '" + str(token) + "', expected one of " + str(list))


# -------------------------------------------------------------------------
Expand Down Expand Up @@ -1547,7 +1547,7 @@ def __Tokenize(contents,log = None):
else:
continue

def __ParseParameterValue(string, tag, mandatory = True, append = True):
def __ParseParameterValue(string, tag, mandatory=True, append=True, relay=None):
formula = (r"(\"[^\"]+\")"
r"|(\'[^\']+\')"
r"|(\*/)|(::)|(==)|(!=)|(>=)|(<=)|(&&)|(\|\|)"
Expand All @@ -1556,7 +1556,7 @@ def __ParseParameterValue(string, tag, mandatory = True, append = True):
r"|([\r\n\t ])")

if append:
tagtokens.append(tag.upper())
tagtokens.append(relay.upper() if relay else tag.upper())

length_str = string[string.index(tag) + len(tag):]
length_tokens = [s.strip() for s in re.split(formula, length_str, flags=re.MULTILINE) if isinstance(s, str) and len(s.strip())]
Expand Down Expand Up @@ -1693,7 +1693,9 @@ def _find(word, string):
tagtokens.append(__ParseParameterValue(token, "@alt:obsolete"))
if _find("@alt-obsolete", token):
tagtokens.append(__ParseParameterValue(token, "@alt-obsolete"))
if _find("@text", token):
if _find("@text:keep", token):
tagtokens.append(__ParseParameterValue(token, "@text", True, True, "@text-global"))
elif _find("@text", token):
tagtokens.append(__ParseParameterValue(token, "@text"))
if _find("@length", token):
tagtokens.append(__ParseParameterValue(token, "@length"))
Expand Down Expand Up @@ -1846,6 +1848,7 @@ def Parse(contents,log = None):
compliant_next = False
iterator_next = False
sourcelocation_next = False
text_next = None
in_typedef = False


Expand Down Expand Up @@ -1894,6 +1897,11 @@ def Parse(contents,log = None):
json_next = False
tokens[i] = ";"
i += 1
elif tokens[i] == "@TEXT-GLOBAL":
text_next = tokens[i + 1][0]
tokens[i] = ";"
tokens[i+1] = ";"
i += 2
elif tokens[i] == "@EXTENDED":
extended_next = True
tokens[i] = ";"
Expand Down Expand Up @@ -1933,6 +1941,7 @@ def Parse(contents,log = None):
compliant_next = False
iterator_next = False
sourcelocation_next = False
text_next = None
in_typedef = False
tokens[i] = ";"
i += 1
Expand Down Expand Up @@ -2078,6 +2087,9 @@ def Parse(contents,log = None):
raise ParserError("@compliant and @uncompliant used together")
if exclude_next:
raise ParserError("@json:omit is invalid here (applies to methods only)")
if text_next:
new_class.meta.text = text_next
text_next = None

json_next = False
json_version = ""
Expand Down
11 changes: 7 additions & 4 deletions ProxyStubGenerator/StubGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2136,21 +2136,24 @@ def GenerateIdentification(name):
print(" @length:void - length is the size of one element")
print("")
print("JSON-RPC-related parameters:")
print(" @json - takes a C++ class in for JSON-RPC generation")
print(" @json [version] - takes a C++ class in for JSON-RPC generation (with optional version specification)")
print(" @json:omit - leaves out a method, property or notification from a class intended for JSON-RPC generation")
print(" @compliant - tags a class to be generated in JSON-RPC compliant format (default)")
print(" @uncompliant:extended - tags a class to be generated in the obsolete 'extended' format")
print(" @uncompliant:collapsed - tags a class to be generated in the obsolete 'collapsed' format")
print(" @text:keep - keeps original C++ names for all JSON names in an interface")
print(" @event - takes a class in to be generated as an JSON-RPC notification")
print(" @property - tags C++ method to be generated as a JSON-RPC property")
print(" @iterator - tags a C++ class to be generated as an JSON-RPC interator")
print(" @property - tags C++ method to be generated as a JSON-RPC property")
print(" @lookup [prefix] - use the method as a lookup function to import another C++ interface by an instance ID (with optional prefix override)")
print(" @statuslistener - emits registration/unregistration status listeners for an event")
print(" @bitmask - indicates that enumerator lists should be packed into into a bit mask")
print(" @index - indicates that a parameter in a JSON-RPC property or notification is an index")
print(" @opaque - indicates that a string parameter is an opaque JSON object")
print(" @extract - indicates that that if only one element is present in the array it shall be taken out of it")
print(" @optional - indicates that a parameter, struct member or property index is optional")
print(" @prefix {name} - prefixes all JSON-RPC methods, properties and notifications names in an interface with a string")
print(" @text {name} - sets a different name for a parameter, enumerator, struct or JSON-RPC method, property or notification")
print(" @text {name} - sets a different name for a parameter, enumerator, struct or JSON-RPC method, struct members, property or notification")
print(" @alt {name} - provides an alternative name a JSON-RPC method or property can by called by")
print(" (for a notification it provides an additional name to send out)")
print(" @alt:deprecated {name} - provides an alternative deprecated name a JSON-RPC method can by called by")
Expand All @@ -2164,7 +2167,7 @@ def GenerateIdentification(name):
print(" @details {desc} - sets a detailed description for a JSON-RPC method, property or notification")
print(" @param {name} {desc} - sets a description for a parameter of a JSON-RPC method or notification")
print(" @retval {desc} - sets a description for a return value of a JSON-RPC method")
print(" @end - indicates end of the enumerator list (don't document further)")
print(" @end - indicates end of the enumerator list (meaning don't document further)")
print("")
print("Tags shall be placed inside C++ comments.")
sys.exit()
Expand Down

0 comments on commit f4ac307

Please sign in to comment.