code-generation: run ruff

Change-Id: I591247ed56f89feba5daf3cbb94211d72a3ba99c
This commit is contained in:
Commentator2.0
2026-01-10 00:07:33 +00:00
committed by Rutile
parent 0d3fdbdb54
commit 8f9ed687da
8 changed files with 385 additions and 259 deletions
+1
View File
@@ -0,0 +1 @@
# noqa: N999 # consistency with rest of the codebase
+64 -45
View File
@@ -1,15 +1,12 @@
from typing import List, NamedTuple
from typing import NamedTuple
from frontmatter import Post
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {"name", "type", "constructorArgs", "implementation", "impure", "renameInGlobalScope"}
KNOWN_KEYS = set([
'name',
'type',
'constructorArgs',
'implementation',
'impure',
'renameInGlobalScope',
])
class BuiltinConstant(NamedTuple):
name: str
@@ -19,75 +16,97 @@ class BuiltinConstant(NamedTuple):
rename_in_global_scope: bool
documentation: str
def parse(datum):
@classmethod
def parse(cls, datum: Post) -> "BuiltinConstant":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise Exception('unknown keys', unknown_keys)
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
if (constructor_args := datum.get("constructorArgs")) is not None:
args = [f"NewValueAs::{datum['type']}"] + constructor_args # type: ignore
impl = f"{{{','.join(args)}}}"
else:
impl = datum["implementation"]
return BuiltinConstant(
name = datum['name'],
type = datum['type'],
implementation = ('{' + ', '.join([f'NewValueAs::{datum["type"]}', *datum['constructorArgs']]) + '}') if 'constructorArgs' in datum else datum['implementation'],
impure = datum.get('impure', False),
rename_in_global_scope = datum.get('renameInGlobalScope', True),
name=datum["name"], # type: ignore
type=datum["type"], # type: ignore
implementation=impl,
impure=datum.get("impure", False), # type: ignore
rename_in_global_scope=datum.get("renameInGlobalScope", True), # type: ignore
documentation=datum.content,
)
VALUE_TYPES = {
'attrs': 'nAttrs',
'boolean': 'nBool',
'integer': 'nInt',
'list': 'nList',
'null': 'nNull',
'string': 'nString',
"attrs": "nAttrs",
"boolean": "nBool",
"integer": "nInt",
"list": "nList",
"null": "nNull",
"string": "nString",
}
HUMAN_TYPES = {
'attrs': 'set',
'boolean': 'Boolean',
'integer': 'integer',
'list': 'list',
'null': 'null',
'string': 'string',
"attrs": "set",
"boolean": "Boolean",
"integer": "integer",
"list": "list",
"null": "null",
"string": "string",
}
def main():
import argparse
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--header', help='Path of the header to generate')
ap.add_argument('--docs', help='Path of the documentation file to generate')
ap.add_argument('defs', help='Builtin definition files', nargs='+')
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap.add_argument("defs", help="Builtin definition files", nargs="+")
args = ap.parse_args()
builtin_constants = load_data(args.defs, BuiltinConstant.parse)
generate_file(args.header, builtin_constants, lambda constant:
# `builtins` is magic and must come first
'' if constant.name == 'builtins' else constant.name,
generate_file(
args.header,
builtin_constants,
lambda constant:
f'''{'if (!evalSettings.pureEval) ' if constant.impure else ''}{{
addConstant({cxx_literal(('__' if constant.rename_in_global_scope else '') + constant.name)}, {constant.implementation}, {{
# `builtins` is magic and must come first
"" if constant.name == "builtins" else constant.name,
lambda constant: f"""{"if (!evalSettings.pureEval) " if constant.impure else ""}{{
addConstant({cxx_literal(("__" if constant.rename_in_global_scope else "") + constant.name)}, {constant.implementation}, {{
.type = {VALUE_TYPES[constant.type]},
.doc = {cxx_literal(constant.documentation)},
.impureOnly = {cxx_literal(constant.impure)},
}});
}}
''')
generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda constant:
f'''<dt id="builtins-{constant.name}">
""",
)
generate_file(
args.docs,
builtin_constants,
lambda constant: constant.name,
lambda constant: f"""<dt id="builtins-{constant.name}">
<a href="#builtins-{constant.name}"><code>{constant.name}</code></a> ({HUMAN_TYPES[constant.type]})
</dt>
<dd>
{constant.documentation}
''' + ('''> **Note**
"""
+ (
"""> **Note**
>
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
''' if constant.impure else '') + '''</dd>
"""
if constant.impure
else ""
)
+ """</dd>
''')
""",
)
if __name__ == '__main__':
if __name__ == "__main__":
main()
+61 -39
View File
@@ -1,61 +1,71 @@
from typing import List, NamedTuple, Optional
from typing import NamedTuple
from frontmatter import Post
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {"name", "implementation", "renameInGlobalScope", "args", "experimentalFeature"}
KNOWN_KEYS = set([
'name',
'implementation',
'renameInGlobalScope',
'args',
'experimentalFeature',
])
class Builtin(NamedTuple):
name: str
implementation: str
rename_in_global_scope: bool
args: List[str]
experimental_feature: Optional[str]
args: list[str]
experimental_feature: str | None
documentation: str
def parse(datum):
@classmethod
def parse(cls, datum: Post) -> "Builtin":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise Exception('unknown keys', unknown_keys)
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
return Builtin(
name = datum['name'],
implementation = datum['implementation'] if 'implementation' in datum else f'prim_{datum["name"]}',
rename_in_global_scope = datum.get('renameInGlobalScope', True),
args = datum['args'],
experimental_feature = datum.get('experimentalFeature', None),
name=datum["name"], # type: ignore
implementation=datum["implementation"]
if "implementation" in datum
else f"prim_{datum['name']}",
rename_in_global_scope=datum.get("renameInGlobalScope", True), # type: ignore
args=datum["args"], # type: ignore
experimental_feature=datum.get("experimentalFeature", None), # type: ignore
documentation=datum.content,
)
def main():
import argparse
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--header', help='Path of the header to generate')
ap.add_argument('--docs', help='Path of the documentation file to generate')
ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
ap.add_argument('defs', help='Builtin definition files', nargs='+')
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap.add_argument(
"--experimental-features", help="Directory containing the experimental feature definitions"
)
ap.add_argument("defs", help="Builtin definition files", nargs="+")
args = ap.parse_args()
builtins = load_data(args.defs, Builtin.parse)
experimental_feature_names = set([builtin.experimental_feature for (_, builtin) in builtins])
experimental_feature_names = {builtin.experimental_feature for (_, builtin) in builtins}
experimental_feature_names.discard(None)
experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
experimental_feature_files = [
f"{args.experimental_features}/{name}.md" for name in experimental_feature_names
]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
experimental_features = dict(map(lambda path_and_feature:
(path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
experimental_features[None] = 'std::nullopt'
experimental_features = {
path_and_feature[1].name: f"Xp::{path_and_feature[1].internal_name}"
for path_and_feature in experimental_features
}
experimental_features[None] = "std::nullopt"
generate_file(args.header, builtins, lambda builtin: builtin.name, lambda builtin:
f'''{'' if builtin.experimental_feature is None else f'if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) '}{{
generate_file(
args.header,
builtins,
lambda builtin: builtin.name,
lambda builtin: f"""{"" if builtin.experimental_feature is None else f"if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) "}{{
addPrimOp({{
.name = {cxx_literal(('__' if builtin.rename_in_global_scope else '') + builtin.name)},
.name = {cxx_literal(("__" if builtin.rename_in_global_scope else "") + builtin.name)},
.args = {cxx_literal(builtin.args)},
.arity = {len(builtin.args)},
.doc = {cxx_literal(builtin.documentation)},
@@ -63,20 +73,32 @@ def main():
.experimentalFeature = {experimental_features[builtin.experimental_feature]},
}});
}}
''')
generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda builtin:
f'''<dt id="builtins-{builtin.name}">
<a href="#builtins-{builtin.name}"><code>{builtin.name} {' '.join([f'<var>{arg}</var>' for arg in builtin.args])}</code></a>
""",
)
generate_file(
args.docs,
builtins,
lambda builtin: builtin.name,
lambda builtin: f"""<dt id="builtins-{builtin.name}">
<a href="#builtins-{builtin.name}"><code>{builtin.name} {" ".join([f"<var>{arg}</var>" for arg in builtin.args])}</code></a>
</dt>
<dd>
{builtin.documentation}
''' + (f'''This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
"""
+ (
f"""This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
''' if builtin.experimental_feature is not None else '') + '''</dd>
"""
if builtin.experimental_feature is not None
else ""
)
+ """</dd>
''')
""",
)
if __name__ == '__main__':
if __name__ == "__main__":
main()
@@ -1,58 +1,77 @@
from typing import NamedTuple
from frontmatter import Post
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {"name", "internalName"}
KNOWN_KEYS = set([
'name',
'internalName',
])
class ExperimentalFeature(NamedTuple):
name: str
internal_name: str
description: str
def parse(datum):
@classmethod
def parse(cls, datum: Post) -> "ExperimentalFeature":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise ValueError('unknown keys', unknown_keys)
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
return ExperimentalFeature(
name = datum['name'],
internal_name = datum['internalName'],
name=datum["name"], # type: ignore
internal_name=datum["internalName"], # type: ignore
description=datum.content,
)
def main():
import argparse
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--deprecated', action='store_true', help='Generate deprecated features')
ap.add_argument('--header', help='Path of the declaration header to generate')
ap.add_argument('--impl-header', help='Path of the implementation header to generate')
ap.add_argument('--descriptions', help='Path of the description file to generate')
ap.add_argument('--shortlist', help='Path of the shortlist file to generate')
ap.add_argument('defs', help='Experimental feature definition files', nargs='+')
ap.add_argument("--deprecated", action="store_true", help="Generate deprecated features")
ap.add_argument("--header", help="Path of the declaration header to generate")
ap.add_argument("--impl-header", help="Path of the implementation header to generate")
ap.add_argument("--descriptions", help="Path of the description file to generate")
ap.add_argument("--shortlist", help="Path of the shortlist file to generate")
ap.add_argument("defs", help="Experimental feature definition files", nargs="+")
args = ap.parse_args()
features = load_data(args.defs, ExperimentalFeature.parse)
generate_file(args.header, features, lambda feature: feature.name, lambda feature:
f' {feature.internal_name},\n')
generate_file(args.impl_header, features, lambda feature: feature.name, lambda feature:
f''' {{
generate_file(
args.header,
features,
lambda feature: feature.name,
lambda feature: f" {feature.internal_name},\n",
)
generate_file(
args.impl_header,
features,
lambda feature: feature.name,
lambda feature: f""" {{
.tag = {"Dep" if args.deprecated else "Xp"}::{feature.internal_name},
.name = {cxx_literal(feature.name)},
.description = {cxx_literal(feature.description)},
}},
''')
generate_file(args.descriptions, features, lambda feature: feature.name, lambda feature:
f'''## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
""",
)
generate_file(
args.descriptions,
features,
lambda feature: feature.name,
lambda feature: f"""## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
{feature.description}
''')
generate_file(args.shortlist, features, lambda feature: feature.name, lambda feature:
f' - [`{feature.name}`](@docroot@/contributing/{"deprecated" if args.deprecated else "experimental"}-features.md#{"dp" if args.deprecated else "xp"}-feature-{feature.name})\n')
""",
)
generate_file(
args.shortlist,
features,
lambda feature: feature.name,
lambda feature: f" - [`{feature.name}`](@docroot@/contributing/{'deprecated' if args.deprecated else 'experimental'}-features.md#{'dp' if args.deprecated else 'xp'}-feature-{feature.name})\n",
)
if __name__ == '__main__':
if __name__ == "__main__":
main()
+122 -73
View File
@@ -1,101 +1,121 @@
from typing import List, NamedTuple, Optional
from typing import NamedTuple, Any
from frontmatter import Post
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {
"name",
"internalName",
"platforms",
"type",
"settingType",
"default",
"defaultExpr",
"defaultText",
"aliases",
"experimentalFeature",
"deprecated",
}
KNOWN_KEYS = set([
'name',
'internalName',
'platforms',
'type',
'settingType',
'default',
'defaultExpr',
'defaultText',
'aliases',
'experimentalFeature',
'deprecated',
])
class Setting(NamedTuple):
name: str
internal_name: str
description: str
platforms: Optional[List[str]]
platforms: list[str] | None
setting_type: str
default_expr: str
default_text: str
aliases: List[str]
experimental_feature: Optional[str]
aliases: list[str]
experimental_feature: str | None
deprecated: bool
def parse(datum):
@classmethod
def parse(cls, datum: Post) -> "Setting":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise ValueError('unknown keys', unknown_keys)
default_text = f'`{nix_conf_literal(datum["default"])}`' if 'default' in datum else datum['defaultText']
if default_text == '``':
default_text = '*empty*'
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
default_text = (
f"`{nix_conf_literal(datum['default'])}`"
if "default" in datum
else datum["defaultText"]
)
if default_text == "``":
default_text = "*empty*"
return Setting(
name = datum['name'],
internal_name = datum['internalName'],
name=datum["name"], # type: ignore
internal_name=datum["internalName"], # type: ignore
description=datum.content,
platforms = datum.get('platforms', None),
setting_type = f'Setting<{datum["type"]}>' if 'type' in datum else datum['settingType'],
default_expr = cxx_literal(datum['default']) if 'default' in datum else datum['defaultExpr'],
platforms=datum.get("platforms", None), # type: ignore
setting_type=f"Setting<{datum['type']}>" if "type" in datum else datum["settingType"],
default_expr=cxx_literal(datum["default"])
if "default" in datum
else datum["defaultExpr"],
default_text=default_text,
aliases = datum.get('aliases', []),
experimental_feature = datum.get('experimentalFeature', None),
deprecated = datum.get('deprecated', False),
aliases=datum.get("aliases", []), # type: ignore
experimental_feature=datum.get("experimentalFeature", None), # type: ignore
deprecated=datum.get("deprecated", False), # type: ignore
)
platform_names = {
'darwin': 'Darwin',
'linux': 'Linux',
}
def nix_conf_literal(v):
platform_names = {"darwin": "Darwin", "linux": "Linux"}
def nix_conf_literal(v: Any) -> str:
if v is None:
return ''
elif isinstance(v, bool) and v == False: # 0 == False
return 'false'
elif isinstance(v, bool) and v == True: # 1 == True
return 'true'
elif isinstance(v, int):
return ""
if v is False:
return "false"
if v is True:
return "true"
if isinstance(v, int):
return str(v)
elif isinstance(v, str):
if isinstance(v, str):
return v
elif isinstance(v, list):
return ' '.join([nix_conf_literal(item) for item in v])
else:
raise NotImplementedError(f'Cannot represent {repr(v)} in nix.conf')
if isinstance(v, list):
return " ".join([nix_conf_literal(item) for item in v])
msg = f"Cannot represent {v!r} in nix.conf"
raise NotImplementedError(msg)
def indent(prefix: str, body: str) -> str:
return "".join(["\n" if not line else f"{prefix}{line}\n" for line in body.split("\n")])
def indent(prefix, body):
return ''.join(['\n' if line == '' else f'{prefix}{line}\n' for line in body.split('\n')])
def main():
import argparse
ap = argparse.ArgumentParser()
ap.add_argument('--kernel', help='Name of the kernel Lix will run on')
ap.add_argument('--header', help='Path of the header to generate')
ap.add_argument('--docs', help='Path of the documentation file to generate')
ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
ap.add_argument('defs', help='Setting definition files', nargs='+')
ap.add_argument("--kernel", help="Name of the kernel Lix will run on")
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap.add_argument(
"--experimental-features", help="Directory containing the experimental feature definitions"
)
ap.add_argument("defs", help="Setting definition files", nargs="+")
args = ap.parse_args()
settings = load_data(args.defs, Setting.parse)
experimental_feature_names = set([setting.experimental_feature for (_, setting) in settings])
experimental_feature_names = {setting.experimental_feature for (_, setting) in settings}
experimental_feature_names.discard(None)
experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
experimental_feature_files = [
f"{args.experimental_features}/{name}.md" for name in experimental_feature_names
]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
experimental_features = dict(map(lambda path_and_feature:
(path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
experimental_features[None] = 'std::nullopt'
experimental_features = {
path_and_feature[1].name: f"Xp::{path_and_feature[1].internal_name}"
for path_and_feature in experimental_features
}
experimental_features[None] = "std::nullopt"
generate_file(args.header, settings, lambda setting: setting.name, lambda setting:
f'''{setting.setting_type} {setting.internal_name} {{
generate_file(
args.header,
settings,
lambda setting: setting.name,
lambda setting: f"""{setting.setting_type} {setting.internal_name} {{
this,
{setting.default_expr},
{cxx_literal(setting.name)},
@@ -106,15 +126,28 @@ def main():
{cxx_literal(setting.deprecated)}
}};
''' if setting.platforms is None or args.kernel in setting.platforms else '')
generate_file(args.docs, settings, lambda setting: setting.name, lambda setting:
f'''- <span id="conf-{setting.name}">[`{setting.name}`](#conf-{setting.name})</span>
"""
if setting.platforms is None or args.kernel in setting.platforms
else "",
)
generate_file(
args.docs,
settings,
lambda setting: setting.name,
lambda setting: f"""- <span id="conf-{setting.name}">[`{setting.name}`](#conf-{setting.name})</span>
{indent(" ", setting.description)}
''' + (f''' > **Note**
> This setting is only available on {', '.join([platform_names[platform] for platform in setting.platforms])} systems.
"""
+ (
f""" > **Note**
> This setting is only available on {", ".join([platform_names[platform] for platform in setting.platforms])} systems.
''' if setting.platforms is not None else '') + (f''' > **Warning**
"""
if setting.platforms is not None
else ""
)
+ (
f""" > **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
@@ -128,14 +161,30 @@ def main():
{setting.name} = ...
```
''' if setting.experimental_feature is not None else '') + (''' > **Warning**
"""
if setting.experimental_feature is not None
else ""
)
+ (
""" > **Warning**
> This setting is deprecated and will be removed in a future version of Lix.
''' if setting.deprecated else '') + f''' **Default:** {setting.default_text}
"""
if setting.deprecated
else ""
)
+ f""" **Default:** {setting.default_text}
''' + (f''' **Deprecated alias:** {', '.join([f'`{item}`' for item in setting.aliases])}
"""
+ (
f""" **Deprecated alias:** {", ".join([f"`{item}`" for item in setting.aliases])}
''' if setting.aliases != [] else ''))
"""
if setting.aliases != []
else ""
),
)
if __name__ == '__main__':
if __name__ == "__main__":
main()
+33 -29
View File
@@ -1,4 +1,5 @@
#!@python@
# ruff: noqa: SIM112 # ignore lowercase env variable names for capnpc as we have them in lower case as arguments
import argparse
import capnp
@@ -7,58 +8,61 @@ import os
import subprocess
import sys
if lang := os.environ.get('lix_capnp_lang'):
outputs = os.environ['lix_capnp_outputs'].split()
old_cwd = os.environ['lix_capnp_old_cwd']
schema = capnp.load('@capnp_include@/capnp/schema.capnp', imports=['@capnp_include@'])
if lang := os.environ.get("lix_capnp_lang"):
outputs = os.environ["lix_capnp_outputs"].split()
old_cwd = os.environ["lix_capnp_old_cwd"]
schema = capnp.load("@capnp_include@/capnp/schema.capnp", imports=["@capnp_include@"])
request = schema.CodeGeneratorRequest.read(sys.stdin)
subprocess.run([lang], input=request.as_builder().to_bytes()).check_returncode()
base_dir = os.getcwd()
base_dir = Path.cwd()
os.chdir(old_cwd)
include = [ str(Path(p).resolve()) for p in os.environ['lix_capnp_include'].split(':') ]
include = [str(Path(p).resolve()) for p in os.environ["lix_capnp_include"].split(":")]
if depfile := os.environ['lix_capnp_depfile']:
if depfile := os.environ["lix_capnp_depfile"]:
deps = ""
for input in request.requestedFiles:
deps += " ".join(f"{input.filename}.{o}" for o in outputs)
for input_file in request.requestedFiles:
deps += " ".join(f"{input_file.filename}.{o}" for o in outputs)
deps += ":"
for dep in input.imports:
for dep in input_file.imports:
if dep.name.startswith("/"):
for candidate in (Path(i + dep.name) for i in include):
if candidate.exists():
deps += " " + str(candidate)
break
else:
raise RuntimeError("not handling relative includes")
msg = "not handling relative includes"
raise RuntimeError(msg)
deps += "\n\n"
Path(depfile).write_text(deps)
else:
parser = argparse.ArgumentParser()
parser.add_argument('--language')
parser.add_argument('--outdir')
parser.add_argument('--src-prefix')
parser.add_argument('--depfile', default="")
parser.add_argument('-I', '--include', action='append', default=['@capnp_include@'])
parser.add_argument('inputs', nargs='+')
parser.add_argument("--language")
parser.add_argument("--outdir")
parser.add_argument("--src-prefix")
parser.add_argument("--depfile", default="")
parser.add_argument("-I", "--include", action="append", default=["@capnp_include@"])
parser.add_argument("inputs", nargs="+")
args = parser.parse_args()
for infile in args.inputs:
os.environ['lix_capnp_lang'] = f"capnpc-{args.language}"
os.environ['lix_capnp_include'] = ':'.join(args.include)
os.environ['lix_capnp_depfile'] = args.depfile
os.environ['lix_capnp_old_cwd'] = os.getcwd()
os.environ["lix_capnp_lang"] = f"capnpc-{args.language}"
os.environ["lix_capnp_include"] = ":".join(args.include)
os.environ["lix_capnp_depfile"] = args.depfile
os.environ["lix_capnp_old_cwd"] = str(Path.cwd())
if args.language == "c++":
os.environ['lix_capnp_outputs'] = "c++ h"
os.environ["lix_capnp_outputs"] = "c++ h"
else:
raise RuntimeError("unknown language " + args.language)
subprocess.run([
'@capnp@',
'compile',
f'-o{sys.argv[0]}:{args.outdir}',
f'--src-prefix={args.src_prefix}',
subprocess.run(
[
"@capnp@",
"compile",
f"-o{sys.argv[0]}:{args.outdir}",
f"--src-prefix={args.src_prefix}",
*(f"-I{i}" for i in args.include),
infile
]).check_returncode()
infile,
]
).check_returncode()
+51 -39
View File
@@ -1,60 +1,72 @@
from collections.abc import Callable
from pathlib import Path
from typing import Any
import frontmatter
import pathlib
from collections import defaultdict
def cxx_escape_character(c):
if ord(c) >= 0x20 and ord(c) < 0x7f and c != '"' and c != '?' and c != '\\':
def cxx_escape_character(c: str) -> str:
if 0x20 <= ord(c) < 0x7F and c != '"' and c != "?" and c != "\\":
return c
elif c == '\t':
return r'\t'
elif c == '\n':
return r'\n'
elif c == '\r':
return r'\r'
elif c == '"':
return r'\"'
elif c == '?':
return r'\?'
elif c == '\\':
return r'\\'
elif ord(c) <= 0xffff:
return str.format(r'\u{:04x}', ord(c))
else:
return str.format(r'\U{:08x}', ord(c))
if c == "\t":
return r"\t"
if c == "\n":
return r"\n"
if c == "\r":
return r"\r"
if c == '"':
return r"\""
if c == "?":
return r"\?"
if c == "\\":
return r"\\"
if ord(c) <= 0xFFFF:
return str.format(r"\u{:04x}", ord(c))
return str.format(r"\U{:08x}", ord(c))
def cxx_literal(v):
def cxx_literal(v: Any) -> str:
if v is None:
return 'std::nullopt'
elif isinstance(v, bool) and v == False: # 0 == False
return 'false'
elif isinstance(v, bool) and v == True: # 1 == True
return 'true'
elif isinstance(v, int):
return "std::nullopt"
if v is False:
return "false"
if v is True:
return "true"
if isinstance(v, int):
return str(v)
elif isinstance(v, str):
return ''.join(['"', *(cxx_escape_character(c) for c in v), '"'])
elif isinstance(v, list):
return f'{{{", ".join([cxx_literal(item) for item in v])}}}'
else:
raise NotImplementedError(f'cannot represent {repr(v)} in C++')
if isinstance(v, str):
return "".join(['"', *(cxx_escape_character(c) for c in v), '"'])
if isinstance(v, list):
return f"{{{', '.join([cxx_literal(item) for item in v])}}}"
msg = f"cannot represent {v!r} in C++"
raise NotImplementedError(msg)
def load_data(defs, parse_function):
def load_data[T](defs: list[str], parse_function: Callable[[frontmatter.Post], T]) -> list[T]:
data = []
for path in defs:
try:
datum = frontmatter.load(path)
data.append((path, parse_function(datum)))
except Exception as e:
e.add_note(f'in {path}')
e.add_note(f"in {path}")
raise
return data
def generate_file(path, data, sort_key_function, generate_function):
def generate_file(
path: str | None,
data: list[Any],
sort_key_function: Callable[[Any], str],
generate_function: Callable[[Any], str],
):
if path is not None:
with open(path, 'w') as out:
for path, datum in sorted(data, key=lambda pathAndDatum: sort_key_function(pathAndDatum[1])):
with Path(path).open("w") as out:
for path, datum in sorted(
data, key=lambda path_and_datum: sort_key_function(path_and_datum[1])
):
try:
out.write(generate_function(datum))
except Exception as e:
e.add_note(f'in {path}')
e.add_note(f"in {path}")
raise
+1 -1
View File
@@ -12,5 +12,5 @@ priority = 0
[formatter.ruff]
command = "ruff"
options = ["check"]
includes = ["tests/functional2/**/*.py", "meson/clang-tidy/*.py", "maintainers/*.py"]
includes = ["tests/functional2/**/*.py", "meson/clang-tidy/*.py", "maintainers/*.py", "lix/code-generation/*.py"]
priority = 1