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