KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sexpr_parser.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016 Mark Roszko <[email protected]>
3 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "sexpr/sexpr_parser.h"
21#include <cctype>
22#include <iterator>
23#include <stdexcept>
24
25#include <fstream>
26#include <streambuf>
27#include <wx/file.h>
28#include <wx/ffile.h>
29
30#include <string_utils.h>
31
32namespace SEXPR
33{
34 const std::string PARSER::whitespaceCharacters = " \t\n\r\b\f\v";
35
37 {
38 }
39
41 {
42 }
43
44 std::unique_ptr<SEXPR> PARSER::Parse( const std::string& aString )
45 {
46 std::string::const_iterator it = aString.begin();
47 return parseString( aString, it );
48 }
49
50 std::unique_ptr<SEXPR> PARSER::ParseFromFile( const std::string& aFileName )
51 {
52 std::string str = GetFileContents( aFileName );
53
54 std::string::const_iterator it = str.begin();
55 return parseString( str, it );
56 }
57
58 std::string PARSER::GetFileContents( const std::string &aFileName )
59 {
60 std::string str;
61
62 // the filename is not always a UTF7 string, so do not use ifstream
63 // that do not work with unicode chars.
64 #if defined( __MINGW32__ )
65 // On msys2 currently using From_UTF8( defined in string_utils.cpp) creates a link
66 // issue. (From_UTF8() undefined symbol). So redefine it here (duplicate) for now.
67 wxString fname( wxString::FromUTF8( aFileName.c_str() ) );
68
69 if( fname.IsEmpty() ) // happens when aString is not a valid UTF8 sequence
70 {
71 fname = wxConvCurrent->cMB2WC( aFileName.c_str() ); // try to use locale conversion
72
73 if( fname.IsEmpty() )
74 fname = wxString::From8BitData( aFileName.c_str() ); // try to use native string
75 }
76 #else
77 wxString fname( From_UTF8( aFileName.c_str() ) );
78 #endif
79 wxFFile file( fname, "rb" );
80 size_t length = file.Length();
81
82 if( length <= 0 )
83 {
84 throw PARSE_EXCEPTION( "Error occurred attempting to read in file or empty file" );
85 }
86
87
88 str.resize( length );
89 file.Read( &str[0], str.length() );
90
91 return str;
92 }
93
94 std::unique_ptr<SEXPR> PARSER::parseString( const std::string& aString,
95 std::string::const_iterator& it )
96 {
97 for( ; it != aString.end(); ++it )
98 {
99 if( *it == '\n' )
100 m_lineNumber++;
101
102 if( whitespaceCharacters.find(*it) != std::string::npos )
103 continue;
104
105 if( *it == '(' )
106 {
107 std::advance( it, 1 );
108
109 auto list = std::make_unique<SEXPR_LIST>( m_lineNumber );
110
111 while( it != aString.end() && *it != ')' )
112 {
113 //there may be newlines in between atoms of a list, so detect these here
114 if( *it == '\n' )
115 m_lineNumber++;
116
117 if( whitespaceCharacters.find( *it ) != std::string::npos )
118 {
119 std::advance( it, 1 );
120 continue;
121 }
122
123 std::unique_ptr<SEXPR> item = parseString( aString, it );
124 list->AddChild( item.release() );
125 }
126
127 if( it != aString.end() )
128 std::advance( it, 1 );
129
130 return list;
131 }
132 else if( *it == ')' )
133 {
134 return nullptr;
135 }
136 else if( *it == '"' )
137 {
138 ++it;
139
140 auto starting_it = it;
141
142 for( ; it != aString.end(); ++it )
143 {
144 auto ch = *it;
145
146 if( ch == '\\' )
147 {
148 // Skip the next escaped character
149 if( ++it == aString.end() )
150 break;
151
152 continue;
153 }
154
155 if( ch == '"' )
156 break;
157 }
158
159 if( it == aString.end() )
160 throw PARSE_EXCEPTION("missing closing quote");
161
162 auto str = std::make_unique<SEXPR_STRING>( std::string( starting_it, it ),
163 m_lineNumber );
164
165 ++it;
166 return str;
167
168 }
169 else
170 {
171 size_t startPos = std::distance( aString.begin(), it );
172 size_t closingPos = aString.find_first_of( whitespaceCharacters + "()", startPos );
173
174 std::string tmp = aString.substr( startPos, closingPos - startPos );
175
176
177 if( closingPos != std::string::npos )
178 {
179 if( tmp.find_first_not_of( "0123456789." ) == std::string::npos ||
180 ( tmp.size() > 1 && tmp[0] == '-'
181 && tmp.find_first_not_of( "0123456789.", 1 ) == std::string::npos ) )
182 {
183 std::unique_ptr<SEXPR> res;
184
185 if( tmp.find( '.' ) != std::string::npos )
186 {
187 // floating point type. Use only a "C" string to double conversion
188 wxString stmp( tmp );
189 double fnumb;
190 stmp.ToCDouble( &fnumb );
191 res = std::make_unique<SEXPR_DOUBLE>( fnumb, m_lineNumber );
192 }
193 else
194 {
195 res = std::make_unique<SEXPR_INTEGER>(
196 strtoll( tmp.c_str(), nullptr, 0 ), m_lineNumber );
197 }
198
199 std::advance( it, closingPos - startPos );
200 return res;
201 }
202 else
203 {
204 auto str = std::make_unique<SEXPR_SYMBOL>( tmp, m_lineNumber );
205 std::advance( it, closingPos - startPos );
206
207 return str;
208 }
209 }
210 else
211 {
212 throw PARSE_EXCEPTION( "format error" );
213 }
214 }
215 }
216
217 return nullptr;
218 }
219}
static const std::string whitespaceCharacters
std::unique_ptr< SEXPR > parseString(const std::string &aString, std::string::const_iterator &it)
static std::string GetFileContents(const std::string &aFilename)
std::unique_ptr< SEXPR > ParseFromFile(const std::string &aFilename)
std::unique_ptr< SEXPR > Parse(const std::string &aString)
wxString From_UTF8(const char *cstring)
VECTOR3I res