Module Qcert.Translation.Lang.JavaScriptAsttoJavaScript


Require Import List.
Require Import String.
Require Import StringAdd.
Require Import Peano_dec.
Require Import EquivDec.
Require Import Utils.
Require Import JavaScriptAstRuntime.
Require Import JavaScriptRuntime.
Require Import JsAst.JsNumber.

Local Open Scope string_scope.

Section ToString.

  Section Util.
    Definition eol:string := String (Ascii.ascii_of_nat 10) EmptyString.
    Definition quotel:string := """".

    Fixpoint indent (i : nat) : string :=
      match i with
      | 0 => ""
      | S j => " " ++ (indent j)
      end.

    Definition comma_list l := concat ", " l.

    Definition js_quote_char (a:ascii) : string :=
      match a with
      | "008"%char => "\b"
      | "009"%char => "\t"
      | "010"%char => "\n"
      | "013"%char => "\r"
      | """"%char => "\"""
      | "\"%char => "\\"
      | _ => String a EmptyString
      end.

    Definition js_quote_string (s:string) : string :=
      flat_map_string js_quote_char s.

    Definition js_quote_number (n:number) : string :=
      if (float_eq n float_infinity) then "Infinity"
      else if (float_eq n float_neg_infinity) then "-Infinity"
      else if (float_eq n float_nan) then "NaN"
      else to_string n.

  End Util.

  Definition string_of_literal (c: literal) : string :=
    match c with
    | literal_null => "null"
    | literal_bool true => "true"
    | literal_bool false => "false"
    | literal_number n => js_quote_number n
    | literal_string s => quotel ++ js_quote_string s ++ quotel
    | literal_bigint z => Z_to_string10 z ++ "n"
    end.

  Definition string_of_propname (name: propname) : string :=
    match name with
    | propname_identifier n => n
    | propname_string s => quotel ++ s ++ quotel
    | propname_number n => to_string n
    end.

  Fixpoint string_of_expr
           (e: expr)
           (i: nat)
           { struct e }
    : string :=
    match e with
    | expr_this => "this"
    | expr_identifier x => x
    | expr_literal c => string_of_literal c
    | expr_object o =>
      let props :=
          List.map
            (fun (prop: (propname * propbody)) =>
               let (name, body) := prop in
               eol ++ (indent (i+1)) ++
                   match body with
                   | propbody_val e =>
                     quotel ++ string_of_propname name ++ quotel
                            ++ ": " ++ "(" ++ string_of_expr e (i+1) ++ ")"
                   | propbody_get funcbody =>
                     "get " ++ quotel ++ string_of_propname name ++ quotel
                            ++ "() {" ++ (string_of_funcbody funcbody (i+1)) ++ "}"
                   | propbody_set args funcbody =>
                     "set " ++ quotel ++ string_of_propname name ++ quotel
                            ++ "(" ++ comma_list args ++ ") {"
                            ++ string_of_funcbody funcbody (i+1) ++ "}"
                   end)
            o
      in
      "{" ++ comma_list props ++ eol ++ indent i ++ "}"
    | expr_array a =>
      let l :=
          List.map
            (fun eopt =>
               match eopt with
               | Some e => string_of_expr e (i+1)
               | None => "null"
               end)
            a
      in
      "[ " ++ comma_list l ++ " ]"
    | expr_function fopt args body =>
      let name :=
          match fopt with
          | Some f => f
          | None => ""
          end
      in
      "(function " ++ name ++ "(" ++ comma_list args ++ ") {" ++ eol ++
       indent (i+1) ++ string_of_funcbody body (i+1) ++ eol ++
       indent i ++ "})"
    | expr_access e1 e2 =>
      string_of_expr e1 i ++ "[" ++ string_of_expr e2 (i+1) ++ "]"
    | expr_member e s =>
      string_of_expr e i ++ "[" ++ quotel ++ s ++ quotel ++ "]"
    | expr_new e args =>
      let args := List.map (fun e => string_of_expr e (i+1)) args in
      "new " ++ string_of_expr e i ++ "(" ++ comma_list args ++ ")"
    | expr_call f args =>
      let args := List.map (fun e => string_of_expr e (i+1)) args in
      string_of_expr f i ++ "(" ++ comma_list args ++ ")"
    | expr_unary_op op e =>
      let e := string_of_expr e (i+1) in
      match op with
      | unary_op_delete => "(delete " ++ e ++ ")"
      | unary_op_void => "(void " ++ e ++ ")"
      | unary_op_typeof => "(typeof " ++ e ++ ")"
      | unary_op_post_incr => "(" ++ e ++ "++)"
      | unary_op_post_decr => "(" ++ e ++ "--)"
      | unary_op_pre_incr => "(++" ++ e ++ ")"
      | unary_op_pre_decr => "(--" ++ e ++ ")"
      | unary_op_add => "(+" ++ e ++ ")"
      | unary_op_neg => "(-" ++ e ++ ")"
      | unary_op_bitwise_not => "(~" ++ e ++ ")"
      | unary_op_not => "(!" ++ e ++ ")"
      end
    | expr_binary_op e1 op e2 =>
      let e1 := string_of_expr e1 (i+1) in
      let e2 := string_of_expr e2 (i+1) in
      match op with
      | binary_op_mult => "(" ++ e1 ++ " * " ++ e2 ++ ")"
      | binary_op_div => "(" ++ e1 ++ " / " ++ e2 ++ ")"
      | binary_op_mod => "(" ++ e1 ++ " % " ++ e2 ++ ")"
      | binary_op_add => "(" ++ e1 ++ " + " ++ e2 ++ ")"
      | binary_op_sub => "(" ++ e1 ++ " - " ++ e2 ++ ")"
      | binary_op_left_shift => "(" ++ e1 ++ " << " ++ e2 ++ ")"
      | binary_op_right_shift => "(" ++ e1 ++ " >> " ++ e2 ++ ")"
      | binary_op_unsigned_right_shift => "(" ++ e1 ++ " >>> " ++ e2 ++ ")"
      | binary_op_lt => "(" ++ e1 ++ " < " ++ e2 ++ ")"
      | binary_op_gt => "(" ++ e1 ++ " > " ++ e2 ++ ")"
      | binary_op_le => "(" ++ e1 ++ " <= " ++ e2 ++ ")"
      | binary_op_ge => "(" ++ e1 ++ " >= " ++ e2 ++ ")"
      | binary_op_instanceof => "(" ++ e1 ++ " instanceof " ++ e2 ++ ")"
      | binary_op_in => "(" ++ e1 ++ " in " ++ e2 ++ ")"
      | binary_op_equal => "(" ++ e1 ++ " == " ++ e2 ++ ")"
      | binary_op_disequal => "(" ++ e1 ++ " != " ++ e2 ++ ")"
      | binary_op_strict_equal => "(" ++ e1 ++ " === " ++ e2 ++ ")"
      | binary_op_strict_disequal => "(" ++ e1 ++ " !== " ++ e2 ++ ")"
      | binary_op_bitwise_and => "(" ++ e1 ++ " & " ++ e2 ++ ")"
      | binary_op_bitwise_or => "(" ++ e1 ++ " | " ++ e2 ++ ")"
      | binary_op_bitwise_xor => "(" ++ e1 ++ " ^ " ++ e2 ++ ")"
      | binary_op_and => "(" ++ e1 ++ " && " ++ e2 ++ ")"
      | binary_op_or => "(" ++ e1 ++ " || " ++ e2 ++ ")"
      | binary_op_coma => "(" ++ e1 ++ ", " ++ e2 ++ ")"
      end
    | expr_conditional e1 e2 e3 =>
      let e1 := string_of_expr e1 (i+1) in
      let e2 := string_of_expr e2 (i+1) in
      let e3 := string_of_expr e3 (i+1) in
      "(" ++ e1 ++ " ? " ++ e2 ++ " : " ++ e3 ++ ")"
    | expr_assign e1 None e2 =>
      let e1 := string_of_expr e1 (i+1) in
      let e2 := string_of_expr e2 (i+1) in
      e1 ++ " = " ++ e2
    | _ => "XXX TODO XXX"
    end

  with string_of_stat
         (s: stat)
         (i: nat)
         { struct s }
       : string :=
         indent i ++
                match s with
                | stat_expr e =>
                  string_of_expr e i ++ ";"
                | stat_label lbl s =>
                  lbl ++ ":" ++ string_of_stat s i
                | stat_block l =>
                  let seq :=
                      List.map (fun s => string_of_stat s (i+1)) l
                  in
                  "{" ++ eol ++
                      concat (";" ++ eol) seq ++ eol ++
                      indent i ++ "}"
                | stat_var_decl l =>
                  let decls :=
                      List.map
                        (fun (x_e_opt: string * option expr) =>
                           let (x, e_opt) := x_e_opt in
                           "var " ++ x ++
                            match e_opt with
                            | Some e =>
                              " = " ++ string_of_expr e (i+1)
                            | None => ""
                            end
                        )
                        l
                  in
                  concat (";" ++ eol) decls
                | stat_let_decl l =>
                  let decls :=
                      List.map
                        (fun (x_e_opt: string * option expr) =>
                           let (x, e_opt) := x_e_opt in
                           "let " ++ x ++
                            match e_opt with
                            | Some e =>
                              " = " ++ string_of_expr e (i+1)
                            | None => ""
                            end
                        )
                        l
                  in
                  concat (";" ++ eol) decls
                | stat_if e s1 s2_opt =>
                  "if (" ++ string_of_expr e (i+1) ++ ") {" ++ eol ++
                   string_of_stat s1 (i+1) ++ eol ++
                   indent i ++ "} else {" ++ eol ++
                   match s2_opt with
                   | Some s2 => string_of_stat s2 (i+1) ++ eol
                   | None => ""
                   end ++
                   indent i ++ "}"
                | stat_return None =>
                  "return ;"
                | stat_return (Some e) =>
                  "return " ++ (string_of_expr e (i+1)) ++ ";"
                | stat_for_var lbl vars e2_opt e3_opt s =>
                  let decls :=
                      List.map
                        (fun (decl: (string * option expr)) =>
                           let (x, e1_opt) := decl in
                           x ++
                             match e1_opt with
                             | None => ""
                             | Some e1 => " = " ++ string_of_expr e1 (i+1)
                             end)
                        vars
                  in

                  "for (" ++
                   "var " ++ comma_list decls ++ "; " ++
                   match e2_opt with
                   | Some e2 => string_of_expr e2 (i+1)
                   | None => ""
                   end ++ "; " ++
                   match e3_opt with
                   | Some e3 => string_of_expr e3 (i+1)
                   | None => ""
                   end ++ ") {" ++ eol ++
                   string_of_stat s (i+1) ++ eol ++
                   indent i ++ "}" ++ eol
                | stat_for_let lbl vars e2_opt e3_opt s =>
                  let decls :=
                      List.map
                        (fun (decl: (string * option expr)) =>
                           let (x, e1_opt) := decl in
                           x ++
                             match e1_opt with
                             | None => ""
                             | Some e1 => " = " ++ string_of_expr e1 (i+1)
                             end)
                        vars
                  in

                  "for (" ++
                   "let " ++ comma_list decls ++ "; " ++
                   match e2_opt with
                   | Some e2 => string_of_expr e2 (i+1)
                   | None => ""
                   end ++ "; " ++
                   match e3_opt with
                   | Some e3 => string_of_expr e3 (i+1)
                   | None => ""
                   end ++ ") {" ++ eol ++
                   string_of_stat s (i+1) ++ eol ++
                   indent i ++ "}" ++ eol
                | stat_for_in_var lbl x e1_opt e2 s =>

                  "for (var " ++ x ++
                   match e1_opt with
                   | Some e => " = " ++ string_of_expr e (i+1)
                   | None => ""
                   end ++
                   " in " ++ string_of_expr e2 (i+1) ++ ") {" ++ eol ++
                   string_of_stat s (i+1) ++ eol ++
                   indent i ++ "}" ++ eol
                | stat_for_in_let lbl x e1_opt e2 s =>

                  "for (let " ++ x ++
                   match e1_opt with
                   | Some e => " = " ++ string_of_expr e (i+1)
                   | None => ""
                   end ++
                   " in " ++ string_of_expr e2 (i+1) ++ ") {" ++ eol ++
                   string_of_stat s (i+1) ++ eol ++
                   indent i ++ "}" ++ eol
                | _ => "XXX TODO XXX"
                end

  with string_of_element
         (e: element)
         (i: nat)
         { struct e }
       : string :=
         match e with
         | element_stat s =>
           string_of_stat s i
         | element_func_decl f params body =>
           eol ++ indent i
               ++ "function " ++ f ++ "(" ++ (comma_list params) ++ ") {" ++ eol
               ++ string_of_funcbody body (i+1) ++ eol ++ indent i ++ "}"
         end

  with string_of_prog
         (p: prog)
         (i: nat)
         { struct p }
       : string :=
         match p with
         | prog_intro _ elems =>
           let elems' := List.map (fun e => string_of_element e i) elems in
           concat eol elems'
         end

  with string_of_funcbody
         (body: funcbody)
         (i: nat)
         { struct body }
       : string :=
         match body with
         | funcbody_intro p _ =>
           string_of_prog p i
         end.

  Definition string_of_funcdecl
             (f:funcdecl)
             (i: nat)
    : string :=
    eol ++ indent i
        ++ "function " ++ (f.(funcdecl_name)) ++ "(" ++ (comma_list f.(funcdecl_parameters)) ++ ") {" ++ eol
    ++ string_of_funcbody f.(funcdecl_body) (i+1) ++ eol ++ indent i ++ "}"
  .

  Definition string_of_method
             (f:funcdecl)
             (i: nat)
    : string :=
    eol ++ indent i ++ "static " ++ f.(funcdecl_name) ++ "(" ++ (comma_list f.(funcdecl_parameters)) ++ ") {" ++ eol
        ++ string_of_funcbody f.(funcdecl_body) (i+1) ++ eol ++ indent i ++ "}"
  .

  Definition string_of_decl(d:topdecl)
    : string :=
    match d with
    | strictmode => eol ++ "'use strict';"
    | comment c => eol ++ "/*" ++ c ++ "*/"
    | elementdecl fd => string_of_element fd 0
    | classdecl cn cd =>
      eol ++ "class " ++ cn ++ "{"
          ++ List.fold_left (fun acc q => append acc (string_of_method q 1)) cd (""%string) ++ eol
          ++ "}"
    | constdecl x e =>
      eol ++ "const " ++ x ++ "=" ++ string_of_expr e 0
    end.
End ToString.

Section JavaScriptAsttoJavaScript.

  Definition js_ast_to_js_top (ja:js_ast) : javascript :=
    List.fold_left (fun acc f => append acc (string_of_decl f)) ja (""%string).

End JavaScriptAsttoJavaScript.