aboutsummaryrefslogtreecommitdiff
path: root/build2/parser
blob: 0962348b159879ad81edaa41142c3fa8a1ee02b2 (plain)
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
// file      : build2/parser -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef BUILD2_PARSER
#define BUILD2_PARSER

#include <stack>

#include <build2/types>
#include <build2/utility>

#include <build2/spec>
#include <build2/lexer>
#include <build2/token>
#include <build2/variable>
#include <build2/diagnostics>

namespace build2
{
  class scope;
  class target;

  class parser
  {
  public:
    using names_type = build2::names;
    using variable_type = build2::variable;

    // If boot is true, then we are parsing bootstrap.build and modules
    // should only be bootstrapped.
    //
    parser (bool boot = false): fail (&path_), boot_ (boot) {}

    // Issue diagnostics and throw failed in case of an error.
    //
    void
    parse_buildfile (istream&, const path& name, scope& root, scope& base);

    buildspec
    parse_buildspec (istream&, const path& name);

    token
    parse_variable (lexer&, scope&, const variable_type&, token_type kind);

    pair<value, token>
    parse_variable_value (lexer&, scope&, const variable_type&);

    names_type
    parse_export_stub (istream& is, const path& p, scope& r, scope& b)
    {
      parse_buildfile (is, p, r, b);
      return move (export_value_);
    }

    // Recursive descent parser.
    //
  protected:
    void
    clause (token&, token_type&);

    void
    print (token&, token_type&);

    void
    source (token&, token_type&);

    void
    include (token&, token_type&);

    void
    import (token&, token_type&);

    void
    export_ (token&, token_type&);

    void
    using_ (token&, token_type&);

    void
    define (token&, token_type&);

    void
    if_else (token&, token_type&);

    void
    variable (token&, token_type&, const variable_type&, token_type kind);

    string
    variable_name (names_type&&, const location&);

    // Note: calls attributes_push() that the caller must pop.
    //
    value
    variable_value (token&, token_type&);

    void
    variable_attributes (const variable_type&);

    void
    value_attributes (const variable_type*, // Optional.
                      value& lhs,
                      value&& rhs,
                      token_type kind);

    value
    eval (token&, token_type&);

    value
    eval_trailer (token&, token_type&);

    // Attributes stack. We can have nested attributes, for example:
    //
    // x = [bool] ([uint64] $x == [uint64] $y)
    //
    // In this example we only apply the value attributes after evaluating
    // the context, which has its own attributes.
    //
    struct attributes
    {
      bool has;                         // Has attributes flag.
      location loc;                     // Start of attributes location.
      vector<pair<string, string>> ats; // Attributes.

      explicit operator bool () const {return has;}
    };

    // Push a new entry into the attributes_ stack. If the next token is '['
    // parse the attribute sequence until ']' setting the 'has' flag and
    // storing the result on the stack. Then get the next token and, if
    // standalone is false, verify it is not newline/eos (i.e., there is
    // something after it). Return the top of the stack.
    //
    attributes&
    attributes_push (token&, token_type&, bool standalone = false);

    attributes
    attributes_pop ()
    {
      attributes r (move (attributes_.top ()));
      attributes_.pop ();
      return r;
    }

    attributes&
    attributes_top () {return attributes_.top ();}

    // If chunk is true, then parse the smallest but complete, name-wise,
    // chunk of input. Note that in this case you may still end up with
    // multiple names, for example, {foo bar}.
    //
    names_type
    names (token& t, token_type& tt, bool chunk = false)
    {
      names_type ns;
      names (t, tt, ns, chunk, 0, nullptr, nullptr, nullptr);
      return ns;
    }

    // As above but return the result as a value (always untyped) indicating
    // if it is NULL. The only way for the value to be NULL is if it is the
    // result of a sole, unquoted variable expansion, function call, or
    // context evaluation.
    //
    value
    names_value (token& t, token_type& tt)
    {
      names_type ns;
      return names (t, tt, ns, false, 0, nullptr, nullptr, nullptr)
        ? value (move (ns))
        : value (nullptr);
    }

    // Return true if the parsed value is NOT NULL.
    //
    bool
    names (token& t, token_type& tt, names_type& ns, bool chunk = false)
    {
      return names (t, tt, ns, chunk, 0, nullptr, nullptr, nullptr);
    }

    bool
    names (token&, token_type&,
           names_type&,
           bool chunk,
           size_t pair,
           const string* prj,
           const dir_path* dir,
           const string* type);

    size_t
    names_trailer (token&, token_type&,
                   names_type&,
                   size_t pair,
                   const string* prj,
                   const dir_path* dir,
                   const string* type);

    // Skip until newline or eos.
    //
    void
    skip_line (token&, token_type&);

    // Skip until block-closing } or eos, taking into account nested blocks.
    //
    void
    skip_block (token&, token_type&);

    // Return true if the name token can be considered a directive keyword.
    //
    bool
    keyword (token&);

    // Buildspec.
    //
    buildspec
    buildspec_clause (token&, token_type&, token_type end);

    // Utilities.
    //
  protected:
    class enter_scope;
    class enter_target;

    // Switch to a new current scope. Note that this function might also have
    // to switch to a new root scope if the new current scope is in another
    // project. So both must be saved and restored.
    //
    void
    switch_scope (const dir_path&);

    void
    process_default_target (token&);

    // Enter buildfile as a target.
    //
    void
    enter_buildfile (const path&);

    // Lexer.
    //
  protected:
    token_type
    next (token&, token_type&);

    // Be careful with peeking and switching the lexer mode. See keyword()
    // for more information.
    //
    token_type
    peek ();

    const token&
    peeked () const
    {
      assert (peeked_);
      return peek_;
    }

    void
    mode (lexer_mode m, char ps = '=')
    {
      if (replay_ != replay::play)
        lexer_->mode (m, ps);
    }

    lexer_mode
    mode () const
    {
      assert (replay_ != replay::play);
      return lexer_->mode ();
    }

    void
    expire_mode ()
    {
      if (replay_ != replay::play)
        lexer_->expire_mode ();
    }

    // Token saving and replaying. Note that it can only be used in certain
    // contexts. Specifically, the lexer mode should be the same and the code
    // that parses a replay must not interact with the lexer directly (e.g.,
    // the keyword() test). For now we don't enforce any of this.
    //
    // Note also that the peeked token is not part of the replay, until it
    // is "got".
    //
    //
    void
    replay_save ()
    {
      assert (replay_ == replay::stop);
      replay_ = replay::save;
    }

    void
    replay_play ()
    {
      assert ((replay_ == replay::save && !replay_data_.empty ()) ||
              (replay_ == replay::play && replay_i_ == replay_data_.size ()));

      replay_i_ = 0;
      replay_ = replay::play;
    }

    void
    replay_stop ()
    {
      replay_data_.clear ();
      replay_ = replay::stop;
    }

    const token&
    replay_next ()
    {
      assert (replay_i_ != replay_data_.size ());
      return replay_data_[replay_i_++];
    }

    struct replay_guard
    {
      replay_guard (parser& p, bool start = true)
          : p_ (start ? &p : nullptr)
      {
        if (p_ != nullptr)
          p_->replay_save ();
      }

      void
      play ()
      {
        if (p_ != nullptr)
          p_->replay_play ();
      }

      ~replay_guard ()
      {
        if (p_ != nullptr)
          p_->replay_stop ();
      }

    private:
      parser* p_;
    };

    // Diagnostics.
    //
  protected:
    const fail_mark<failed> fail;

  protected:
    bool boot_;

    const path* path_; // Current path.
    lexer* lexer_;
    target* target_; // Current target, if any.
    scope* scope_;   // Current base scope (out_base).
    scope* root_;    // Current root scope (out_root).

    std::stack<attributes> attributes_;

    target* default_target_;
    names_type export_value_;

    token peek_ = token (token_type::eos, false, 0, 0);
    bool peeked_ = false;

    enum class replay {stop, save, play} replay_ = replay::stop;
    vector<token> replay_data_;
    size_t replay_i_; // Position of the next token during replay.
  };
}

#endif // BUILD2_PARSER