ocaml-llists / pa_llistcomp.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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
(* Lazy list comprehension syntax extension
 *
 * Author: Vadim Shender
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version,
 * with the special exception on linking described in file LICENSE.
 *
 * 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *)


open Camlp4.PreCast
open Syntax


let fresh_typevar =
  let cnt = ref 0 in
  function _loc ->
    incr cnt ; <:ctyp< '$lid:"llistcomp_typevar_" ^ (string_of_int !cnt)$ >>


(* lookahead grammar entries for lazy list comprehension items distinction *)

let rec loop n = function
    []     -> None
  | [x, _] -> if n = 1 then Some x else None
  | _ :: l -> loop (n - 1) l

let stream_peek_nth n strm = loop n (Stream.npeek n strm)

let test_patt_lessminus =
  Gram.Entry.of_parser "test_patt_lessminus"
    ( function strm ->
        let rec skip_patt n =
          match stream_peek_nth n strm with
            Some (KEYWORD "<-")         -> n
          | Some (KEYWORD ("[" | "[<")) ->
              skip_patt (ignore_upto "]" (n + 1) + 1)
          | Some (KEYWORD "(")          ->
              skip_patt (ignore_upto ")" (n + 1) + 1)
          | Some (KEYWORD "{")          ->
              skip_patt (ignore_upto "}" (n + 1) + 1)
          | Some (KEYWORD ("as" | "::" | "," | "_"))
          | Some (LIDENT _ | UIDENT _)  -> skip_patt (n + 1)
          | Some _ | None               -> raise Stream.Failure
        and     ignore_upto end_kwd n =
          match stream_peek_nth n strm with
            Some (KEYWORD prm) when prm = end_kwd -> n
          | Some (KEYWORD ("[" | "[<"))           ->
              ignore_upto end_kwd (ignore_upto "]" (n + 1) + 1)
          | Some (KEYWORD "(")                    ->
              ignore_upto end_kwd (ignore_upto ")" (n + 1) + 1)
          | Some (KEYWORD "{")                    ->
              ignore_upto end_kwd (ignore_upto "}" (n + 1) + 1)
          | Some _                                -> ignore_upto end_kwd (n + 1)
          | None                                  -> raise Stream.Failure
        in skip_patt 1 )

let test_patt_let_without_in =
  Gram.Entry.of_parser "test_patt_let_without_in"
    ( function strm ->
        let rec skip_patt n =
          match stream_peek_nth n strm with
            Some (KEYWORD "let") ->
              skip_patt (ignore_upto "in" (n + 1) + 1)
          | Some (KEYWORD ("[" | "[<")) ->
              skip_patt (ignore_upto "]" (n + 1) + 1)
          | Some (KEYWORD "(")          ->
              skip_patt (ignore_upto ")" (n + 1) + 1)
          | Some (KEYWORD "{")          ->
              skip_patt (ignore_upto "}" (n + 1) + 1)
          | Some (KEYWORD ("in")) -> raise Stream.Failure
          | Some (KEYWORD ("]" | ";" | "|")) | None -> n
          | Some _ -> skip_patt (n + 1)
        and     ignore_upto end_kwd n =
          match stream_peek_nth n strm with
            Some (KEYWORD prm) when prm = end_kwd -> n
          | Some (KEYWORD "let") ->
              ignore_upto end_kwd (ignore_upto "in" (n + 1) + 1)
          | Some (KEYWORD ("[" | "[<"))           ->
              ignore_upto end_kwd (ignore_upto "]" (n + 1) + 1)
          | Some (KEYWORD "(")                    ->
              ignore_upto end_kwd (ignore_upto ")" (n + 1) + 1)
          | Some (KEYWORD "{")                    ->
              ignore_upto end_kwd (ignore_upto "}" (n + 1) + 1)
          | Some _                                -> ignore_upto end_kwd (n + 1)
          | None                                  -> raise Stream.Failure
        in
        match stream_peek_nth 1 strm with
          Some (KEYWORD "let") -> skip_patt 2
        | _                    -> raise Stream.Failure )


(* generation of list comprehension expansion *)

module S = Set.Make (String)

let vars_of_patt =
  let fold_vars_of_patt =
    object (self)
      inherit Ast.fold as super

      val vars = S.empty

      method vars = vars
      method patt = function
          <:patt< $lid:s$ >> | <:patt< ~ $lid:s$ >> | <:patt< ? $s$ >> ->
            {< vars = S.add s vars >}
        | p -> super#patt p
    end
  in function p -> (fold_vars_of_patt#patt p)#vars

let rec binding_vars_of_binding = function
    <:binding< $p$ = $e$ and $rest$ >> ->
      S.union (vars_of_patt p) (binding_vars_of_binding rest)
  | <:binding< $p$ = $e$ >>            ->
      vars_of_patt p
  | _                                  ->
      failwith "[bound_vars_of_let] impossible case"

let free_vars_of_expr =
  let fold_free_vars_of_expr =
    object (self)
      inherit Ast.fold as super

      val env  = S.empty
      val free = S.empty

      method free = free
      method set_env env    = {< env = env >}
      method add_atom s     = {< env = S.add s env >}
      method add_binding bi = {< env = binding_vars_of_binding bi >}
      method add_patt p     = {< env = vars_of_patt p >}

      method expr = function
          <:expr< $lid:s$ >> | <:expr< ~ $s$ >> | <:expr< ? $s$ >> ->
            if S.mem s env then self else {< free = S.add s free >}
        | <:expr< let $bi$ in $e$ >> ->
            ((((self#add_binding bi)#expr e)#set_env env)#binding bi)#set_env env
        | <:expr< let rec $bi$ in $e$ >> ->
            (((self#add_binding bi)#expr e)#binding bi)#set_env env
        | <:expr< for $s$ = $e1$ $to:_$ $e2$ do { $e3$ } >> ->
            ((((self#expr e1)#expr e2)#add_atom s)#expr e3)#set_env env
        | e -> super#expr e

      method match_case = function
          <:match_case< $p$ when $w$ -> $e$ >> ->
            (((self#add_patt p)#expr w)#expr e)#set_env env
        | mc -> super#match_case mc
    end
  in
  function e -> (fold_free_vars_of_expr#expr e)#free

let does_binding_bind_free_vars_of_expr bi e =
  S.cardinal (S.inter (binding_vars_of_binding bi) (free_vars_of_expr e)) > 0


let map _loc p e l =
  match (p, e) with
    (<:patt< $lid:x$ >>, <:expr< $lid:y$ >>) when x = y -> l
  | _ ->
      if Ast.is_irrefut_patt p then
        <:expr< Lazy_list.map (fun $p$ -> $e$) $l$ >>
      else
        <:expr< Lazy_list.map
                  (fun [ $p$ -> $e$  | _ -> failwith "[map] impossible case" ])
                  ( Lazy_list.filter
                      (fun [ $p$ -> True | _ -> False ])
                       $l$ ) >>

let filter _loc p b l =
  if Ast.is_irrefut_patt p then
    <:expr< Lazy_list.filter (fun $p$ -> $b$) $l$ >>
  else
    <:expr< Lazy_list.filter (fun [ $p$ -> $b$ | _ -> False ]) $l$ >>

let concat _loc l =
  <:expr< Lazy_list.concat $l$ >>

let let_binding _loc rf bi e =
  <:expr< let $rec:rf$ $bi$ in $e$ >>

let conjunction _loc b1 b2 =
  <:expr< $b1$ && $b2$ >>


let rec compr_branch_aux _loc e = function
  | `Gen (p, l) :: items ->
      ( match compr_branch_aux _loc e items with
          `C_gen e'             -> `C_gen (concat _loc (map _loc p e' l))
        | `C_guard (b, e')      -> `C_gen (concat _loc (map _loc p e' (filter _loc p b l)))
        | `C_expr e'            -> `C_gen (map _loc p e' l)
        | `C_guard_expr (b, e') -> `C_gen (map _loc p e' (filter _loc p b l)) )
  | `Guard b :: items ->
      ( match compr_branch_aux _loc e items with
          `C_gen e'              -> `C_guard (b, e')
        | `C_guard (b', e')      -> `C_guard (conjunction _loc b b', e')
        | `C_expr e'             -> `C_guard_expr (b, e')
        | `C_guard_expr (b', e') -> `C_guard_expr (conjunction _loc b b', e') )
  | `Let (rf, bi) :: items ->
      ( match compr_branch_aux _loc e items with
          `C_gen e'             -> `C_gen (let_binding _loc rf bi e')
        | `C_guard (b, e')      ->
            let fb = does_binding_bind_free_vars_of_expr bi b
            and fe = does_binding_bind_free_vars_of_expr bi e' in
            `C_guard ( (if fb then let_binding _loc rf bi b else b),
                       (if fe || not fb then let_binding _loc rf bi e' else e') )
        | `C_expr e'            -> `C_expr (let_binding _loc rf bi e')
        | `C_guard_expr (b, e') ->
            let fb = does_binding_bind_free_vars_of_expr bi b
            and fe = does_binding_bind_free_vars_of_expr bi e' in
            `C_guard_expr ( (if fb then let_binding _loc rf bi b else b),
                            (if fe || not fb then let_binding _loc rf bi e' else e') ) )
  | [] -> `C_expr e
  | _ -> raise Stream.Failure

let compr_branch _loc e tl =
  match compr_branch_aux _loc e tl with
    `C_gen e' | `C_expr e' -> e'
  | `C_guard (b, e') | `C_guard_expr (b, e') ->
      (* It surely will cause the error "Unbound value .." *)
      <:expr< if $b$ then $e$ else Lazy_list.empty >>

let rec comprehension _loc e branches =
  compr_branch _loc e (List.hd branches)  (* parallel list comprehension coming soon... *)


(* new parser for infixop3 grammar entry, which releases "%:" operator *)

let symbolchar =
  let list = [ '$'; '!'; '%'; '&'; '*'; '+'; '-'; '.'; '/'; ':';
               '<'; '='; '>'; '?'; '@'; '^'; '|'; '~'; '\\' ] in
  let rec loop s i = i == String.length s || (List.mem s.[i] list && loop s (i + 1))
  in loop

let list = ['*'; '/'; '%'; '\\'] in
Gram.Entry.setup_parser infixop3
  ( parser
      [< '((KEYWORD x | SYMBOL x), _loc)
           when
             x <> "%:" &&
             String.length x >= 1 && List.mem x.[0] list &&
             (x.[0] <> '*' || String.length x < 2 || x.[1] <> '*') &&
             symbolchar x 1 >] ->
        <:expr< $lid:x$ >> )


let lazy_list_content      = Gram.Entry.mk "lazy_list_content"
let lazy_list_content_rest = Gram.Entry.mk "lazy_list_content_rest"


let is_revised =
  try
    DELETE_RULE Gram str_item: value_let; opt_rec; binding END ;
    true
  with Not_found ->
    false

let () = Gram.Entry.clear str_item


(* syntax for cons operator *)

if is_revised then begin
  EXTEND Gram
    GLOBAL: lazy_list_content lazy_list_content_rest;

    lazy_list_content:
      [ [ hd = expr LEVEL "top"; "::"; tl = expr LEVEL "top" ->
            <:expr< (lazy (Lazy_list.Cons $hd$ $tl$) : Lazy_list.t $fresh_typevar _loc$) >> ] ] ;

    lazy_list_content_rest:
      [ [ hd' = expr LEVEL "top"; "::"; tl = expr LEVEL "top" ->
          fun hd -> <:expr< lazy (Lazy_list.Cons $hd$ (lazy (Lazy_list.Cons $hd'$ $tl$))) >> ] ] ;
  END
end else begin
  DELETE_RULE Gram expr: expr; infixop3; expr END ;

  EXTEND Gram
    GLOBAL: expr infixop3 ;

    expr: LEVEL "*"
      [ [ e1 = expr; op = infixop3; e2 = expr ->
            <:expr< $op$ $e1$ $e2$ >> ] ] ;

    expr: AFTER "^"
      [ "%:" RIGHTA
        [ hd = expr; "%:" ; tl = expr ->
            <:expr< (lazy (Lazy_list.Cons $hd$ $tl$) : Lazy_list.t $fresh_typevar _loc$) >> ] ] ;
  END
end


(* syntax for lazy list generators and lazy list comprehension *)

EXTEND Gram
  GLOBAL: expr lazy_list_content lazy_list_content_rest ;

  expr: LEVEL "simple"  (* LEFTA *)
    [ [ "[%"; "]" ->
          <:expr< Lazy_list.empty >>
      | "[%"; content = lazy_list_content; "]" ->
          <:expr< ($content$ : Lazy_list.t $fresh_typevar _loc$) >> ] ] ;

  lazy_list_content:
    [ [ e = expr LEVEL "top"; ";"; rest = lazy_list_content_rest ->
          <:expr< $rest e$ >>
      | e = expr LEVEL "top" ->
          <:expr< lazy (Lazy_list.Cons $e$ Lazy_list.empty) >>

      | first = expr LEVEL "top"; "..";
        last  = expr LEVEL "top"  ->
          <:expr< Lazy_list.interval $first$ $last$ >>
      | first = expr LEVEL "top"; "..";
        last  = expr LEVEL "top"; ";";
        list  = lazy_list_content ->
          <:expr< Lazy_list.append (Lazy_list.interval $first$ $last$) $list$ >>

      | first = expr LEVEL "top"; ".." ->
          <:expr< Lazy_list.interval_inf $first$ >>

      | e = expr LEVEL "top"; "|"; branches = LIST1 branch SEP "|" ->
          comprehension _loc e branches ] ] ;

  lazy_list_content_rest:
    [ [ e = expr LEVEL "top"; ";"; rest = lazy_list_content_rest ->
          fun hd -> <:expr< lazy (Lazy_list.Cons $hd$ ($rest e$)) >>
      | e = expr LEVEL "top" ->
          fun hd -> <:expr< lazy (Lazy_list.Cons $hd$ (lazy (Lazy_list.Cons $e$ Lazy_list.empty))) >>

      | second = expr LEVEL "top"; "..";
        last   = expr LEVEL "top" ->
          fun first -> <:expr< Lazy_list.interval $first$ $last$ ~step:($second$ - $first$) >>
      | second = expr LEVEL "top"; "..";
        last   = expr LEVEL "top"; ";";
        list   = lazy_list_content ->
          fun first ->
            <:expr< Lazy_list.append
                      (Lazy_list.interval $first$ $last$ ~step:($second$ - $first$))
                      $list$ >>

      | second = expr LEVEL "top"; ".." ->
          fun first -> <:expr< Lazy_list.interval_inf $first$ ~step:($second$ - $first$) >> ] ] ;

  branch:
    [ [ items = LIST1 item SEP ";" -> items ] ] ;

  item:
    [ [ test_patt_lessminus; p = patt; "<-"; e = expr LEVEL "top" ->
          `Gen (p, e)
      | test_patt_let_without_in;
        "let"; rf = opt_rec; bl = LIST1 item_let_binding SEP "and" ->
          `Let ( rf, List.fold_left
                       (fun bi bl -> <:binding< $bi$ and $bl$ >>)
                       (List.hd bl) (List.tl bl) )
      | e = expr LEVEL "top" ->
          `Guard e ] ] ;

  item_let_binding:
    [ [ p = patt; "="; e = expr LEVEL "top" -> <:binding< $p$ = $e$ >> ] ] ;

END


(* syntax for lazy list pattern matching *)

if is_revised then
  EXTEND Gram
    GLOBAL: patt ;

    patt: LEVEL "simple" 
      [ [ "[%"; "]" ->
            <:patt< $uid:"lazy_nil"$ >>
        | "[%"; mk_list = sem_patt_for_conses; "::"; last = patt; "]" ->
            <:patt< $mk_list last$ >>
        | "[%"; mk_list = sem_patt_for_conses; "]" ->
            <:patt< $mk_list <:patt< $uid:"lazy_nil"$ >>$ >> ] ] ;

    sem_patt_for_conses:
      [ [ p = patt; ";"; pl = SELF -> fun acc -> <:patt< $uid:"lazy_cons"$ $p$ $pl acc$ >>
        | p = patt                 -> fun acc -> <:patt< $uid:"lazy_cons"$ $p$ $acc$ >> ] ] ;
  END
else
  EXTEND Gram
    GLOBAL: patt ;

    patt: BEFORE "apply"
      [ "%:" RIGHTA
        [ p1 = patt; "%:"; p2 = patt -> <:patt< $uid:"lazy_cons"$ $p1$ $p2$ >> ] ] ;

    patt: LEVEL "simple" 
      [ [ "[%"; "]" ->
            <:patt< $uid:"lazy_nil"$ >>
        | "[%"; (n, mk_list) = sem_patt_for_list'; "]" ->
            <:patt< $uid:"lazy_list"$ $`int:n$ $mk_list <:patt< [] >>$ >> ] ] ;

    sem_patt_for_list':
      [ [ p = patt; ";"; (pn, pl) = SELF -> pn + 1, fun acc -> <:patt< [ $p$ :: $pl acc$ ] >>
        | p = patt                       -> 1, fun acc -> <:patt< [ $p$ :: $acc$ ] >> ] ] ;
  END


object
  inherit Patterns.extension
  method translate v = function
      <:patt@_loc< $uid:"lazy_nil"$ >> ->
        Some ( <:expr< Lazy.force $v$ >>,
               <:patt< Lazy_list.Nil >> )
    | <:patt@_loc< $uid:"lazy_cons"$ $p1$ $p2$ >> ->
        Some ( <:expr< Lazy.force $v$ >>,
               <:patt< Lazy_list.Cons ($p1$, $p2$) >> )
    | <:patt@_loc< $uid:"lazy_list"$ $n$ $pl$ >> ->
        ( match n with
            <:patt< $int:limit$>> ->
              Some ( <:expr< Lazy_list.to_list ~limit:$int:limit$ $v$ >>, pl )
          | _ -> failwith "[translate] impossible case" )
    | _ -> None
end


(* top-level/filter problem workaround *)

let mk_anti ?(c = "") n s = "\\$" ^ n ^ c ^ ":" ^ s

if is_revised then
  EXTEND Gram
    GLOBAL: str_item ;

    str_item:
      [ "top"
        [ value_let; r = opt_rec; bi = binding ->
            Patterns.filter <:str_item< value $rec:r$ $bi$ >> ] ] ;
  END
else
  EXTEND Gram
    GLOBAL: str_item;

    str_item:
      [ "top"
        [ "let"; r = opt_rec; bi = binding; "in"; x = expr ->
            Patterns.filter <:str_item< let $rec:r$ $bi$ in $x$ >>
        | "let"; r = opt_rec; bi = binding ->
            ( match bi with
                <:binding< _ = $e$ >> -> Patterns.filter <:str_item< $exp:e$ >>
              | _ ->                     Patterns.filter <:str_item< value $rec:r$ $bi$ >> )
        | "let"; "module"; m = a_UIDENT; mb = module_binding0; "in"; e = expr ->
             Patterns.filter <:str_item< let module $m$ = $mb$ in $e$ >> ] ] ;
  END

EXTEND Gram
  GLOBAL: str_item ;

  string_list:
    [ [ `ANTIQUOT ((""|"str_list"), s) -> Ast.LAnt (mk_anti "str_list" s)
      | `STRING (_, x); xs = string_list -> Ast.LCons (x, xs)
      | `STRING (_, x)                   -> Ast.LCons (x, Ast.LNil) ] ]
  ;

  str_item:
    [ "top"
(**)  [ "exception"; t = constructor_declaration ->
          Patterns.filter <:str_item< exception $t$ >>
(**)  | "exception"; t = constructor_declaration; "="; i = type_longident ->
          Patterns.filter <:str_item< exception $t$ = $i$ >>
(**)  | "external"; i = a_LIDENT; ":"; t = ctyp; "="; sl = string_list ->
          Patterns.filter <:str_item< external $i$ : $t$ = $sl$ >>
(**)  | "include"; me = module_expr -> Patterns.filter <:str_item< include $me$ >>
      | "module"; i = a_UIDENT; mb = module_binding0 ->
          Patterns.filter <:str_item< module $i$ = $mb$ >>
      | "module"; "rec"; mb = module_binding ->
          Patterns.filter <:str_item< module rec $mb$ >>
      | "module"; "type"; i = a_UIDENT; "="; mt = module_type ->
          Patterns.filter <:str_item< module type $i$ = $mt$ >>
(**)  | "open"; i = module_longident -> Patterns.filter <:str_item< open $i$ >>
(**)  | "type"; td = type_declaration ->
          Patterns.filter <:str_item< type $td$ >>
      | "class"; cd = class_declaration ->
          Patterns.filter <:str_item< class $cd$ >>
      | "class"; "type"; ctd = class_type_declaration ->
          Patterns.filter <:str_item< class type $ctd$ >>
      | `ANTIQUOT ((""|"stri"|"anti"|"list" as n), s) ->
          Patterns.filter <:str_item< $anti:mk_anti ~c:"str_item" n s$ >>
      | `QUOTATION (x) -> Patterns.filter (Quotation.expand _loc x Quotation.DynAst.str_item_tag)
      | e = expr -> Patterns.filter <:str_item< $exp:e$ >> ] ] ;
END
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.