(*sprejme .asm file in pretvori v neko interno predstavitev*)

type line = {
  line_no : int;
  label : string option;
  opcode : string;
  operand : string list; (*list of operands*)
  comment : string option;
  mutable loc : int option;   (* assigned in pass1 *)
}


let parseLine (lineNo : int) (line : string) : line option= 
  let trimmedLine = String.trim (line) in (*Znebimo se zacetnih in koncnih presledkov*)
    (*empty line or comment line*)
    if trimmedLine = "" then None
    else if trimmedLine.[0] = '.' then
    Some {
      line_no = lineNo;
      label = None;
      opcode = "COMMENT";
      operand = [];
      comment = Some trimmedLine ;
      loc = None;

    }

    else
      (*razdelimo na kodo in komentar za njo*)
      let code, (comment : string option) = 
      match String.index_opt line '.' with
      | None -> line, None
      | Some i ->   
          (*izluščimo komentar in komentarju odstranimo predhodnje presledke*)
          let c = String.sub line i (String.length line - i) |> String.trim in 
          (*izluščimo kodo, je pa ne trimamo, ker rabimo prvo presledek da razločimo opcode od label-a*)
          let code = String.sub line 0 i in
          code, Some c
      in

      let tokens =   
        code
        |> String.map (fun c -> if c = ',' then ' ' else c) (*zamenja , z ' ' da lepo parsamo npr RMO A, B*)
        |> String.split_on_char ' '
        |> List.map String.trim           (* removes surrounding spaces and \r *)
        |> List.filter (fun s -> s <> "") in
      match tokens with
      | [] -> 
        Some{
          line_no = lineNo;
          label = None;
          opcode = "";
          operand = [];
          comment;
          loc = None;
        }
      | x :: xs -> 
        if code.[0] <> ' ' && code.[0] <> '\t' then (*pogledamo whitespace ali tab*)
          (*se ne začne z presledkom ali tab -> imamo labelo*)
          let label = Some x in 
          match xs with
          (*samo labela*)
          | [] -> Some{line_no = lineNo; label = label; opcode = ""; operand = []; comment = comment; loc = None}
          (*labela + opcode + še nekaj*)
          | opcode :: operand -> 
            Some{line_no = lineNo; label = label;opcode = opcode; operand = operand; comment = comment; loc = None}
        else
          (*nimamo labele*)
          let opcode = x in 
          let operand = xs in
          Some{line_no = lineNo; label = None; opcode = opcode; operand = operand; comment = comment; loc = None} 

(*naredi listo line-ov iz filename*)
let parser (filename : string) : line list = 
  let ic = open_in filename in
  let rec loop line_no acc =
    match input_line ic with
    | line ->
        begin
          match parseLine line_no line with
          | Some parsed_line -> loop (line_no + 1) (parsed_line :: acc)
          | None -> loop (line_no + 1) acc 
        end
    | exception End_of_file ->
        close_in ic;
        List.rev acc
  in
  loop 1 []
