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 <cstdlib> /* strtod */
23#include <iterator>
24#include <stdexcept>
25
26#include <fstream>
27#include <streambuf>
28#include <wx/file.h>
29#include <wx/ffile.h>
30
31#include <string_utils.h>
32
33namespace SEXPR
34{
35 const std::string PARSER::whitespaceCharacters = " \t\n\r\b\f\v";
36
38 {
39 }
40
42 {
43 }
44
45 std::unique_ptr<SEXPR> PARSER::Parse( const std::string& aString )
46 {
47 std::string::const_iterator it = aString.begin();
48 return parseString( aString, it );
49 }
50
51 std::unique_ptr<SEXPR> PARSER::ParseFromFile( const std::string& aFileName )
52 {
53 std::string str = GetFileContents( aFileName );
54
55 std::string::const_iterator it = str.begin();
56 return parseString( str, it );
57 }
58
59 std::string PARSER::GetFileContents( const std::string &aFileName )
60 {
61 std::string str;
62
63 // the filename is not always a UTF7 string, so do not use ifstream
64 // that do not work with unicode chars.
65 #if defined( __MINGW32__ )
66 // On msys2 currently using From_UTF8( defined in string_utils.cpp) creates a link
67 // issue. (From_UTF8() undefined symbol). So redefine it here (duplicate) for now.
68 wxString fname( wxString::FromUTF8( aFileName.c_str() ) );
69
70 if( fname.IsEmpty() ) // happens when aString is not a valid UTF8 sequence
71 {
72 fname = wxConvCurrent->cMB2WC( aFileName.c_str() ); // try to use locale conversion
73
74 if( fname.IsEmpty() )
75 fname = wxString::From8BitData( aFileName.c_str() ); // try to use native string
76 }
77 #else
78 wxString fname( From_UTF8( aFileName.c_str() ) );
79 #endif
80 wxFFile file( fname, "rb" );
81 size_t length = file.Length();
82
83 if( length <= 0 )
84 {
85 throw PARSE_EXCEPTION( "Error occurred attempting to read in file or empty file" );
86 }
87
88
89 str.resize( length );
90 file.Read( &str[0], str.length() );
91
92 return str;
93 }
94
95 std::unique_ptr<SEXPR> PARSER::parseString( const std::string& aString,
96 std::string::const_iterator& it )
97 {
98 for( ; it != aString.end(); ++it )
99 {
100 if( *it == '\n' )
101 m_lineNumber++;
102
103 if( whitespaceCharacters.find(*it) != std::string::npos )
104 continue;
105
106 if( *it == '(' )
107 {
108 std::advance( it, 1 );
109
110 auto list = std::make_unique<SEXPR_LIST>( m_lineNumber );
111
112 while( it != aString.end() && *it != ')' )
113 {
114 //there may be newlines in between atoms of a list, so detect these here
115 if( *it == '\n' )
116 m_lineNumber++;
117
118 if( whitespaceCharacters.find( *it ) != std::string::npos )
119 {
120 std::advance( it, 1 );
121 continue;
122 }
123
124 std::unique_ptr<SEXPR> item = parseString( aString, it );
125 list->AddChild( item.release() );
126 }
127
128 if( it != aString.end() )
129 std::advance( it, 1 );
130
131 return list;
132 }
133 else if( *it == ')' )
134 {
135 return nullptr;
136 }
137 else if( *it == '"' )
138 {
139 ++it;
140
141 auto starting_it = it;
142
143 for( ; it != aString.end(); ++it )
144 {
145 auto ch = *it;
146
147 if( ch == '\\' )
148 {
149 // Skip the next escaped character
150 if( ++it == aString.end() )
151 break;
152
153 continue;
154 }
155
156 if( ch == '"' )
157 break;
158 }
159
160 if( it == aString.end() )
161 throw PARSE_EXCEPTION("missing closing quote");
162
163 auto str = std::make_unique<SEXPR_STRING>( std::string( starting_it, it ),
164 m_lineNumber );
165
166 ++it;
167 return str;
168
169 }
170 else
171 {
172 size_t startPos = std::distance( aString.begin(), it );
173 size_t closingPos = aString.find_first_of( whitespaceCharacters + "()", startPos );
174
175 std::string tmp = aString.substr( startPos, closingPos - startPos );
176
177
178 if( closingPos != std::string::npos )
179 {
180 if( tmp.find_first_not_of( "0123456789." ) == std::string::npos ||
181 ( tmp.size() > 1 && tmp[0] == '-'
182 && tmp.find_first_not_of( "0123456789.", 1 ) == std::string::npos ) )
183 {
184 std::unique_ptr<SEXPR> res;
185
186 if( tmp.find( '.' ) != std::string::npos )
187 {
188 res = std::make_unique<SEXPR_DOUBLE>(
189 strtod( tmp.c_str(), nullptr ), m_lineNumber );
190 //floating point type
191 }
192 else
193 {
194 res = std::make_unique<SEXPR_INTEGER>(
195 strtoll( tmp.c_str(), nullptr, 0 ), m_lineNumber );
196 }
197
198 std::advance( it, closingPos - startPos );
199 return res;
200 }
201 else
202 {
203 auto str = std::make_unique<SEXPR_SYMBOL>( tmp, m_lineNumber );
204 std::advance( it, closingPos - startPos );
205
206 return str;
207 }
208 }
209 else
210 {
211 throw PARSE_EXCEPTION( "format error" );
212 }
213 }
214 }
215
216 return nullptr;
217 }
218}
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