PageRenderTime 89ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/CMakeModules/TokenList2DsnLexer.cmake

https://gitlab.com/micrenda/kicad
CMake | 387 lines | 252 code | 61 blank | 74 comment | 24 complexity | 9d68d3762d6185de8b89d85472d360fb MD5 | raw file
  1. # This program source code file is part of KICAD, a free EDA CAD application.
  2. #
  3. # Copyright (C) 2010 Wayne Stambaugh <stambaughw@verizon.net>
  4. # Copyright (C) 2010 Kicad Developers, see AUTHORS.txt for contributors.
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License
  8. # as published by the Free Software Foundation; either version 2
  9. # of the License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, you may find one here:
  18. # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. # or you may search the http://www.gnu.org website for the version 2 license,
  20. # or you may write to the Free Software Foundation, Inc.,
  21. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. #
  23. #
  24. # This script converts a plain text file with a line feed separated list
  25. # of token names into the appropriate source and header files required by
  26. # the DSN lexer. See files "<base_source_path>/common/dsnlexer.cpp" and
  27. # "<base_source_path>/include/dsnlexer.h" for more information about how
  28. # the DSN lexer works. The token list file format requires a single token
  29. # per line. Tokens can only contain lower case letters, numbers, and
  30. # underscores. The first letter of each token must be a lower case letter.
  31. # Tokens must be unique. If any of the above criteria are not met, the
  32. # source and header files will not be generated and a build error will
  33. # occur.
  34. #
  35. # Valid tokens: a a1 foo_1 foo_bar2
  36. # Invalid tokens: 1 A _foo bar_ foO
  37. #
  38. # Invocation Parameters are: enum, inputFile, outCppFile, outHeaderFile
  39. #
  40. # enum - Required, namespace in which the enum T will be placed.
  41. # Keep it short because from outside the class you want a short enum name
  42. # like enum::T. Enums are contained in their own namespace to avoid
  43. # collisions on enum value names, a problem with C++ unless the enum
  44. # itself is in a separate namespace.
  45. #
  46. # inputFile - Required, name of the token list file, or "*.keywords" file.
  47. # Choose the basefilename carefully, it decides the class name
  48. # used in the generated *_lexer.h file.
  49. #
  50. # outCppFile - Optional, full path and file name of where to save the generated
  51. # cpp keywords file. If not defined, the output path is the same
  52. # path as the token list file path, with a file name of *_keywords.cpp
  53. #
  54. # outHeaderFile - Optional, full path and file name of where to save the generated
  55. # *.h lexfer file. If not defined, the output path is the same
  56. # path as the token list file path, with a file name of *_lexer.h
  57. #
  58. # Use the max_lexer() CMake function from functions.cmake for invocation convenience.
  59. #message( STATUS "TokenList2DsnLexer.cmake" ) # indicate we are running
  60. set( tokens "" )
  61. set( lineCount 0 )
  62. set( dsnErrorMsg "TokenList2DsnLexer.cmake failure:" )
  63. if( NOT EXISTS ${inputFile} )
  64. message( FATAL_ERROR "${dsnErrorMsg} file ${inputFile} cannot be found." )
  65. endif()
  66. if( NOT DEFINED enum )
  67. message( FATAL_ERROR "${dsnErrorMsg} missing \"enum\" processing ${inputFile}." )
  68. endif()
  69. get_filename_component( outputPath "${inputFile}" PATH )
  70. # the keywords filename without extension is important, it sets the classname into RESULT
  71. get_filename_component( result "${inputFile}" NAME_WE )
  72. string( TOUPPER "${result}" RESULT )
  73. set( LEXERCLASS "${RESULT}_LEXER" )
  74. set( PARSERCLASS "${RESULT}_PARSER" )
  75. #message( "enum:'${enum}' result:'${result}' outputPath:'${outputPath}' inputFile:'${inputFile}'" )
  76. if( NOT DEFINED outCppFile )
  77. set( outCppFile "${outputPath}/${result}_keywords.cpp" )
  78. endif()
  79. if( NOT DEFINED outHeaderFile )
  80. set( outHeaderFile "${outputPath}/${result}_lexer.h" )
  81. endif()
  82. # Create tag for generating header file.
  83. set( headerTag "${LEXERCLASS}_H_" )
  84. set( includeFileHeader
  85. "
  86. /* Do not modify this file it was automatically generated by the
  87. * TokenList2DsnLexer CMake script.
  88. */
  89. #ifndef ${headerTag}
  90. #define ${headerTag}
  91. #include <dsnlexer.h>
  92. /**
  93. * C++ does not put enum _values_ in separate namespaces unless the enum itself
  94. * is in a separate namespace. All the token enums must be in separate namespaces
  95. * otherwise the C++ compiler will eventually complain if it sees more than one
  96. * DSNLEXER in the same compilation unit, say by mutliple header file inclusion.
  97. * Plus this also enables re-use of the same enum name T. A typedef can always be used
  98. * to clarify which enum T is in play should that ever be a problem. This is
  99. * unlikely since Parse() functions will usually only be exposed to one header
  100. * file like this one. But if there is a problem, then use:
  101. * typedef ${enum}::T T;
  102. * within that problem area.
  103. */
  104. namespace ${enum}
  105. {
  106. /// enum T contains all this lexer's tokens.
  107. enum T
  108. {
  109. // these first few are negative special ones for syntax, and are
  110. // inherited from DSNLEXER.
  111. T_NONE = DSN_NONE,
  112. T_COMMENT = DSN_COMMENT,
  113. T_STRING_QUOTE = DSN_STRING_QUOTE,
  114. T_QUOTE_DEF = DSN_QUOTE_DEF,
  115. T_DASH = DSN_DASH,
  116. T_SYMBOL = DSN_SYMBOL,
  117. T_NUMBER = DSN_NUMBER,
  118. T_RIGHT = DSN_RIGHT, // right bracket: ')'
  119. T_LEFT = DSN_LEFT, // left bracket: '('
  120. T_STRING = DSN_STRING, // a quoted string, stripped of the quotes
  121. T_EOF = DSN_EOF, // special case for end of file
  122. "
  123. )
  124. set( sourceFileHeader
  125. "
  126. /* Do not modify this file it was automatically generated by the
  127. * TokenList2DsnLexer CMake script.
  128. *
  129. * Include this file in your lexer class to provide the keywords for
  130. * your DSN lexer.
  131. */
  132. #include <${result}_lexer.h>
  133. using namespace ${enum};
  134. #define TOKDEF(x) { #x, T_##x }
  135. const KEYWORD ${LEXERCLASS}::keywords[] = {
  136. "
  137. )
  138. file( STRINGS ${inputFile} lines NO_HEX_CONVERSION )
  139. foreach( line ${lines} )
  140. math( EXPR lineCount "${lineCount} + 1" )
  141. # strip any comment from # to end of line
  142. string( REGEX REPLACE "#.*$" "" tmpToken "${line}" )
  143. string( STRIP "${tmpToken}" token )
  144. # Ignore empty lines.
  145. if( NOT token STREQUAL "" ) # if token is "off" simple if( token) does not work
  146. # Make sure token is valid.
  147. #message( "token=${token}" )
  148. string( REGEX MATCH "[a-z][_0-9a-z]*" validToken "${token}" )
  149. #message( "validToken=${validToken}" )
  150. if( validToken STREQUAL token )
  151. list( APPEND tokens "${validToken}" )
  152. else()
  153. message( FATAL_ERROR
  154. "Invalid token string \"${tmpToken}\" at line ${lineCount} in file "
  155. "<${inputFile}>." )
  156. endif()
  157. endif()
  158. endforeach()
  159. list( SORT tokens )
  160. # Check for duplicates.
  161. list( LENGTH tokens tokensBefore )
  162. list( REMOVE_DUPLICATES tokens )
  163. list( LENGTH tokens tokensAfter )
  164. if( NOT ( tokensBefore EQUAL tokensAfter ) )
  165. message( FATAL_ERROR "Duplicate tokens found in file <${inputFile}>." )
  166. endif()
  167. file( WRITE "${outHeaderFile}" "${includeFileHeader}" )
  168. file( WRITE "${outCppFile}" "${sourceFileHeader}" )
  169. set( lineCount 1 )
  170. foreach( token ${tokens} )
  171. if( lineCount EQUAL 1 )
  172. file( APPEND "${outHeaderFile}" " T_${token} = 0" )
  173. else( lineCount EQUAL 1 )
  174. file( APPEND "${outHeaderFile}" " T_${token}" )
  175. endif( lineCount EQUAL 1 )
  176. file(APPEND "${outCppFile}" " TOKDEF( ${token} )" )
  177. if( lineCount EQUAL tokensAfter )
  178. file( APPEND "${outHeaderFile}" "\n" )
  179. file( APPEND "${outCppFile}" "\n" )
  180. else( lineCount EQUAL tokensAfter )
  181. file( APPEND "${outHeaderFile}" ",\n" )
  182. file( APPEND "${outCppFile}" ",\n" )
  183. endif( lineCount EQUAL tokensAfter )
  184. math( EXPR lineCount "${lineCount} + 1" )
  185. endforeach()
  186. file( APPEND "${outHeaderFile}"
  187. " };
  188. } // namespace ${enum}
  189. /**
  190. * Class ${LEXERCLASS}
  191. * is an automatically generated class using the TokenList2DnsLexer.cmake
  192. * technology, based on keywords provided by file:
  193. * ${inputFile}
  194. */
  195. class ${LEXERCLASS} : public DSNLEXER
  196. {
  197. /// Auto generated lexer keywords table and length:
  198. static const KEYWORD keywords[];
  199. static const unsigned keyword_count;
  200. public:
  201. /**
  202. * Constructor ( const std::string&, const wxString& )
  203. * @param aSExpression is (utf8) text possibly from the clipboard that you want to parse.
  204. * @param aSource is a description of the origin of @a aSExpression, such as a filename.
  205. * If left empty, then _(\"clipboard\") is used.
  206. */
  207. ${LEXERCLASS}( const std::string& aSExpression, const wxString& aSource = wxEmptyString ) :
  208. DSNLEXER( keywords, keyword_count, aSExpression, aSource )
  209. {
  210. }
  211. /**
  212. * Constructor ( FILE* )
  213. * takes @a aFile already opened for reading and @a aFilename as parameters.
  214. * The opened file is assumed to be positioned at the beginning of the file
  215. * for purposes of accurate line number reporting in error messages. The
  216. * FILE is closed by this instance when its destructor is called.
  217. * @param aFile is a FILE already opened for reading.
  218. * @param aFilename is the name of the opened file, needed for error reporting.
  219. */
  220. ${LEXERCLASS}( FILE* aFile, const wxString& aFilename ) :
  221. DSNLEXER( keywords, keyword_count, aFile, aFilename )
  222. {
  223. }
  224. /**
  225. * Constructor ( LINE_READER* )
  226. * intializes a lexer and prepares to read from @a aLineReader which
  227. * is assumed ready, and may be in use by other DSNLEXERs also. No ownership
  228. * is taken of @a aLineReader. This enables it to be used by other lexers also.
  229. * The transition between grammars in such a case, must happen on a text
  230. * line boundary, not within the same line of text.
  231. *
  232. * @param aLineReader is any subclassed instance of LINE_READER, such as
  233. * STRING_LINE_READER or FILE_LINE_READER. No ownership is taken of aLineReader.
  234. */
  235. ${LEXERCLASS}( LINE_READER* aLineReader ) :
  236. DSNLEXER( keywords, keyword_count, aLineReader )
  237. {
  238. }
  239. /**
  240. * Function TokenName
  241. * returns the name of the token in ASCII form.
  242. */
  243. static const char* TokenName( ${enum}::T aTok );
  244. /**
  245. * Function NextTok
  246. * returns the next token found in the input file or T_EOF when reaching
  247. * the end of file. Users should wrap this function to return an enum
  248. * to aid in grammar debugging while running under a debugger, but leave
  249. * this lower level function returning an int (so the enum does not collide
  250. * with another usage).
  251. * @return ${enum}::T - the type of token found next.
  252. * @throw IO_ERROR - only if the LINE_READER throws it.
  253. */
  254. ${enum}::T NextTok() throw( IO_ERROR )
  255. {
  256. return (${enum}::T) DSNLEXER::NextTok();
  257. }
  258. /**
  259. * Function NeedSYMBOL
  260. * calls NextTok() and then verifies that the token read in
  261. * satisfies bool IsSymbol().
  262. * If not, an IO_ERROR is thrown.
  263. * @return int - the actual token read in.
  264. * @throw IO_ERROR, if the next token does not satisfy IsSymbol()
  265. */
  266. ${enum}::T NeedSYMBOL() throw( IO_ERROR )
  267. {
  268. return (${enum}::T) DSNLEXER::NeedSYMBOL();
  269. }
  270. /**
  271. * Function NeedSYMBOLorNUMBER
  272. * calls NextTok() and then verifies that the token read in
  273. * satisfies bool IsSymbol() or tok==T_NUMBER.
  274. * If not, an IO_ERROR is thrown.
  275. * @return int - the actual token read in.
  276. * @throw IO_ERROR, if the next token does not satisfy the above test
  277. */
  278. ${enum}::T NeedSYMBOLorNUMBER() throw( IO_ERROR )
  279. {
  280. return (${enum}::T) DSNLEXER::NeedSYMBOLorNUMBER();
  281. }
  282. /**
  283. * Function CurTok
  284. * returns whatever NextTok() returned the last time it was called.
  285. */
  286. ${enum}::T CurTok()
  287. {
  288. return (${enum}::T) DSNLEXER::CurTok();
  289. }
  290. /**
  291. * Function PrevTok
  292. * returns whatever NextTok() returned the 2nd to last time it was called.
  293. */
  294. ${enum}::T PrevTok()
  295. {
  296. return (${enum}::T) DSNLEXER::PrevTok();
  297. }
  298. };
  299. // example usage
  300. /**
  301. * Class ${LEXCLASS}_PARSER
  302. * holds data and functions pertinent to parsing a S-expression file .
  303. *
  304. class ${PARSERCLASS} : public ${LEXERCLASS}
  305. {
  306. };
  307. */
  308. #endif // ${headerTag}
  309. "
  310. )
  311. file( APPEND "${outCppFile}"
  312. "};
  313. const unsigned ${LEXERCLASS}::keyword_count = unsigned( sizeof( ${LEXERCLASS}::keywords )/sizeof( ${LEXERCLASS}::keywords[0] ) );
  314. const char* ${LEXERCLASS}::TokenName( T aTok )
  315. {
  316. const char* ret;
  317. if( aTok < 0 )
  318. ret = DSNLEXER::Syntax( aTok );
  319. else if( (unsigned) aTok < keyword_count )
  320. ret = keywords[aTok].name;
  321. else
  322. ret = \"token too big\";
  323. return ret;
  324. }
  325. "
  326. )