package pfff

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

Source file parse_lisp.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
(* Yoann Padioleau
 * 
 * Copyright (C) 2010 Facebook
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License (GPL)
 * version 2 as published by the Free Software Foundation.
 * 
 * This program 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 Common 

open Parser_lisp
open Ast_lisp
module PI = Parse_info
(* we don't need a full grammar for lisp code, so we put everything,
 * the token type, the helper in parser_ml. No token_helpers_lisp.ml
 *)
module TH = Parser_lisp

(*****************************************************************************)
(* Prelude *)
(*****************************************************************************)
(* 
 * alt: 
 *  - Could reuse the parser in ocamlsexp ? but they just have Atom | Sexp
 *    and I need to differentiate numbers in the highlighter, and
 *    also handling quoted, anti-quoted and other lisp special things.
 *)

(*****************************************************************************)
(* Types *)
(*****************************************************************************)

(* the token list contains also the comment-tokens *)
type program_and_tokens = 
  Ast_lisp.program option * Parser_lisp.token list

(*****************************************************************************)
(* Lexing only *)
(*****************************************************************************)

(* could factorize and take the tokenf and visitor_of_infof in argument
 * but sometimes copy-paste is ok.
 *)
let tokens2 file = 
  let token = Lexer_lisp.token in
  Parse_info.tokenize_all_and_adjust_pos 
    file token TH.visitor_info_of_tok TH.is_eof

let tokens a = 
  Common.profile_code "Parse_lisp.tokens" (fun () -> tokens2 a)

(*****************************************************************************)
(* Parser *)
(*****************************************************************************)

(* simple recursive descent parser *)
let rec sexps toks =
  match toks with
  | [] -> [], []
  | [EOF _] -> [], []
  | (TCParen _ | TCBracket _)::_ -> [], toks
  | xs ->
    let s, rest = sexp xs in
    let xs, rest = sexps rest in
    s::xs, rest

and sexp toks =
  match toks with
  | [] -> raise Todo
  | x::xs ->
    (match x with
    | TComment _ | TCommentSpace _ | TCommentNewline _ -> raise Impossible

    | TNumber x -> Atom (Number x), xs
    | TString x -> Atom (String x), xs
    | TIdent x -> Atom (Id x), xs

    | TOParen t1 -> 
      let (xs, rest) = sexps xs in
      (match rest with
      | TCParen t2::rest ->
          Sexp ((t1, xs, t2)), rest
      | _ -> raise (PI.Other_error ("unclosed parenthesis", t1))
      )

    | TOBracket t1 -> 
      let (xs, rest) = sexps xs in
      (match rest with
      | TCBracket t2::rest ->
          Sexp ((t1, xs, t2)), rest
      | _ -> raise (PI.Other_error ("unclosed bracket", t1))
      )

    | TCParen t | TCBracket t ->
      raise (PI.Other_error ("closing bracket/paren without opening one", t))

    | TQuote t ->
      let (s, rest) = sexp xs in
      Special ((Quote, t), s), rest
    | TBackQuote t ->
      let (s, rest) = sexp xs in
      Special ((BackQuote, t), s), rest
    | TAt t ->
      let (s, rest) = sexp xs in
      Special ((At, t), s), rest
    | TComma t ->
      let (s, rest) = sexp xs in
      Special ((Comma, t), s), rest

    (* hmmm probably unicode *)
    | TUnknown t ->
      Atom (String (PI.str_of_info t, t)), xs

    | EOF t ->
      raise (PI.Other_error ("unexpected eof", t))
    )
      

(*****************************************************************************)
(* Main entry point *)
(*****************************************************************************)

let parse2 filename =

  let stat = Parse_info.default_stat filename in
  let toks_orig = tokens filename in

  let toks = toks_orig |> Common.exclude TH.is_comment in
  let nblines = Common2.nblines filename in

  let ast = 
    try
      (match sexps toks with
      | xs, [] ->
        stat.PI.correct <- nblines;
        Some xs
      | _, x::_xs ->
        raise (PI.Other_error ("trailing constructs", (TH.info_of_tok x)))
      )
    with
    | PI.Other_error (s, info) ->
      pr2 (spf "Parse error: %s, {%s} at %s" 
             s 
             (PI.str_of_info info)
             (PI.string_of_info info));
      stat.PI.bad <- nblines;
      None
    | exn -> 
      raise exn
  in
  (ast, toks_orig), stat

let parse a = 
  Common.profile_code "Parse_lisp.parse" (fun () -> parse2 a)

let parse_program file =
  let (ast, _toks), _stat =  parse file in
  Common2.some ast
OCaml

Innovation. Community. Security.