Refactored ASTGenerator #3

Merged
aCompetentBean merged 8 commits from ayrton/refactor into main 2023-11-24 07:34:47 -07:00
3 changed files with 133 additions and 37 deletions
Showing only changes of commit 8cfbd08708 - Show all commits

View file

@ -170,7 +170,7 @@ class AstGenerator:
if return_value is None: if return_value is None:
self.generate_expression(return_type) self.generate_expression(return_type)
else: else:
self.current_ast_element.append(self.make_literal(return_type, return_value)) self.generate_literal(return_type, return_value)
# return to the parent # return to the parent
self.current_ast_element = parent self.current_ast_element = parent
@ -373,10 +373,17 @@ class AstGenerator:
self.current_ast_element = parent 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 pass
def generate_conditional(self): 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']: if self.current_control_flow_nesting_depth >= self.settings['generation-options']['max-nesting-depth']:
return return
@ -384,24 +391,25 @@ class AstGenerator:
'block-termination-probability']: 'block-termination-probability']:
return return
element = build_xml_element([], name=GAZ_IF_TAG)
self.current_ast_element.append(element)
parent = self.current_ast_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.current_control_flow_nesting_depth += 1
self.push_scope()
self.generate_expression(GAZ_BOOL_KEY) 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.generate_block(tag=[("type", GAZ_FALSE_BLOCK_TAG)])
self.pop_scope() self.current_control_flow_nesting_depth -= 1
self.current_ast_element = parent 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) # 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']: if self.current_control_flow_nesting_depth >= self.settings['generation-options']['max-nesting-depth']:
return return
@ -412,35 +420,44 @@ class AstGenerator:
init_var = self.generate_zero_declaration() init_var = self.generate_zero_declaration()
parent = self.current_ast_element 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.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, 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 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): def generate_zero_declaration(self):
"""
@brief generate a declaration int a = 0 for some a
@return: None
"""
parent = self.current_ast_element parent = self.current_ast_element
element = build_xml_element([], name=GAZ_DECLARATION_TAG)
self.current_ast_element.append(element) self.make_element(GAZ_DECLARATION_TAG, [])
self.current_ast_element = element
# Initialize variable
variable = self.generate_variable(GAZ_INT_KEY, 'var') variable = self.generate_variable(GAZ_INT_KEY, 'var')
self.current_ast_element.append(variable.xml) self.current_ast_element.append(variable.xml)
self.current_scope.append(variable.name, variable) self.current_scope.append(variable.name, variable)
self.generate_xhs(GAZ_RHS_TAG, variable.type, is_zero=True) self.generate_xhs(GAZ_RHS_TAG, variable.type, is_zero=True)
self.current_ast_element = parent self.current_ast_element = parent
return variable return variable
def generate_assignment(self): def generate_assignment(self):
"""
@brief generate an assignment
@return: None
"""
possible_vars = self.current_scope.get_all_defined_mutable_vars() possible_vars = self.current_scope.get_all_defined_mutable_vars()
if len(possible_vars) == 0: if len(possible_vars) == 0:
raise ValueError("No possible variables to assign to!") raise ValueError("No possible variables to assign to!")
@ -448,9 +465,7 @@ class AstGenerator:
# same structure as a declaration # same structure as a declaration
parent = self.current_ast_element parent = self.current_ast_element
element = build_xml_element([], name=GAZ_ASSIGNMENT_TAG) self.make_element(GAZ_ASSIGNMENT_TAG, [])
self.current_ast_element.append(element)
self.current_ast_element = element
variable = random.choice(possible_vars) variable = random.choice(possible_vars)
@ -466,29 +481,48 @@ class AstGenerator:
self.generate_stream(GAZ_IN_STREAM) self.generate_stream(GAZ_IN_STREAM)
def generate_stream(self, stream_type): 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 parent = self.current_ast_element
args = [ args = [
("type", stream_type), ("type", stream_type),
] ]
element = build_xml_element(args, name=GAZ_STREAM_TAG) self.make_element(GAZ_STREAM_TAG, args)
self.current_ast_element.append(element)
self.current_ast_element = element
self.generate_expression(ANY_TYPE) self.generate_expression(ANY_TYPE)
self.current_ast_element = parent self.current_ast_element = parent
def generate_variable(self, var_type: str, mut=None): 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: if mut is None:
return Variable(self.get_name(GAZ_VAR_TAG), var_type, self.get_qualifier()) return Variable(self.get_name(GAZ_VAR_TAG), var_type, self.get_qualifier())
else: else:
return Variable(self.get_name(GAZ_VAR_TAG), var_type, mut) return Variable(self.get_name(GAZ_VAR_TAG), var_type, mut)
def generate_literal(self, var_type: str, value=None): 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: if value is None:
value = self.get_value(var_type) value = self.get_value(var_type)
else: else:
value = value value = value
args = [ args = [
("type", var_type), ("type", var_type),
("value", str(value)), ("value", str(value)),
@ -496,7 +530,7 @@ class AstGenerator:
element = build_xml_element(args, name=GAZ_LIT_TAG) element = build_xml_element(args, name=GAZ_LIT_TAG)
self.current_ast_element.append(element) self.current_ast_element.append(element)
def make_literal(self, type, value): def make_literal(self, type, value): # TODO eliminate this function
args = [ args = [
("type", type), ("type", type),
("value", value), ("value", value),
@ -505,6 +539,11 @@ class AstGenerator:
return element return element
def generate_global(self): def generate_global(self):
"""
@brief generate a global const declaration
@return: None
"""
current_scope = self.current_scope current_scope = self.current_scope
current_element = self.current_ast_element current_element = self.current_ast_element
@ -517,6 +556,13 @@ class AstGenerator:
self.current_ast_element = current_element self.current_ast_element = current_element
def generate_expression(self, expr_type: str, is_zero=False): 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: if is_zero:
self.generate_literal(expr_type, value=0) self.generate_literal(expr_type, value=0)
return return
@ -530,11 +576,17 @@ class AstGenerator:
elif expr_type == GAZ_CHAR_KEY: elif expr_type == GAZ_CHAR_KEY:
self.generate_char_expr() self.generate_char_expr()
elif expr_type == ANY_TYPE: # TODO implement the choice of any type 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: else:
raise NotImplementedError(f"Expression type {expr_type} not implemented") 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'], number = random.randint(self.settings['properties']['number-of-arguments']['min'],
self.settings['properties']['number-of-arguments']['max']) self.settings['properties']['number-of-arguments']['max'])
args = [] args = []
@ -701,7 +753,7 @@ class AstGenerator:
# add the check 'if loop_var >= self.settings['generation_options']['max-loop-iterations']: break' # add the check 'if loop_var >= self.settings['generation_options']['max-loop-iterations']: break'
operation = build_xml_element([("op", ">=")], name=GAZ_OPERATOR_TAG) operation = build_xml_element([("op", ">=")], name=GAZ_OPERATOR_TAG)
rhs = self._loop_heloper(loop_var, operation) 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']) + "'")) self.make_literal(GAZ_INT_KEY, "'" + str(self.settings['generation-options']['max-loop-iterations']) + "'"))
true_block = build_xml_element([], name=GAZ_BLOCK_TAG) true_block = build_xml_element([], name=GAZ_BLOCK_TAG)
@ -742,7 +794,7 @@ class AstGenerator:
operation = build_xml_element([("op", "+")], name=GAZ_OPERATOR_TAG) operation = build_xml_element([("op", "+")], name=GAZ_OPERATOR_TAG)
rhs = self._loop_heloper(loop_var, operation) 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 # return everything to normalcy
self.current_ast_element = parent self.current_ast_element = parent
@ -814,4 +866,3 @@ class AstGenerator:
self.generate_binary(op, random.choice([GAZ_INT_KEY, GAZ_FLOAT_KEY])) self.generate_binary(op, random.choice([GAZ_INT_KEY, GAZ_FLOAT_KEY]))
else: else:
self.generate_binary(op, random.choice(expr_type)) self.generate_binary(op, random.choice(expr_type))

View file

@ -1,5 +1,6 @@
import os import os
import random import random
import sys
import warnings import warnings
from contextlib import redirect_stdout from contextlib import redirect_stdout
@ -30,10 +31,11 @@ class Fuzzer():
os.mkdir("fuzzer") os.mkdir("fuzzer")
os.mkdir("fuzzer/input") os.mkdir("fuzzer/input")
os.mkdir("fuzzer/debug") os.mkdir("fuzzer/debug")
os.mkdir("fuzzer/debug/ast") os.mkdir("fuzzer/debug/ast_err")
os.mkdir("fuzzer/instream") os.mkdir("fuzzer/instream")
os.mkdir("fuzzer/outputs") os.mkdir("fuzzer/outputs")
os.mkdir("fuzzer/ground_truth") os.mkdir("fuzzer/ground_truth")
os.system("cp tester_config.json fuzzer/tester_config.json")
for i in range(self.batch): for i in range(self.batch):
try: try:
self.fuzzer.fuzz() 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" warnings.warn("None Tag Exception encountered, writing stack trace and xml to debug/ast/{}.xml\n"
"Look for a top-level <argument> tag or send it to Ayrton and I'll see what I can see" "Look for a top-level <argument> tag or send it to Ayrton and I'll see what I can see"
"".format(r)) "".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()) f.write(xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')).toprettyxml())
continue continue
dom = xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8')) dom = xml.dom.minidom.parseString(ET.tostring(self.fuzzer.ast).decode('utf-8'))
@ -58,9 +60,16 @@ class Fuzzer():
except (OverflowError, ZeroDivisionError, ValueError): except (OverflowError, ZeroDivisionError, ValueError):
os.system("rm -f fuzzer/ground_truth/{}_{}.py".format(self.file_name, i)) os.system("rm -f fuzzer/ground_truth/{}_{}.py".format(self.file_name, i))
continue 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: with open("fuzzer/input/{}_{}.in".format(self.file_name, i), 'w') as f:
f.write(self.fuzzer.source) 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) f.write(pretty)
# y.write(self.fuzzer.out) # y.write(self.fuzzer.out)
# with open("fuzzer/instream/{}.in".format(i), 'w') as f: # with open("fuzzer/instream/{}.in".format(i), 'w') as f:

36
tester_config.json Normal file
View file

@ -0,0 +1,36 @@
{
"inDir": "<inDir>",
"outDir": "<outDir>",
"inStrDir": "<inStrDir>",
"testedExecutablePaths": {
"<team id>": "<path_to_gazc_exe>"
},
"runtimes": {
"<team id>": "<path_to_libgazrt.so>"
},
"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
}
]
}
}