include "primitives.alh"
include "conformance_scd.alh"
include "library.alh"
include "io.alh"
include "modelling.alh"
include "utils.alh"

Element while_stack = ?
Element variable_map = ?

Element function construct_function_list(list : Element):
	String command
	Element result
	Element main_function
	Boolean continue
	Element prev_element
	Element first_element

	list = list_reverse(list)

	// Initialize variables
	prev_element = read_root()
	main_function = read_root()

	// Clear global variables
	while_stack = list_create()
	variable_map = dict_create()

	continue = True
	while (continue):
		command = list_pop_final(list)
		if (command == "global"):
			result = construct_global(list)
		elif (command == "funcdef"):
			result = construct_funcdef(False, list)
		elif (command == "mutable_funcdef"):
			result = construct_funcdef(True, list)
		else:
			log("ERROR (1): did not understand command " + cast_value(command))
			output("ERROR: compiled code not understood: " + cast_value(command))
			return read_root()!

		continue = list_pop_final(list)
		if (prev_element != read_root()):
			dict_add_fast(prev_element, "next", result["start"])
		else:
			first_element = result["start"]

		if (bool_and(element_eq(main_function, read_root()), command != "global")):
			// In case no main function is defined, it is the first defined function
			// This is mostly there to ensure backwards compatibility
			main_function = result["instruction"]

		prev_element = result["end"]

		if (value_eq(result["name"], "main")):
			// It was the function that we want to call
			main_function = result["instruction"]

	if (element_eq(main_function, read_root())):
		log("ERROR (2): no main function found")
		output("ERROR: no main function found")
		return read_root()!

	// Overwrite the main function with our declaration function
	prev_element = main_function["body"]
	dict_delete(main_function, "body")
	dict_add_fast(main_function, "body", first_element)
	dict_add_fast(result["end"], "next", prev_element)
	
	return main_function!

Action function construct_global(list : Element):
	Action this_element
	String declared_element
	String op

	this_element = create_value(!global)
	declared_element = list_pop_final(list)
	dict_add_fast(this_element, "var", declared_element)

	op = list_pop_final(list)
	if (op != "none"):
		// Defines
		Action assign
		Action resolve
		Action value
		assign = create_value(!assign)
		dict_add_fast(this_element, "next", assign)
		resolve = create_value(!resolve)
		dict_add_fast(assign, "var", resolve)
		dict_add_fast(resolve, "var", declared_element)
		if (op == "deref"):
			value = create_value(!constant)
			dict_add_fast(value, "node", import_node(list_pop_final(list)))
		elif (op == "empty"):
			value = create_value(!call)
			Element res
			Element acc
			res = create_value(!resolve)
			acc = create_value(!access)
			dict_add_fast(value, "func", acc)
			dict_add_fast(acc, "var", res)
			dict_add_fast(res, "var", "create_node")
		elif (op == "const"):
			value = create_value(!constant)
			dict_add_fast(value, "node", list_pop_final(list))
		dict_add_fast(assign, "value", value)

		Element result
		result = dict_create()
		dict_add_fast(result, "name", "")
		dict_add_fast(result, "instruction", this_element)
		dict_add_fast(result, "start", this_element)
		dict_add_fast(result, "end", assign)
		return result!
	else:
		Element result
		result = dict_create()
		dict_add_fast(result, "name", "")
		dict_add_fast(result, "instruction", this_element)
		dict_add_fast(result, "start", this_element)
		dict_add_fast(result, "end", this_element)
		return result!

Action function construct_funcdef(mutable : Boolean, list : Element):
	Action assign
	Action resolve
	Action constant
	Element formal
	Element func
	Element params
	Action declare
	String name

	declare = create_value(!global)
	assign = create_value(!assign)
	resolve = create_value(!resolve)
	constant = create_value(!constant)
	name = list_pop_final(list)
	if (dict_in(variable_map, name)):
		formal = dict_read(variable_map, name)
	else:
		formal = name
	func = create_node()
	params = create_node()
	dict_add_fast(declare, "var", formal)
	dict_add_fast(declare, "next", assign)
	dict_add_fast(assign, "var", resolve)
	dict_add_fast(assign, "value", constant)
	dict_add_fast(resolve, "var", formal)
	dict_add_fast(constant, "node", func)
	dict_add_fast(func, "params", params)

	if (mutable):
		dict_add_fast(func, "mutable", create_node())

	Integer nrParams
	nrParams = list_pop_final(list)
	Integer counter
	counter = 0
	Element param

	String arg_names_decl
	arg_names_decl = "abcdefghijklmnopqrstuvwxyz"

	while (counter < nrParams):
		param = create_node()
		dict_add_fast(params, string_get(arg_names_decl, counter), param)
		dict_add_fast(param, "name", string_get(arg_names_decl, counter))
		dict_add_fast(variable_map, list_pop_final(list), param)
		// Output each parameter in turn
		counter = counter + 1

	// Now add the body
	dict_add_fast(func, "body", construct_unknown(list))

	Element result
	result = dict_create()
	dict_add_fast(result, "name", name)
	dict_add_fast(result, "instruction", func)
	dict_add_fast(result, "start", declare)
	dict_add_fast(result, "end", assign)
	return result!

Element function construct_unknown(list : Element):
	String elem
	Element new_model
	Element new_model_model
	elem = list_pop_final(list)

	if (elem == "if"):
		return construct_if(list)!
	elif (elem == "while"):
		return construct_while(list)!
	elif (elem == "access"):
		return construct_access(list)!
	elif (elem == "resolve"):
		return construct_resolve(list)!
	elif (elem == "assign"):
		return construct_assign(list)!
	elif (elem == "call"):
		return construct_call(list)!
	elif (elem == "return"):
		return construct_return(list)!
	elif (elem == "const"):
		return construct_const(list)!
	elif (elem == "declare"):
		return construct_declare(list)!
	elif (elem == "output"):
		return construct_output(list)!
	elif (elem == "input"):
		return construct_input(list)!
	elif (elem == "deref"):
		return construct_deref(list)!
	elif (elem == "break"):
		return construct_break(list)!
	elif (elem == "continue"):
		return construct_continue(list)!
	else:
		log("ERROR (2): did not understand command " + cast_value(elem))
		output("ERROR: compiled code not understood: " + cast_value(elem))
		return read_root()!

Action function construct_if(list : Element):
	Action this_element
	this_element = create_value(!if)
	dict_add_fast(this_element, "cond", construct_unknown(list))
	dict_add_fast(this_element, "then", construct_unknown(list))
	if (list_pop_final(list)):
		dict_add_fast(this_element, "else", construct_unknown(list))
	if (list_pop_final(list)):
		dict_add_fast(this_element, "next", construct_unknown(list))
	return this_element!

Action function construct_while(list : Element):
	Action this_element
	this_element = create_value(!while)
	dict_add_fast(this_element, "cond", construct_unknown(list))

	list_append(while_stack, this_element)
	dict_add_fast(this_element, "body", construct_unknown(list))
	list_delete(while_stack, list_len(while_stack) - 1)

	if (list_pop_final(list)):
		dict_add_fast(this_element, "next", construct_unknown(list))
	return this_element!

Action function construct_access(list : Element):
	Action this_element
	this_element = create_value(!access)
	dict_add_fast(this_element, "var", construct_unknown(list))
	return this_element!

Action function construct_resolve(list : Element):
	Action this_element
	Element linked_element
	String name

	this_element = create_value(!resolve)
	name = list_pop_final(list)
	if dict_in(variable_map, name):
		linked_element = variable_map[name]
	else:
		linked_element = name
	dict_add_fast(this_element, "var", linked_element)
	return this_element!

Action function construct_assign(list : Element):
	Action this_element
	this_element = create_value(!assign)
	dict_add_fast(this_element, "var", construct_unknown(list))
	dict_add_fast(this_element, "value", construct_unknown(list))
	if (list_pop_final(list)):
		dict_add_fast(this_element, "next", construct_unknown(list))
	return this_element!

Action function construct_call(list : Element):
	Action this_element
	this_element = create_value(!call)
	dict_add_fast(this_element, "func", construct_unknown(list))

	Integer nrParams
	nrParams = list_pop_final(list)
	Integer counter
	counter = 0

	Element param
	Element prev_param
	String arg_names_call
	arg_names_call = "abcdefghijklmnopqrstuvwxyz"

	while (counter < nrParams):
		param = create_node()

		dict_add_fast(param, "name", string_get(arg_names_call, counter))
		dict_add_fast(param, "value", construct_unknown(list))

		if (counter == 0):
			dict_add_fast(this_element, "params", param)
		else:
			dict_add_fast(prev_param, "next_param", param)
		prev_param = param

		counter = counter + 1

	if (nrParams > 0):
		dict_add_fast(this_element, "last_param", prev_param)

	if (list_pop_final(list)):
		dict_add_fast(this_element, "next", construct_unknown(list))
	return this_element!

Action function construct_return(list : Element):
	if (list_pop_final(list)):
		Action this_element
		this_element = create_value(!return)
		dict_add_fast(this_element, "value", construct_unknown(list))
		return this_element!
	else:
		return create_value(!return)!

Action function construct_const(list : Element):
	Action this_element
	this_element = create_value(!constant)
	dict_add_fast(this_element, "node", list_pop_final(list))
	return this_element!

Action function construct_declare(list : Element):
	Action this_element
	Element declared_element
	String name

	this_element = create_value(!declare)
	declared_element = create_node()
	dict_add_fast(this_element, "var", declared_element)
	name = list_pop_final(list)
	dict_add_fast(variable_map, name, declared_element)

	// Assign value
	String op
	op = list_pop_final(list)
	if (op != "none"):
		Action assign
		Action resolve
		Action value
		assign = create_value(!assign)
		dict_add_fast(this_element, "next", assign)
		resolve = create_value(!resolve)
		dict_add_fast(assign, "var", resolve)
		dict_add_fast(resolve, "var", declared_element)
		if (op == "deref"):
			value = create_value(!constant)
			dict_add_fast(value, "node", import_node(list_pop_final(list)))
		elif (op == "empty"):
			value = create_value(!call)
			Element res
			Element acc
			res = create_value(!resolve)
			acc = create_value(!access)
			dict_add_fast(value, "func", acc)
			dict_add_fast(acc, "var", res)
			dict_add_fast(res, "var", "create_node")
		elif (op == "const"):
			value = create_value(!constant)
			dict_add_fast(value, "node", list_pop_final(list))
		dict_add_fast(assign, "value", value)

		if (list_pop_final(list)):
			dict_add_fast(assign, "next", construct_unknown(list))
	else:
		if (list_pop_final(list)):
			dict_add_fast(this_element, "next", construct_unknown(list))
	return this_element!

Action function construct_input(list : Element):
	Action this_element
	this_element = create_value(!input)
	return this_element!

Action function construct_output(list : Element):
	Action this_element
	this_element = create_value(!output)
	dict_add_fast(this_element, "value", construct_unknown(list))
	if (list_pop_final(list)):
		dict_add_fast(this_element, "next", construct_unknown(list))
	return this_element!

Action function construct_deref(list : Element):
	Action this_element
	this_element = create_value(!constant)
	dict_add_fast(this_element, "node", import_node(list_pop_final(list)))
	return this_element!

Action function construct_break(list : Element):
	Action this_element
	this_element = create_value(!break)
	dict_add_fast(this_element, "while", while_stack[list_len(while_stack) - 1])
	return this_element!

Action function construct_continue(list : Element):
	Action this_element
	this_element = create_value(!continue)
	dict_add_fast(this_element, "while", while_stack[list_len(while_stack) - 1])
	return this_element!
