KiCad PCB EDA Suite
sexpr_parser.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016 Mark Roszko <[email protected]>
3 * Copyright (C) 2018-2021 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 <macros.h>
32
33namespace SEXPR
34{
35 const std::string PARSER::whitespaceCharacters = " \t\n\r\b\f\v";
36
37 PARSER::PARSER() : m_lineNumber( 1 )
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 wxString fname( FROM_UTF8( aFileName.c_str() ) );
66 wxFFile file( fname, "rb" );
67 size_t length = file.Length();
68
69 if( length <= 0 )
70 {
71 throw PARSE_EXCEPTION( "Error occurred attempting to read in file or empty file" );
72 }
73
74
75 str.resize( length );
76 file.Read( &str[0], str.length() );
77
78 return str;
79 }
80
81 std::unique_ptr<SEXPR> PARSER::parseString(
82 const std::string& aString, std::string::const_iterator& it )
83 {
84 for( ; it != aString.end(); ++it )
85 {
86 if( *it == '\n' )
88
89 if( whitespaceCharacters.find(*it) != std::string::npos )
90 continue;
91
92 if( *it == '(' )
93 {
94 std::advance( it, 1 );
95
96 auto list = std::make_unique<SEXPR_LIST>( m_lineNumber );
97
98 while( it != aString.end() && *it != ')' )
99 {
100 //there may be newlines in between atoms of a list, so detect these here
101 if( *it == '\n' )
102 m_lineNumber++;
103
104 if( whitespaceCharacters.find(*it) != std::string::npos )
105 {
106 std::advance( it, 1 );
107 continue;
108 }
109
110 std::unique_ptr<SEXPR> item = parseString( aString, it );
111 list->AddChild( item.release() );
112 }
113
114 if( it != aString.end() )
115 std::advance( it, 1 );
116
117 return list;
118 }
119 else if( *it == ')' )
120 {
121 return nullptr;
122 }
123 else if( *it == '"' )
124 {
125 ++it;
126
127 auto starting_it = it;
128
129 for( ; it != aString.end(); ++it )
130 {
131 auto ch = *it;
132
133 if( ch == '\\' )
134 {
135 // Skip the next escaped character
136 if( ++it == aString.end() )
137 break;
138
139 continue;
140 }
141
142 if( ch == '"' )
143 break;
144 }
145
146 if( it == aString.end() )
147 throw PARSE_EXCEPTION("missing closing quote");
148
149 auto str = std::make_unique<SEXPR_STRING>( std::string( starting_it, it ),
150 m_lineNumber );
151
152 ++it;
153 return str;
154
155 }
156 else
157 {
158 size_t startPos = std::distance( aString.begin(), it );
159 size_t closingPos = aString.find_first_of( whitespaceCharacters + "()", startPos );
160
161 std::string tmp = aString.substr( startPos, closingPos - startPos );
162
163
164 if( closingPos != std::string::npos )
165 {
166 if( tmp.find_first_not_of( "0123456789." ) == std::string::npos ||
167 ( tmp.size() > 1 && tmp[0] == '-'
168 && tmp.find_first_not_of( "0123456789.", 1 ) == std::string::npos ) )
169 {
170 std::unique_ptr<SEXPR> res;
171
172 if( tmp.find( '.' ) != std::string::npos )
173 {
174 res = std::make_unique<SEXPR_DOUBLE>(
175 strtod( tmp.c_str(), nullptr ), m_lineNumber );
176 //floating point type
177 }
178 else
179 {
180 res = std::make_unique<SEXPR_INTEGER>(
181 strtoll( tmp.c_str(), nullptr, 0 ), m_lineNumber );
182 }
183
184 std::advance( it, closingPos - startPos );
185 return res;
186 }
187 else
188 {
189 auto str = std::make_unique<SEXPR_SYMBOL>( tmp, m_lineNumber );
190 std::advance( it, closingPos - startPos );
191
192 return str;
193 }
194 }
195 else
196 {
197 throw PARSE_EXCEPTION( "format error" );
198 }
199 }
200 }
201
202 return nullptr;
203 }
204}
static const std::string whitespaceCharacters
Definition: sexpr_parser.h:43
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)
This file contains miscellaneous commonly used macros and functions.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)