1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef LIBTEXTCLASSIFIER_UTILS_GRAMMAR_SEMANTICS_EVALUATORS_PARSE_NUMBER_EVAL_H_
18 #define LIBTEXTCLASSIFIER_UTILS_GRAMMAR_SEMANTICS_EVALUATORS_PARSE_NUMBER_EVAL_H_
19 
20 #include <string>
21 
22 #include "utils/base/arena.h"
23 #include "utils/base/statusor.h"
24 #include "utils/grammar/semantics/eval-context.h"
25 #include "utils/grammar/semantics/evaluator.h"
26 #include "utils/grammar/semantics/expression_generated.h"
27 #include "utils/grammar/semantics/value.h"
28 #include "utils/strings/numbers.h"
29 
30 namespace libtextclassifier3::grammar {
31 
32 // Parses a string as a number.
33 class ParseNumberEvaluator : public SemanticExpressionEvaluator {
34  public:
ParseNumberEvaluator(const SemanticExpressionEvaluator * composer)35   explicit ParseNumberEvaluator(const SemanticExpressionEvaluator* composer)
36       : composer_(composer) {}
37 
Apply(const EvalContext & context,const SemanticExpression * expression,UnsafeArena * arena)38   StatusOr<const SemanticValue*> Apply(const EvalContext& context,
39                                        const SemanticExpression* expression,
40                                        UnsafeArena* arena) const override {
41     TC3_DCHECK_EQ(expression->expression_type(),
42                   SemanticExpression_::Expression_ParseNumberExpression);
43     const ParseNumberExpression* parse_number_expression =
44         expression->expression_as_ParseNumberExpression();
45 
46     // Evaluate argument.
47     TC3_ASSIGN_OR_RETURN(
48         const SemanticValue* value,
49         composer_->Apply(context, parse_number_expression->value(), arena));
50     if (value == nullptr) {
51       return nullptr;
52     }
53     if (!value->Has<StringPiece>()) {
54       return Status(StatusCode::INVALID_ARGUMENT,
55                     "Argument didn't evaluate as a string value.");
56     }
57     const std::string data = value->Value<std::string>();
58 
59     // Parse the string data as a number.
60     const reflection::BaseType type =
61         static_cast<reflection::BaseType>(parse_number_expression->base_type());
62     if (flatbuffers::IsLong(type)) {
63       TC3_ASSIGN_OR_RETURN(const int64 value, TryParse<int64>(data));
64       return SemanticValue::Create(type, value, arena);
65     } else if (flatbuffers::IsInteger(type)) {
66       TC3_ASSIGN_OR_RETURN(const int32 value, TryParse<int32>(data));
67       return SemanticValue::Create(type, value, arena);
68     } else if (flatbuffers::IsFloat(type)) {
69       TC3_ASSIGN_OR_RETURN(const double value, TryParse<double>(data));
70       return SemanticValue::Create(type, value, arena);
71     } else {
72       return Status(StatusCode::INVALID_ARGUMENT,
73                     "Unsupported type: " + std::to_string(type));
74     }
75   }
76 
77  private:
78   template <typename T>
79   bool Parse(const std::string& data, T* value) const;
80 
81   template <>
Parse(const std::string & data,int32 * value)82   bool Parse(const std::string& data, int32* value) const {
83     return ParseInt32(data.data(), value);
84   }
85 
86   template <>
Parse(const std::string & data,int64 * value)87   bool Parse(const std::string& data, int64* value) const {
88     return ParseInt64(data.data(), value);
89   }
90 
91   template <>
Parse(const std::string & data,double * value)92   bool Parse(const std::string& data, double* value) const {
93     return ParseDouble(data.data(), value);
94   }
95 
96   template <typename T>
TryParse(const std::string & data)97   StatusOr<T> TryParse(const std::string& data) const {
98     T result;
99     if (!Parse<T>(data, &result)) {
100       return Status(StatusCode::INVALID_ARGUMENT, "Could not parse value.");
101     }
102     return result;
103   }
104 
105   const SemanticExpressionEvaluator* composer_;
106 };
107 
108 }  // namespace libtextclassifier3::grammar
109 
110 #endif  // LIBTEXTCLASSIFIER_UTILS_GRAMMAR_SEMANTICS_EVALUATORS_PARSE_NUMBER_EVAL_H_
111