diff --git a/ast_generator/ast_generator.py b/ast_generator/ast_generator.py index c23e64f..31c4480 100644 --- a/ast_generator/ast_generator.py +++ b/ast_generator/ast_generator.py @@ -170,7 +170,7 @@ class AstGenerator: if return_value is None: self.generate_expression(return_type) else: - self.current_ast_element.append(self.make_literal(return_type, return_value)) + self.generate_literal(return_type, return_value) # return to the parent self.current_ast_element = parent @@ -373,10 +373,17 @@ class AstGenerator: self.current_ast_element = parent - def generate_routine_call(self): # we should generate a test case with arbitrary number of args + def generate_routine_call(self): # we should generate a test case with arbitrary number of args pass def generate_conditional(self): + """ + @brief generate a conditional statement + + @effects: modifies the current_ast_element + + @return: None + """ if self.current_control_flow_nesting_depth >= self.settings['generation-options']['max-nesting-depth']: return @@ -384,24 +391,25 @@ class AstGenerator: 'block-termination-probability']: return - element = build_xml_element([], name=GAZ_IF_TAG) - self.current_ast_element.append(element) parent = self.current_ast_element - self.current_ast_element = element + self.make_scoped_element(GAZ_IF_TAG, []) self.current_control_flow_nesting_depth += 1 - self.push_scope() - self.generate_expression(GAZ_BOOL_KEY) - self.generate_block(tag=[("type", GAZ_TRUE_BLOCK_TAG)]) + self.generate_block(tag=[("type", GAZ_TRUE_BLOCK_TAG)]) # FIXME this inhibits elif blocks self.generate_block(tag=[("type", GAZ_FALSE_BLOCK_TAG)]) - self.pop_scope() - self.current_ast_element = parent + self.current_control_flow_nesting_depth -= 1 + self.exit_scoped_element(parent) - def generate_loop(self): # fixme generation of infinite loops happens too often... + def generate_loop(self): + """ + @brief generate a loop + + @return: None + """ # FIXME make sure that loop conditions are evaluated at least once (assert true or make a config param) if self.current_control_flow_nesting_depth >= self.settings['generation-options']['max-nesting-depth']: return @@ -412,35 +420,44 @@ class AstGenerator: init_var = self.generate_zero_declaration() parent = self.current_ast_element - element = build_xml_element([], name=GAZ_LOOP_TAG) - self.current_ast_element.append(element) - self.current_ast_element = element + self.make_scoped_element(GAZ_LOOP_TAG, []) self.current_control_flow_nesting_depth += 1 - self.push_scope() - self.generate_expression(GAZ_BOOL_KEY) + + self.generate_expression(GAZ_BOOL_KEY) # the loop entry condition #TODO force true self.generate_block(block_type=GAZ_LOOP_TAG, loop_var=init_var) # append a variable increment and prepend a break statement if var is > max loop iterations - self.pop_scope() - self.current_ast_element = parent + + self.current_control_flow_nesting_depth -= 1 + self.exit_scoped_element(parent) def generate_zero_declaration(self): + """ + @brief generate a declaration int a = 0 for some a + + @return: None + """ parent = self.current_ast_element - element = build_xml_element([], name=GAZ_DECLARATION_TAG) - self.current_ast_element.append(element) - self.current_ast_element = element + self.make_element(GAZ_DECLARATION_TAG, []) + # Initialize variable variable = self.generate_variable(GAZ_INT_KEY, 'var') self.current_ast_element.append(variable.xml) self.current_scope.append(variable.name, variable) self.generate_xhs(GAZ_RHS_TAG, variable.type, is_zero=True) + self.current_ast_element = parent return variable def generate_assignment(self): + """ + @brief generate an assignment + + @return: None + """ possible_vars = self.current_scope.get_all_defined_mutable_vars() if len(possible_vars) == 0: raise ValueError("No possible variables to assign to!") @@ -448,9 +465,7 @@ class AstGenerator: # same structure as a declaration parent = self.current_ast_element - element = build_xml_element([], name=GAZ_ASSIGNMENT_TAG) - self.current_ast_element.append(element) - self.current_ast_element = element + self.make_element(GAZ_ASSIGNMENT_TAG, []) variable = random.choice(possible_vars) @@ -466,29 +481,48 @@ class AstGenerator: self.generate_stream(GAZ_IN_STREAM) def generate_stream(self, stream_type): + """ + @brief generate a stream statment from a stream type + + @param stream_type: whether the stream is an input or output + @return: + """ parent = self.current_ast_element + args = [ ("type", stream_type), ] - element = build_xml_element(args, name=GAZ_STREAM_TAG) - self.current_ast_element.append(element) - self.current_ast_element = element - + self.make_element(GAZ_STREAM_TAG, args) self.generate_expression(ANY_TYPE) self.current_ast_element = parent def generate_variable(self, var_type: str, mut=None): + """ + @brief generate a variable + + @param var_type: they type of the variable + @param mut: mutability of the variable + @return: None + """ if mut is None: return Variable(self.get_name(GAZ_VAR_TAG), var_type, self.get_qualifier()) else: return Variable(self.get_name(GAZ_VAR_TAG), var_type, mut) def generate_literal(self, var_type: str, value=None): + """ + @brief generate a literal + + @param var_type: Type of the literal + @param value: optional value of the literal + @return: None + """ if value is None: value = self.get_value(var_type) else: value = value + args = [ ("type", var_type), ("value", str(value)), @@ -496,7 +530,7 @@ class AstGenerator: element = build_xml_element(args, name=GAZ_LIT_TAG) self.current_ast_element.append(element) - def make_literal(self, type, value): + def make_literal(self, type, value): # TODO eliminate this function args = [ ("type", type), ("value", value), @@ -505,6 +539,11 @@ class AstGenerator: return element def generate_global(self): + """ + @brief generate a global const declaration + + @return: None + """ current_scope = self.current_scope current_element = self.current_ast_element @@ -517,6 +556,13 @@ class AstGenerator: self.current_ast_element = current_element def generate_expression(self, expr_type: str, is_zero=False): + """ + @brief generate an expression + + @param expr_type: the type of the expression + @param is_zero: if the expression should eval to 0 + @return: None + """ if is_zero: self.generate_literal(expr_type, value=0) return @@ -530,11 +576,17 @@ class AstGenerator: elif expr_type == GAZ_CHAR_KEY: self.generate_char_expr() elif expr_type == ANY_TYPE: # TODO implement the choice of any type - self.generate_int_expr() + ty = self.get_type(GAZ_RHS_TAG) + self.generate_expression(ty) else: raise NotImplementedError(f"Expression type {expr_type} not implemented") - def generate_routine_args(self): + def generate_routine_args(self) -> list[Argument]: + """ + @brief generate a list of arguments for a routine + + @return: a list of arguments + """ number = random.randint(self.settings['properties']['number-of-arguments']['min'], self.settings['properties']['number-of-arguments']['max']) args = [] @@ -701,7 +753,7 @@ class AstGenerator: # add the check 'if loop_var >= self.settings['generation_options']['max-loop-iterations']: break' operation = build_xml_element([("op", ">=")], name=GAZ_OPERATOR_TAG) rhs = self._loop_heloper(loop_var, operation) - rhs.append( + rhs.append( # TODO refactor this to use generate_literal instead of make_literal self.make_literal(GAZ_INT_KEY, "'" + str(self.settings['generation-options']['max-loop-iterations']) + "'")) true_block = build_xml_element([], name=GAZ_BLOCK_TAG) @@ -742,7 +794,7 @@ class AstGenerator: operation = build_xml_element([("op", "+")], name=GAZ_OPERATOR_TAG) rhs = self._loop_heloper(loop_var, operation) - rhs.append(self.make_literal(GAZ_INT_KEY, '1')) + rhs.append(self.make_literal(GAZ_INT_KEY, '1')) # TODO refactor this to use generate_literal instead of make_literal # return everything to normalcy self.current_ast_element = parent @@ -814,4 +866,3 @@ class AstGenerator: self.generate_binary(op, random.choice([GAZ_INT_KEY, GAZ_FLOAT_KEY])) else: self.generate_binary(op, random.choice(expr_type)) - diff --git a/gazprea_fuzzer.py b/gazprea_fuzzer.py index 576f02f..9e26176 100644 --- a/gazprea_fuzzer.py +++ b/gazprea_fuzzer.py @@ -1,5 +1,6 @@ import os import random +import sys import warnings from contextlib import redirect_stdout @@ -30,10 +31,11 @@ class Fuzzer(): os.mkdir("fuzzer") os.mkdir("fuzzer/input") os.mkdir("fuzzer/debug") - os.mkdir("fuzzer/debug/ast") + os.mkdir("fuzzer/debug/ast_err") os.mkdir("fuzzer/instream") os.mkdir("fuzzer/outputs") os.mkdir("fuzzer/ground_truth") + os.system("cp tester_config.json fuzzer/tester_config.json") for i in range(self.batch): try: self.fuzzer.fuzz() @@ -42,7 +44,7 @@ class Fuzzer(): warnings.warn("None Tag Exception encountered, writing stack trace and xml to debug/ast/{}.xml\n" "Look for a top-level tag or send it to Ayrton and I'll see what I can see" "".format(r)) - with open("fuzzer/debug/ast/{}.xml".format(r), 'w') as f: + with open("fuzzer/debug/ast_err/{}.xml".format(r), 'w') as f: f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml()) continue dom = xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')) @@ -58,9 +60,16 @@ class Fuzzer(): except (OverflowError, ZeroDivisionError, ValueError): os.system("rm -f fuzzer/ground_truth/{}_{}.py".format(self.file_name, i)) continue + except KeyboardInterrupt: + r = random.randint(0, 1000000) + warnings.warn("Execution halted, result written to debug/ast/{}.xml\n" + "".format(r)) + with open("fuzzer/debug/ast_err/{}.xml".format(r), 'w') as f: + f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml()) + sys.exit(1) with open("fuzzer/input/{}_{}.in".format(self.file_name, i), 'w') as f: f.write(self.fuzzer.source) - with open("fuzzer/debug/{}_{}.out".format(self.file_name, i), 'w') as f: + with open("fuzzer/debug/{}_{}.xml".format(self.file_name, i), 'w') as f: f.write(pretty) # y.write(self.fuzzer.out) # with open("fuzzer/instream/{}.in".format(i), 'w') as f: diff --git a/tester_config.json b/tester_config.json new file mode 100644 index 0000000..602d1ff --- /dev/null +++ b/tester_config.json @@ -0,0 +1,36 @@ +{ + "inDir": "", + "outDir": "", + "inStrDir": "", + "testedExecutablePaths": { + "": "" + }, + "runtimes": { + "": "" + }, + "toolchains": { + "gazprea": [ + { + "stepName": "gazc", + "executablePath": "$EXE", + "arguments": [ + "$INPUT", + "$OUTPUT" + ], + "output": "gazc.ll", + "allowError": true + }, + { + "stepName": "lli", + "executablePath": "/cshome/cmput415/415-resources/llvm-project/build/bin/lli", + "arguments": [ + "$INPUT" + ], + "output": "-", + "usesRuntime": true, + "usesInStr": true, + "allowError": true + } + ] + } +}