def print_lines(self, var: Variable, size: str, indent_lvl: int) -> None: """Print the content of a var that holds in one or more lines""" if var.fits_in_one_line(self.input.structs): self.print_line(var, size, indent_lvl) else: if var.type.main == TypeEnum.STRUCT: struct = self.input.get_struct(var.type.struct_name) struct = self.input.get_struct(var.type.struct_name) for f_name, f_type, f_size in struct.fields_name_type_size( f"{var.name}.{{}}", var_name): self.print_lines(Variable(f_name, "", f_type), f_size, indent_lvl) else: assert var.type.main == TypeEnum.LIST assert var.type.encapsulated is not None index = self.iterator.new_it() self.method.append( "{0}for (int {1} = 0; {1} < {2}; ++{1}) {{".format( " " * self.indentation * indent_lvl, index, size)) self.print_lines( Variable(f"{var.name}[{index}]", "", var.type.encapsulated), var_name(var.type.encapsulated.size), indent_lvl + 1, ) self.method.append(" " * self.indentation * indent_lvl + "}") self.iterator.pop_it()
def read_lines( self, var: Variable, size: str, already_allocated: bool = False, ) -> List[str]: """Read one or several lines and store them into the right place(s)""" lines = [] if var.type.main == TypeEnum.LIST and not already_allocated: lines.append(f"{var.name} = make({type_str(var.type)}, {size})") if var.fits_in_one_line(self.input.structs): return lines + self.read_line(var.name, size, var.type) if var.type.main == TypeEnum.STRUCT: struct = self.input.get_struct(var.type.struct_name) for f_name, f_type, f_size in struct.fields_name_type_size( var.name + ".{}", var_name ): lines.extend(self.read_lines(Variable(f_name, "", f_type), f_size)) return lines assert var.type.main == TypeEnum.LIST assert var.type.encapsulated is not None inner_name = self.iterator.new_it() lines.append(f"for {inner_name} := range {var.name} {{") lines.extend( INDENTATION + i for i in self.read_lines( Variable(f"{var.name}[{inner_name}]", "", var.type.encapsulated), var_name(var.type.encapsulated.size), ) ) lines.append("}") self.iterator.pop_it() return lines
def read_lines(self, decl: bool, var: Variable, size: str, indent_lvl: int) -> List[str]: # pylint: disable=too-many-arguments # pylint: disable=too-many-locals """Read one or several lines and store them into the right place(s)""" if var.fits_in_one_line(self.input.structs): return self.read_line(decl, var.name, var.type, indent_lvl) indent = INDENTATION * indent_lvl if var.type.main == TypeEnum.STRUCT: lines = [ indent + "{}{} = new {}();".format( class_name(var.type.struct_name) + " " if decl else "", var.name, class_name(var.type.struct_name), ) ] struct = self.input.get_struct(var.type.struct_name) for f_name, f_type, f_size in struct.fields_name_type_size( f"{var.name}.{{}}", var_name): lines.extend( self.read_lines(False, Variable(f_name, "", f_type), f_size, indent_lvl)) return lines assert var.type.main == TypeEnum.LIST assert var.type.encapsulated is not None far_inner_type = var.type.encapsulated list_suffix = "" while far_inner_type.main == TypeEnum.LIST: assert far_inner_type.encapsulated is not None far_inner_type = far_inner_type.encapsulated list_suffix += "[]" lines = [ "{}{}{} = new {}[{}]{};".format( indent, (type_str(var.type) + " ") if decl else "", var.name, type_str(far_inner_type), size, list_suffix, ) ] index = self.iterator.new_it() self.words.push_scope() lines.append("{0}for (int {1} = 0; {1} < {2}; ++{1}) {{".format( indent, index, size)) lines.extend( self.read_lines( False, Variable(f"{var.name}[{index}]", "", var.type.encapsulated), var_name(var.type.encapsulated.size), indent_lvl + 1, )) self.words.pop_scope() self.iterator.pop_it() return lines + [indent + "}"]
def read_vars(input_data: Input, iterator: IteratorName) -> List[str]: """Read all input variables""" all_lines = [] for variables in input_data.get_all_vars(): if len(variables) == 1: var = variables[0] lines = read_lines( Variable(var_name(var.name), "", var.type, var.format_style), var_name(var.type.size), input_data, iterator, ) if lines[0].startswith("local"): # struct lines[1] = "local " + lines[1] else: lines[0] = "local " + lines[0] all_lines.extend(lines) else: assert all(var.type.main == TypeEnum.INT for var in variables) all_lines.append( 'local {} = string.match(io.read(), "{}")'.format( ", ".join(var_name(i.name) for i in variables), " ".join(["(-?%d+)"] * len(variables)), ) ) return all_lines
def call(self, reprint: bool) -> None: """Declare and call the function take all inputs in arguments""" name = var_name(self.input.name) arguments = [] for arg in self.input.input: arg_name = var_name(arg.name) self.method.append("/// \\param {} {}".format( arg_name, arg.comment)) arguments.append("{} {}".format(self.type_str(arg.type), arg_name)) self.method.append("void {}({}) {{".format(name, ", ".join(arguments))) if reprint: for var in self.input.input: self.print_lines( Variable(var_name(var.name), "", var.type, var.format_style), var_name(var.type.size), 1, ) else: self.method.extend([ " " * self.indentation + i for i in textwrap.wrap("/* TODO " + self.input.output + " */", 79 - self.indentation) ]) self.method.append("}")
def error_parse_variable(dic: Dict[str, str]) -> str: """Explain why we a variable fails to parse""" # pylint: disable=too-many-return-statements assert Variable.from_dict(dic) is None if "name" not in dic: return "missing name field" if not isinstance(dic["name"], str): return "name field is not a string" for field in ("comment", "type"): if field not in dic: return "missing {} field for {}".format(field, dic["name"]) if not isinstance(dic[field], str): return "{} field for {} is not a string".format(field, dic["name"]) type_ = Type.from_string(dic["type"]) if type_ is None: return "unable to parse type {} for {}: {}".format( dic["type"], dic["name"], error_parse_type(dic["type"])) if dic.get("format", "") == "no_endline" and type_.main != TypeEnum.INT: return f'{dic["name"]} has format "no_endline" but is {dic["type"]}, not int' if dic.get("format", "") == "force_newlines" and ( type_.main != TypeEnum.LIST or type_.encapsulated is None or type_.encapsulated.main != TypeEnum.INT): return ( f'{dic["name"]} has format "force_newlines" but is {dic["type"]}' ", not List[int]") return "unknown error"
def error_parse_input(dic: Dict[str, Any]) -> str: """Explain why we an input fails to parse""" # pylint: disable=too-many-return-statements # pylint: disable=too-many-branches assert Input.from_dict(dic) is None if "function_name" not in dic: if "name" in dic: dic["function_name"] = dic["name"] else: return "missing function_name field" if not isinstance(dic["function_name"], str): return "function_name is not a string" if "structs" in dic: # non mandatory if not isinstance(dic["structs"], list): return "'structs' is not a list" for node in dic["structs"]: if Struct.from_dict(node) is None: return "failed to parse struct: " + error_parse_struct(node) if "input" not in dic: return "missing input field" if not isinstance(dic["input"], list): return "output is not a list" for node in dic["input"]: if Variable.from_dict(node) is None: return "failed to parse variable: " + error_parse_variable(node) if "output" not in dic: return "missing output field" if not isinstance(dic["output"], str): return "output is not a string" return "unknown error"
def read_lines(var: Variable, decl: str, size: str, input_data: Input, words: WordsName) -> List[str]: """Generate the Ruby code to read the lines for a given type""" if var.fits_in_one_line(input_data.structs): return read_line(var.name, decl, var.type, input_data, words) if var.type.main == TypeEnum.LIST: assert var.type.encapsulated is not None lines = [ decl.format("()" if var.name[0] == "@" else "[]"), "for (1..{}) {{".format(size), ] words.push_scope() array_name = var.name.replace("{", "{{").replace("}", "}}") if array_name[0] != "@": array_name = "@{{" + array_name + "}}" lines.extend([ INDENTATION + i for i in read_lines( Variable("$" + var.name[1:] + "[-1]", "", var.type.encapsulated), "push({}, {{}});".format(array_name), size_name(var.type.encapsulated.size), input_data, words, ) ]) words.pop_scope() return lines + ["}"] assert var.type.main == TypeEnum.STRUCT struct = input_data.get_struct(var.type.struct_name) sizes = [size_name(field.type.size) for field in struct.fields] if struct.is_sized_struct(): sizes = ["", f"{var.name}{{'{struct.fields[0].name}'}}"] lines = [decl.format("()" if var.name[0] == "%" else "{}")] for (field, f_size) in zip(struct.fields, sizes): f_name = f"${var.name[1:]}{{'{field.name}'}}" lines.extend( read_lines( Variable(f_name, "", field.type), f_name.replace("{", "{{").replace("}", "}}") + " = {};", f_size, input_data, words, )) return lines
def read_var(self, var: Variable, in_error_context: bool = False) -> str: """Return a Rust command for parsing a variable""" name = var_name(var.name) unwrap_method = "?" if in_error_context else None if var.fits_in_one_line(self.input.structs): read_method = self.read_line(var.type, 1, var.name, unwrap_method) else: read_method = "\n".join(self.read_lines(name, var.type, 1, unwrap_method)) return f" let {name} = {read_method};"
def generate_lines(self, var: Variable) -> List[str]: """Generate the raw input for a type""" if var.fits_in_one_line(self.input.structs): return [self.generate_line(var.name, var.type, var.constraints)] if var.type.main == TypeEnum.LIST: assert var.type.encapsulated inner = Variable("", "", var.type.encapsulated) inner.constraints = var.constraints lines = [] for _ in range(self.eval_var(var.type.size)): lines.extend(self.generate_lines(inner)) return lines if var.type.main == TypeEnum.STRUCT: struct = self.input.get_struct(var.type.struct_name) lines = [] for field in struct.fields: lines.extend(self.generate_lines(field)) return lines assert False return []
def content(self, reprint: bool) -> str: """Return the parser content""" output = "" for struct in self.input.structs: output += f"/**\n * {struct.comment}\n */\n" output += "class {}\n{{\n".format(class_name(struct.name)) for field in struct.fields: decl_field = "{0}/**\n{0} * {1}\n{0} */\n{0}public {2} {3};\n" output += decl_field.format( INDENTATION, field.comment, type_str(field.type), var_name(field.name), ) output += "}\n\n" output += "class Main {\n" output += "\n".join(self.call(reprint)) + "\n\n" output += ( INDENTATION + "public static void main(String[] args) throws java.io.IOException {\n" ) output += 2 * INDENTATION + ( "BufferedReader reader = " "new BufferedReader(new InputStreamReader(System.in));\n") for variables in self.input.get_all_vars(): if len(variables) == 1: var = variables[0] for line in self.read_lines( True, Variable(var_name(var.name), "", var.type, var.format_style), var_name(var.type.size), 2, ): output += line + "\n" else: words = self.words.next_name() output += (f"{INDENTATION * 2}String[] {words}" ' = reader.readLine().split(" ");\n') for i, var in enumerate(variables): assert var.type.main == TypeEnum.INT output += ( INDENTATION * 2 + f"int {var_name(var.name)} = Integer.parseInt({words}[{i}]);\n" ) args = (var_name(var.name) for var in self.input.input) output += "\n{}{}({});\n".format(INDENTATION * 2, var_name(self.input.name), ", ".join(args)) output += INDENTATION + "}\n}\n" return "".join(f"import {i};\n" for i in sorted(self.imports)) + "\n" + output
def print_lines( self, var: Variable, size: str, indent_lvl: int, ) -> List[str]: """Print the content of a var that holds in one or more lines""" if var.fits_in_one_line(self.input.structs): return [ INDENTATION * indent_lvl + self.print_line(var.name, var.type) ] if var.type.main == TypeEnum.STRUCT: struct = self.input.get_struct(var.type.struct_name) lines = [] for f_name, f_type, f_size in struct.fields_name_type_size( f"{var.name}.{{}}", var_name): lines.extend( self.print_lines(Variable(f_name, "", f_type), f_size, indent_lvl)) return lines assert var.type.main == TypeEnum.LIST assert var.type.encapsulated is not None index = self.iterator.new_it() self.words.push_scope() lines = [ "{0}for (int {1} = 0; {1} < {2}; ++{1}) {{".format( INDENTATION * indent_lvl, index, size) ] lines.extend( self.print_lines( Variable(f"{var.name}[{index}]", "", var.type.encapsulated), var_name(var.type.encapsulated.size), indent_lvl + 1, )) lines.append(INDENTATION * indent_lvl + "}") self.words.pop_scope() self.iterator.pop_it() return lines
def read_lines(self, decl: bool, var: Variable, size: str, indent_lvl: int) -> List[str]: # pylint: disable=too-many-arguments """Generate the Javascript code to read the lines for a given type""" if var.fits_in_one_line(self.input.structs): return self.read_line(decl, var.name, var.type, size, indent_lvl) indent = INDENTATION * indent_lvl if var.type.main == TypeEnum.LIST: assert var.type.encapsulated is not None lines = [indent + f"{'const ' if decl else ''}{var.name} = [];"] iterator = self.iterator.new_it() inner_name = self.iterator.new_it() lines.append(indent + "for (let {0} = 0; {0} < {1}; {0}++) {{".format( iterator, size)) self.words.push_scope() lines.extend( self.read_lines( True, Variable(inner_name, "", var.type.encapsulated), var_name(var.type.encapsulated.size), indent_lvl + 1, )) lines.append(indent + INDENTATION + f"{var.name}.push({inner_name});") self.words.pop_scope() self.iterator.pop_it() self.iterator.pop_it() return lines + [indent + "}"] assert var.type.main == TypeEnum.STRUCT struct = self.input.get_struct(var.type.struct_name) lines = [indent + f"{'const ' if decl else ''}{var.name} = {{}};"] for f_name, f_type, f_size in struct.fields_name_type_size( f"{var.name}.{{}}", var_name): lines.extend( self.read_lines(False, Variable(f_name, "", f_type), f_size, indent_lvl)) return lines
def read_lines( var: Variable, size: str, input_data: Input, iterator: IteratorName ) -> List[str]: """Generate the Lua code to read the lines for a given type""" if var.fits_in_one_line(input_data.structs): return read_line(var.name, var.type, input_data, iterator) if var.type.main == TypeEnum.LIST: assert var.type.encapsulated is not None inner = iterator.new_it() lines = read_lines( Variable(f"{var.name}[{inner}]", "", var.type.encapsulated), var_name(var.type.encapsulated.size), input_data, iterator, ) iterator.pop_it() return ( [f"{var.name} = {{}}", f"for {inner} = 1, {size} do"] + [INDENTATION + i for i in lines] + ["end"] ) assert var.type.main == TypeEnum.STRUCT struct = input_data.get_struct(var.type.struct_name) lines = [var.name + " = {}"] for i, field in enumerate(struct.fields): f_size = var_name(field.type.size) if i == 1 and struct.is_sized_struct(): f_size = f'{var.name}["{struct.fields[0].name}"]' lines.extend( read_lines( Variable(f'{var.name}["{field.name}"]', "", field.type), f_size, input_data, iterator, ) ) return lines
def type_str(var: Variable, decl: bool = False) -> str: """Return the Pascal name for a type""" # pylint: disable=too-many-return-statements if var.type.main == TypeEnum.INT: return "longint" if var.type.main == TypeEnum.CHAR: return "char" if var.type.main == TypeEnum.STR: return "AnsiString" if var.type.main == TypeEnum.STRUCT: return var_name(var.type.struct_name) assert var.type.main == TypeEnum.LIST assert var.type.encapsulated if var.type.encapsulated.main == TypeEnum.CHAR: return "AnsiString" if decl: return "array of " + type_str(Variable("", "", var.type.encapsulated), True) return "T_" + var_name(var.name)
def read_vars(self) -> List[str]: """Generate the Javascript code to read all input variables""" lines = [] for variables in self.input.get_all_vars(): if len(variables) == 1: var = variables[0] lines.extend( self.read_lines( True, Variable(var_name(var.name), "", var.type, var.format_style), var_name(var.type.size), 1, )) else: lines.append(INDENTATION + "const [" + ", ".join(var_name(i.name) for i in variables) + '] = stdin[line++].split(" ").map(Number);') return lines
def read_vars(input_data: Input, words: WordsName) -> List[str]: """Read all input variables""" lines = [] for variables in input_data.get_all_vars(): if len(variables) == 1: var = variables[0] lines.extend( read_lines( Variable(var_name(var), "", var.type, var.format_style), "my {} = {{}};".format(var_name(var)), size_name(var.type.size), input_data, words, )) else: split = words.next_name() lines.append(f"my @{split} = split / /, <>;") for i, var in enumerate(variables): lines.append(f"my {var_name(var)} = int(${split}[{i}]);") return lines
def call(self, reprint: bool) -> List[str]: """Declare and call the function take all inputs in arguments""" lines = [INDENTATION + "/**"] arguments = [] for arg in self.input.input: arg_name = var_name(arg.name) lines.append(INDENTATION + " * @param {} {}".format(arg_name, arg.comment)) arguments.append("{} {}".format(type_str(arg.type), arg_name)) lines.append(INDENTATION + " */") lines.append("{}static void {}({}) {{".format( INDENTATION, var_name(self.input.name), ", ".join(arguments))) if reprint: for variables in self.input.get_all_vars(): if len(variables) == 1: var = variables[0] lines.extend( self.print_lines( Variable(var_name(var.name), "", var.type, var.format_style), var_name(var.type.size), 2, )) else: lines.append(INDENTATION * 2 + 'System.out.printf("{}\\n", {});'.format( " ".join(["%d"] * len(variables)), ", ".join( var_name(i.name) for i in variables), )) else: lines.extend([ 2 * INDENTATION + i for i in textwrap.wrap( "/* TODO " + self.input.output + " */", 79 - 2 * len(INDENTATION), ) ]) return lines + [INDENTATION + "}"]
def error_parse_struct( dic: Dict[str, Union[str, List[Dict[str, str]]]]) -> str: """Explain why we a struct fails to parse""" # pylint: disable=too-many-return-statements assert Struct.from_dict(dic) is None if "name" not in dic: return "missing name field" if not isinstance(dic["name"], str): return "name field is not a string" for field in ("comment", "fields"): if field not in dic: return "missing {} field for {}".format(field, dic["name"]) if not isinstance(dic["comment"], str): return "{} field for {} is not a string".format(field, dic["name"]) if not isinstance(dic["fields"], list): return "fields field for {} is not a list".format(dic["name"]) for i in dic["fields"]: if not isinstance(i, dict): return "a field for {}.fields is not a map".format(dic["name"]) if Variable.from_dict(i) is None: return "failed to parse field of {}: {}".format( dic["name"], error_parse_variable(i)) return "unknown error"
def read_var(self, var: Variable) -> List[str]: """Read a variable""" make = False if var.type.main == TypeEnum.LIST: assert var.type.encapsulated is not None if var.type.encapsulated.main != TypeEnum.CHAR: make = True lines = [] if make: lines.append( "{} := make({}, {})".format( var_name(var.name), type_str(var.type), var_name(var.type.size) ) ) else: lines.append("var {} {}".format(var_name(var.name), type_str(var.type))) lines.extend( self.read_lines( Variable(var_name(var.name), "", var.type, var.format_style), var_name(var.type.size), already_allocated=True, ) ) return lines