summaryrefslogtreecommitdiff
path: root/CMakeModules/TokenList2DsnLexer.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'CMakeModules/TokenList2DsnLexer.cmake')
-rw-r--r--CMakeModules/TokenList2DsnLexer.cmake387
1 files changed, 387 insertions, 0 deletions
diff --git a/CMakeModules/TokenList2DsnLexer.cmake b/CMakeModules/TokenList2DsnLexer.cmake
new file mode 100644
index 0000000..d11bc5c
--- /dev/null
+++ b/CMakeModules/TokenList2DsnLexer.cmake
@@ -0,0 +1,387 @@
+
+# This program source code file is part of KICAD, a free EDA CAD application.
+#
+# Copyright (C) 2010 Wayne Stambaugh <stambaughw@verizon.net>
+# Copyright (C) 2010 Kicad Developers, see AUTHORS.txt for contributors.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you may find one here:
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# or you may search the http://www.gnu.org website for the version 2 license,
+# or you may write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#
+#
+# This script converts a plain text file with a line feed separated list
+# of token names into the appropriate source and header files required by
+# the DSN lexer. See files "<base_source_path>/common/dsnlexer.cpp" and
+# "<base_source_path>/include/dsnlexer.h" for more information about how
+# the DSN lexer works. The token list file format requires a single token
+# per line. Tokens can only contain lower case letters, numbers, and
+# underscores. The first letter of each token must be a lower case letter.
+# Tokens must be unique. If any of the above criteria are not met, the
+# source and header files will not be generated and a build error will
+# occur.
+#
+# Valid tokens: a a1 foo_1 foo_bar2
+# Invalid tokens: 1 A _foo bar_ foO
+#
+# Invocation Parameters are: enum, inputFile, outCppFile, outHeaderFile
+#
+# enum - Required, namespace in which the enum T will be placed.
+# Keep it short because from outside the class you want a short enum name
+# like enum::T. Enums are contained in their own namespace to avoid
+# collisions on enum value names, a problem with C++ unless the enum
+# itself is in a separate namespace.
+#
+# inputFile - Required, name of the token list file, or "*.keywords" file.
+# Choose the basefilename carefully, it decides the class name
+# used in the generated *_lexer.h file.
+#
+# outCppFile - Optional, full path and file name of where to save the generated
+# cpp keywords file. If not defined, the output path is the same
+# path as the token list file path, with a file name of *_keywords.cpp
+#
+# outHeaderFile - Optional, full path and file name of where to save the generated
+# *.h lexfer file. If not defined, the output path is the same
+# path as the token list file path, with a file name of *_lexer.h
+#
+# Use the max_lexer() CMake function from functions.cmake for invocation convenience.
+
+
+#message( STATUS "TokenList2DsnLexer.cmake" ) # indicate we are running
+
+set( tokens "" )
+set( lineCount 0 )
+set( dsnErrorMsg "TokenList2DsnLexer.cmake failure:" )
+
+if( NOT EXISTS ${inputFile} )
+ message( FATAL_ERROR "${dsnErrorMsg} file ${inputFile} cannot be found." )
+endif()
+
+if( NOT DEFINED enum )
+ message( FATAL_ERROR "${dsnErrorMsg} missing \"enum\" processing ${inputFile}." )
+endif()
+
+get_filename_component( outputPath "${inputFile}" PATH )
+
+# the keywords filename without extension is important, it sets the classname into RESULT
+get_filename_component( result "${inputFile}" NAME_WE )
+string( TOUPPER "${result}" RESULT )
+
+set( LEXERCLASS "${RESULT}_LEXER" )
+set( PARSERCLASS "${RESULT}_PARSER" )
+
+#message( "enum:'${enum}' result:'${result}' outputPath:'${outputPath}' inputFile:'${inputFile}'" )
+
+if( NOT DEFINED outCppFile )
+ set( outCppFile "${outputPath}/${result}_keywords.cpp" )
+endif()
+
+if( NOT DEFINED outHeaderFile )
+ set( outHeaderFile "${outputPath}/${result}_lexer.h" )
+endif()
+
+# Create tag for generating header file.
+set( headerTag "${LEXERCLASS}_H_" )
+
+set( includeFileHeader
+"
+/* Do not modify this file it was automatically generated by the
+ * TokenList2DsnLexer CMake script.
+ */
+
+#ifndef ${headerTag}
+#define ${headerTag}
+
+#include <dsnlexer.h>
+
+/**
+ * C++ does not put enum _values_ in separate namespaces unless the enum itself
+ * is in a separate namespace. All the token enums must be in separate namespaces
+ * otherwise the C++ compiler will eventually complain if it sees more than one
+ * DSNLEXER in the same compilation unit, say by mutliple header file inclusion.
+ * Plus this also enables re-use of the same enum name T. A typedef can always be used
+ * to clarify which enum T is in play should that ever be a problem. This is
+ * unlikely since Parse() functions will usually only be exposed to one header
+ * file like this one. But if there is a problem, then use:
+ * typedef ${enum}::T T;
+ * within that problem area.
+ */
+namespace ${enum}
+{
+ /// enum T contains all this lexer's tokens.
+ enum T
+ {
+ // these first few are negative special ones for syntax, and are
+ // inherited from DSNLEXER.
+ T_NONE = DSN_NONE,
+ T_COMMENT = DSN_COMMENT,
+ T_STRING_QUOTE = DSN_STRING_QUOTE,
+ T_QUOTE_DEF = DSN_QUOTE_DEF,
+ T_DASH = DSN_DASH,
+ T_SYMBOL = DSN_SYMBOL,
+ T_NUMBER = DSN_NUMBER,
+ T_RIGHT = DSN_RIGHT, // right bracket: ')'
+ T_LEFT = DSN_LEFT, // left bracket: '('
+ T_STRING = DSN_STRING, // a quoted string, stripped of the quotes
+ T_EOF = DSN_EOF, // special case for end of file
+
+"
+)
+
+
+set( sourceFileHeader
+"
+/* Do not modify this file it was automatically generated by the
+ * TokenList2DsnLexer CMake script.
+ *
+ * Include this file in your lexer class to provide the keywords for
+ * your DSN lexer.
+ */
+
+#include <${result}_lexer.h>
+
+using namespace ${enum};
+
+#define TOKDEF(x) { #x, T_##x }
+
+const KEYWORD ${LEXERCLASS}::keywords[] = {
+"
+)
+
+file( STRINGS ${inputFile} lines NO_HEX_CONVERSION )
+
+foreach( line ${lines} )
+ math( EXPR lineCount "${lineCount} + 1" )
+
+ # strip any comment from # to end of line
+ string( REGEX REPLACE "#.*$" "" tmpToken "${line}" )
+ string( STRIP "${tmpToken}" token )
+
+ # Ignore empty lines.
+ if( NOT token STREQUAL "" ) # if token is "off" simple if( token) does not work
+ # Make sure token is valid.
+
+ #message( "token=${token}" )
+
+ string( REGEX MATCH "[a-z][_0-9a-z]*" validToken "${token}" )
+ #message( "validToken=${validToken}" )
+
+ if( validToken STREQUAL token )
+ list( APPEND tokens "${validToken}" )
+ else()
+ message( FATAL_ERROR
+ "Invalid token string \"${tmpToken}\" at line ${lineCount} in file "
+ "<${inputFile}>." )
+ endif()
+ endif()
+endforeach()
+
+list( SORT tokens )
+
+# Check for duplicates.
+list( LENGTH tokens tokensBefore )
+list( REMOVE_DUPLICATES tokens )
+list( LENGTH tokens tokensAfter )
+
+if( NOT ( tokensBefore EQUAL tokensAfter ) )
+ message( FATAL_ERROR "Duplicate tokens found in file <${inputFile}>." )
+endif()
+
+file( WRITE "${outHeaderFile}" "${includeFileHeader}" )
+file( WRITE "${outCppFile}" "${sourceFileHeader}" )
+
+set( lineCount 1 )
+
+foreach( token ${tokens} )
+ if( lineCount EQUAL 1 )
+ file( APPEND "${outHeaderFile}" " T_${token} = 0" )
+ else( lineCount EQUAL 1 )
+ file( APPEND "${outHeaderFile}" " T_${token}" )
+ endif( lineCount EQUAL 1 )
+
+ file(APPEND "${outCppFile}" " TOKDEF( ${token} )" )
+
+ if( lineCount EQUAL tokensAfter )
+ file( APPEND "${outHeaderFile}" "\n" )
+ file( APPEND "${outCppFile}" "\n" )
+ else( lineCount EQUAL tokensAfter )
+ file( APPEND "${outHeaderFile}" ",\n" )
+ file( APPEND "${outCppFile}" ",\n" )
+ endif( lineCount EQUAL tokensAfter )
+ math( EXPR lineCount "${lineCount} + 1" )
+endforeach()
+
+file( APPEND "${outHeaderFile}"
+" };
+} // namespace ${enum}
+
+
+/**
+ * Class ${LEXERCLASS}
+ * is an automatically generated class using the TokenList2DnsLexer.cmake
+ * technology, based on keywords provided by file:
+ * ${inputFile}
+ */
+class ${LEXERCLASS} : public DSNLEXER
+{
+ /// Auto generated lexer keywords table and length:
+ static const KEYWORD keywords[];
+ static const unsigned keyword_count;
+
+public:
+ /**
+ * Constructor ( const std::string&, const wxString& )
+ * @param aSExpression is (utf8) text possibly from the clipboard that you want to parse.
+ * @param aSource is a description of the origin of @a aSExpression, such as a filename.
+ * If left empty, then _(\"clipboard\") is used.
+ */
+ ${LEXERCLASS}( const std::string& aSExpression, const wxString& aSource = wxEmptyString ) :
+ DSNLEXER( keywords, keyword_count, aSExpression, aSource )
+ {
+ }
+
+ /**
+ * Constructor ( FILE* )
+ * takes @a aFile already opened for reading and @a aFilename as parameters.
+ * The opened file is assumed to be positioned at the beginning of the file
+ * for purposes of accurate line number reporting in error messages. The
+ * FILE is closed by this instance when its destructor is called.
+ * @param aFile is a FILE already opened for reading.
+ * @param aFilename is the name of the opened file, needed for error reporting.
+ */
+ ${LEXERCLASS}( FILE* aFile, const wxString& aFilename ) :
+ DSNLEXER( keywords, keyword_count, aFile, aFilename )
+ {
+ }
+
+ /**
+ * Constructor ( LINE_READER* )
+ * intializes a lexer and prepares to read from @a aLineReader which
+ * is assumed ready, and may be in use by other DSNLEXERs also. No ownership
+ * is taken of @a aLineReader. This enables it to be used by other lexers also.
+ * The transition between grammars in such a case, must happen on a text
+ * line boundary, not within the same line of text.
+ *
+ * @param aLineReader is any subclassed instance of LINE_READER, such as
+ * STRING_LINE_READER or FILE_LINE_READER. No ownership is taken of aLineReader.
+ */
+ ${LEXERCLASS}( LINE_READER* aLineReader ) :
+ DSNLEXER( keywords, keyword_count, aLineReader )
+ {
+ }
+
+ /**
+ * Function TokenName
+ * returns the name of the token in ASCII form.
+ */
+ static const char* TokenName( ${enum}::T aTok );
+
+ /**
+ * Function NextTok
+ * returns the next token found in the input file or T_EOF when reaching
+ * the end of file. Users should wrap this function to return an enum
+ * to aid in grammar debugging while running under a debugger, but leave
+ * this lower level function returning an int (so the enum does not collide
+ * with another usage).
+ * @return ${enum}::T - the type of token found next.
+ * @throw IO_ERROR - only if the LINE_READER throws it.
+ */
+ ${enum}::T NextTok() throw( IO_ERROR )
+ {
+ return (${enum}::T) DSNLEXER::NextTok();
+ }
+
+ /**
+ * Function NeedSYMBOL
+ * calls NextTok() and then verifies that the token read in
+ * satisfies bool IsSymbol().
+ * If not, an IO_ERROR is thrown.
+ * @return int - the actual token read in.
+ * @throw IO_ERROR, if the next token does not satisfy IsSymbol()
+ */
+ ${enum}::T NeedSYMBOL() throw( IO_ERROR )
+ {
+ return (${enum}::T) DSNLEXER::NeedSYMBOL();
+ }
+
+ /**
+ * Function NeedSYMBOLorNUMBER
+ * calls NextTok() and then verifies that the token read in
+ * satisfies bool IsSymbol() or tok==T_NUMBER.
+ * If not, an IO_ERROR is thrown.
+ * @return int - the actual token read in.
+ * @throw IO_ERROR, if the next token does not satisfy the above test
+ */
+ ${enum}::T NeedSYMBOLorNUMBER() throw( IO_ERROR )
+ {
+ return (${enum}::T) DSNLEXER::NeedSYMBOLorNUMBER();
+ }
+
+ /**
+ * Function CurTok
+ * returns whatever NextTok() returned the last time it was called.
+ */
+ ${enum}::T CurTok()
+ {
+ return (${enum}::T) DSNLEXER::CurTok();
+ }
+
+ /**
+ * Function PrevTok
+ * returns whatever NextTok() returned the 2nd to last time it was called.
+ */
+ ${enum}::T PrevTok()
+ {
+ return (${enum}::T) DSNLEXER::PrevTok();
+ }
+};
+
+// example usage
+
+/**
+ * Class ${LEXCLASS}_PARSER
+ * holds data and functions pertinent to parsing a S-expression file .
+ *
+class ${PARSERCLASS} : public ${LEXERCLASS}
+{
+
+};
+*/
+
+#endif // ${headerTag}
+"
+)
+
+file( APPEND "${outCppFile}"
+"};
+
+const unsigned ${LEXERCLASS}::keyword_count = unsigned( sizeof( ${LEXERCLASS}::keywords )/sizeof( ${LEXERCLASS}::keywords[0] ) );
+
+
+const char* ${LEXERCLASS}::TokenName( T aTok )
+{
+ const char* ret;
+
+ if( aTok < 0 )
+ ret = DSNLEXER::Syntax( aTok );
+ else if( (unsigned) aTok < keyword_count )
+ ret = keywords[aTok].name;
+ else
+ ret = \"token too big\";
+
+ return ret;
+}
+"
+)