package pfff

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file resolve_python.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
(* Yoann Padioleau
 *
 * Copyright (C) 2019 r2c
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation, with the
 * special exception on linking described in file license.txt.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
 * license.txt for more details.
 *)
open Ast_python
module Ast = Ast_python
module V = Visitor_python

(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(* Identifiers tagger (so we can colorize them differently in codemap/efuns).
 *
 * See also Ast_python.resolved_name.
 *)

(*****************************************************************************)
(* Type *)
(*****************************************************************************)
type context = 
  | AtToplevel
  | InClass
  | InFunction
 
type env = {
  ctx: context ref;
  names: (string * resolved_name) list ref;
}

let default_env () = {
  ctx = ref AtToplevel;
  names = ref [];
}

(*****************************************************************************)
(* Helpers *)
(*****************************************************************************)

(* because we use Visitor_python instead of a clean recursive 
 * function passing down an environment, we need to emulate a scoped
 * environment by using save_excursion.
 *)
let with_added_env xs env f = 
  let newnames = xs @ !(env.names) in
  Common.save_excursion env.names newnames f

let add_name_env name kind env =
  env.names := (Ast.str_of_name name, kind)::!(env.names)

let with_new_context ctx env f = 
  Common.save_excursion env.ctx ctx f


let params_of_parameters params =
  params |> Common.map_filter (function
    | ParamClassic ((name, _), _)
    | ParamStar (name, _) | ParamPow (name, _) 
    -> Some name
   )

(*****************************************************************************)
(* Entry point *)
(*****************************************************************************)

let resolve prog =
  let env = default_env () in

  (* helper to factorize code related to (polymorphic comprehension) *)
  let comprehension ke v (e, xs) =
    let new_vars = 
      xs |> Common.map_filter (function
        | CompFor (target, _) -> 
            (match target with
            | Name (name, _ctx, _res) -> Some name
            (* tuples? *)
            | _ -> None
            )
        | CompIf _ -> None
     )
   in 
   let new_names = new_vars |> List.map (fun name ->
       Ast.str_of_name name, Ast.LocalVar
   ) in
   with_added_env new_names env (fun () -> ke e);

   (* TODO: should fold and use new env *)
   xs |> List.iter (function
     | CompFor (e1, e2) -> 
          v (Expr e1); v (Expr e2)
     | CompIf e ->
          v (Expr e)
    )
  in    

  (* would be better to use a classic recursive with environment visit *)
  let visitor = V.mk_visitor { V.default_visitor with
    (* No need to resolve at the definition sites (for parameters, locals).
     * This will be patterned-match specially anyway in the highlighter. What
     * you want is to tag the use sites, and to maintain the right environment.
     *)
    V.kexpr = (fun (k, v) x ->
      match x with
      | Name (name, ctx, resolved) ->
          (match ctx with
          | Load | AugLoad ->
            (* assert resolved = NotResolved *)
            let s = Ast.str_of_name name in
            (match List.assoc_opt s !(env.names) with
            | Some x -> resolved := x
            | None -> () (* will be tagged as Error by highlighter later *)
            )
          | Store | AugStore ->
            let kind = 
              match !(env.ctx) with
              | AtToplevel -> GlobalVar
              | InClass -> ClassField
              | InFunction -> LocalVar
            in
            env |> add_name_env name kind;
            resolved := kind; (* optional *)
            
          | Del -> (* should remove from env *)
             ()
          | Param -> 
            resolved := Parameter; (* optional *)
          );
          k x
      | Tuple (CompForIf x, _)
      | List (CompForIf x, _)
        (* bugfix: do not pass just k here, because we want to intercept
         * the processing of e too!
         *)
        -> comprehension (fun e -> v (Expr e)) v x
      | DictOrSet (CompForIf x) -> 
        comprehension (fun elt -> v (DictElem elt)) v x

      (* general case *)
      | _ -> k x
    );
    V.kstmt = (fun (k, v) x ->
      match x with
      | FunctionDef (name, params, _typopt, _body, _decorators) ->
          let new_params = params_of_parameters (params: parameters) in
          let new_names = new_params |> List.map (fun name ->
               Ast.str_of_name name, Ast.Parameter
          ) in
          with_added_env new_names env (fun () -> 
            with_new_context InFunction env (fun () ->
               k x              
          ));
         (* nested function *)
         if !(env.ctx) = InFunction
         then env |> add_name_env name (LocalVar);
     | ClassDef (name, _bases, _body, _decorators) ->
           env |> add_name_env name (ImportedEntity []); (* could be more precise *)
           with_new_context InClass env (fun () ->
               k x              
            )
    
     | Import (aliases) ->
         aliases |> List.iter (fun (dotted_name, asname_opt) ->
           (match dotted_name with
           | [name] -> env |> add_name_env name (ImportedModule dotted_name)
           | _ -> (* TODO *) ()
           );
           asname_opt |> Common.do_option (fun asname ->
             env |> add_name_env asname (ImportedModule dotted_name)
           );
         );
         k x

     | ImportFrom (dotted_name, aliases, _) ->
         aliases |> List.iter (fun (name, asname_opt) ->
           env |> add_name_env name (ImportedEntity dotted_name);
           asname_opt |> Common.do_option (fun asname ->
             env |> add_name_env asname (ImportedEntity dotted_name)
           );
         );
         k x
     | With (e, eopt, stmts) ->
       v (Expr e);
       (match eopt with
       | None -> v (Stmts stmts)
       | Some (Name (name, _ctx, _res)) ->
          (* the scope of name is valid only inside the body, but the
           * body may define variables that are used then outside the with
           * so simpler to use add_name_env() here, not with_add_env()
          let new_names = (fst name, LocalVar)::!(env.names) in
          with_added_env new_names env (fun () ->
            v (Stmts stmts)
          )
           *)
          env |> add_name_env name LocalVar;
          v (Stmts stmts);
       (* todo: tuples? *)
       | Some e -> 
           v (Expr e);
           v (Stmts stmts)
       )
     | TryExcept (stmts1, excepts, stmts2) ->
       v (Stmts stmts1);
       excepts |> List.iter (fun (ExceptHandler (_typ, e, body)) ->
         match e with
         | None -> v (Stmts body)
         | Some (Name (name, _ctx, _res)) ->
           let new_names = (fst name, LocalVar)::!(env.names) in
           with_added_env new_names env (fun () ->
             v (Stmts body)
           )
         (* tuples? *)
         | Some e -> 
            v (Expr e);
            v (Stmts body)
       );
       v (Stmts stmts2);
     
     (* general case *)
     | _ -> k x
    );  
  } in
  visitor (Program prog)

OCaml

Innovation. Community. Security.