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

Element while_stack = ?
Element variable_map = ?

Action function construct_top():
	String command
	while (True):
		command = input()
		if (command == "global"):
			return construct_global()
		elif (command == "funcdef"):
			return construct_top_funcdef()
		else:
			log("ERROR: did not understand command " + cast_e2s(command))

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

	log("Construct global")
	this_element = create_value(!global)
	declared_element = input()
	dict_add(this_element, "var", declared_element)

	// Defines
	Action assign
	Action resolve
	Action value
	assign = create_value(!assign)
	dict_add(this_element, "next", assign)
	resolve = create_value(!resolve)
	dict_add(assign, "var", resolve)
	dict_add(resolve, "var", declared_element)
	op = input()
	if (op == "deref"):
		value = create_value(!constant)
		dict_add(value, "node", import_node(input()))
	elif (op == "empty"):
		log("EMPTY")
		value = create_value(!call)
		Element res
		Element acc
		res = create_value(!resolve)
		acc = create_value(!access)
		dict_add(value, "func", acc)
		dict_add(acc, "var", res)
		dict_add(res, "var", "create_node")
		log("Created create_node call")
	elif (op == "const"):
		value = create_value(!constant)
		dict_add(value, "node", input())
	dict_add(assign, "value", value)

	if (input()):
		dict_add(assign, "next", construct_top())
	return this_element

Action function construct_top_funcdef():
	Action assign
	Action resolve
	Action constant
	Element formal
	Element func
	Element params
	Action global
	String name

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

	Integer nrParams
	nrParams = input()
	Integer counter
	counter = 0
	Element param

	String arg_names_decl
	arg_names_decl = "abcdefghijklmnopqrstuvwxyz"

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

	// Now add the body
	dict_add(func, "body", construct_unknown())

	if (input()):
		dict_add(assign, "next", construct_top())

	return global

Action function construct_unknown():
	String elem
	Element new_model
	Element new_model_model
	elem = input()

	if (elem == "if"):
		return construct_if()
	elif (elem == "while"):
		return construct_while()
	elif (elem == "access"):
		return construct_access()
	elif (elem == "resolve"):
		return construct_resolve()
	elif (elem == "assign"):
		return construct_assign()
	elif (elem == "call"):
		return construct_call()
	elif (elem == "return"):
		return construct_return()
	elif (elem == "const"):
		return construct_const()
	elif (elem == "declare"):
		return construct_declare()
	elif (elem == "output"):
		return construct_output()
	elif (elem == "input"):
		return construct_input()
	elif (elem == "deref"):
		return construct_deref()
	elif (elem == "break"):
		return construct_break()
	elif (elem == "continue"):
		return construct_continue()
	elif (elem == "model"):
		construct_model()
		log("Constructed model")
		return construct_unknown()
	else:
		log("ERROR: did not understand command " + cast_e2s(elem))

Action function construct_if():
	Action this_element
	this_element = create_value(!if)
	dict_add(this_element, "cond", construct_unknown())
	dict_add(this_element, "then", construct_unknown())
	if (input()):
		dict_add(this_element, "else", construct_unknown())
	if (input()):
		dict_add(this_element, "next", construct_unknown())
	return this_element

Action function construct_while():
	Action this_element
	this_element = create_value(!while)
	dict_add(this_element, "cond", construct_unknown())

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

	if (input()):
		dict_add(this_element, "next", construct_unknown())
	return this_element

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

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

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

Action function construct_assign():
	Action this_element
	this_element = create_value(!assign)
	dict_add(this_element, "var", construct_unknown())
	dict_add(this_element, "value", construct_unknown())
	if (input()):
		dict_add(this_element, "next", construct_unknown())
	return this_element

Action function construct_call():
	Action this_element
	this_element = create_value(!call)
	dict_add(this_element, "func", construct_unknown())

	Integer nrParams
	nrParams = input()
	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(param, "name", string_get(arg_names_call, counter))
		dict_add(param, "value", construct_unknown())

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

		counter = counter + 1

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

	if (input()):
		dict_add(this_element, "next", construct_unknown())
	return this_element

Action function construct_return():
	if (input()):
		Action this_element
		this_element = create_value(!return)
		dict_add(this_element, "value", construct_unknown())
		return this_element
	else:
		return create_value(!return)

Action function construct_const():
	Action this_element
	this_element = create_value(!constant)
	dict_add(this_element, "node", input())
	return this_element

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

	this_element = create_value(!declare)
	declared_element = create_node()
	dict_add(this_element, "var", declared_element)
	name = input()
	dict_add(variable_map, name, declared_element)
	if (input()):
		dict_add(this_element, "next", construct_unknown())
	return this_element

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

Action function construct_output():
	Action this_element
	this_element = create_value(!output)
	dict_add(this_element, "value", construct_unknown())
	if (input()):
		dict_add(this_element, "next", construct_unknown())
	return this_element

Action function construct_deref():
	Action this_element
	this_element = create_value(!constant)
	dict_add(this_element, "node", import_node(input()))
	return this_element

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

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

Action function construct_function():
	Action func
	Integer nrParams
	Integer counter
	Element param
	Element params
	String arg_names_decl

	while (value_neq(input(), "funcdef")):
		// We skip over everything that is not a funcdef, as these are all just definitions of global stuff
		log("Skip over input!")

	// Consume the name
	input()

	params = create_node()
	nrParams = input()
	counter = 0
	func = create_node()
	arg_names_decl = "abcdefghijklmnopqrstuvwxyz"
	dict_add(func, "params", params)

	while (counter < nrParams):
		param = create_node()
		dict_add(params, string_get(arg_names_decl, counter), param)
		dict_add(variable_map, input(), param)
		// Output each parameter in turn
		counter = counter + 1

	// Now add the body
	dict_add(func, "body", construct_unknown())

	// Consume the final 'false', to indicate that no additional code will come
	input()

	return func
