Address comments

Signed-off-by: JCW <a1q123456@users.noreply.github.com>
This commit is contained in:
JCW
2026-03-03 15:56:32 +00:00
parent 78d8f89267
commit 672f35aed5
124 changed files with 25946 additions and 191 deletions

View File

@@ -17,7 +17,12 @@ from jinja2 import Environment, FileSystemLoader
import pyparsing as pp
# Import common utilities
from macro_parser_common import CppCleaner, parse_sfields_macro, parse_field_list
from macro_parser_common import (
CppCleaner,
parse_sfields_macro,
parse_field_list,
generate_cpp_class,
)
def create_ledger_entry_parser():
@@ -70,7 +75,7 @@ def parse_ledger_entry_args(args_list):
value = args_list[1]
name = args_list[2]
rpc_name = args_list[3]
fields_str = args_list[4]
fields_str = args_list[-1]
# Parse fields: ({field1, field2, ...})
fields = parse_field_list(fields_str)
@@ -115,40 +120,6 @@ def parse_macro_file(file_path):
return entries
def generate_cpp_class(entry_info, header_dir, jinja_env, field_types):
"""Generate C++ header file for a ledger entry type."""
# Enrich field information with type data
for field in entry_info["fields"]:
field_name = field["name"]
if field_name in field_types:
field["typed"] = field_types[field_name]["typed"]
field["stiSuffix"] = field_types[field_name]["stiSuffix"]
field["typeData"] = field_types[field_name]["typeData"]
else:
# Unknown field - assume typed for safety
field["typed"] = True
field["stiSuffix"] = None
field["typeData"] = None
template = jinja_env.get_template("LedgerEntry.h.jinja2")
# Render the template
header_content = template.render(
name=entry_info["name"],
tag=entry_info["tag"],
value=entry_info["value"],
rpc_name=entry_info["rpc_name"],
fields=entry_info["fields"],
)
# Write header file
header_path = Path(header_dir) / f"{entry_info['name']}.h"
with open(header_path, "w") as f:
f.write(header_content)
print(f"Generated {header_path}")
def main():
parser = argparse.ArgumentParser(
description="Generate C++ ledger entry classes from ledger_entries.macro"
@@ -157,7 +128,7 @@ def main():
parser.add_argument(
"--header-dir",
help="Output directory for header files",
default="include/xrpl/protocol/ledger_objects",
default="include/xrpl/protocol_autogen/ledger_objects",
)
parser.add_argument(
"--sfields-macro",
@@ -207,7 +178,9 @@ def main():
header_dir.mkdir(parents=True, exist_ok=True)
for entry in entries:
generate_cpp_class(entry, header_dir, jinja_env, field_types)
generate_cpp_class(
entry, header_dir, jinja_env, field_types, "LedgerEntry.h.jinja2"
)
print(f"\nGenerated {len(entries)} ledger entry classes")

View File

@@ -14,7 +14,12 @@ from jinja2 import Environment, FileSystemLoader
import pyparsing as pp
# Import common utilities
from macro_parser_common import CppCleaner, parse_sfields_macro, parse_field_list
from macro_parser_common import (
CppCleaner,
parse_sfields_macro,
parse_field_list,
generate_cpp_class,
)
def create_transaction_parser():
@@ -70,7 +75,7 @@ def parse_transaction_args(args_list):
delegable = args_list[3]
amendments = args_list[4]
privileges = args_list[5]
fields_str = args_list[6]
fields_str = args_list[-1]
# Parse fields: ({field1, field2, ...})
fields = parse_field_list(fields_str)
@@ -117,35 +122,6 @@ def parse_macro_file(filepath):
return transactions
def generate_cpp_class(tx_info, header_dir, jinja_env, field_types):
"""Generate a header-only template class for a transaction using Jinja2 templates."""
class_name = tx_info["name"]
# Enrich field information with type data
for field in tx_info["fields"]:
field_name = field["name"]
if field_name in field_types:
field["typed"] = field_types[field_name]["typed"]
field["stiSuffix"] = field_types[field_name]["stiSuffix"]
field["typeData"] = field_types[field_name]["typeData"]
else:
# Unknown field - assume typed for safety
field["typed"] = True
field["stiSuffix"] = None
field["typeData"] = None
# Load template
header_template = jinja_env.get_template("Transaction.h.jinja2")
# Render header
header_content = header_template.render(tx_info)
header_path = header_dir / f"{class_name}.h"
with open(header_path, "w") as f:
f.write(header_content)
return header_path
# TransactionBase is a static file in the repository at:
# - include/xrpl/protocol/TransactionBase.h
# - src/libxrpl/protocol/TransactionBase.cpp
@@ -160,7 +136,7 @@ def main():
parser.add_argument(
"--header-dir",
help="Output directory for header files",
default="include/xrpl/protocol/transactions",
default="include/xrpl/protocol_autogen/transactions",
)
parser.add_argument(
"--sfields-macro",
@@ -212,12 +188,14 @@ def main():
generated_files = []
for tx_info in transactions:
header_path = generate_cpp_class(tx_info, header_dir, jinja_env, field_types)
header_path = generate_cpp_class(
tx_info, header_dir, jinja_env, field_types, "Transaction.h.jinja2"
)
generated_files.append(header_path)
print(f" Generated: {tx_info['name']}.h")
print(
f"\n Successfully generated {len(transactions)} transaction classes ({len(generated_files)} header files)"
f"\n Successfully generated {len(transactions)} transaction classes ({len(generated_files)} header files)"
)
print(f" Headers: {header_dir.absolute()}")

View File

@@ -8,6 +8,7 @@ and ledger_entries.macro files using pcpp and pyparsing.
# cspell:words sfields
import re
from pathlib import Path
import pyparsing as pp
from pcpp import Preprocessor
@@ -98,9 +99,9 @@ def parse_sfields_macro(sfields_path):
"getter_method": "at",
"setter_method": "",
"setter_use_brackets": True,
"setter_type": f"SF_{sti_suffix}::type::value_type const&",
"setter_type": f"std::decay_t<typename SF_{sti_suffix}::type::value_type> const&",
"return_type": f"SF_{sti_suffix}::type::value_type",
"return_type_optional": f"std::optional<SF_{sti_suffix}::type::value_type>",
"return_type_optional": f"protocol_autogen::Optional<SF_{sti_suffix}::type::value_type>",
},
}
@@ -191,3 +192,41 @@ def parse_field_list(fields_str):
return fields
except pp.ParseException as e:
raise ValueError(f"Failed to parse field list: {e}")
def generate_cpp_class(entry_info, header_dir, jinja_env, field_types, template_name):
"""Generate C++ header file from a template.
Args:
entry_info: Dict containing entry information (name, fields, etc.)
header_dir: Output directory for generated header files
jinja_env: Jinja2 Environment for loading templates
field_types: Dict mapping field names to type information
template_name: Name of the Jinja2 template file to use
"""
# Enrich field information with type data
for field in entry_info["fields"]:
field_name = field["name"]
if field_name in field_types:
field["typed"] = field_types[field_name]["typed"]
field["paramName"] = field_name[2].lower() + field_name[3:]
field["stiSuffix"] = field_types[field_name]["stiSuffix"]
field["typeData"] = field_types[field_name]["typeData"]
else:
# Unknown field - assume typed for safety
field["typed"] = True
field["paramName"] = ""
field["stiSuffix"] = None
field["typeData"] = None
template = jinja_env.get_template(template_name)
# Render the template - pass entry_info directly so templates can access any field
header_content = template.render(**entry_info)
# Write header file
header_path = Path(header_dir) / f"{entry_info['name']}.h"
with open(header_path, "w") as f:
f.write(header_content)
print(f"Generated {header_path}")

View File

@@ -10,6 +10,8 @@
#include <stdexcept>
#include <optional>
# cspell:words equalto
namespace xrpl::ledger_entries {
// Forward declaration
@@ -121,16 +123,21 @@ public:
*/
class {{ name }}Builder : public LedgerEntryBuilderBase<{{ name }}Builder>
{
{%- set required_fields = fields | selectattr('requirement', 'equalto', 'soeREQUIRED') | list %}
public:
{{ name }}Builder()
{{ name }}Builder({%- for field in required_fields %}
{{ field.typeData.setter_type }} {{ field.paramName }}{% if not loop.last %},{% endif %}
{%- endfor %})
: LedgerEntryBuilderBase<{{ name }}Builder>({{ tag }})
{
// Initialize with ledger entry type
object_[sfLedgerEntryType] = {{ tag }};
{%- for field in required_fields %}
set{{ field.name[2:] }}({{ field.paramName }});
{%- endfor %}
}
{{ name }}Builder(SLE const& sle)
{
if (object_[sfLedgerEntryType] != {{ tag }})
if (sle[sfLedgerEntryType] != {{ tag }})
{
throw std::runtime_error("Invalid ledger entry type for {{ name }}");
}

View File

@@ -10,6 +10,8 @@
#include <stdexcept>
#include <optional>
# cspell:words equalto
namespace xrpl::transactions {
// Forward declaration
@@ -124,11 +126,20 @@ public:
*/
class {{ name }}Builder : public TransactionBuilderBase<{{ name }}Builder>
{
{%- set required_fields = fields | selectattr('requirement', 'equalto', 'soeREQUIRED') | list %}
public:
{{ name }}Builder()
{{ name }}Builder(SF_ACCOUNT::type::value_type account,
SF_UINT32::type::value_type sequence,
SF_AMOUNT::type::value_type fee,
SF_VL::type::value_type signingPubKey{% if required_fields %},{% endif %}
{%- for field in required_fields %}
{{ field.typeData.setter_type }} {{ field.paramName }}{% if not loop.last %},{% endif %}
{%- endfor %})
: TransactionBuilderBase<{{ name }}Builder>(account, sequence, fee, signingPubKey, {{ tag }})
{
// Initialize with transaction type
object_[sfTransactionType] = {{ tag }};
{%- for field in required_fields %}
set{{ field.name[2:] }}({{ field.paramName }});
{%- endfor %}
}
{{ name }}Builder(STTx const& tx)