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