implementation module fclc;

// author: John van Groningen
// University of Nijmegen

import StdEnv;

TypeDefNormal :== 0;
TypeDefAbs :== 1;
TypeDefSemiAbs :== 2;

::	HDefinition
	=	Constant String String
	|	Enum [String]
	|	CFunction String String FunctionArgumentTypes String
	|	CleanFunction String CleanTypes CleanType
	|	CTypeDefinition String String
	|	CleanTypeDefinition String CleanType IsUnique Int /*abs_kind*/ 
	|	Include String
	|	Import String
	|	ErrorHFile !String !Int;

:: HError
	= HError !String !Int;

::	FunctionArgumentTypes
	=	NilFunctionArgumentTypes
	|	FunctionArgumentType String OptionalArgumentName FunctionArgumentTypes;

::	OptionalArgumentName
	=	ArgumentName !String
	|	NoArgumentName;

::	CleanTypes
	=	CleanTypes CleanType CleanTypes
	|	NilCleanTypes;

:: IsUnique :== Bool;
IsUnique :== True;
IsNotUnique :== False;
UnqDon`tCare :== undef;

::	CleanType
	=	CleanType IsUnique String
	|	CleanTupleType IsUnique CleanTypes
	|	CleanErrorType;

(>:) infixl;
(>:) f g:==g f;

(<:) infixr;
(<:) f g:==f g;

(<::) infixr;
(<::) f g:==let { (a,b)=g; } in f a b;

(<:::) infixr;
(<:::) f g:==let { (a,b,c)=g; } in f a b c;

skip_comment line_n hf
	# (ok,c,hf) = freadc hf;
	| not ok
		= (False,c,line_n,hf);
	| c=='*'
		= skip_comment2 line_n hf;
	| c=='\n'
		#! line_n=line_n+1;
		= skip_comment line_n hf;
		= skip_comment line_n hf;
	
	skip_comment2 line_n hf
		# (ok,c,hf) = freadc hf;
		| not ok
			= (False,c,line_n,hf);
		| c=='/'
			= read_char_and_skip_space line_n hf;
		| c=='*'
			= skip_comment2 line_n hf;
		| c=='\n'
			#! line_n=line_n+1;
			= skip_comment line_n hf;
			= skip_comment line_n hf;

read_char_and_skip_space line_n hf
	# (ok,c,hf) = freadc hf;
	| not ok
		= (False,c,line_n,hf);
		= skip_space c line_n hf;

skip_space_and_read_char c line_n hf
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (ok,c,line_n,hf);
	# (ok,c,hf) = freadc hf;
	= (ok,c,line_n,hf);

skip_space c line_n hf
	| c==' ' || c=='\t'
		= read_char_and_skip_space line_n hf;
	| c=='\n'
		#! line_n=line_n+1;
		= read_char_and_skip_space line_n hf;
	| c=='/'
		# (ok,c2,hf) = freadc hf;
		| not ok
			= (True,c,line_n,hf);
		| c2=='*'
			= skip_comment line_n hf;
		| c2=='/'
			= skip_line_comment line_n hf;
			# (_,hf) = fseek hf (-1) FSeekCur;
			= (True,c,line_n,hf);
		= (True,c,line_n,hf);
	where {
		skip_line_comment line_n hf
			# (ok,c,hf) = freadc hf;
			| not ok
				= (False,c,line_n,hf);
			| c=='\n'
				#! line_n=line_n+1;
				= read_char_and_skip_space line_n hf;
				= skip_line_comment line_n hf;
	}

skip_line line_n hf
	# (ok,c,hf) = freadc hf;
	| not ok
		= (line_n,hf);
	| c=='\n'
		= (line_n+1,hf);
		= skip_line line_n hf;

is_ident_character c 
	# cl=(toInt c) bitor 0x20;
	| (cl>=toInt 'a' && cl<=toInt 'z') || (c>='0' && c<='9') || c=='_'
		= True;
		= False;

read_identifier_c s c hf
	| is_ident_character c
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (False,s,nc,hf);
			= read_identifier_c s nc hf;
		= (True,s,c,hf); 

read_identifier_and_skip_space_c c line_n hf
	# (ok,identifier,c,hf) = read_identifier_c "" c hf;
	| not ok
		= (ok,"",c,line_n,hf);	
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	= (ok,identifier,c,line_n,hf);

read_identifier_and_skip_space line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (False,"",c,line_n,hf);
		= read_identifier_and_skip_space_c c line_n hf;

read_integer_or_char c l hf
	| c=='\''
		# (ok,c2,hf) = freadc hf;
		| not ok
			= (ok,"",c2,l,hf);
		# (ok,c,hf) = freadc hf;
		| not ok
			= (ok,"",c,l,hf);
		| c<>'\''
			= (True,"",c,[ErrorHFile "\' expected" 0:l],hf);
			# (ok,c,hf) = freadc hf;
			= (True,"'"+++toString c2+++"'",c,l,hf);
	| c=='+' || c=='-'
		# (ok,nc,hf) = freadc hf;
		| not ok
			= (ok,"",nc,l,hf);
		# (ok,s,nc,l,hf) = read_integer_denotation "" nc l hf;
		= (ok,toString c+++s,nc,l,hf)
		= read_integer_denotation "" c l hf;

read_integer_denotation s c l hf
	| c=='0'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (False,s,nc,l,hf);
			| nc=='x'
				# (ok,nnc,hf) = freadc hf;
				  s = (s+++toString nc);
				| not ok
					= (True,s,nc,[ErrorHFile "Hex digit expected" 0:l],hf);
					= read_hex_digits s c l hf;
				= read_more_digits s nc l hf;
		= read_digits s c l hf;

read_digits s c l hf
	| c>='0' && c<='9'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (ok,s,nc,l,hf);
			= read_more_digits s nc l hf;
		= (True,s,c,[ErrorHFile "Digit expected" 0:l],hf);

read_more_digits s c l hf
	| c>='0' && c<='9'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (False,s,nc,l,hf);
			= read_more_digits s nc l hf;
		= (True,s,c,l,hf); 

read_hex_digits s c l hf
	| c>='0' && c<='9' || c>='a' && c<='f' || c>='A' && c<='F'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (ok,s,nc,l,hf);
			= read_more_hex_digits s nc l hf;
		= (True,s,c,[ErrorHFile "Hex digit expected" 0:l],hf);

read_more_hex_digits s c l hf
	| c>='0' && c<='9' || c>='a' && c<='f' || c>='A' && c<='F'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (False,s,nc,l,hf);
			= read_more_hex_digits s nc l hf;
		= (True,s,c,l,hf); 

read_declaration_specifiers_and_pointers type l c line_n hf
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (ok,type,l,c,line_n,hf);
	| c=='*'
		# (ok,c,hf) = freadc hf;
		| not ok
			= (ok,type,l,c,line_n,hf);
			= read_declaration_specifiers_and_pointers (type+++"*") l c line_n hf;
		= (ok,type,l,c,line_n,hf);

read_optional_struct_name_and_declaration_specifiers_and_pointers type=:"struct" l c line_n hf
	| is_ident_character c
		# (ok,struct_name,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
		| not ok
			= (ok,type,l,c,line_n,hf);
			= read_declaration_specifiers_and_pointers (type+++" "+++struct_name) l c line_n hf;
		= (True,"",[ErrorHFile "struct name expected" line_n:l],c,line_n,hf);
read_optional_struct_name_and_declaration_specifiers_and_pointers type l c line_n hf
	= read_declaration_specifiers_and_pointers type l c line_n hf;

read_char_space_declaration_specifiers_and_pointers l line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (ok,"",l,c,line_n,hf);
	# (ok,identifier,c,line_n,hf)=read_identifier_and_skip_space_c c line_n hf;
	| not ok
		= (ok,identifier,l,c,line_n,hf);
		= read_optional_struct_name_and_declaration_specifiers_and_pointers identifier l c line_n hf;

read_function_argument_types l line_n hf
	# (ok,identifier,l,c,line_n,hf) = read_char_space_declaration_specifiers_and_pointers l line_n hf
	| not ok || identifier=="void"
		= (ok,c,NilFunctionArgumentTypes,hf);
		= read_more_function_argument_types_s identifier l c line_n hf;

read_more_function_argument_types l line_n hf
	# (ok,identifier,l,c,line_n,hf) = read_char_space_declaration_specifiers_and_pointers l line_n hf
	| not ok
		= (ok,c,NilFunctionArgumentTypes,hf);
		= read_more_function_argument_types_s identifier l c line_n hf;

read_more_function_argument_types_s identifier l c line_n hf
	| size identifier>0
		| c==','
			# (ok,c,argument_types,hf) = read_more_function_argument_types l line_n hf;
			= (ok,c,FunctionArgumentType identifier NoArgumentName argument_types,hf);
		// read optional argument name
		| is_ident_character c
			# (ok,argument_name,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
			| not ok
				= (False,c,FunctionArgumentType identifier NoArgumentName NilFunctionArgumentTypes,hf);
			| c==','
				# (ok,c,argument_types,hf) = read_more_function_argument_types l line_n hf;
				= (ok,c,FunctionArgumentType identifier (ArgumentName argument_name) argument_types,hf);
				= (True,c,FunctionArgumentType identifier (ArgumentName argument_name) NilFunctionArgumentTypes,hf);

			= (True,c,FunctionArgumentType identifier NoArgumentName NilFunctionArgumentTypes,hf);
		= (True,c,NilFunctionArgumentTypes,hf);

read_function_type type c l line_n hf
	| size type>0
		# (ok,type,l,c,line_n,hf) = read_optional_struct_name_and_declaration_specifiers_and_pointers type l c line_n hf;
		| not ok
			= (l,hf);
		# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
		| not ok
			= (l,hf);	
		| size identifier>0
			| c=='('
				# (ok,c,argument_types,hf) = read_function_argument_types l line_n hf;
				| not ok
					= (l,hf)
				| c==')'
					# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
					| not ok
						= (l,hf);
					# l = [CFunction identifier type argument_types "":l];
					| c==';'
						= read_h_file l line_n hf;
						= read_h_file [ErrorHFile "; expected" line_n:l] line_n hf;
					= read_h_file_c [ErrorHFile ") expected" line_n:l] c line_n hf;
				= read_h_file_c [ErrorHFile "( expected" line_n:l] c line_n hf;
			= read_h_file_c [ErrorHFile "function name expected" line_n:l] c line_n hf;
		= read_h_file [ErrorHFile ("Declaration expected: "+++toString c) line_n:l] line_n hf;

skip_to end_c line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (line_n,hf);
	| c==end_c
		= (line_n,hf);
	| c=='\n'
		#! line_n=line_n+1;
		= skip_to end_c line_n hf;
	| c=='('
		= skip_to end_c <:: skip_to ')' line_n hf;
	| c=='{'
		= skip_to end_c <:: skip_to '}' line_n hf;
		= skip_to end_c line_n hf;


read_typedef l c line_n hf
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (eof_error line_n l,hf);
	# (ok,type,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
	| not ok
		= (eof_error line_n l,hf);
	| size type>0
		| type=="enum"
			= read_typedef_enum l c line_n hf;
		# (ok,type,l,c,line_n,hf) = read_optional_struct_name_and_declaration_specifiers_and_pointers type l c line_n hf;
		| not ok
			= (eof_error line_n l,hf);
		# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
		| not ok
			= (eof_error line_n l,hf);
		| size identifier>0
			# (ok,c,line_n,hf) = skip_space c line_n hf;
			| not ok
				= (eof_error line_n l,hf);
			# l = [CTypeDefinition identifier type:l];
			| c==';'
				= read_h_file l line_n hf;
				= read_h_file [ErrorHFile "; expected" line_n:l] line_n hf;
			= read_h_file_c [ErrorHFile ("Identifier expected: "+++toString c) line_n:l] c line_n hf;
		= read_h_file [ErrorHFile ("Type expected: "+++toString c) line_n:l] line_n hf;

read_enum l c line_n hf
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (True,[],eof_error line_n l,line_n,hf);
	| c<>'{'
		= (False,[],[ErrorHFile ("{ expected: "+++toString c) line_n:l],line_n,hf);
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (True,[],eof_error line_n l,line_n,hf);
	# (enum_constants,l,c,line_n,hf) = read_enum_constants l c line_n hf;
	| c<>'}'
		= (False,enum_constants,[ErrorHFile ("} expected: "+++toString c) line_n:l],line_n,hf);
		= (False,enum_constants,l,line_n,hf);

read_enum_semicolon_and_h_file l c line_n hf
	# (eof,enum_constants,l,line_n,hf) = read_enum l c line_n hf;
	| eof
		= (l,hf);
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (eof_error line_n l,hf);
	| c<>';'
		= read_h_file [ErrorHFile ("; expected: "+++toString c) line_n:l] line_n hf;
		= read_h_file [Enum enum_constants:l] line_n hf;

read_typedef_enum l c line_n hf
	# (eof,enum_constants,l,line_n,hf) = read_enum l c line_n hf;
	| eof
		= (l,hf);
	# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space line_n hf;
	| not ok
		= (eof_error line_n l,hf);
	| size identifier<=0
		= read_h_file_c [ErrorHFile ("Identifier expected: "+++toString c) line_n:l] c line_n hf;
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (eof_error line_n l,hf);
	# l = [Enum enum_constants:l];
	| c==';'
		= read_h_file l line_n hf;
		= read_h_file [ErrorHFile "; expected" line_n:l] line_n hf;

read_enum_constants l c line_n hf
	| is_ident_character c
		# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
		| c==','
			# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
			| not ok
				= ([identifier],eof_error line_n l,c,line_n,hf);
				# (enum_constants,l,c,line_n,hf) = read_enum_constants l c line_n hf;
				= ([identifier:enum_constants],l,c,line_n,hf);
			= ([identifier],l,c,line_n,hf);
		= ([],l,c,line_n,hf);

/*
skip_struct l line_n hf
	= read_h_file l <:: skip_to ';' line_n hf;
*/

eof_error line_n l = [ErrorHFile "Unexpected end of file" line_n:l];

want_char wanted_c l line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (False,ok,eof_error line_n l,c,line_n,hf);
	| c<>wanted_c
		= (False,ok,[ErrorHFile (toString wanted_c+++" expected, got "+++toString c) line_n:l],c,line_n,hf);
		= (True,ok,l,c,line_n,hf);

want_char_c wanted_c c l line_n hf
	| c<>wanted_c
		= (False,[ErrorHFile (toString wanted_c+++" expected, got "+++toString c) line_n:l],c,line_n,hf);
		= (True,l,c,line_n,hf);

read_clean_type_def l line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (ok,eof_error line_n l,c,line_n,hf);
	| c=='*'
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,eof_error line_n l,c,line_n,hf);
			= read_clean_type_def2 IsUnique l c line_n hf;
			= read_clean_type_def2 IsNotUnique l c line_n hf;

read_clean_type_def2 is_unique_type l c line_n hf
	# (ok,type_name,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
	| not ok
		= (ok,eof_error line_n l,c,line_n,hf);
	| size type_name==0
		= (ok,[ErrorHFile ("Type name expected, got "+++toString c) line_n:l],c,line_n,hf);
	| c == '('
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,l,c,line_n,hf);
		# (ok,clean_type,_,l,c,line_n,hf) = read_clean_type_def_rhs False l c line_n hf
		| isCleanErrorType clean_type
			= (ok,l,c,line_n,hf);
		# (parse_ok,ok,l,c,line_n,hf) = want_char ')' l line_n hf;
		| not parse_ok
			= (ok,l,c,line_n,hf);
			= (ok,[CleanTypeDefinition type_name clean_type is_unique_type TypeDefSemiAbs:l],c,line_n,hf);
	# (ok,clean_type,abs,l,c,line_n,hf) = read_clean_type_def_rhs True l c line_n hf
	| isCleanErrorType clean_type
		= (ok,l,c,line_n,hf);
		# abs_kind = if abs TypeDefAbs TypeDefNormal
	= (ok,[CleanTypeDefinition type_name clean_type is_unique_type abs_kind:l],c,line_n,hf);

read_clean_type_def_rhs abs_allowed l c line_n hf
	# (parse_ok,l,c,line_n,hf) = want_char_c ':' c l line_n hf;
	| not parse_ok
		= (parse_ok,CleanErrorType,False,l,c,line_n,hf);
	# (parse_ok,ok,l,c,line_n,hf) = want_char '=' l line_n hf;
	| not parse_ok
		= (ok,CleanErrorType,False,l,c,line_n,hf);
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (ok,CleanErrorType,False,l,c,line_n,hf);
	| abs_allowed && c<>'='
		# (ok,clean_type,c,l,line_n,hf) = read_attr_clean_type c l line_n hf;
		= (ok,clean_type,True,l,c,line_n,hf);
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (ok,CleanErrorType,False,eof_error line_n l,c,line_n,hf);
	# (ok,clean_type,c,l,line_n,hf) = read_attr_clean_type c l line_n hf;
	| not ok
		= (ok,CleanErrorType,False,l,c,line_n,hf);
	= (ok,clean_type,False,l,c,line_n,hf);

isNilCleanTypes NilCleanTypes = True;
isNilCleanTypes _ = False;

read_attr_clean_type c l line_n hf
	| c == '*'
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,CleanErrorType,c,l,line_n,hf);
			= read_clean_type IsUnique c l line_n hf;
		= read_clean_type IsNotUnique c l line_n hf;

read_clean_type is_unique c l line_n hf
	| c=='('
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,CleanErrorType,c,l,line_n,hf);
			# (ok,clean_types,c,l,line_n,hf) = read_clean_tuple_argument_types c l line_n hf;
			| not ok
				= (ok,CleanErrorType,c,eof_error line_n l,line_n,hf);
			| isNilCleanTypes clean_types
				= (ok,CleanErrorType,c,l,line_n,hf)
			| c==')'
				# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
				| not ok
					= (ok,CleanErrorType,c,eof_error line_n l,line_n,hf);
					= (ok,CleanTupleType is_unique clean_types,c,l,line_n,hf)
				= (ok,CleanErrorType,c,l,line_n,hf);
	| c=='{'
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok || c<>'#'
			= (False,CleanErrorType,c,l,line_n,hf);
		# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space line_n hf;
		| not ok
			= (False,CleanErrorType,c,eof_error line_n l,line_n,hf);
		| identifier<>"Int" && identifier<>"Real" && identifier<>"Char"
			= (False,CleanErrorType,c,l,line_n,hf);
		| c<>'}'
			= (False,CleanErrorType,c,l,line_n,hf);				
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (False,CleanErrorType,c,eof_error line_n l,line_n,hf);
			= (True,CleanType is_unique ("{#"+++identifier+++"}"),c,l,line_n,hf);

		# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
		| not ok
			= (ok,CleanErrorType,c,eof_error line_n l,line_n,hf);
		| size identifier>0
			= (ok,CleanType is_unique identifier,c,l,line_n,hf);
	= (ok,CleanErrorType,c,l,line_n,hf);

isCleanErrorType CleanErrorType = True;
isCleanErrorType t = False;

read_clean_function_argument_types c l line_n hf
	# (ok,clean_type,c,l,line_n,hf) = read_attr_clean_type c l line_n hf;
	| isCleanErrorType clean_type
		= (ok,NilCleanTypes,c,line_n,hf);
	 	# (ok,clean_types,c,line_n,hf) = read_clean_function_argument_types c l line_n hf;
		= (ok,CleanTypes clean_type clean_types,c,line_n,hf);

read_clean_tuple_argument_types c l line_n hf
	# (ok,clean_type,c,l,line_n,hf) = read_attr_clean_type c l line_n hf;
	| not ok
		= (ok,NilCleanTypes,c,l,line_n,hf);
	| isCleanErrorType clean_type
		= (ok,NilCleanTypes,c,[ErrorHFile "Type expected" line_n:l],line_n,hf);
	| c==','
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,NilCleanTypes,c,eof_error line_n l,line_n,hf);
	 	# (ok,clean_types,c,l,line_n,hf) = read_clean_tuple_argument_types c l line_n hf;
		= (ok,CleanTypes clean_type clean_types,c,l,line_n,hf);
		= (ok,CleanTypes clean_type NilCleanTypes,c,l,line_n,hf);

read_clean_function function_name l line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (ok,eof_error line_n l,c,line_n,hf);
	# (ok,argument_types,c,line_n,hf) = read_clean_function_argument_types c l line_n hf;
	| not ok
		= (ok,eof_error line_n l,c,line_n,hf);
	| c=='-'
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,eof_error line_n l,c,line_n,hf);
		| c=='>'
			# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
			| not ok
				= (ok,eof_error line_n l,c,line_n,hf);
			# (ok,result_type,c,l,line_n,hf) = read_attr_clean_type c l line_n hf;
			| not ok
				= (ok,eof_error line_n l,c,line_n,hf);
			| isCleanErrorType result_type
				= (ok,[ErrorHFile "Function result type expected" line_n:l],c,line_n,hf);
				= (ok,[CleanFunction function_name argument_types result_type:l],c,line_n,hf);
			= (ok,[ErrorHFile ("> expected, got "+++toString c) line_n:l],c,line_n,hf);
	| c==')'
		= case argument_types of {
			(CleanTypes type NilCleanTypes)
				-> (ok,[CleanFunction function_name NilCleanTypes type:l],c,line_n,hf);
			_
				-> (ok,[ErrorHFile ("-> expected, got "+++toString c) line_n:l],c,line_n,hf);
		}
		= (ok,[ErrorHFile ("-> or ) expected, got "+++toString c) line_n:l],c,line_n,hf);

read_import_file_name_and_skip_space_c s c line_n hf
	| toInt c>=33 && c<>',' && c<>')' && c<>';'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (False,s,nc,line_n,hf);
			= read_import_file_name_and_skip_space_c s nc line_n hf;
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	= (ok,s,c,line_n,hf);

read_clean_import l c line_n hf
 	# (ok,file_name,c,line_n,hf) = read_import_file_name_and_skip_space_c "" c line_n hf;
 	| not ok
 		= (ok,eof_error line_n l,c,line_n,hf);
 	| size file_name==0
 		= (ok,[ErrorHFile "Header name expected" line_n:l],c,line_n,hf);
 	# l=[Import file_name:l];
 	| c==','
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (ok,eof_error line_n l,c,line_n,hf);
 		= read_clean_import l c line_n hf;
	= (True,l,c,line_n,hf);

read_clean l c line_n hf
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (ok,eof_error line_n l,line_n,hf);
	| c<>'('
		= (ok,[ErrorHFile ("( expected, got "+++toString c) line_n:l],line_n,hf);
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		= read_clean_lines ok c l line_n hf;

	read_clean_lines ok c l line_n hf
		# (ok,l,c,line_n,hf)=read_clean_line ok c l line_n hf;
		# (ok,c,line_n,hf) = skip_space c line_n hf;
		| not ok
			= (ok,eof_error line_n l,line_n,hf);
		| c==')'
			= (ok,l,line_n,hf);
		| c==';'
			# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
			| c==')'
				= (ok,l,line_n,hf);
				= read_clean_lines ok c l line_n hf;
			= (ok,[ErrorHFile (") or ; expected, got "+++toString c) line_n:l],line_n,hf);

	read_clean_line ok c l line_n hf
		| not ok
			= (ok,eof_error line_n l,c,line_n,hf);
		| c==':'
			# (parse_ok,ok,l,c,line_n,hf) = want_char ':' l line_n hf;
			| not parse_ok
				= (ok,l,c,line_n,hf);
				= read_clean_type_def l line_n hf;
		# (ok,function_name,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
		| not ok
			= (ok,eof_error line_n l,c,line_n,hf);
		| size function_name==0
			= (ok,[ErrorHFile ("import or clean function name expected, got "+++toString c) line_n:l],c,line_n,hf);
		| function_name=="import"
			= read_clean_import l c line_n hf;
		# (parse_ok,l,c,line_n,hf) = want_char_c ':' c l line_n hf;
		| not parse_ok
			= (True,l,c,line_n,hf);
		# (parse_ok,ok,l,c,line_n,hf) = want_char ':' l line_n hf;
		| not parse_ok
			= (ok,l,c,line_n,hf);
			= read_clean_function function_name l line_n hf;

read_h_file_name line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (ok,"",c,line_n,hf);
	# (ok,file_name,c2,hf) = read_h_file_name_c "" c hf;
	= (ok,file_name,c2,line_n,hf);

read_h_file_name_c s c hf
	| toInt c>=32 && c<>'>' && c<>'"'
		# (ok,nc,hf) = freadc hf;
		  s = (s+++toString c);
		| not ok
			= (False,s,nc,hf);
			= read_h_file_name_c s nc hf;
		= (True,s,c,hf); 

read_h_file_c l c line_n hf
	# (ok,c,line_n,hf) = skip_space c line_n hf;
	| not ok
		= (eof_error line_n l,hf);
		= read_h_file_no_space_c l c line_n hf;

read_h_file l line_n hf
	# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
	| not ok
		= (l,hf);
		= read_h_file_no_space_c l c line_n hf;

read_h_file_no_space_c l c line_n hf
	| c=='#'
		# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space line_n hf;
		| not ok
			= (l,hf);
		| identifier=="define"
			# (ok,c,line_n,hf) = skip_space c line_n hf;
			| not ok
				= (l,hf);
			# (ok,variable,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
			| not ok
				= (l,hf);
			# (ok,integer,c,l,hf) = read_integer_or_char c l hf;
			| not ok
				= (l,hf);
				= read_h_file_c [Constant variable integer:l] c line_n hf;
		| identifier=="include"
			# (ok,c1,line_n,hf) = skip_space c line_n hf;
			| not ok
				= (eof_error line_n l,hf);
			| c1<>'"' && c1<>'<'
				= ([ErrorHFile ("\" or < expected, got: "+++toString c1) line_n:l],hf);
			# (ok,file_name,c2,line_n,hf) = read_h_file_name line_n hf;
			| not ok
				= (eof_error line_n l,hf);
			| size file_name==0
				= ([ErrorHFile "File name expected" line_n:l],hf);
			# c1=if (c1=='<') '>' c1;
			| c1<>c2
				= ([ErrorHFile (toString c1+++" expected") line_n:l],hf);
				= read_h_file [Include file_name:l] line_n hf;
		| identifier=="pragma"
			# (line_n,hf) = skip_line line_n hf;
			= read_h_file l line_n hf
			= read_h_file_c [ErrorHFile "define expected" line_n:l] c line_n hf;
	# (ok,identifier,c,line_n,hf) = read_identifier_and_skip_space_c c line_n hf;
	| not ok
		= (l,hf);
	| identifier=="typedef"
		= read_typedef l c line_n hf;
	| identifier=="enum"
		= read_enum_semicolon_and_h_file l c line_n hf;
/*
	| identifier=="struct"
		= skip_struct l line_n hf;
*/
	| identifier=="Clean"
		# (ok,l,line_n,hf) = read_clean l c line_n hf;
		| not ok
			= (l,hf);
		# (ok,c,line_n,hf) = read_char_and_skip_space line_n hf;
		| not ok
			= (l,hf);
		| c==';'
			= read_h_file l line_n hf;
			= read_h_file_c l c line_n hf;
		= read_function_type identifier c l line_n hf;

find_clean_function function_name f=:[CleanFunction name _ _:l]
	| name==function_name
		= f;
		= find_clean_function function_name l;
find_clean_function function_name [_:l]
	= find_clean_function function_name l;
find_clean_function function_name []
	= [];

find_clean_type type_name f=:[CleanTypeDefinition name _ _ _:l]
	| name==type_name
		= f;
		= find_clean_type type_name l;
find_clean_type type_name [_:l]
	= find_clean_type type_name l;
find_clean_type type_name []
	= [];

find_c_type type_name f=:[CTypeDefinition name _:l]
	| name==type_name
		= f;
		= find_c_type type_name l;
find_c_type type_name [_:l]
	= find_c_type type_name l;
find_c_type type_name []
	= [];

occurs_in_list a []
	= False;
occurs_in_list a [b:l]
	| a==b
		= True;
		= occurs_in_list a l;

arity_of_clean_type type h_all_types
	# (n,cyclic_synonyms)=add_arity_of_clean_type type 0 [] [] h_all_types;
	= n;

arity_and_cycles_of_clean_type type h_all_types
	=add_arity_of_clean_type type 0 [] [] h_all_types;

arity_of_clean_types types h_all_types
	# (n,cyclic_synonyms)=add_arity_of_clean_types types 0 [] [] h_all_types;
	= n;

arity_and_cycles_of_clean_types types h_all_types
	= add_arity_of_clean_types types 0 [] [] h_all_types;

	add_arity_of_clean_type (CleanType _ clean_type_name) n expanded_synonyms cyclic_synonyms h_all_types
		| occurs_in_list clean_type_name expanded_synonyms
			= (n+1,[clean_type_name:cyclic_synonyms]);
			= case (find_clean_type clean_type_name h_all_types) of {
				[CleanTypeDefinition _ clean_type _ _:_]
					-> add_arity_of_clean_type clean_type n [clean_type_name:expanded_synonyms] cyclic_synonyms h_all_types;
				_
					-> (n+1,cyclic_synonyms);
			  };
	add_arity_of_clean_type (CleanTupleType _ tuple_arguments) n expanded_synonyms cyclic_synonyms h_all_types
		= add_arity_of_clean_types tuple_arguments n expanded_synonyms cyclic_synonyms h_all_types;
	add_arity_of_clean_types NilCleanTypes n expanded_synonyms cyclic_synonyms h_all_types
		= (n,cyclic_synonyms);
	add_arity_of_clean_types (CleanTypes clean_type clean_types) n expanded_synonyms cyclic_synonyms h_all_types
		# (n,cyclic_synonyms) = add_arity_of_clean_type clean_type n expanded_synonyms cyclic_synonyms h_all_types;
		= add_arity_of_clean_types clean_types n expanded_synonyms cyclic_synonyms h_all_types;

add_arity_of_c_types NilFunctionArgumentTypes n
	= n;
add_arity_of_c_types (FunctionArgumentType argument_type _ argument_types) n
	= add_arity_of_c_types argument_types (add_arity_of_c_type argument_type n);

add_arity_of_c_type type_name n
	| type_name<>"void"
		= n+1;
		= n;

apply_to_first_of_2 f t :== let{ (a,b) = t; }in (f a,b);

take_n_arguments2 0 rest_arguments
	= (NilFunctionArgumentTypes,rest_arguments);
take_n_arguments2 n (FunctionArgumentType argument argument_name arguments)
	= apply_to_first_of_2 (FunctionArgumentType argument argument_name) (take_n_arguments2 (n-1) arguments);

take_n_arguments 0 _
	= NilFunctionArgumentTypes;
take_n_arguments n (FunctionArgumentType argument argument_name arguments)
	= FunctionArgumentType argument argument_name (take_n_arguments (n-1) arguments);

take_n_arguments n NilFunctionArgumentTypes
	= NilFunctionArgumentTypes;

write_icl_file module_name h_imports h_constants h_types h_functions h_all_types iclf
	= iclf
		>: fwrites "implementation module " >: fwrites module_name >: fwrites ";\n\n"
		>: write_imports h_imports
		>: write_icl_file_types h_types >: write_icl_file h_functions >: write_file_constants h_constants;
{
	write_icl_file [CFunction function_name result_type arguments extra_ccall_arg_types:h_info] iclf
		= case (find_clean_function function_name h_functions) of {
			[CleanFunction _ clean_argument_types clean_result_type:_]
				# n_clean_arguments = arity_of_clean_types clean_argument_types h_all_types;
				# n_clean_results = arity_of_clean_type clean_result_type h_all_types;
				# n_c_arguments = add_arity_of_c_types arguments (add_arity_of_c_type result_type 0);
				# n_extra_arguments_2 = n_clean_arguments+n_clean_results-n_c_arguments;
				# n_extra_arguments = n_extra_arguments_2 >> 1;
				# iclf = iclf
					>: fwritec '\n' >: fwrites function_name >: fwrites " :: "
					>: write_clean_function_type clean_argument_types clean_result_type >: fwrites ";\n"
					>: fwrites function_name >: fwrites " "
					>: write_clean_function_arguments 0 clean_argument_types >: fwrites " = code {\n"
				
					>: fwrites "\tccall " >: fwrites function_name >: fwrites " \"";
				# (result_c_arguments,iclf) = write_n_ccall_arguments (n_clean_arguments-n_extra_arguments) arguments iclf;
					with {
						write_n_ccall_arguments 0 arguments iclf
							= (arguments,iclf);
						write_n_ccall_arguments n NilFunctionArgumentTypes iclf
							= (NilFunctionArgumentTypes,iclf);
						write_n_ccall_arguments n (FunctionArgumentType s _ arguments) iclf
							= write_n_ccall_arguments (n-1) arguments (iclf >: write_ccall_type_char s h_all_types);
					}
				# iclf = iclf
					>: fwritec ':'
					>: write_ccall_type_char result_type h_all_types
					>: write_ccall_result_arguments result_c_arguments h_all_types
					>: fwrites extra_ccall_arg_types
					>: fwrites "\"\n}\n"
					>: write_c_function_type_in_icl_or_dcl_file result_type function_name arguments;

				-> write_icl_file h_info iclf;
			_
				-> write_icl_file h_info
					(iclf >: fwritec '\n'
					>: fwrites function_name >: fwrites " :: " >: write_clean_from_c_function_type arguments result_type >: fwrites ";\n"

					>: fwrites function_name >: fwritec ' ' >: write_clean_c_function_arguments 0 arguments  >: fwrites " = code {\n"
				
					>: fwrites "\tccall " >: fwrites function_name >: fwrites " \"" >: write_ccall_arguments arguments h_all_types
					>: fwritec ':' >: write_ccall_type_char result_type h_all_types >: fwrites "\"\n}\n"
					>: write_c_function_type_in_icl_or_dcl_file result_type function_name arguments
					);
			};
	write_icl_file [CleanFunction function_name argument_types result_type:h_info] iclf
		= write_icl_file h_info iclf;
	write_icl_file [] iclf
		= iclf;
}

	write_icl_file_types [CTypeDefinition s1 s2:h_info] clf
		= write_icl_file_types h_info clf;
	write_icl_file_types [CleanTypeDefinition s1 s2 is_unique_type _:h_info] clf
		# type = CleanTypeDefinition s1 s2 is_unique_type TypeDefNormal
		= write_icl_file_types h_info (write_clean_type_definition type clf);
	write_icl_file_types [] clf
		= clf;

	write_dcl_file_types [CTypeDefinition s1 s2:h_info] clf
		= write_dcl_file_types h_info clf;
	write_dcl_file_types [type:h_info] clf
		= write_dcl_file_types h_info (write_clean_type_definition type clf);
	write_dcl_file_types [] clf
		= clf;

	write_clean_type_definition (CleanTypeDefinition s1 s2 is_unique_type abs_kind) clf
		= clf	>: fwrites (if is_unique_type ":: *" ":: ")
				>: fwrites s1 >: write_clean_type_rhs s2 abs_kind >: fwrites ";\n";

	write_clean_type_rhs type_rhs abs_kind clf
		| abs_kind == TypeDefAbs
			= clf;
		| abs_kind == TypeDefSemiAbs
			= clf
				>: fwrites " (:== "
				>: write_clean_type type_rhs
				>: fwritec ')';
		| abs_kind == TypeDefNormal
			= clf
				>: fwrites " :== "
				>: write_clean_type type_rhs;

	write_file_constants [Constant s1 s2:h_info] iclf
		= write_file_constants h_info (iclf >: fwrites s1 >: fwrites ":==" >: fwrites s2 >: fwrites ";\n");
	write_file_constants [Enum enum_constants:h_info] iclf
		= write_file_constants h_info (iclf >: write_read_enum_constants enum_constants 0);
	write_file_constants [] iclf
		= iclf;

	write_read_enum_constants [] n iclf
		= iclf;
	write_read_enum_constants [v:vs] n iclf
		= write_read_enum_constants vs (n+1) (iclf >: fwrites v >: fwrites ":==" >: fwritei n >: fwrites ";\n");

write_clean_function_type NilCleanTypes result_type iclf
	= iclf >: write_clean_type result_type;
write_clean_function_type argument_types result_type iclf
	= iclf >: write_clean_argument_types ' ' argument_types >: fwrites " -> " >: write_clean_type result_type;

write_clean_argument_types separator_char NilCleanTypes iclf
	= iclf;
write_clean_argument_types separator_char (CleanTypes argument_type NilCleanTypes) iclf
	= iclf >: fwritec '!' >: write_clean_type argument_type;
write_clean_argument_types separator_char (CleanTypes argument_type argument_types) iclf
	= write_clean_argument_types separator_char argument_types (iclf
		>: fwritec '!' >: write_clean_type argument_type >: fwritec separator_char);

write_clean_type (CleanType is_unique s) iclf
	= iclf >: write_unq_attribute is_unique >: fwrites s;
write_clean_type (CleanTupleType is_unique t) iclf
	= iclf >: write_unq_attribute is_unique >: fwritec '(' >: write_clean_argument_types ',' t >: fwritec ')';
write_clean_type CleanErrorType iclf
	= iclf;

write_unq_attribute is_unique clf
	| is_unique
		= clf >: fwritec '*';
	= clf;

write_imports [] clf
	= clf;
write_imports [Import module_name:imports_and_includes] clf
	= write_imports imports_and_includes (clf >: fwrites "import " >: fwrites module_name >: fwrites ";\n");
write_imports [Include _:imports_and_includes] clf
	= write_imports imports_and_includes clf;

write_dcl_file module_name h_imports h_constants h_types h_functions dclf
	= dclf
		>: fwrites "definition module " >: fwrites module_name >: fwrites ";\n\n"
		>: write_imports h_imports
		>: write_dcl_file_types h_types >: write_dcl_file_functions h_functions >: write_file_constants h_constants;
{
	write_dcl_file_functions [CFunction function_name result_type arguments _:h_info] dclf
		= case (find_clean_function function_name h_functions) of {
			[CleanFunction _ clean_argument_types clean_result_type:_]
				# dclf = dclf
					>: fwrites function_name >: fwrites " :: "
					>: write_clean_function_type clean_argument_types clean_result_type >: fwrites ";\n"
					>: write_c_function_type_in_icl_or_dcl_file result_type function_name arguments;
				-> write_dcl_file_functions h_info dclf;
			_
				# dclf = dclf
					>: fwrites function_name >: fwrites " :: "
					>: write_clean_from_c_function_type arguments result_type >: fwrites ";\n"
					>: write_c_function_type_in_icl_or_dcl_file result_type function_name arguments;
				-> write_dcl_file_functions h_info dclf;
		  };
	write_dcl_file_functions [CleanFunction s1 at rt:h_info] dclf
		= write_dcl_file_functions h_info dclf;
	write_dcl_file_functions [] dclf
		= dclf;
}

write_c_function_type_in_icl_or_dcl_file result_type function_name arguments f
	= f >: fwrites "// " >: fwrites result_type >: fwritec ' ' >: fwrites function_name >: fwrites " (" >: write_c_function_arguments arguments >: fwrites ");\n";

write_c_function_arguments NilFunctionArgumentTypes iclf
	= iclf;
write_c_function_arguments (FunctionArgumentType s optional_argument_name NilFunctionArgumentTypes) iclf
	= iclf >: fwrites s >: write_c_optional_argument_name optional_argument_name;
write_c_function_arguments (FunctionArgumentType s optional_argument_name arguments) iclf
	= write_c_function_arguments arguments (iclf >: fwrites s >: write_c_optional_argument_name optional_argument_name >: fwritec ',');

write_c_optional_argument_name NoArgumentName iclf
	=	iclf;
write_c_optional_argument_name (ArgumentName argument_name) iclf
	=	iclf >: fwrites " " >: fwrites argument_name;

write_clean_from_c_function_type NilFunctionArgumentTypes result_type clf
	= clf	>: fwrites (c_type_to_clean_type result_type);
write_clean_from_c_function_type arguments result_type clf
	= clf	>: write_clean_from_c_function_argument_types arguments  >: fwrites " -> "
			>: fwrites (c_type_to_clean_type result_type);

write_clean_from_c_function_argument_types NilFunctionArgumentTypes iclf
	= iclf;
write_clean_from_c_function_argument_types (FunctionArgumentType s _ NilFunctionArgumentTypes) iclf
	= iclf >: fwritec '!' >: fwrites (c_type_to_clean_type s);
write_clean_from_c_function_argument_types (FunctionArgumentType s _ arguments) iclf
	= write_clean_from_c_function_argument_types arguments
		(iclf >: fwritec '!' >: fwrites (c_type_to_clean_type s) >: fwritec ' ');

c_type_to_clean_type "int"
	= "Int";
c_type_to_clean_type "double"
	= "Real";
c_type_to_clean_type "CleanString"
	= "{#Char}";
c_type_to_clean_type "CleanIntArray"
	= "{#Int}";
c_type_to_clean_type "CleanRealArray"
	= "{#Real}";
c_type_to_clean_type "CleanCharArray"
	= "{#Char}";
c_type_to_clean_type t
	# s=size t;
	| s>0 && t.[s-1]=='*'
//		= c_type_to_clean_type (t % (0,s-2)) +++ "Ptr";
		= "Int";
		= t;

expand_typedefs_in_type c_type h_types = expand_typedefs_in_type_except c_type h_types [];

expand_typedefs_in_type_to_typedef c_type typedef_name h_types = expand_typedefs_in_type_except c_type h_types [typedef_name];

expand_typedefs_in_type_except c_type h_types already_expanded_typedefs
	| c_type=="int" || c_type=="char" || c_type=="double" || c_type=="CleanString" || c_type=="CleanIntArray" || c_type=="CleanRealArray" || c_type=="CleanCharArray"
		= c_type;
	| occurs_in_list c_type already_expanded_typedefs
		= c_type;
		= case (find_c_type c_type h_types) of {
			[CTypeDefinition _ expanded_c_type:_]
				-> expand_typedefs_in_type_except expanded_c_type h_types [c_type:already_expanded_typedefs];
			_
				-> c_type;
		};

is_basic_clean_type clean_type_name
	=	clean_type_name=="Int" || clean_type_name=="Char" || clean_type_name=="Bool" || clean_type_name=="Real"
		|| clean_type_name=="String" || clean_type_name=="World";

basic_clean_and_c_types_ok c_type clean_type_name
	=	(c_type=="int" && (clean_type_name=="Int" || clean_type_name=="Char" || clean_type_name=="Bool")) ||
		(c_type=="char" && clean_type_name=="Char") ||
		(c_type=="double" && clean_type_name=="Real") ||
		(c_type=="CleanString" && clean_type_name=="String") ||
		(c_type=="CleanIntArray" && clean_type_name=="{#Int}") ||
		(c_type=="CleanRealArray" && clean_type_name=="{#Real}") ||
		(c_type=="CleanCharArray" && clean_type_name=="{#Char}") ||
		(size c_type>0 && c_type.[size c_type-1]=='*' && clean_type_name=="Int");

basic_clean_and_c_result_types_ok c_type clean_type_name
	=	(c_type=="int" && (clean_type_name=="Int" || clean_type_name=="Char" || clean_type_name=="Bool")) ||
		(c_type=="char" && clean_type_name=="Char") ||
		(c_type=="double" && clean_type_name=="Real") ||
		(c_type=="CleanString" && clean_type_name=="String") ||
		(size c_type>0 && c_type.[size c_type-1]=='*' && clean_type_name=="Int");

remove_pointer_from_c_type c_type all_h_info
	# size_c_type=size c_type;
	| size_c_type>0 && c_type.[size_c_type-1]=='*'
		= (True,c_type % (0,size_c_type-2));
		= case (find_c_type c_type all_h_info) of {
			[CTypeDefinition _ expanded_c_type:_]
				# size_expanded_c_type=size expanded_c_type;
				| size_expanded_c_type>0 && expanded_c_type.[size_c_type-1]=='*'
					-> (True,expanded_c_type % (0,size_expanded_c_type-2));
			_
				-> (False,c_type);
		  };

write_clean_c_function_arguments argument_n NilFunctionArgumentTypes iclf
	= iclf;
write_clean_c_function_arguments argument_n (FunctionArgumentType _ _ NilFunctionArgumentTypes) iclf
	= iclf >: fwritec 'a' >: fwritei argument_n;
write_clean_c_function_arguments argument_n (FunctionArgumentType _ _ arguments) iclf
	= write_clean_c_function_arguments (argument_n+1) arguments
		(iclf >: fwritec 'a' >: fwritei argument_n >: fwritec ' ');

write_clean_function_arguments argument_n NilCleanTypes iclf
	= iclf;
write_clean_function_arguments argument_n (CleanTypes _ NilCleanTypes) iclf
	= iclf >: fwritec 'a' >: fwritei argument_n;
write_clean_function_arguments argument_n (CleanTypes _ arguments) iclf
	= write_clean_function_arguments (argument_n+1) arguments
		(iclf >: fwritec 'a' >: fwritei argument_n >: fwritec ' ');

write_ccall_arguments NilFunctionArgumentTypes h_types iclf
	= iclf;
write_ccall_arguments (FunctionArgumentType s _ arguments) h_types iclf
	= write_ccall_arguments arguments h_types (iclf >: write_ccall_type_char s h_types);

write_ccall_result_arguments NilFunctionArgumentTypes h_types iclf
	= iclf;
write_ccall_result_arguments (FunctionArgumentType s _ arguments) h_types iclf
	= write_ccall_result_arguments arguments h_types (iclf >: write_ccall_result_type_char s h_types);

write_ccall_result_type_char s h_types clf
	| size s>0 && s.[size s-1]=='*'
		= write_ccall_type_char (s % (0,size s-2)) h_types clf;
	# s=expand_typedefs_in_type s h_types;
	| size s>0 && s.[size s-1]=='*'
		= write_ccall_type_char (s % (0,size s-2)) h_types clf;	

write_ccall_type_char s h_types clf
	# s=expand_typedefs_in_type s h_types;
	| s=="int" || (size s>0 && s.[size s-1]=='*')
		= fwritec 'I' clf;
	| s=="char"
		= fwritec 'I' clf;
	| s=="double"
		= fwritec 'R' clf;
	| s=="void"
		= fwritec 'V' clf;
	| s=="CleanString"
		= fwritec 'S' clf;
	| s=="CleanIntArray" || s=="CleanRealArray"
		= fwritec 'A' clf;
	| s=="CleanCharArray"
		= fwritec 's' clf;

:: HF = {
			hf_constants::![HDefinition],
			hf_types::![HDefinition],
			hf_functions::![HDefinition],
			hf_includes_and_imports::![HDefinition],
			hf_all_types::![HDefinition],
			hf_errors::![HError]
		};

reverse_h_info [n=:Constant s1 s2:h_info] hf
	= reverse_h_info h_info {hf & hf_constants=[n:hf.hf_constants]};
reverse_h_info [n=:Enum enum_constants:h_info] hf
	= reverse_h_info h_info {hf & hf_constants=[n:hf.hf_constants]};
reverse_h_info [n=:CFunction s1 s2 a extra_ccall_arg_types:h_info] hf
	= reverse_h_info h_info {hf & hf_functions=[n:hf.hf_functions]};
reverse_h_info [n=:CleanFunction s1 at rt:h_info] hf
	= reverse_h_info h_info {hf & hf_functions=[n:hf.hf_functions]};
reverse_h_info [n=:CTypeDefinition s1 s2:h_info] hf
	= reverse_h_info h_info {hf & hf_types=[n:hf.hf_types]};
reverse_h_info [n=:CleanTypeDefinition s1 s2 is_unique_type _:h_info] hf
	= reverse_h_info h_info {hf & hf_types=[n:hf.hf_types]};
reverse_h_info [ErrorHFile error_s line_n:h_info] hf
	= reverse_h_info h_info {hf & hf_errors=[HError error_s line_n:hf.hf_errors]};
reverse_h_info [n=:Include file_name:h_info] hf
	= reverse_h_info h_info {hf & hf_includes_and_imports=[n:hf.hf_includes_and_imports]};
reverse_h_info [n=:Import file_name:h_info] hf
	= reverse_h_info h_info {hf & hf_includes_and_imports=[n:hf.hf_includes_and_imports]};
reverse_h_info [] hf
	= hf;

append_clean_types NilCleanTypes types = types;
append_clean_types (CleanTypes clean_type clean_types) types = CleanTypes clean_type (append_clean_types clean_types types);

check_interface_function_argument_types function_name clean_argument_types c_arguments all_h_info errors
	= check_interface_function_argument_types clean_argument_types c_arguments errors NilCleanTypes;
{
	check_interface_function_argument_types clean_argument_types NilFunctionArgumentTypes errors extra_clean_arguments
		= (errors,clean_argument_types);
	check_interface_function_argument_types NilCleanTypes _ errors extra_clean_arguments
		= (errors,extra_clean_arguments);
	check_interface_function_argument_types (CleanTypes clean_type clean_argument_types) (FunctionArgumentType c_type _ c_arguments) errors extra_clean_arguments
		# (c_arguments,errors,extra_clean_arguments) = check_interface_type clean_type c_type c_arguments errors extra_clean_arguments;
		= check_interface_function_argument_types clean_argument_types c_arguments errors extra_clean_arguments;

	check_interface_type (CleanType _ clean_type_name) c_type c_arguments errors extra_clean_arguments
		| is_c_typedef clean_type_name all_h_info && expand_typedefs_in_type_to_typedef c_type clean_type_name all_h_info<>clean_type_name
			= (c_arguments,[HError ("Error in function type of: "+++function_name) 0: errors],extra_clean_arguments);

			# c_type = expand_typedefs_in_type c_type all_h_info;
			| basic_clean_and_c_types_ok c_type clean_type_name
				= (c_arguments,errors,extra_clean_arguments);
				= case (find_clean_type clean_type_name all_h_info) of {
					[CleanTypeDefinition _ clean_type _ _:_]
						-> check_interface_type clean_type c_type c_arguments errors extra_clean_arguments;
					_
						-> (c_arguments,[HError ("Error in function type of: "+++function_name) 0:errors],extra_clean_arguments);
				};
	check_interface_type (CleanTupleType _ (CleanTypes (CleanType _ first_tuple_element_type_name) clean_tuple_argument_types)) c_type c_arguments errors extra_clean_arguments
		# c_type = expand_typedefs_in_type c_type all_h_info;
		| basic_clean_and_c_types_ok c_type first_tuple_element_type_name
			= check_interface_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_arguments;
			= case (find_clean_type first_tuple_element_type_name all_h_info) of {
				[CleanTypeDefinition _ clean_type _ _:_]
					#  (c_arguments,errors,extra_clean_arguments) = check_interface_type clean_type c_type c_arguments errors extra_clean_arguments;
					-> check_interface_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_arguments;
				_
					#  errors = [HError ("Error in function type of: "+++function_name) 0:errors];
					-> check_interface_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_arguments;
			};

	check_interface_type (CleanTupleType _ (CleanTypes (CleanTupleType _ first_tuple_type) clean_tuple_argument_types)) c_type c_arguments errors extra_clean_arguments
		= check_interface_type (CleanTupleType UnqDon`tCare (append_clean_types first_tuple_type clean_tuple_argument_types)) c_type c_arguments errors extra_clean_arguments;

	check_interface_tuple_arguments NilCleanTypes c_arguments errors extra_clean_arguments
		= (c_arguments,errors,extra_clean_arguments);
	check_interface_tuple_arguments clean_tuple_argument_types NilFunctionArgumentTypes errors extra_clean_arguments
		= (c_arguments,errors,append_clean_types clean_tuple_argument_types extra_clean_arguments);
	check_interface_tuple_arguments (CleanTypes (CleanType _ tuple_element_type_name) clean_tuple_argument_types) (FunctionArgumentType c_argument _ c_arguments) errors extra_clean_arguments
		# c_argument = expand_typedefs_in_type c_argument all_h_info;
		| basic_clean_and_c_types_ok c_argument tuple_element_type_name
			= check_interface_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_arguments;
			# errors = [HError ("Error in function type of: "+++function_name) 0:errors];
			= check_interface_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_arguments;

	check_interface_tuple_arguments (CleanTypes (CleanTupleType _ first_tuple_arguments) clean_tuple_argument_types) function_argument_types errors extra_clean_arguments
		= check_interface_tuple_arguments (append_clean_types first_tuple_arguments clean_tuple_argument_types) function_argument_types errors extra_clean_arguments;

}

is_c_typedef c_type all_h_info
	| c_type=="int" || c_type=="char" || c_type=="double" || c_type=="CleanString" || c_type=="CleanIntArray" || c_type=="CleanRealArray" || c_type=="CleanCharArray"
			|| (size c_type>0 && c_type.[size c_type-1]=='*')
		= False;
		= case (find_c_type c_type all_h_info) of {
			[CTypeDefinition _ expanded_c_type:_]
				-> True;
			_
				-> False;
		};

check_interface_result_type function_name c_result_type clean_type c_arguments all_h_info errors
	| c_result_type=="void"
		# (_,errors,extra_result_types)=check_interface_void_result_type clean_type c_arguments errors;
		= (errors,extra_result_types);
		# (_,errors,extra_result_types)=check_interface_non_pointer_result_type clean_type c_result_type c_arguments errors NilCleanTypes;
		= (errors,extra_result_types);
where {
	check_interface_void_result_type (CleanType _ clean_type_name) (FunctionArgumentType c_type _ c_arguments) errors
		# (is_pointer,c_type) = remove_pointer_from_c_type c_type all_h_info;
		| not is_pointer
			= (c_arguments,[HError ("Error in function type of: "+++function_name) 0:errors],NilCleanTypes);
			= check_interface_non_pointer_result_type (CleanType UnqDon`tCare clean_type_name) c_type c_arguments errors NilCleanTypes;
	check_interface_void_result_type (CleanTupleType _ (CleanTypes (CleanType _ first_tuple_element_type_name) clean_tuple_argument_types)) (FunctionArgumentType c_type _ c_arguments) errors
		# (is_pointer,c_type) = remove_pointer_from_c_type c_type all_h_info;
		| not is_pointer
			# errors = [HError ("Error in function type of: "+++function_name) 0:errors];
			= check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors NilCleanTypes;
			# (c_arguments,errors,extra_clean_types) = check_interface_non_pointer_result_type (CleanType UnqDon`tCare first_tuple_element_type_name) c_type c_arguments errors NilCleanTypes;
			= check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_types;

	check_interface_void_result_type (CleanTupleType _ (CleanTypes (CleanTupleType _ first_tuple_types) clean_tuple_argument_types)) function_argument_types errors
		= 	check_interface_void_result_type (CleanTupleType UnqDon`tCare (append_clean_types first_tuple_types clean_tuple_argument_types)) function_argument_types errors;
		
	check_interface_void_result_type clean_type NilFunctionArgumentTypes errors
		= (NilFunctionArgumentTypes,errors,CleanTypes clean_type NilCleanTypes);
	check_interface_non_pointer_result_type (CleanType _ clean_type_name) c_type c_arguments errors extra_clean_types
		| is_c_typedef clean_type_name all_h_info && expand_typedefs_in_type_to_typedef c_type clean_type_name all_h_info<>clean_type_name
			= (c_arguments,[HError ("Error in function type of: "+++function_name) 0: errors],extra_clean_types);
			
			# c_type = expand_typedefs_in_type c_type all_h_info;
			| basic_clean_and_c_result_types_ok c_type clean_type_name
				= (c_arguments,errors,extra_clean_types);
				= case (find_clean_type clean_type_name all_h_info) of {
					[CleanTypeDefinition _ clean_type _ _:_]
						-> check_interface_non_pointer_result_type clean_type c_type c_arguments errors extra_clean_types;
					_
						-> (c_arguments,[HError ("Error in function type of: "+++function_name) 0: errors],extra_clean_types);
				  };
	check_interface_non_pointer_result_type (CleanTupleType _ (CleanTypes (CleanType _ first_tuple_element_type_name) clean_tuple_argument_types)) c_type c_arguments errors extra_clean_types
		| basic_clean_and_c_result_types_ok c_type first_tuple_element_type_name
			= check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_types;
			= case (find_clean_type first_tuple_element_type_name all_h_info) of {
				[CleanTypeDefinition _ clean_type _ _:_]
					# (c_arguments,errors,extra_clean_types)=check_interface_non_pointer_result_type clean_type c_type c_arguments errors extra_clean_types;
					-> check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_types;
				_
					#  errors = [HError ("Error in function type of: "+++function_name) 0:errors];
					-> check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_types;
			  };
	check_interface_non_pointer_result_type (CleanTupleType _ (CleanTypes (CleanTupleType _ first_tuple_element_types) clean_tuple_argument_types)) c_type c_arguments errors extra_clean_types
		= check_interface_non_pointer_result_type (CleanTupleType UnqDon`tCare (append_clean_types first_tuple_element_types clean_tuple_argument_types)) c_type c_arguments errors extra_clean_types;

	check_interface_result_tuple_arguments NilCleanTypes c_arguments errors extra_clean_types
		= (c_arguments,errors,extra_clean_types);
	check_interface_result_tuple_arguments clean_tuple_argument_types NilFunctionArgumentTypes errors extra_clean_types
		= (c_arguments,errors,append_clean_types clean_tuple_argument_types extra_clean_types);
	check_interface_result_tuple_arguments (CleanTypes (CleanType _ tuple_element_type_name) clean_tuple_argument_types) (FunctionArgumentType c_argument _ c_arguments) errors extra_clean_types
		# (is_pointer,c_argument) = remove_pointer_from_c_type c_argument all_h_info;
		| not is_pointer
			# errors = [HError ("Error in function type of: "+++function_name) 0:errors];
			= check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_types;
			# (c_arguments,errors,extra_clean_types) = check_interface_non_pointer_result_type (CleanType UnqDon`tCare tuple_element_type_name) c_argument c_arguments errors extra_clean_types;
			= check_interface_result_tuple_arguments clean_tuple_argument_types c_arguments errors extra_clean_types;
	check_interface_result_tuple_arguments (CleanTypes (CleanTupleType _ tuple_elements) clean_tuple_argument_types) (FunctionArgumentType c_argument x c_arguments) errors extra_clean_types
		= check_interface_result_tuple_arguments (append_clean_types tuple_elements clean_tuple_argument_types) (FunctionArgumentType c_argument x c_arguments) errors extra_clean_types;
	check_interface_tuple_arguments (CleanTypes (CleanTupleType _ first_tuple_arguments) clean_tuple_argument_types) function_argument_types errors extra_clean_arguments
		= check_interface_tuple_arguments (append_clean_types first_tuple_arguments clean_tuple_argument_types) function_argument_types errors extra_clean_arguments;
}

/*
write_graph :: !a -> Bool;
write_graph g = code {
.d 1 0
	jsr	_print_graph
.o 0 0
	pushB TRUE
}
*/

check_extra_arguments_and_results extra_clean_arguments extra_clean_results all_h_info function_name errors
	= check_extra_arguments_and_results extra_clean_arguments extra_clean_results "" errors;
{		
	check_extra_arguments_and_results NilCleanTypes NilCleanTypes extra_ccall_arg_types errors
		| size extra_ccall_arg_types==0
			= (extra_ccall_arg_types,errors);
			= (":"+++extra_ccall_arg_types,errors);
	check_extra_arguments_and_results (CleanTypes (CleanType _ clean_argument_type) clean_argument_types) (CleanTypes (CleanType _ clean_result_type) clean_result_types) extra_ccall_arg_types errors
		| is_basic_clean_type clean_argument_type
			| is_basic_clean_type clean_result_type
				= check_extra_arguments_and_results clean_argument_types clean_result_types ("I"+++extra_ccall_arg_types) errors;
				= case (find_clean_type clean_result_type all_h_info) of {
					[CleanTypeDefinition _ clean_result_type _ _:_]
						-> check_extra_arguments_and_results (CleanTypes (CleanType UnqDon`tCare clean_argument_type) clean_argument_types) (CleanTypes clean_result_type clean_result_types) extra_ccall_arg_types errors;
					_
						#  errors = [HError ("Error in extra argument or result types in :"+++function_name) 0:errors];
						-> check_extra_arguments_and_results clean_argument_types clean_result_types extra_ccall_arg_types errors;
				  }
			= case (find_clean_type clean_argument_type all_h_info) of {
				[CleanTypeDefinition _ clean_argument_type _ _:_]
					-> check_extra_arguments_and_results (CleanTypes clean_argument_type clean_argument_types) (CleanTypes (CleanType UnqDon`tCare clean_result_type) clean_result_types) extra_ccall_arg_types errors;
				_
					#  errors = [HError ("Error in extra argument or result types in :"+++function_name) 0:errors];
					-> check_extra_arguments_and_results clean_argument_types clean_result_types extra_ccall_arg_types errors;
			  };
	check_extra_arguments_and_results (CleanTypes (CleanTupleType _ clean_tuple_arguments) clean_argument_types) clean_result_types extra_ccall_arg_types errors
		= check_extra_arguments_and_results (append_clean_types clean_tuple_arguments clean_argument_types) clean_result_types extra_ccall_arg_types errors;
	check_extra_arguments_and_results clean_argument_types (CleanTypes (CleanTupleType _ clean_tuple_result) clean_result_types) extra_ccall_arg_types errors
		= check_extra_arguments_and_results clean_argument_types (append_clean_types clean_tuple_result clean_result_types) extra_ccall_arg_types errors;
	check_extra_arguments_and_results NilCleanTypes result_types extra_ccall_arg_types errors
		= ("",[HError ("Too many extra result types in :"+++function_name) 0:errors]);
	check_extra_arguments_and_results _ NilCleanTypes extra_ccall_arg_types errors
		= ("",[HError ("Too many extra argument types in : "+++function_name) 0:errors]);
}	

not_nil [] = False;
not_nil _ = True;

synonym_cycle_error function_name [type:_] cycles2
	= "Cycle in type synonym: "+++type+++" in function: "+++function_name;
synonym_cycle_error function_name [] cycles2=:[type:_]
	= synonym_cycle_error function_name cycles2 [];

check_interface h_all_types h_functions errors
	= check_interface h_functions errors;
{
	check_interface [CFunction function_name result_type arguments extra_ccall_arg_types:h_info] errors
		= case (find_clean_function function_name h_functions) of {
			[CleanFunction _ clean_argument_types clean_result_type:_]
				# (n_clean_arguments,cycles1) = arity_and_cycles_of_clean_types clean_argument_types h_all_types;
				# (n_clean_results,cycles2) = arity_and_cycles_of_clean_type clean_result_type h_all_types;
				| not_nil cycles1 || not_nil cycles2
					# (h_info,errors) = check_interface h_info errors;
					# errors = [HError (synonym_cycle_error function_name cycles1 cycles2) 0:errors];
					-> ([CFunction function_name result_type arguments extra_ccall_arg_types:h_info],errors);
				# n_c_arguments = add_arity_of_c_types arguments (add_arity_of_c_type result_type 0);
				# n_extra_arguments_2 = n_clean_arguments+n_clean_results-n_c_arguments;
				# n_extra_arguments = n_extra_arguments_2 >> 1;
				| n_clean_arguments+n_clean_results < n_c_arguments
					# (h_info,errors)=check_interface h_info [HError ("Too few arguments or results in clean type of: "+++function_name) 0:errors];
					-> ([CFunction function_name result_type arguments extra_ccall_arg_types:h_info],errors);
				| n_extra_arguments_2 bitand 1<>0
					-> check_interface h_info [HError "Incorrect number of arguments or results in clean type" 0:errors];
					#  (input_arguments,rest_arguments) = take_n_arguments2 (n_clean_arguments-n_extra_arguments) arguments;
					#  output_arguments=take_n_arguments (n_clean_results-n_extra_arguments) rest_arguments;
					#  (errors,extra_clean_arguments) = check_interface_function_argument_types function_name clean_argument_types input_arguments h_all_types errors;
					#  (errors,extra_clean_results) = check_interface_result_type function_name result_type clean_result_type output_arguments h_all_types errors;
					#  (extra_ccall_arg_types,errors) = check_extra_arguments_and_results extra_clean_arguments extra_clean_results h_all_types function_name errors;
					#  (h_info,errors) = check_interface h_info errors;
					-> ([CFunction function_name result_type arguments extra_ccall_arg_types:h_info],errors);
			_
				# errors=errors >: check_c_result_type result_type h_all_types >: check_c_arguments arguments h_all_types;
				# (h_info,errors) = check_interface h_info errors;				 
				-> ([CFunction function_name result_type arguments extra_ccall_arg_types:h_info],errors);
				where {
					check_c_arguments NilFunctionArgumentTypes h_all_types errors
						= errors;
					check_c_arguments (FunctionArgumentType s _ l) h_all_types errors
						= check_c_arguments l h_all_types (check_c_argument_type s h_all_types errors);

					check_c_argument_type s1 h_types errors
						# s=expand_typedefs_in_type s1 h_types;
						| s=="int" || (size s>0 && s.[size s-1]=='*') || s=="char" || s=="double" || s=="void" || s=="CleanString" || s=="CleanIntArray" || s=="CleanRealArray" || s=="CleanCharArray"
							= errors;
							= [HError ("Error in argument type: '"+++toString s1+++"' of function: "+++function_name) 0:errors];

					check_c_result_type s1 h_types errors
						# s=expand_typedefs_in_type s1 h_types;
						| s=="int" || (size s>0 && s.[size s-1]=='*') || s=="char" || s=="double" || s=="CleanString"
							= errors;
							= [HError ("Error in result type: '"+++toString s1+++"' of function: "+++function_name) 0:errors];
				}
		  };
	check_interface [CleanFunction function_name argument_types result_type:h_info] errors
		# (h_info,errors) = check_interface h_info errors;
		= ([CleanFunction function_name argument_types result_type:h_info],errors);
	check_interface [] errors
		= ([],errors);

}

file_name_occurs_in_list file_name [] = False;
file_name_occurs_in_list file_name [e:l] = e==file_name || file_name_occurs_in_list file_name l;

append_included_hf_lists hf [] = hf;
append_included_hf_lists hf [e:l] = append_included_hf hf (append_included_hf_lists e l);
{
	append_included_hf hf=:{hf_constants,hf_types,hf_functions,hf_includes_and_imports,hf_errors} hf2
		= {	hf &
			hf_constants=hf_constants++hf2.hf_constants,
			hf_types=hf_types++hf2.hf_types,
			hf_functions=hf_functions++hf2.hf_functions,
			hf_includes_and_imports=hf_includes_and_imports++hf2.hf_includes_and_imports,
			hf_errors=hf_errors++hf2.hf_errors
		  };
}

append_imported_hf_lists [] types errors
	= (types,errors);
append_imported_hf_lists [e:l] types errors
	# (types,errors) = append_imported_hf_lists l types errors;
	= (e.hf_types++types,e.hf_errors++errors);

read_h_files directory_name h_file_name files
	# (hf,files) = read_h_file_without_include_files h_file_name files;
 	# (hf_list,file_name_list,import_list,files) = read_included_files hf.hf_includes_and_imports [] [h_file_name] [] files;
 	# hf = append_included_hf_lists hf (reverse hf_list);
	# (imported_hf_list,file_name_list,files) = read_imported_files import_list [] file_name_list files;
	# (imported_types,imported_errors) = append_imported_hf_lists (reverse imported_hf_list) [] [];
	# hf = {hf & hf_errors=hf.hf_errors++imported_errors,hf_all_types=hf.hf_types++imported_types};
	= (hf,files);
{
	read_h_file_without_include_files h_file_name files
		# hf={hf_constants=[],hf_types=[],hf_functions=[],hf_includes_and_imports=[],hf_errors=[],hf_all_types=[]};
		# (ok,hfile,files) = fopen (directory_name+++h_file_name) FReadText files;
		| not ok
			= ({hf & hf_errors=[HError ("Cannot read file: "+++directory_name+++h_file_name) 0]},files);
		# (h_info,hfile) = read_h_file [] 1 hfile;
		  (ok,files) = fclose hfile files;
 		  hf = reverse_h_info h_info hf;
		= (hf,files);

	read_included_files include_list hf_list file_name_list import_list files
	 = case include_list of {
		[]
			-> ([],file_name_list,import_list,files);
		[Include h_file_name:include_list]
			| file_name_occurs_in_list h_file_name file_name_list
				-> read_included_files include_list hf_list file_name_list import_list files;
				# (hf,files) = read_h_file_without_include_files h_file_name files;
		 		# (hf_list,file_name_list,import_list,files) = read_included_files hf.hf_includes_and_imports hf_list [h_file_name:file_name_list] import_list files;
				# (hf_list,file_name_list,import_list,files) = read_included_files include_list hf_list file_name_list import_list files;
				-> ([hf:hf_list],file_name_list,import_list,files);
		[Import h_file_name:include_list]
			-> read_included_files include_list hf_list file_name_list [Import h_file_name:import_list] files;
	};

	read_imported_files [] hf_list file_name_list files
		= ([],file_name_list,files);
	read_imported_files include_list hf_list file_name_list files
		= case include_list of {
			[Include h_file_name:include_list] -> read_imported_or_included_file h_file_name include_list;
			[Import h_file_name:include_list]  -> read_imported_or_included_file (h_file_name+++".h") include_list;
		}
		where {
			 read_imported_or_included_file h_file_name include_list
				| file_name_occurs_in_list h_file_name file_name_list
					= read_imported_files include_list hf_list file_name_list files;
					# (hf,files) = read_h_file_without_include_files h_file_name files;
				 	# (hf_list,file_name_list,files) = read_imported_files hf.hf_includes_and_imports hf_list [h_file_name:file_name_list] files;
					# (hf_list,file_name_list,files) = read_imported_files include_list hf_list file_name_list files;
					= ([hf:hf_list],file_name_list,files);
		}
}

compile_header :: !{#Char} !{#Char} !*Files -> *(![HError],!*Files);
compile_header directory_name file_name files
	# (hf,files) = read_h_files directory_name (file_name+++".h") files;
	| not (case hf.hf_errors of {[] -> True ; _ -> False})
		= (hf.hf_errors,files);
	# (hf_functions,interface_errors) = check_interface hf.hf_all_types hf.hf_functions [];
	# hf = {hf & hf_functions=hf_functions};
	| not (case interface_errors of {[] -> True ; _ -> False})
	  = (interface_errors,files);
	# icl_file_name = directory_name+++file_name+++".icl";
	# (ok,iclf,files) = fopen icl_file_name FWriteText files;
	| not ok
		= ([HError ("Cannot create file: "+++icl_file_name) 0],files);
 	# iclf=write_icl_file file_name hf.hf_includes_and_imports hf.hf_constants hf.hf_types hf.hf_functions hf.hf_all_types iclf;
	  (ok,files) = fclose iclf files;
 	  dcl_file_name = directory_name+++file_name+++".dcl";
 	  (ok,dclf,files) = fopen dcl_file_name FWriteText files;
	| not ok
		= ([HError ("Cannot create file: "+++dcl_file_name) 0],files);
 	# dclf=write_dcl_file file_name hf.hf_includes_and_imports hf.hf_constants hf.hf_types hf.hf_functions dclf;
	  (ok,files) = fclose dclf files;
	= (hf.hf_errors,files);
/*
Start :: *World -> (![HError],!*World);
Start world
//	= accFiles (compile_header "" "interface") world;
	= accFiles (compile_header "" "types") world;
*/
