-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
920 additions
and
604 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import ast | ||
|
||
from codegen.common import Generator | ||
from codegen.common import to_class_name | ||
from codegen.lexicon import Lexicon | ||
|
||
|
||
class ArrayGenerator(Generator): | ||
def __init__(self, lexicon: Lexicon) -> None: | ||
self.lexicon = lexicon | ||
|
||
def generate(self) -> ast.Assign: | ||
return ast.Assign( | ||
targets=[ | ||
ast.Name( | ||
id=to_class_name(self.lexicon.get_def_id()), | ||
ctx=ast.Store() | ||
) | ||
], | ||
value=self.lexicon.get_array_annotation( | ||
self.lexicon.current, True | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import ast | ||
|
||
from typing import Any | ||
from typing import Union | ||
|
||
from codegen.common import Generator | ||
from codegen.common import to_description | ||
from codegen.function import FunctionGenerator | ||
from codegen.lexicon import Lexicon | ||
|
||
|
||
class CallGenerator(Generator): | ||
# for query and procedure | ||
def __init__(self, lexicon: Lexicon, | ||
properties: dict[str, Any], required: list[str], | ||
query_params: ast.List, | ||
data: Union[ast.Dict, ast.Constant, ast.Name], | ||
headers: ast.Dict) -> None: | ||
self.lexicon = lexicon | ||
self.query_params = query_params | ||
self.data = data | ||
self.headers = headers | ||
|
||
self.generator = FunctionGenerator(lexicon, properties, required) | ||
|
||
def generate(self) -> ast.FunctionDef: | ||
return self.generator.generate( | ||
self._args(), self._body(), ast.Name(id='bytes', ctx=ast.Load()) | ||
) | ||
|
||
def _args(self) -> ast.arguments: | ||
self.lexicon.add_module('chitose') | ||
args = [ | ||
self.generator.get_func_arg('call', 'XrpcCall') | ||
] + self.generator.get_property_args() | ||
|
||
self.lexicon.append_function( | ||
self.generator.get_info(args, 'call') | ||
) | ||
|
||
return self.generator.get_args(args) | ||
|
||
def _body(self) -> list[Union[ast.Expr, ast.Return]]: | ||
description_lines = self.generator.get_description_lines() | ||
return [ | ||
ast.Expr( | ||
value=ast.Constant( | ||
value=to_description(description_lines, 4) | ||
) | ||
), | ||
ast.Return( | ||
value=ast.Call( | ||
func=ast.Name(id='call', ctx=ast.Load()), | ||
args=[ | ||
ast.Constant(value=self.lexicon.get_id()), | ||
self.query_params, | ||
self.data, | ||
self. headers, | ||
], | ||
keywords=[] | ||
) | ||
) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import ast | ||
|
||
from typing import Any | ||
from typing import Union | ||
|
||
from codegen.common import to_private_function_name | ||
from codegen.common import to_snake | ||
from codegen.common import FunctionInfo | ||
from codegen.lexicon import Lexicon | ||
|
||
|
||
class FunctionGenerator: | ||
# for query, procedure and subscription | ||
def __init__(self, lexicon: Lexicon, | ||
properties: dict[str, Any], required: list[str]) -> None: | ||
self.lexicon = lexicon | ||
self.properties = properties | ||
self.required = required | ||
|
||
def generate(self, args: ast.arguments, | ||
body: list[Union[ast.Expr, ast.Return]], | ||
returns: Union[ast.Constant, ast.Name]) -> ast.FunctionDef: | ||
return ast.FunctionDef( | ||
name=to_private_function_name(self.lexicon.get_name()), | ||
args=args, | ||
body=body, | ||
decorator_list=[], | ||
returns=returns, | ||
) | ||
|
||
@staticmethod | ||
def get_query_properties_and_required(lexicon: Lexicon) \ | ||
-> tuple[dict[str, Any], list[str]]: | ||
if 'parameters' not in lexicon.current: | ||
return {}, [] | ||
|
||
parameters = lexicon.current['parameters'] | ||
assert parameters['type'] == 'params' | ||
|
||
properties = parameters['properties'] | ||
required = parameters.get('required', []) | ||
|
||
properties = lexicon.reorder_properties(properties, required) | ||
return properties, required | ||
|
||
@staticmethod | ||
def get_query_params(properties: dict[str, Any]) -> ast.List: | ||
return ast.List( | ||
elts=[ | ||
ast.Tuple( | ||
elts=[ | ||
ast.Constant(value=property), | ||
ast.Name(id=to_snake(property), ctx=ast.Load()) | ||
], | ||
ctx=ast.Load() | ||
) | ||
for property in properties | ||
] | ||
) | ||
|
||
def get_func_arg(self, func: str, attr: str) -> ast.arg: | ||
return ast.arg( | ||
arg=func, | ||
annotation=ast.Attribute( | ||
value=ast.Attribute( | ||
value=ast.Name(id='chitose', ctx=ast.Load()), | ||
attr='xrpc', | ||
ctx=ast.Load() | ||
), | ||
attr=attr, | ||
ctx=ast.Load() | ||
), | ||
) | ||
|
||
def get_property_args(self) -> list[ast.arg]: | ||
return [ | ||
ast.arg( | ||
arg=to_snake(property), | ||
annotation=self.lexicon.get_annotation( | ||
self.properties, self.required, property) | ||
) | ||
for property in self.properties | ||
] | ||
|
||
def _none_count(self): | ||
return len(self.properties) - len(self.required) | ||
|
||
def get_args(self, args: list[ast.arg]) -> ast.arguments: | ||
none_count = self._none_count() | ||
return ast.arguments( | ||
posonlyargs=[], | ||
args=args, | ||
kwonlyargs=[], | ||
kw_defaults=[], | ||
defaults=[ | ||
ast.Constant(value=None) | ||
for _ in range(none_count) | ||
] | ||
) | ||
|
||
def get_description_lines(self) -> list[str]: | ||
lines = [self.lexicon.current.get('description', '')] | ||
for property, detail in self.properties.items(): | ||
if 'description' not in detail: | ||
continue | ||
|
||
params = f':param {to_snake(property)}: {detail["description"]}' | ||
lines.append(params) | ||
return lines | ||
|
||
def get_info(self, args: list[ast.arg], func: str) -> FunctionInfo: | ||
none_count = self._none_count() | ||
return FunctionInfo( | ||
name=to_snake(self.lexicon.get_name()), | ||
description_lines=self.get_description_lines(), | ||
args=args[1:], | ||
none_count=none_count, | ||
modules=self.lexicon.get_annotation_modules(), | ||
func=func, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import ast | ||
|
||
from typing import Any | ||
|
||
from codegen.common import Generator | ||
from codegen.common import to_class_name | ||
from codegen.common import to_description | ||
from codegen.common import to_snake | ||
from codegen.lexicon import Lexicon | ||
|
||
|
||
class ClassGenerator(Generator): | ||
# for object and record | ||
def __init__(self, lexicon: Lexicon, | ||
properties: dict[str, Any], required: list[str]) -> None: | ||
self.lexicon = lexicon | ||
self.properties = properties | ||
self.required = required | ||
|
||
self.def_id = lexicon.get_def_id() | ||
self.current = lexicon.get_current() | ||
|
||
def generate(self) -> ast.ClassDef: | ||
name = self.lexicon.get_name() | ||
if self.def_id != 'main': | ||
if self.def_id == name: | ||
# ex. | ||
# id: spam.ham.eggs | ||
# name: eggs | ||
# def_id: eggs | ||
# => name: EggsEggs | ||
name = to_class_name(name) * 2 | ||
else: | ||
name = self.def_id | ||
|
||
name = to_class_name(name) | ||
base_name = to_class_name(self.current['type']) | ||
self.lexicon.add_module('chitose') | ||
return ast.ClassDef( | ||
name=name, | ||
bases=[ | ||
ast.Name(id=f'chitose.{base_name}', ctx=ast.Load()), | ||
], | ||
keywords=[], | ||
body=[ | ||
self._class_comment(self.properties), | ||
self._init_function(self.properties, self.required), | ||
self._to_dict_function(self.properties) | ||
], | ||
decorator_list=[] | ||
) | ||
|
||
def _class_comment(self, properties: dict[str, Any]) -> ast.Expr: | ||
lines = [''] | ||
for property in properties: | ||
description = properties[property].get('description') | ||
if description: | ||
lines.append(f':param {to_snake(property)}: {description}') | ||
|
||
return ast.Expr(value=ast.Constant(value=to_description(lines, 4))) | ||
|
||
def _init_function(self, properties: dict[str, Any], | ||
required: list[str]) -> ast.FunctionDef: | ||
args = [ | ||
ast.arg(arg='self') | ||
] | ||
args += [ | ||
ast.arg( | ||
arg=to_snake(property), | ||
annotation=self.lexicon.get_annotation( | ||
properties, required, property | ||
) | ||
) | ||
for property in properties | ||
] | ||
return ast.FunctionDef( | ||
name='__init__', | ||
args=ast.arguments( | ||
posonlyargs=[], | ||
args=args, | ||
kwonlyargs=[], | ||
kw_defaults=[], | ||
defaults=[ | ||
ast.Constant(value=None) | ||
for _ in range(len(properties) - len(required)) | ||
]), | ||
body=[ | ||
ast.Assign( | ||
targets=[ | ||
ast.Attribute( | ||
value=ast.Name(id='self', ctx=ast.Load()), | ||
attr=to_snake(property), | ||
ctx=ast.Store()) | ||
], | ||
value=ast.Name(id=to_snake(property), ctx=ast.Load()) | ||
) | ||
for property in properties | ||
], | ||
decorator_list=[], | ||
returns=ast.Constant(value=None)) | ||
|
||
def _get_ref(self): | ||
ref = self.lexicon.get_id() | ||
if self.def_id != 'main': | ||
ref += f'#{self.def_id}' | ||
return ref | ||
|
||
def _to_dict_function(self, | ||
properties: dict[str, Any]) -> ast.FunctionDef: | ||
return ast.FunctionDef( | ||
name='to_dict', | ||
args=ast.arguments( | ||
posonlyargs=[], | ||
args=[ | ||
ast.arg(arg='self')], | ||
kwonlyargs=[], | ||
kw_defaults=[], | ||
defaults=[]), | ||
body=[ | ||
ast.Return( | ||
value=ast.Dict( | ||
keys=[ | ||
ast.Constant(value=property) | ||
for property in properties | ||
] + [ | ||
ast.Constant(value='$type') | ||
], | ||
values=[ | ||
ast.Attribute( | ||
value=ast.Name(id='self', ctx=ast.Load()), | ||
attr=to_snake(property), | ||
ctx=ast.Load()) | ||
for property in properties | ||
] + [ | ||
ast.Constant(value=self._get_ref(), ctx=ast.Load()) | ||
] | ||
|
||
) | ||
) | ||
], | ||
decorator_list=[], | ||
returns=ast.Name(id='dict', ctx=ast.Load()) | ||
) |
Oops, something went wrong.