1/*
2 * Copyright (c) 2011-2014, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30#include "RuleParser.h"
31#include "CompoundRule.h"
32#include "SelectionCriterionRule.h"
33#include "AlwaysAssert.hpp"
34#include <assert.h>
35
36using std::string;
37
38// Matches
39const char *CRuleParser::_acDelimiters[CRuleParser::ENbStatuses] = {
40    "{",   // EInit
41    "{} ", // EBeginCompoundRule
42    ",}",  // EEndCompoundRule
43    ",}",  // ECriterionRule
44    "{ ",  // EContinue
45    ""     // EDone
46};
47
48CRuleParser::CRuleParser(const string &strApplicationRule,
49                         const CSelectionCriteriaDefinition *pSelectionCriteriaDefinition)
50    : _strApplicationRule(strApplicationRule),
51      _pSelectionCriteriaDefinition(pSelectionCriteriaDefinition)
52{
53}
54
55CRuleParser::~CRuleParser()
56{
57    delete _pRootRule;
58}
59
60// Parse
61bool CRuleParser::parse(CCompoundRule *pParentRule, string &strError)
62{
63    while (true) {
64        // Iterate till next relevant delimiter
65        if (!iterate(strError)) {
66
67            return false;
68        }
69        switch (_eStatus) {
70        case EBeginCompoundRule: {
71
72            // Create new compound rule
73            CCompoundRule *pCompoundRule = new CCompoundRule;
74
75            // Parse
76            if (!pCompoundRule->parse(*this, strError)) {
77
78                delete pCompoundRule;
79
80                return false;
81            }
82            // Parent rule creation context?
83            if (pParentRule) {
84
85                // Chain
86                pParentRule->addChild(pCompoundRule);
87            } else {
88                // Root rule
89                delete _pRootRule;
90                _pRootRule = pCompoundRule;
91            }
92            // Parse
93            if (!parse(pCompoundRule, strError)) {
94
95                return false;
96            }
97            // Go on
98            break;
99        }
100        case EEndCompoundRule:
101            return true;
102        case EContinue:
103            // Seek for new rule
104            break;
105        case ECriterionRule: {
106            // Create new criterion rule
107            CSelectionCriterionRule *pCriterionRule = new CSelectionCriterionRule;
108
109            // Parse
110            if (!pCriterionRule->parse(*this, strError)) {
111
112                delete pCriterionRule;
113
114                return false;
115            }
116
117            ALWAYS_ASSERT(pParentRule != NULL, "Invalid parent rule given to rule parser");
118            // Chain
119            pParentRule->addChild(pCriterionRule);
120
121            // Go on
122            break;
123        }
124        case EDone: {
125            // If the current state is EDone, check that at least one rule has been found.
126            if (_pRootRule) {
127
128                // At least one rule found
129                return true;
130            } else {
131
132                strError = "Syntax error, no rule found";
133
134                return false;
135            }
136        }
137        default:
138            assert(0);
139            return false;
140        }
141    }
142
143    return true;
144}
145
146// Iterate
147bool CRuleParser::iterate(string &strError)
148{
149    string::size_type delimiter;
150
151    ALWAYS_ASSERT(_uiCurrentPos <= _strApplicationRule.length(), "Current Position outside range");
152
153    // Consume spaces
154    if ((delimiter = _strApplicationRule.find_first_not_of(" ", _uiCurrentPos)) != string::npos) {
155
156        // New pos
157        _uiCurrentPos = delimiter;
158    }
159
160    // Parse
161    if ((_uiCurrentPos != _strApplicationRule.length()) &&
162        ((delimiter = _strApplicationRule.find_first_of(_acDelimiters[_eStatus], _uiCurrentPos)) !=
163         string::npos)) {
164
165        switch (_strApplicationRule[delimiter]) {
166
167        case '{':
168            _eStatus = EBeginCompoundRule;
169            // Extract type
170            _strRuleType = _strApplicationRule.substr(_uiCurrentPos, delimiter - _uiCurrentPos);
171            _currentDeepness++;
172            break;
173        case '}':
174            _eStatus = EEndCompoundRule;
175
176            if (!_currentDeepness--) {
177
178                strError = "Missing opening brace";
179
180                return false;
181            }
182            break;
183        case ' ':
184            _eStatus = ECriterionRule;
185            // Extract type
186            _strRuleType = _strApplicationRule.substr(_uiCurrentPos, delimiter - _uiCurrentPos);
187            break;
188        case ',':
189            _eStatus = EContinue;
190            break;
191        }
192        // New pos
193        _uiCurrentPos = delimiter + 1;
194    } else {
195
196        if (_currentDeepness) {
197
198            strError = "Missing closing brace";
199
200            return false;
201        }
202
203        // Remaining characters
204        if (_uiCurrentPos != _strApplicationRule.length()) {
205
206            strError = "Syntax error";
207
208            return false;
209        }
210        // Done
211        _eStatus = EDone;
212    }
213    return true;
214}
215
216// Rule type
217const string &CRuleParser::getType() const
218{
219    return _strRuleType;
220}
221
222// Criteria defintion
223const CSelectionCriteriaDefinition *CRuleParser::getSelectionCriteriaDefinition() const
224{
225    return _pSelectionCriteriaDefinition;
226}
227
228// Root rule
229CCompoundRule *CRuleParser::grabRootRule()
230{
231    CCompoundRule *pRootRule = _pRootRule;
232
233    assert(pRootRule);
234
235    _pRootRule = NULL;
236
237    return pRootRule;
238}
239
240// Next word
241bool CRuleParser::next(string &strNext, string &strError)
242{
243    string::size_type delimiter;
244
245    // Consume spaces
246    if ((delimiter = _strApplicationRule.find_first_not_of(" ", _uiCurrentPos)) != string::npos) {
247
248        // New pos
249        _uiCurrentPos = delimiter;
250    }
251
252    if ((delimiter = _strApplicationRule.find_first_of("{} ,", _uiCurrentPos)) == string::npos) {
253
254        strError = "Syntax error";
255
256        return false;
257    }
258
259    strNext = _strApplicationRule.substr(_uiCurrentPos, delimiter - _uiCurrentPos);
260
261    // New pos
262    _uiCurrentPos = delimiter;
263
264    return true;
265}
266