type tokens = | Number of float | String of string | ID of string | Plus | Minus | Times | Divide | PlusEquals | MinusEquals | TimesEquals | DivideEquals | Power | Semicolon | Colon | LeftBrace | RightBrace | Percent | Whitespace | Indent | Comma | LeftSBrace | RightSBrace | LeftCBrace | RightCBrace | Dot | Define | Newline | Return | EOF | Assign | If | Elif | Else | Equals | GreaterThan | LessThan | GreaterThanEquals | LessThanEquals | For | In | True | False | And | Or | While | Not | Nonlocal | Global | Class let program = ref Bytes.empty let () = let ic = open_in Sys.argv.(1) in try let n = in_channel_length ic in let s = Bytes.create n in really_input ic s 0 n; program := s; close_in ic with e -> close_in_noerr ic; raise e let program = Bytes.to_string !program let tokenise program = let tokens = ref [] in let index = ref 0 in let advance () = index := !index + 1; if !index > String.length program then raise (Failure "Reached EOF while tokenising!") in let devance () = index := !index - 1; in let current () = String.get program !index in let peek () = if !index + 1 < String.length program then String.get program (!index + 1) else raise (Failure "Cannot peak past EOF!") in let is_digit c = match c with '0'..'9' -> true | _ -> false in let is_alpha c = match c with 'a'..'z' -> true | 'A'..'Z' -> true | _ -> false in let is_alnum c = match c with 'a'..'z' -> true | 'A'..'Z' -> true | '0'..'9' -> true | '_' -> true | _ -> false in while !index < String.length program do (match (current ()) with '0'..'9' -> ( let number = ref 0. in while is_digit (current ()) do number := !number *. 10.; number := !number +. (float_of_string (String.make 1 (current ()))); advance () done; if current() = '.' then ( advance (); let decimal_places = ref 0 in let decimal_number = ref 0. in while is_digit (current ()) do decimal_places := !decimal_places + 1; decimal_number := !decimal_number *. 10.; decimal_number := !decimal_number +. (float_of_string (String.make 1 (current ()))); advance () done; number := !number +. (!decimal_number /. (float !decimal_places)) ); devance (); tokens := !tokens @ [Number !number] ) | ('a'..'z'|'A'..'Z'|'_') -> ( let text = ref "" in while is_alnum (current ()) do text := !text ^ (String.make 1 (current ())); advance() done; devance (); match !text with | "def" -> tokens := !tokens @ [Define] | "return" -> tokens := !tokens @ [Return] | "if" -> tokens := !tokens @ [If] | "elif" -> tokens := !tokens @ [Elif] | "else" -> tokens := !tokens @ [Else] | "for" -> tokens := !tokens @ [For] | "in" -> tokens := !tokens @ [In] | "and" -> tokens := !tokens @ [And] | "or" -> tokens := !tokens @ [Or] | "True" -> tokens := !tokens @ [True] | "False" -> tokens := !tokens @ [False] | "not" -> tokens := !tokens @ [Not] | "while" -> tokens := !tokens @ [While] | "global" -> tokens := !tokens @ [Global] | "nonlocal" -> tokens := !tokens @ [Nonlocal] | "class" -> tokens := !tokens @ [Class] | _ -> tokens := !tokens @ [ID !text] ) | '"' -> ( let text = ref "" in advance (); while current () <> '"' do text := !text ^ (String.make 1 (current ())); advance () done; tokens := !tokens @ [String !text] ) | '\'' -> ( let text = ref "" in advance (); while current () <> '\'' do text := !text ^ (String.make 1 (current ())) done; tokens := !tokens @ [String !text] ) | '#' -> ( while current () <> '\n' do advance () done ) | '=' -> ( match peek () with '=' -> tokens := !tokens @ [Equals]; advance () | _ -> tokens := !tokens @ [Assign] ) | '>' -> ( match peek () with '=' -> tokens := !tokens @ [GreaterThanEquals]; advance () | _ -> tokens := !tokens @ [GreaterThan] ) | '<' -> ( match peek () with '=' -> tokens := !tokens @ [LessThanEquals]; advance () | _ -> tokens := !tokens @ [LessThan] ) | '+' -> ( match peek () with '=' -> tokens := !tokens @ [PlusEquals]; advance () | _ -> tokens := !tokens @ [Plus] ) | '-' -> ( match peek () with '=' -> tokens := !tokens @ [MinusEquals]; advance () | _ -> tokens := !tokens @ [Minus] ) | '*' -> ( match peek () with '=' -> tokens := !tokens @ [TimesEquals]; advance () | '*' -> tokens := !tokens @ [Power]; advance () | _ -> tokens := !tokens @ [Times] ) | '/' -> ( match peek () with '=' -> tokens := !tokens @ [DivideEquals]; advance () | _ -> tokens := !tokens @ [Divide] ) | ' ' -> tokens := !tokens @ [Whitespace] | ';' -> tokens := !tokens @ [Semicolon] | ':' -> tokens := !tokens @ [Colon] | '.' -> tokens := !tokens @ [Dot] | ',' -> tokens := !tokens @ [Comma] | '%' -> tokens := !tokens @ [Percent] | '\n' -> tokens := !tokens @ [Newline] | '\t' -> tokens := !tokens @ [Indent] | '(' -> tokens := !tokens @ [LeftBrace] | ')' -> tokens := !tokens @ [RightBrace] | '[' -> tokens := !tokens @ [LeftSBrace] | ']' -> tokens := !tokens @ [RightSBrace] | '{' -> tokens := !tokens @ [LeftCBrace] | '}' -> tokens := !tokens @ [RightCBrace] | _ -> raise (Failure ("Invalid Character: " ^ (String.make 1 (current ())))) ); advance (); done; tokens := !tokens @ [EOF]; !tokens let string_of_token tok = match tok with Plus -> "Plus" | Minus -> "Minus" | Times -> "Times" | Divide -> "Divide" | PlusEquals -> "PlusEquals" | MinusEquals -> "MinusEquals" | TimesEquals -> "TimesEquals" | DivideEquals -> "DivideEquals" | Power -> "Power" | Number n -> ("Number (" ^ string_of_float n ^ ")") | String s -> ("String (\"" ^ s ^ "\")") | ID id -> ("ID (" ^ id ^ ")") | Semicolon -> "Semicolon" | Colon -> "Colon" | LeftBrace -> "Left Bracket" | RightBrace -> "Right Bracket" | LeftSBrace -> "Left Square Bracket" | RightSBrace -> "Right Square Bracket" | LeftCBrace -> "Left Curly Bracket" | RightCBrace -> "Right Curly Bracket" | Comma -> "Comma" | Whitespace -> "Whitespace" | Dot -> "Dot" | Indent -> "Indent" | Percent -> "Percent" | Define -> "Define" | Return -> "Return" | Newline -> "Newline" | EOF -> "End of File" | Assign -> "Assign" | Class -> "Class" | _ -> "NOT IMPLEMENTED YET" let print_tokens tokens = for i = 0 to List.length tokens - 1 do print_endline ("Found: " ^ string_of_token (List.nth tokens i)) done type tree_operation = | NumberValue of float | StringValue of string | Variable of string | BoolValue of bool | DictValue of (tree_operation, tree_operation) Hashtbl.t | ListValue of tree_operation list | Compound of tree_operation list | Add of tree_operation * tree_operation | Sub of tree_operation * tree_operation | Mul of tree_operation * tree_operation | Div of tree_operation * tree_operation | Modulo of tree_operation * tree_operation | Pow of tree_operation * tree_operation | ItemOf of tree_operation * tree_operation | TestEquals of tree_operation * tree_operation | TestGreaterThan of tree_operation * tree_operation | TestLessThan of tree_operation * tree_operation | TestGreaterThanEquals of tree_operation * tree_operation | TestLessThanEquals of tree_operation * tree_operation | AndStatement of tree_operation * tree_operation | OrStatement of tree_operation * tree_operation | TestIn of tree_operation * tree_operation | NotStatement of tree_operation | IfStatement of tree_operation * tree_operation | WhileLoop of tree_operation * tree_operation | IfElseStatement of tree_operation * tree_operation * tree_operation | ForLoop of tree_operation * tree_operation * tree_operation | AssignVar of tree_operation * tree_operation | FunctionDeclaration of tree_operation * tree_operation * tree_operation | Action of tree_operation * tree_operation | RelativeAction of tree_operation * tree_operation * tree_operation | ArgList of tree_operation list | InputList of tree_operation list | Negate of tree_operation | PlaceHolder | Return of tree_operation | DefineGlobal of tree_operation | DefineNonlocal of tree_operation let build_ast tokens = let index = ref 0 in let current () = List.nth tokens !index in let eat token = if current () = token then ( index := !index + 1; ) else raise (Failure ("Invalid Token! Expected: " ^ string_of_token token ^ ", got: " ^ string_of_token (current ()))) in let eat_number () = if (match current () with Number n -> true | _ -> false) then ( index := !index + 1; (* print_endline "Ate: Number" *) ) else raise (Failure ("Invalid Token! Expected: Number, got: " ^ string_of_token (current ()))) in let eat_string () = if (match current () with String s -> true | _ -> false) then ( index := !index + 1; (* print_endline "Ate: String" *) ) else raise (Failure ("Invalid Token! Expected: String, got: " ^ string_of_token (current ()))) in let eat_id () = if (match current () with ID id -> true | _ -> false) then ( index := !index + 1; (* print_endline "Ate: ID" *) ) else raise (Failure ("Invalid Token! Expected: ID, got: " ^ string_of_token (current ()))) in let eat_statement_separator () = (match current () with Newline -> eat Newline | EOF -> () | _ -> eat Semicolon); while current () = Newline do eat Newline done in let eat_whitespace () = while current () = Whitespace do eat Whitespace done in let list_of_compound c = match c with Compound c -> c | _ -> raise (Failure "Not a Compound") in let list_of_arglist al = match al with ArgList al -> al | _ -> raise (Failure "Not a ArgList") in let list_of_inputlist il = match il with InputList il -> il | _ -> raise (Failure "Not a ArgList") in let string_of_id tok = match tok with ID id -> id | _ -> raise (Failure "Not an ID") in let eat_indents () = let found_indents = ref 0 in while current () = Indent do eat Indent; found_indents := !found_indents + 1 done; !found_indents in let devance ?(by=1) () = index := !index - by in let rec variable () = eat_whitespace (); let node = ref (Variable (string_of_id (current ()))) in eat_id (); !node and factor () = let node = ref PlaceHolder in eat_whitespace (); if (match current () with Number n -> true | _ -> false) then ( node := NumberValue (match current() with Number n -> n | _ -> 0.); eat_number (); ) else if (match current () with String s -> true | _ -> false) then ( node := StringValue (match current() with String s -> s | _ -> ""); eat_string (); ) else if current() = True then ( node := BoolValue true; eat True ) else if current() = False then ( node := BoolValue false; eat False ) else if current () = Minus then ( eat Minus; node := Negate ( factor () ); eat_whitespace () ) else if current () = LeftBrace then ( eat LeftBrace; eat_whitespace (); node := expr (); eat_whitespace (); eat RightBrace ) else if current () = LeftSBrace then ( eat LeftSBrace; eat_whitespace (); let tmp_list = ref [] in if current () <> RightSBrace then ( tmp_list := !tmp_list @ [expr ()]; while current () = Comma do eat Comma; tmp_list := !tmp_list @ [expr ()]; done ); eat RightSBrace; node := ListValue !tmp_list ) else if current () = LeftCBrace then ( eat LeftCBrace; eat_whitespace; let tmp_Hashtbl = Hashtbl.create 100 in let tmp_key = ref (factor ()) in eat Colon; let tmp_value = ref (factor ()) in Hashtbl.replace tmp_Hashtbl !tmp_key !tmp_value; while current () = Comma do eat Comma; let tmp_key = ref (factor ()) in eat Colon; let tmp_value = ref (factor ()) in Hashtbl.replace tmp_Hashtbl !tmp_key !tmp_value; done; eat RightCBrace; node := DictValue tmp_Hashtbl ) else ( node := variable (); eat_whitespace (); if current () = LeftBrace then node := Action( !node, action () ) ); while current() = LeftSBrace do eat LeftSBrace; node := ItemOf ( !node, factor () ); eat RightSBrace done; if current () = Dot then ( eat Dot; let var = variable () in node := RelativeAction (var, !node, action ()) ); eat_whitespace (); !node and power () = let node = ref (factor ()) in while current () = Power do eat Power; node := Pow (!node, factor ()) done; !node and term () = let node = ref (power ()) in while (match current () with (Times|Divide|Percent) -> true | _ -> false) do if current () = Times then ( eat Times; node := Mul(!node, power ()) ) else if current () = Divide then ( eat Divide; node := Div(!node, power ()) ) else ( eat Percent; node := Modulo(!node, power ()) ) done; !node and expr () = let node = ref (term ()) in while (match current () with (Plus|Minus) -> true | _ -> false) do if current () = Plus then ( eat Plus; node := Add(!node, term ()) ) else ( eat Minus; node := Sub(!node, term ()) ) done; !node and function_def ?(indents=0) () = let node = ref PlaceHolder in eat Define; eat_whitespace (); let name = variable () in eat_whitespace (); eat LeftBrace; let inputs = ref (InputList []) in if (match current () with ID id -> true | _ -> false) then ( inputs := InputList ((list_of_inputlist !inputs) @ [variable ()]); while current () = Comma do eat Comma; eat_whitespace (); inputs := InputList ((list_of_inputlist !inputs) @ [variable ()]); done ); eat RightBrace; eat Colon; eat_whitespace (); eat Newline; let to_exec = statement_list ~indents:(indents+1) () in node := FunctionDeclaration(name, !inputs, to_exec); !node and action () = let node = ref (ArgList []) in eat_whitespace (); eat LeftBrace; if current () <> RightBrace then ( node := ArgList ((list_of_arglist !node) @ [expr ()]); eat_whitespace (); while current () = Comma do eat Comma; eat_whitespace (); node := ArgList ((list_of_arglist !node) @ [expr ()]) done ); eat RightBrace; !node and assignment () = let node = ref PlaceHolder in eat_whitespace (); eat Assign; node := expr (); !node and assign_action () = let node = ref (variable ()) in eat_whitespace (); (match current () with Assign -> node := AssignVar (!node, assignment ()) | PlusEquals -> ( eat PlusEquals; eat_whitespace (); node := AssignVar (!node, Add ( !node, expr () )) ) | MinusEquals -> ( eat MinusEquals; eat_whitespace (); node := AssignVar (!node, Sub ( !node, expr () )) ) | TimesEquals -> ( eat TimesEquals; eat_whitespace (); node := AssignVar (!node, Mul ( !node, expr () )) ) | DivideEquals -> ( eat DivideEquals; eat_whitespace (); node := AssignVar (!node, Div ( !node, expr () )) ) | Dot -> ( eat Dot; let var = variable () in node := RelativeAction(var, !node, action ()) ) | _ -> node := Action (!node, action ())); !node and base_equiv () = let node = ref PlaceHolder in eat_whitespace (); if current () = Not then ( eat Not; node := NotStatement ( base_equiv () ); !node ) else ( node := factor (); (match current () with Equals -> eat Equals; node := TestEquals ( !node, factor () ) | GreaterThan -> eat GreaterThan; node := TestGreaterThan ( !node, factor () ) | LessThan -> eat LessThan; node := TestLessThan ( !node, factor () ) | GreaterThanEquals -> eat GreaterThanEquals; node := TestGreaterThanEquals ( !node, factor () ) | In -> eat In; node := TestIn (!node, factor ()) | LessThanEquals -> eat LessThanEquals; node := TestLessThanEquals ( !node, factor () ) | _ -> ()); !node ) and equiv_expr () = let node = ref (base_equiv ()) in while (match current () with (And|Or) -> true | _ -> false) do if current () = And then ( eat And; node := AndStatement (!node, base_equiv ()) ) else ( eat Or; node := OrStatement (!node, base_equiv ()) ) done; !node and statement ?(indents=0) () = let node = ref PlaceHolder in let found_indents = eat_indents () in let rec if_statement () = let if_comp = equiv_expr () in eat Colon; let if_body = statement_list ~indents:(indents+1) () in let else_body = ref PlaceHolder in let found_indents = eat_indents () in if found_indents = indents then ( if current () = Else then ( eat Else; eat Colon; else_body := statement_list ~indents:(indents+1) () ) else if current () = Elif then ( eat Elif; else_body := if_statement () ); ) else ( devance ~by:found_indents () ); if !else_body = PlaceHolder then IfStatement (if_comp, if_body) else IfElseStatement (if_comp, if_body, !else_body) in if found_indents = indents then ( (match current () with ID id -> ( node := assign_action (); eat_statement_separator () ) | Return -> ( eat Return; node := Return (expr ()); eat_statement_separator () ) | If -> ( eat If; node := if_statement () ) | While -> ( eat While; let while_comp = equiv_expr () in eat Colon; let while_body = statement_list ~indents:(indents+1) () in node := WhileLoop (while_comp, while_body) ) | For -> ( eat For; eat_whitespace (); let var = variable () in eat_whitespace (); eat In; let for_expr = factor () in eat Colon; let for_body = statement_list ~indents:(indents+1) () in node := ForLoop (var, for_expr, for_body) ) | Global -> ( eat Global; node := DefineGlobal (variable ()); eat_statement_separator () ) | Nonlocal -> ( eat Nonlocal; node := DefineNonlocal (variable ()); eat_statement_separator () ) | _ -> node := function_def ~indents:(indents) () ) ) else ( devance ~by:found_indents () ); !node and statement_list ?(indents = 0) () = if current () = Newline then eat_statement_separator (); let node = ref (Compound [statement ~indents:indents ()]) in let found_indents = ref (eat_indents ()) in while (!found_indents = indents && (match current () with ID id -> true | (Return|If|Define|For|While) -> true | _ -> false)) do devance ~by:!found_indents (); node := Compound ((list_of_compound !node) @ [statement ~indents:indents ()]); found_indents := eat_indents (); eat_whitespace () done; devance ~by:!found_indents (); !node and program () = let node = ref (statement_list ()) in eat_statement_separator (); eat EOF; !node in program () let print_ast ast = let rec string_of_ast ast = match ast with NumberValue n -> ("(Number: " ^ string_of_float n ^ ")") | StringValue s -> ("String: " ^ s ^ ")") | Variable v -> ("(Variable: " ^ v ^ ")") | Compound c -> ( let tmp = ref "" in for i = 0 to List.length c - 1 do tmp := (!tmp ^ string_of_ast (List.nth c i) ^ ", ") done; tmp := "(Compoud: " ^ !tmp ^ ")"; !tmp ) | Add (left, right) -> ("(" ^ string_of_ast left ^ " + " ^ string_of_ast right ^ ")") | Sub (left, right) -> ("(" ^ string_of_ast left ^ " - " ^ string_of_ast right ^ ")") | Mul (left, right) -> ("(" ^ string_of_ast left ^ " * " ^ string_of_ast right ^ ")") | Div (left, right) -> ("(" ^ string_of_ast left ^ " / " ^ string_of_ast right ^ ")") | AssignVar (var, value) -> ("(Assign " ^ string_of_ast var ^ " to " ^ string_of_ast value ^ ")") | FunctionDeclaration (name, inputs, to_exec) -> ("(Create Function " ^ string_of_ast name ^ " with the inputs " ^ string_of_ast inputs ^ " which runs the code " ^ string_of_ast to_exec ^ ")") | Action (name, args) -> ("(Run the Function " ^ string_of_ast name ^ " with the arguments " ^ string_of_ast args ^ ")") | ArgList al -> ( let tmp = ref "" in for i = 0 to List.length al - 1 do tmp := (!tmp ^ string_of_ast (List.nth al i) ^ ", ") done; tmp := "(ArgList: " ^ !tmp ^ ")"; !tmp ) | InputList il -> ( let tmp = ref "" in for i = 0 to List.length il - 1 do tmp := (!tmp ^ string_of_ast (List.nth il i) ^ ", ") done; tmp := "(InputList: " ^ !tmp ^ ")"; !tmp ) | Negate t -> ("(- " ^ string_of_ast t ^ ")") | PlaceHolder -> "(PlaceHolder, if this is displaying something has gone horribly wrong!!!)" | Return t -> ("(Return: " ^ string_of_ast t ^ ")") | _ -> "(UNIMPLEMENTED FUNCTION PLEASE IMPLEMENT)" in print_endline (string_of_ast ast) type value_wrapper = | Num of float | Str of string | Bool of bool | List of value_wrapper list | Dict of (value_wrapper, value_wrapper) Hashtbl.t | Nothing let interpret ast= let variables = Hashtbl.create 100 in let (functions: (tree_operation, tree_operation) Hashtbl.t) = Hashtbl.create 100 in let (functions_input: (tree_operation, tree_operation) Hashtbl.t) = Hashtbl.create 100 in let get_var var = if Hashtbl.mem variables var then Hashtbl.find variables var else raise (Failure "Reference to undefined variables") in let rmod a b = (a mod b + b) mod b in let set_function funcs funcinputs func inputs value= if List.length funcs = 1 then ( if Hashtbl.mem functions func <> true && Hashtbl.mem variables func <> true then ( Hashtbl.replace functions func value; Hashtbl.replace functions_input func inputs ) else raise (Failure "Collision between function name and function name or function name and variable name") ) else ( let collision = ref false in for i = 0 to List.length funcs - 1 do if Hashtbl.mem (List.nth funcs i) func = true then collision := true done; if Hashtbl.mem functions func = true && Hashtbl.mem variables func = true then collision := true; if !collision = true then raise (Failure "Collision between function name and function name or function name and variable name") else ( Hashtbl.replace (List.nth funcs 0) func value; Hashtbl.replace (List.nth funcinputs 0) func inputs ) ) in let float_of_num num = match num with Num n -> n | _ -> raise (Failure "Not a Num") in let string_of_str str = match str with Str s -> s | _ -> raise (Failure "Not a Str") in let list_of_arglist al = match al with ArgList al -> al | _ -> raise (Failure "Not a ArgList") in let list_of_inputlist il = match il with InputList il -> il | _ -> raise (Failure "Not a ArgList") in let name_of_variable v = match v with Variable v -> v | _ -> raise (Failure "Not a Variable") in let list_of_list l = match l with List l -> l | _ -> raise (Failure "Not a List") in let return_stack = ref [] in let string_of_valuewrapper wrapper = match wrapper with | Num n -> ("Num: " ^ string_of_float n) | Str s -> ("Str: " ^ s) | Nothing -> "Nothing" in let print_return_stack () = for i = 0 to List.length !return_stack - 1 do print_endline (string_of_valuewrapper (List.nth !return_stack i)) done in let is_digit c = match c with '0'..'9' -> true | _ -> false in let is_alpha c = match c with 'a'..'z' -> true | 'A'..'Z' -> true | _ -> false in let is_alnum c = match c with 'a'..'z' -> true | 'A'..'Z' -> true | '0'..'9' -> true | '_' -> true | _ -> false in let (empty_vars_table: (tree_operation, value_wrapper) Hashtbl.t) = Hashtbl.create 0 in let (empty_funcs_table(*: (tree_operation, tree_operation) Hashtbl.t*)) = Hashtbl.create 0 in let (empty_funcinputs_table: (tree_operation, tree_operation) Hashtbl.t) = Hashtbl.create 0 in let empty_globals = ref [[]] in let empty_nonlocals = ref [[]] in let rec set_var var value vars globals nonlocals funcs funcinputs= let found = ref false in if List.mem var (List.nth !nonlocals 0) || List.mem var (List.nth !globals 0) then ( for i = 0 to List.length vars - 1 do if Hashtbl.mem (List.nth vars i) (var) then ( found := true; Hashtbl.replace (List.nth vars i) var value ) done ) else ( found := true; Hashtbl.replace (List.nth vars 0) var value ); if !found <> true then if List.mem var (List.nth !globals 0) then Hashtbl.replace variables var value else if List.length !globals = 1 && List.length !nonlocals = 1 then Hashtbl.replace variables var value else Hashtbl.replace (List.nth vars 0) var value (*raise (Failure ("Could not assign variable: " ^ name_of_variable var ^ "! It does not exist within this scope"));*) and eval vars globals nonlocals funcs funcinputs ast = match ast with | DefineGlobal v -> ( let tmp_globals = ref !globals in let tmp_item = List.nth !tmp_globals 0 in tmp_globals := List.tl !tmp_globals; globals := [tmp_item @ [v]] @ !tmp_globals; Nothing ) | DefineNonlocal v -> ( let tmp_nonlocals = ref !nonlocals in let tmp_item = List.nth !tmp_nonlocals 0 in tmp_nonlocals := List.tl !tmp_nonlocals; nonlocals := [tmp_item @ [v]] @ !tmp_nonlocals; Nothing ) | NumberValue nv -> Num nv | StringValue sv -> Str sv | BoolValue bv -> Bool bv | DictValue dv -> ( let tmp_hashtbl = Hashtbl.create 100 in Hashtbl.iter (fun k v -> Hashtbl.replace tmp_hashtbl (eval vars globals nonlocals funcs funcinputs k) (eval vars globals nonlocals funcs funcinputs v)) dv; Dict tmp_hashtbl ) | ListValue lv -> ( let tmp_list = ref [] in for i = 0 to List.length lv - 1 do tmp_list := !tmp_list @ [eval vars globals nonlocals funcs funcinputs (List.nth lv i)] done; List !tmp_list ) | Variable v -> ( let found = ref false in let returnable = ref Nothing in let _ = try for i = 0 to List.length vars - 1 do if Hashtbl.mem (List.nth vars i) (Variable v) then ( found := true; returnable := Hashtbl.find (List.nth vars i) (Variable v); raise Exit; ) done with Exit -> () in if !found <> true then returnable := get_var (Variable v); !returnable ) | Compound c -> ( for i = 0 to List.length c - 1 do eval vars globals nonlocals funcs funcinputs (List.nth c i) done; Nothing ) | Add (left, right) -> ( match eval vars globals nonlocals funcs funcinputs left with Num n -> Num (float_of_num (eval vars globals nonlocals funcs funcinputs left) +. float_of_num (eval vars globals nonlocals funcs funcinputs right)) | Str s -> Str (string_of_str (eval vars globals nonlocals funcs funcinputs left) ^ string_of_str (eval vars globals nonlocals funcs funcinputs right)) ) | Sub (left, right) -> Num (float_of_num (eval vars globals nonlocals funcs funcinputs left) -. float_of_num (eval vars globals nonlocals funcs funcinputs right)) | Mul (left, right) -> ( match eval vars globals nonlocals funcs funcinputs left with Num n -> Num (float_of_num (eval vars globals nonlocals funcs funcinputs left) *. float_of_num (eval vars globals nonlocals funcs funcinputs right)) | Str s -> ( let tmp_str = ref "" in let tmp_left = string_of_str (eval vars globals nonlocals funcs funcinputs left) in for i = 0 to int_of_float (float_of_num (eval vars globals nonlocals funcs funcinputs right)) - 1 do tmp_str := !tmp_str ^ tmp_left done; Str !tmp_str ) ) | Pow (left, right) -> Num (float_of_num (eval vars globals nonlocals funcs funcinputs left) ** float_of_num (eval vars globals nonlocals funcs funcinputs right)) | Div (left, right) -> Num (float_of_num (eval vars globals nonlocals funcs funcinputs left) /. float_of_num (eval vars globals nonlocals funcs funcinputs right)) | AssignVar (var, value) -> ( let tmp_nonlocals = ref !nonlocals in let tmp_item = List.nth !tmp_nonlocals 0 in tmp_nonlocals := List.tl !tmp_nonlocals; nonlocals := [tmp_item @ [var]] @ !tmp_nonlocals; set_var var (eval vars globals nonlocals funcs funcinputs value) vars globals nonlocals funcs funcinputs; Nothing ) | FunctionDeclaration (func, inputs, to_exec) -> ( set_function funcs funcinputs func inputs to_exec; Nothing ) | Action (func, args) -> ( match name_of_variable func with "print" -> ( let tmp_str = ref "" in let tmp_args = list_of_arglist args in for i = 0 to List.length tmp_args - 1 do tmp_str := !tmp_str ^ (string_of_str (eval vars globals nonlocals funcs funcinputs (List.nth tmp_args i))) ^ " " done; print_endline !tmp_str; Nothing ) | "input" -> ( let returnable = ref Nothing in let tmp_args = list_of_arglist args in if List.length tmp_args = 1 then ( print_string (string_of_str (eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0))); returnable := Str (read_line ()) ) else ( raise (Failure "Too many arguments for input()") ); !returnable ) | "str" -> ( let returnable = ref Nothing in let tmp_args = list_of_arglist args in if List.length tmp_args = 1 then ( let tmp_return = eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0) in match tmp_return with Num n -> returnable := Str (string_of_float (n)) | Str s -> returnable := tmp_return ) else ( raise (Failure "Too many arguments for str()") ); !returnable ) | "len" -> ( let returnable = ref Nothing in let tmp_args = list_of_arglist args in if List.length tmp_args = 1 then ( let tmp_return = eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0) in match tmp_return with Str s -> returnable := Num (float_of_int (String.length s)) ) else ( raise (Failure "Too many arguments for len()") ); !returnable ) | "int" -> ( let returnable = ref Nothing in let tmp_args = list_of_arglist args in if List.length tmp_args = 1 then ( let tmp_return = eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0) in match tmp_return with Str s -> returnable := Num (float_of_string s) | Num n -> returnable := tmp_return ) else ( raise (Failure "Too many arguments for int()") ); !returnable ) | "range" -> ( let tmp_list = ref (List []) in let tmp_args = list_of_arglist args in if List.length tmp_args <> 2 then raise (Failure "Too many / not enough arguments for range()"); let left = int_of_float (float_of_num ( eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0))) in let right = int_of_float (float_of_num ( eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 1))) in for i = left to right - 1 do tmp_list := List (list_of_list !tmp_list @ [Num (float i)]) done; !tmp_list ) | "chr" -> ( let tmp_args = list_of_arglist args in if List.length tmp_args <> 1 then raise (Failure "Too many / not enough arguments for chr()"); Str (String.make 1 (char_of_int (int_of_float (float_of_num (eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0)))))) ) | "ord" -> ( let tmp_args = list_of_arglist args in if List.length tmp_args <> 1 then raise (Failure "Too many / not enough arguments for chr()"); Num (float (int_of_char (string_of_str (eval vars globals nonlocals funcs funcinputs (List.nth tmp_args 0))).[0])) ) | _ -> ( let returnable = ref Nothing in let function_call = ref PlaceHolder in let function_call_input = ref PlaceHolder in for i = 0 to List.length funcs - 1 do if Hashtbl.mem (List.nth funcs i) func then ( function_call := Hashtbl.find (List.nth funcs i) func; function_call_input := Hashtbl.find (List.nth funcinputs i) func ) done; if !function_call = PlaceHolder then ( if Hashtbl.mem functions func then ( function_call := Hashtbl.find functions func; function_call_input := Hashtbl.find functions_input func ) ); if !function_call <> PlaceHolder then ( let arglist = list_of_arglist args in let inputlist = list_of_inputlist !function_call_input in if List.length arglist = List.length inputlist then ( let func_vars = Hashtbl.copy (List.nth vars 0) in let func_funcs = Hashtbl.copy (List.nth funcs 0) in let func_funcinputs = Hashtbl.copy (List.nth funcinputs 0) in for i = 0 to List.length inputlist - 1 do Hashtbl.replace func_vars (List.nth inputlist i) (eval vars globals nonlocals funcs funcinputs (List.nth arglist i)); done; returnable := eval ([func_vars] @ vars) (ref ([[]] @ !globals)) (ref ([[]] @ !nonlocals)) ([func_funcs] @ funcs) ([func_funcinputs] @ funcinputs) !function_call; if List.length !return_stack >= 1 then ( returnable := (List.nth !return_stack 0); return_stack := List.tl !return_stack; ); !returnable ) else ( raise (Failure ("Too many / too little args for function: " ^ name_of_variable func)) ) ) else ( raise (Failure ("Undefined Function: " ^ name_of_variable func)) ); !returnable ) ) | RelativeAction (func, actor, args) -> ( match name_of_variable func with "isalpha" -> ( let returnable = ref true in let tmp_actor = string_of_str (eval vars globals nonlocals funcs funcinputs actor) in for i = 0 to String.length tmp_actor - 1 do if is_alpha tmp_actor.[i] = false then returnable := false done; Bool !returnable ) | "append" -> ( set_var actor (List (list_of_list (eval vars globals nonlocals funcs funcinputs actor) @ [(eval vars globals nonlocals funcs funcinputs (List.nth (list_of_arglist args) 0))])) vars globals nonlocals funcs funcinputs; Nothing ) | "isdigit" -> ( let returnable = ref true in let tmp_actor = string_of_str (eval vars globals nonlocals funcs funcinputs actor) in for i = 0 to String.length tmp_actor - 1 do if is_digit tmp_actor.[i] = false then returnable := false done; Bool !returnable ) | "isalnum" -> ( let returnable = ref true in let tmp_actor = string_of_str (eval vars globals nonlocals funcs funcinputs actor) in for i = 0 to String.length tmp_actor - 1 do if is_alnum tmp_actor.[i] = false then returnable := false done; Bool !returnable ) ) | Negate t -> Num (-1. *. float_of_num (eval vars globals nonlocals funcs funcinputs t)) | Return t -> ( return_stack := [eval vars globals nonlocals funcs funcinputs t] @ !return_stack; Nothing ) | TestEquals (left, right) -> Bool (eval vars globals nonlocals funcs funcinputs left = eval vars globals nonlocals funcs funcinputs right) | TestGreaterThan (left, right) -> ( if float_of_num (eval vars globals nonlocals funcs funcinputs left) > float_of_num (eval vars globals nonlocals funcs funcinputs right) then Bool true else Bool false ) | TestLessThan (left, right) -> ( if float_of_num (eval vars globals nonlocals funcs funcinputs left) < float_of_num (eval vars globals nonlocals funcs funcinputs right) then Bool true else Bool false ) | TestGreaterThanEquals (left, right) -> ( if float_of_num (eval vars globals nonlocals funcs funcinputs left) >= float_of_num (eval vars globals nonlocals funcs funcinputs right) then Bool true else Bool false ) | TestLessThanEquals (left, right) -> ( if float_of_num (eval vars globals nonlocals funcs funcinputs left) <= float_of_num (eval vars globals nonlocals funcs funcinputs right) then Bool true else Bool false ) | TestIn (left, right) -> ( let tmp_left = eval vars globals nonlocals funcs funcinputs left in let tmp_right = eval vars globals nonlocals funcs funcinputs right in let tmp_list = list_of_list tmp_right in let found = ref false in for i = 0 to List.length tmp_list - 1 do if tmp_left = List.nth tmp_list i then found := true done; Bool !found ) | AndStatement (left, right) -> Bool (eval vars globals nonlocals funcs funcinputs left = Bool true && eval vars globals nonlocals funcs funcinputs right = Bool true) | OrStatement (left, right) -> Bool (eval vars globals nonlocals funcs funcinputs left = Bool true || eval vars globals nonlocals funcs funcinputs right = Bool true) | IfStatement (test, func) -> ( if (eval vars globals nonlocals funcs funcinputs test) = Bool true then eval vars globals nonlocals funcs funcinputs func else Nothing ) | NotStatement t -> ( match eval vars globals nonlocals funcs funcinputs t with Bool true -> Bool false | Bool false -> Bool true ) | IfElseStatement (test, true_func, false_func) -> ( if (eval vars globals nonlocals funcs funcinputs test) = Bool true then eval vars globals nonlocals funcs funcinputs true_func else eval vars globals nonlocals funcs funcinputs false_func; Nothing ) | ForLoop (variable, for_expr, for_body) -> ( let tmp_expr = eval vars globals nonlocals funcs funcinputs for_expr in (match tmp_expr with List l -> ( let func_vars = Hashtbl.copy (List.nth vars 0) in for i = 0 to List.length l - 1 do Hashtbl.replace func_vars variable (List.nth l i); eval ([func_vars] @ vars) globals nonlocals funcs funcinputs for_body done; ) | Str s -> ( let func_vars = Hashtbl.copy (List.nth vars 0) in for i = 0 to String.length s - 1 do Hashtbl.replace func_vars variable (Str (String.make 1 s.[i])); eval ([func_vars] @ vars) globals nonlocals funcs funcinputs for_body done; ) ); Nothing ) | WhileLoop (comparitor, body) -> ( while eval vars globals nonlocals funcs funcinputs comparitor = Bool true do eval vars globals nonlocals funcs funcinputs body; done; Nothing ) | ItemOf (item, index) -> ( let returnable = ref Nothing in (match eval vars globals nonlocals funcs funcinputs item with Str s -> returnable := Str (String.make 1 (s).[int_of_float (float_of_num (eval vars globals nonlocals funcs funcinputs index))]) | List l -> returnable := List.nth (l) (int_of_float (float_of_num (eval vars globals nonlocals funcs funcinputs index))) | Dict d -> returnable := (Hashtbl.find d (eval vars globals nonlocals funcs funcinputs index)) | _ -> raise (Failure "Cannot use ItemOf on this")); !returnable ) | Modulo (left, right) -> ( let tmp_left = eval vars globals nonlocals funcs funcinputs left in let tmp_right = eval vars globals nonlocals funcs funcinputs right in Num (float (rmod (int_of_float (float_of_num tmp_left)) (int_of_float (float_of_num tmp_right)))) ) | _ -> raise (Failure "Executed BAD AST") in eval [empty_vars_table] empty_globals empty_nonlocals [empty_funcs_table] [empty_funcinputs_table] ast let tokens = tokenise program;; let ast = build_ast tokens;; interpret ast;