aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/rfnoc/nocscript/expression.hpp
blob: 83fc5bcbc869cb972451558537559772e6191229 (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
375
376
377
378
379
380
//
// Copyright 2015 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <uhd/exception.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/function.hpp>
#include <boost/make_shared.hpp>
#include <vector>
#include <map>

#ifndef INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_EXPR_HPP
#define INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_EXPR_HPP

namespace uhd { namespace rfnoc { namespace nocscript {

// Forward declaration for expression::eval()
class expression_literal;

/*! Virtual base class for Noc-Script expressions.
 */
class expression
{
  public:
    typedef boost::shared_ptr<expression> sptr;

    //! All the possible return types for expressions within Noc-Script
    enum type_t {
        TYPE_INT,
        TYPE_DOUBLE,
        TYPE_STRING,
        TYPE_BOOL,
        TYPE_INT_VECTOR
    };

    // TODO make this a const and fix the [] usage
    static std::map<type_t, std::string> type_repr;

    //! Returns the type of this expression without evaluating it
    virtual type_t infer_type() const = 0;

    //! Evaluate current expression and return its return value
    virtual expression_literal eval() = 0;
};

/*! Literal (constant) expression class
 *
 * A literal is any value that is literally given in the NoC-Script
 * source code, such as '5', '"FOO"', or '2.3'.
 */
class expression_literal : public expression
{
  public:
    typedef boost::shared_ptr<expression_literal> sptr;

    template <typename expr_type>
    static sptr make(expr_type x) { return boost::make_shared<expression_literal>(x); };

    /*! Generate the literal expression from its token string representation.
     * This includes markup, e.g. a string would still have the quotes, and
     * a hex value would still have leading 0x.
     */
    expression_literal(
            const std::string token_val,
            expression::type_t type
    );

    //! Create a boolean literal expression from a C++ bool.
    expression_literal(bool b=false);
    //! Create an integer literal expression from a C++ int.
    expression_literal(int i);
    //! Create a double literal expression from a C++ double.
    expression_literal(double d);
    //! Create a string literal expression from a C++ string.
    expression_literal(const std::string &s);
    //! Create an int vector literal expression from a C++ vector<int>.
    expression_literal(std::vector<int> v);

    expression::type_t infer_type() const
    {
        return _type;
    }

    //! Literals aren't evaluated as such, so the evaluation
    //  simply returns a copy of itself.
    expression_literal eval()
    {
        return *this; // TODO make sure this is copy
    }

    /*! A 'type cast' to bool. Cast rules are similar to most
     * scripting languages:
     * - Integers and doubles are false if zero, true otherwise
     * - Strings are false if empty, true otherwise
     * - Vectors are false if empty, true otherwise
     */
    bool to_bool() const;

    /*! Convenience function to typecast to C++ int
     *
     * Note that the current type must be TYPE_INT.
     *
     * \return C++ int representation of current literal
     * \throws uhd::type_error if type didn't match
     */
    int get_int() const;

    /*! Convenience function to typecast to C++ double
     *
     * Note that the current type must be TYPE_DOUBLE.
     *
     * \return C++ double representation of current literal
     * \throws uhd::type_error if type didn't match
     */
    double get_double() const;

    /*! Convenience function to typecast to C++ std::string.
     *
     * Note that the current type must be TYPE_STRING.
     *
     * \return String representation of current literal.
     * \throws uhd::type_error if type didn't match.
     */
    std::string get_string() const;

    /*! Convenience function to typecast to C++ int vector.
     *
     * Note that the current type must be TYPE_INT_VECTOR.
     *
     * \return String representation of current literal.
     * \throws uhd::type_error if type didn't match.
     */
    std::vector<int> get_int_vector() const;

    /*! Convenience function to typecast to C++ bool.
     *
     * Note that the current type must be TYPE_BOOL.
     * See also expression_literal::to_bool() for a type-cast
     * style function.
     *
     * \return bool representation of current literal.
     * \throws uhd::type_error if type didn't match.
     */
    bool get_bool() const;

    //! String representation
    std::string repr() const;

    bool operator==(const expression_literal &rhs) const;

  private:
    //! For TYPE_BOOL
    bool _bool_val;

    //! For TYPE_INT
    int _int_val;

    //! For TYPE_DOUBLE
    double _double_val;

    //! For TYPE_INT_VECTOR
    std::vector<int> _int_vector_val;

    //! Store the token value
    std::string _val;

    //! Current expression type
    expression::type_t _type;
};

UHD_INLINE std::ostream& operator<< (std::ostream& out, const expression_literal &l)
{
    out << l.repr();
    return out;
}

UHD_INLINE std::ostream& operator<< (std::ostream& out, const expression_literal::sptr &l)
{
    out << l->repr();
    return out;
}

/*! Contains multiple (sub-)expressions.
 */
class expression_container : public expression
{
  public:
    typedef boost::shared_ptr<expression_container> sptr;
    typedef std::vector<expression::sptr> expr_list_type;

    //! Return an sptr to an empty container
    static sptr make();

    //! List of valid combination types (see expression_container::eval()).
    enum combiner_type {
        COMBINE_ALL,
        COMBINE_AND,
        COMBINE_OR,
        COMBINE_NOTSET
    };

    //! Create an empty container
    expression_container() : _combiner(COMBINE_NOTSET) {};

    /*! Type-deduction rules for containers are:
     * - If the combination type is COMBINE_ALL or COMBINE_AND,
     *   return value must be TYPE_BOOL
     * - In all other cases, we return the last expression return
     *   value, and hence its type is relevant
     */
    expression::type_t infer_type() const;

    /*! Add another expression container to this container.
     */
    virtual void add(expression::sptr new_expr);

    virtual bool empty() const;

    void set_combiner_safe(const combiner_type c);

    void set_combiner(const combiner_type c) { _combiner = c; };

    combiner_type get_combiner() const { return _combiner; };

    /*! Evaluate a container by evaluating its sub-expressions.
     *
     * If a container contains multiple sub-expressions, the rules
     * for evaluating them depend on the combiner_type:
     * - COMBINE_ALL: Run all the sub-expressions and return the last
     *   expression's return value
     * - COMBINE_AND: Run sub-expressions, in order, until one of them
     *   returns false. Following expressions are not evaluated (like
     *   most C++ compilers).
     * - COMBINE_OR: Run sub-expressions, in order, until one of them
     *   returns true. Following expressions are not evaluated.
     *
     * In the special case where no sub-expressions are contained, always
     * returns true.
     */
    virtual expression_literal eval();

  protected:
    //! Store all the sub-expressions, in order
    expr_list_type _sub_exprs;
    combiner_type _combiner;
};

// Forward declaration:
class function_table;
/*! A function call is a special type of container.
 *
 * All arguments are sub-expressions. The combiner type is
 * always COMBINE_ALL in this case (changing the combiner type
 * does not affect anything).
 *
 * The actual function maps to a C++ function available through
 * a uhd::rfnoc::nocscript::function_table object.
 *
 * The recommended to use this is:
 * 1. Create a function object giving its name (e.g. ADD)
 * 2. Use the add() method to add all the function arguments
 *    in the right order (left to right).
 * 3. Once step 2 is complete, the function object can be used.
 *    Call infer_type() to get the return value, if required.
 * 4. Calling eval() will call into the function table. The
 *    argument expressions are evaluated, if so required, inside
 *    the function (lazy evalulation). Functions do not need
 *    to evaluate arguments.
 */
class expression_function : public expression_container
{
  public:
    typedef boost::shared_ptr<expression_function> sptr;
    typedef std::vector<expression::type_t> argtype_list_type;

    //! Return an sptr to a function object without args
    static sptr make(
            const std::string &name,
            const boost::shared_ptr<function_table> func_table
    );

    static std::string to_string(const std::string &name, const argtype_list_type &types);

    expression_function(
            const std::string &name,
            const boost::shared_ptr<function_table> func_table
    );

    //! Add an argument expression
    virtual void add(expression::sptr new_expr);

    /*! Looks up the function type in the function table.
     *
     * Note that this will only work after all arguments have been
     * added, as they are also used to look up a function's type in the
     * function table.
     */
    expression::type_t infer_type() const;

    /*! Evaluate all arguments, then the function itself.
     */
    expression_literal eval();

    //! String representation
    std::string repr() const;

  private:
    std::string _name;
    const boost::shared_ptr<function_table> _func_table;
    std::vector<expression::type_t> _arg_types;
};


/*! Variable expression
 *
 * Variables are like literals, only their type and value aren't known
 * at parse-time. Instead, we provide a function object to look up
 * variable's types and value.
 */
class expression_variable : public expression
{
  public:
    typedef boost::shared_ptr<expression_variable> sptr;
    typedef boost::function<expression::type_t(const std::string &)> type_getter_type;
    typedef boost::function<expression_literal(const std::string &)> value_getter_type;

    static sptr make(
            const std::string &token_val,
            type_getter_type type_getter,
            value_getter_type value_getter
    );

    /*! Create a variable object from its token value
     *  (e.g. '$spp', i.e. including the '$' symbol). The variable
     *  does not have to exist at this point.
     */
    expression_variable(
            const std::string &token_val,
            type_getter_type type_getter,
            value_getter_type value_getter
    );

    /*! Looks up the variable type in the variable table.
     *
     * \throws Depending on \p type_getter, this may throw when the variable does not exist.
     *         Recommended behaviour is to throw uhd::syntax_error.
     */
    expression::type_t infer_type() const;

    /*! Look up a variable's value in the variable table.
     *
     * \throws Depending on \p value_getter, this may throw when the variable does not exist.
     *         Recommended behaviour is to throw uhd::syntax_error.
     */
    expression_literal eval();

  private:
    std::string _varname;
    type_getter_type _type_getter;
    value_getter_type _value_getter;
};

}}} /* namespace uhd::rfnoc::nocscript */

#endif /* INCLUDED_LIBUHD_RFNOC_NOCSCRIPT_EXPR_HPP */
// vim: sw=4 et: