open Linkst
type provide_environment = string list
type type_environment = (string * link_type) list
exception Linker_error of string
let concat s = String.concat "" s
let raise_linker_error s = raise (Linker_error s)
let vm_defs = [ LnkProvide ("to_string",
LnkFunction ([LnkAnything],LnkString));
LnkProvide ("parseint",
LnkFunction ([LnkString],LnkInt));
LnkProvide ("parsefloat",
LnkFunction ([LnkString],LnkFloat));
LnkProvide ("array_length",
LnkFunction ([LnkArray LnkAnything],LnkInt));
LnkProvide ("string_length",
LnkFunction ([LnkString],LnkInt));
LnkProvide ("rand",
LnkFunction ([LnkVoid],LnkFloat));
LnkProvide ("print",
LnkFunction ([LnkAnything],LnkVoid));
LnkProvide ("print_error",
LnkFunction ([LnkAnything],LnkVoid));
LnkProvide ("println",
LnkFunction ([LnkAnything],LnkVoid));]
let rec extract_code lst =
match lst with
car::cdr ->
(match car with
LnkCode s -> s
| _ -> extract_code cdr)
| [] -> ""
let extract_provide def = match def with
LnkProvide (name,_) -> [name]
| _ -> []
let extract_provide_type def = match def with
LnkProvide (name,ty) -> [(name,ty)]
| _ -> []
let extract_type def = match def with
LnkProvide (name,ty) -> [name, ty]
| LnkRequire (name,ty) -> [name, ty]
| _ -> []
let rec check_duplicates provide_list = match provide_list with
car::cdr when List.mem car cdr ->
raise_linker_error (concat ["Function ";car;" is provided by more than one actor/vm."])
| _::cdr -> check_duplicates cdr
| [] -> ()
let check_require provide_list def = match def with
LnkRequire (name,_) ->
if List.mem name provide_list then
()
else
raise_linker_error (concat ["Function ";name;" was required but not provided."])
| _ -> ()
let rec are_assoclists_compatible l1 l2 = match l1 with
(x,y)::rest ->
if List.mem_assoc x l2
then y = List.assoc x l2
else are_assoclists_compatible rest l2
| [] -> true
let rec check_unified t1 t2 = match (t1,t2) with
x,y when x = y -> []
| LnkAnything, x -> []
| x, LnkAnything -> []
| LnkTemplate s, t2 -> [(s,t2)]
| t1, LnkTemplate s -> [(s,t1)]
| LnkEnumType (name1,size1), LnkEnumType (name2,size2) ->
if (name1 = name2) && (size1 = size2) then
[]
else
raise_linker_error
(concat ["Failed to reconcile enums ";
name1;"(";(string_of_int size1);") and ";
name2;"(";(string_of_int size2);")."])
| LnkEnum (name1,size1), LnkEnum (name2,size2) ->
if (name1 = name2) && (size1 = size2) then
[]
else
raise_linker_error
(concat ["Failed to reconcile enums ";
name1;"(";(string_of_int size1);") and ";
name2;"(";(string_of_int size2);")."])
| LnkStruct (name1,fields1), LnkStruct (name2,fields2) ->
let _ = List.map2 check_unified fields1 fields2 in
if name1 = name2 then
[]
else
raise_linker_error
(concat ["Failed to reconcile structs ";
name1;" and ";name2;"."])
| LnkArray t_arr1, LnkArray t_arr2 ->
check_unified t_arr1 t_arr2
| LnkList t_lst1, LnkList t_lst2 ->
check_unified t_lst1 t_lst2
| LnkFunction (args1,ret1), LnkFunction (args2,ret2) ->
let assoc1 = check_unified ret1 ret2 in
let assoc2 = List.flatten (List.map2 check_unified args1 args2) in
if are_assoclists_compatible assoc1 assoc2 then
assoc1 @ assoc2
else
raise_linker_error "Could not reconcile templated arrow types."
| LnkRef t_ref1, LnkRef t_ref2 ->
check_unified t_ref1 t_ref2
| _ -> raise_linker_error "Irreconciliable types."
let rec check_types types provided_types = match types with
(name,ty)::rest ->
if List.mem_assoc name provided_types then
let other_type = List.assoc name provided_types in
let _ =
try
check_unified ty other_type
with
Linker_error s -> raise_linker_error (concat ["Conflicting type definitions for ";name;": ";s])
in
check_types rest provided_types
else
check_types rest provided_types
| [] -> ()
let check_consistent defs =
let provided = List.flatten (List.map extract_provide defs) in
let types = List.flatten (List.map extract_type defs) in
let provided_types = List.flatten (List.map extract_provide_type defs) in
let _ = check_duplicates provided in
let _ = List.map (check_require provided) defs in
let _ = check_types types provided_types in
()
let main () =
try
let _ = print_string "Linking...\n"; flush stdout; in
let n_files =
if Array.length Sys.argv >= 5 then 2 else 1
in
let filename_list =
if n_files = 2 then
[Sys.argv.(1); Sys.argv.(3)]
else
[Sys.argv.(1)]
in
let outfile_names =
if n_files = 2 then
[Sys.argv.(2); Sys.argv.(4)]
else
[Sys.argv.(2)]
in
let file_list = List.map open_in filename_list in
let lexing_list = List.map Lexing.from_channel file_list in
let linkst_list = List.map (Bmdlinkparse.link Bmdlinklex.bmd_link) lexing_list in
let code_list = List.map extract_code linkst_list in
let defs = List.flatten (vm_defs::linkst_list) in
let _ = check_consistent defs in
let outfile_list = List.map open_out outfile_names in
let _ = List.map2 output_string outfile_list code_list in
let _ = print_string "Done.\n"; flush stdout; in
()
with
Parsing.Parse_error ->
begin
print_string "Syntax error on line ";
print_string (string_of_int !Bmdlinklex.line_count);
print_string ".\n";
flush stdout;
end
| Sys_error s ->
begin
print_string "Could not open input file ";
print_string s;
print_string "\n";
flush stdout;
end
| Linker_error s ->
begin
print_string s;
print_string "\n";
flush stdout;
end
| _ ->
begin
print_string "Linking failed.\n";
end
let _ = main ()