1 // SExp - A S-Expression Parser for C++
2 // Copyright (C) 2015 Ingo Ruhnke <grumbel@gmail.com>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 #include <gtest/gtest.h>
18 
19 #include <sstream>
20 
21 #include "sexp/io.hpp"
22 #include "sexp/parser.hpp"
23 #include "sexp/util.hpp"
24 #include "sexp/value.hpp"
25 
TEST(ParserTest,single)26 TEST(ParserTest, single)
27 {
28   auto result = sexp::Parser::from_string("(1 2.5 foo \"TEXT\" bar)");
29   auto expected = sexp::Value::list(sexp::Value::integer(1),
30                                     sexp::Value::real(2.5),
31                                     sexp::Value::symbol("foo"),
32                                     sexp::Value::string("TEXT"),
33                                     sexp::Value::symbol("bar"));
34   ASSERT_EQ(expected, result);
35 }
36 
TEST(ParserTest,single_fail)37 TEST(ParserTest, single_fail)
38 {
39   std::istringstream in("(1 2.5 foo \"TEXT\" bar bar) 5");
40   ASSERT_THROW({
41       sexp::Value cons = sexp::Parser::from_stream(in);
42     },
43     std::runtime_error);
44 }
45 
TEST(ParserTest,parse_many)46 TEST(ParserTest, parse_many)
47 {
48   std::istringstream in("1 2.5 foo \"TEXT\" bar");
49   std::vector<sexp::Value> value = sexp::Parser::from_stream_many(in);
50   ASSERT_EQ(5, value.size());
51   ASSERT_EQ(value[0].get_type(), sexp::Value::Type::INTEGER);
52   ASSERT_EQ(value[1].get_type(), sexp::Value::Type::REAL);
53   ASSERT_EQ(value[2].get_type(), sexp::Value::Type::SYMBOL);
54   ASSERT_EQ(value[3].get_type(), sexp::Value::Type::STRING);
55   ASSERT_EQ(value[4].get_type(), sexp::Value::Type::SYMBOL);
56 }
57 
TEST(ParserTest,parse_positive_integer)58 TEST(ParserTest, parse_positive_integer)
59 {
60   auto sx = sexp::Parser::from_string("12345");
61   ASSERT_EQ(12345, sx.as_int());
62 }
63 
TEST(ParserTest,parse_negative_integer)64 TEST(ParserTest, parse_negative_integer)
65 {
66   {
67     auto sx = sexp::Parser::from_string("-12345");
68     ASSERT_EQ(-12345, sx.as_int());
69   }
70 
71   {
72     sexp::Value sx = sexp::Parser::from_string("-");
73     ASSERT_TRUE(sx.is_symbol());
74   }
75 }
76 
TEST(ParserTest,parse_positive_real)77 TEST(ParserTest, parse_positive_real)
78 {
79   {
80     auto sx = sexp::Parser::from_string("0.125");
81     ASSERT_EQ(sexp::Value::Type::REAL, sx.get_type());
82     ASSERT_EQ(0.125f, sx.as_float());
83   }
84 
85   {
86     auto sx = sexp::Parser::from_string(".125");
87     ASSERT_EQ(sexp::Value::Type::REAL, sx.get_type());
88     ASSERT_EQ(0.125f, sx.as_float());
89   }
90 }
91 
TEST(ParserTest,parse_negative_real)92 TEST(ParserTest, parse_negative_real)
93 {
94   {
95     auto sx = sexp::Parser::from_string("-0.125");
96     ASSERT_EQ(sexp::Value::Type::REAL, sx.get_type());
97     ASSERT_EQ(-0.125f, sx.as_float());
98   }
99 
100   {
101     auto sx = sexp::Parser::from_string("-.125");
102     ASSERT_EQ(sexp::Value::Type::REAL, sx.get_type());
103     ASSERT_EQ(-0.125f, sx.as_float());
104   }
105 }
106 
TEST(ParserTest,parse_scientific_real)107 TEST(ParserTest, parse_scientific_real)
108 {
109   {
110     auto sx = sexp::Parser::from_string("1.2345e-13");
111     ASSERT_EQ(sexp::Value::Type::REAL, sx.get_type());
112     ASSERT_EQ(1.2345e-13f, sx.as_float());
113   }
114 
115   {
116     auto sx = sexp::Parser::from_string("-1.2345e+13");
117     ASSERT_EQ(sexp::Value::Type::REAL, sx.get_type());
118     ASSERT_EQ(-1.2345e+13f, sx.as_float());
119   }
120 }
121 
TEST(ParserTest,parse_string)122 TEST(ParserTest, parse_string)
123 {
124   {
125     auto sx = sexp::Parser::from_string("\"Hello\\nWorld\"");
126     ASSERT_EQ(sexp::Value::Type::STRING, sx.get_type());
127     ASSERT_EQ("Hello\nWorld", sx.as_string());
128   }
129 
130   {
131     auto sx = sexp::Parser::from_string("\"\\\"Hello\\nWorld\\\"\"");
132     ASSERT_EQ(sexp::Value::Type::STRING, sx.get_type());
133     ASSERT_EQ("\"Hello\nWorld\"", sx.as_string());
134   }
135 }
136 
TEST(ParserTest,parse_symbol)137 TEST(ParserTest, parse_symbol)
138 {
139   {
140     auto sx = sexp::Parser::from_string("HelloWorld");
141     ASSERT_EQ(sexp::Value::Type::SYMBOL, sx.get_type());
142     ASSERT_EQ("HelloWorld", sx.as_string());
143   }
144 
145   {
146     auto sx = sexp::Parser::from_string("5.6.7");
147     ASSERT_EQ(sexp::Value::Type::SYMBOL, sx.get_type());
148     ASSERT_EQ("5.6.7", sx.as_string());
149   }
150 }
151 
TEST(ParserTest,parse_array)152 TEST(ParserTest, parse_array)
153 {
154   char const* sx_str = "#(1 \"foo\" #(bar))";
155   auto sx = sexp::Parser::from_string(sx_str);
156   ASSERT_TRUE(sx.is_array());
157   ASSERT_EQ(sexp::Value::Type::ARRAY, sx.get_type());
158   ASSERT_EQ(sx_str, sx.str());
159 }
160 
161 // FIXME: Compare data structure or use simple strings?!
162 // "(foo . bar)" as string is ambigous in the current parser as . can be handled as symbol, not pair
TEST(ParserTest,simple_pair)163 TEST(ParserTest, simple_pair)
164 {
165   sexp::Value sx = sexp::Parser::from_string("(foo . bar)");
166   ASSERT_EQ("(foo . bar)", sx.str());
167   ASSERT_EQ("foo", sx.get_car().as_string());
168   ASSERT_EQ("bar", sx.get_cdr().as_string());
169   ASSERT_EQ(sexp::Value::Type::CONS, sx.get_type());
170   ASSERT_EQ(sexp::Value::Type::SYMBOL, sx.get_car().get_type());
171   ASSERT_EQ(sexp::Value::Type::SYMBOL, sx.get_cdr().get_type());
172 
173   ASSERT_EQ("(foo . bar)", sexp::Parser::from_string("(foo . bar)").str());
174 }
175 
TEST(ParserTest,list_pair)176 TEST(ParserTest, list_pair)
177 {
178   sexp::Value sx = sexp::Parser::from_string("(1 2 3 4 5 . 6)");
179   ASSERT_EQ("(1 2 3 4 5 . 6)", sx.str());
180 }
181 
TEST(ParserTest,line_numbers)182 TEST(ParserTest, line_numbers)
183 {
184   sexp::Value sx = sexp::Parser::from_string("("
185                                              "line1\n"
186                                              "line2\n"
187                                              "line3\n"
188                                              ")\n");
189 
190   ASSERT_EQ(1, sexp::list_ref(sx, 0).get_line());
191   ASSERT_EQ(2, sexp::list_ref(sx, 1).get_line());
192   ASSERT_EQ(3, sexp::list_ref(sx, 2).get_line());
193 }
194 
195 
196 #ifdef SEXP_USE_LOCALE
197 
198 // C++ locale support comes in the form of ugly global state that
199 // spreads over most string formating functions, changing locale can
200 // break a lot of stuff.
201 class ParserLocaleTest : public ::testing::Test
202 {
203 private:
204   std::locale m_oldlocale;
205 
206 protected:
ParserLocaleTest()207   ParserLocaleTest() : m_oldlocale()
208   {}
209 
SetUp()210   virtual void SetUp()
211   {
212     std::locale::global(std::locale("de_DE.UTF-8"));
213   }
214 
TearDown()215   virtual void TearDown()
216   {
217     std::locale::global(m_oldlocale);
218   }
219 };
220 
TEST_F(ParserLocaleTest,locale_safe_input)221 TEST_F(ParserLocaleTest, locale_safe_input)
222 {
223   sexp::Value sx = sexp::Parser::from_string("0.015625");
224   ASSERT_EQ(0.015625f, sx.as_float());
225 }
226 
TEST_F(ParserLocaleTest,locale_safe_output)227 TEST_F(ParserLocaleTest, locale_safe_output)
228 {
229   sexp::Value sx = sexp::Value::real(0.015625f);
230   ASSERT_EQ("0.015625", sx.str());
231 }
232 
233 #endif
234 
235 /* EOF */
236