2
BNF Converter: Flex generator
3
Copyright (C) 2004 Author: Michael Pellauer
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.
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.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
**************************************************************
24
Description : This module generates the Flex file. It is
25
similar to JLex but with a few peculiarities.
27
Author : Michael Pellauer (pellauer@cs.chalmers.se)
29
License : GPL (GNU General Public License)
31
Created : 5 August, 2003
33
Modified : 22 August, 2006 by Aarne Ranta
36
**************************************************************
38
module CFtoFlex (cf2flex) where
42
import Utils((+++), (++++))
47
--The environment must be returned for the parser to use.
48
cf2flex :: Maybe String -> String -> CF -> (String, SymEnv)
49
cf2flex inPackage name cf = (unlines
51
prelude inPackage name,
54
restOfFlex inPackage cf env'
57
env = makeSymEnv (symbols cf ++ reservedWords cf) (0 :: Int)
58
env' = env ++ (makeSymEnv (fst (unzip (tokenPragmas cf))) (length env))
60
makeSymEnv (s:symbs) n = (s, nsDefine inPackage "_SYMB_" ++ (show n)) : (makeSymEnv symbs (n+1))
62
prelude :: Maybe String -> String -> String
63
prelude inPackage name = unlines
65
maybe "" (\ns -> "%option prefix=\"" ++ ns ++ "yy\"") inPackage,
66
"/* This FLex file was machine-generated by the BNF converter */",
68
"#include <string.h>",
69
"#include \"Parser.H\"",
70
"#define YY_BUFFER_LENGTH 4096",
71
"extern int " ++ nsString inPackage ++ "yy_mylinenumber ;", --- hack to get line number. AR 2006
72
"static char YY_PARSED_STRING[YY_BUFFER_LENGTH];",
73
"static void YY_BUFFER_APPEND(char *s)",
75
" strcat(YY_PARSED_STRING, s); //Do something better here!",
77
"static void YY_BUFFER_RESET(void)",
79
" for(int x = 0; x < YY_BUFFER_LENGTH; x++)",
80
" YY_PARSED_STRING[x] = 0;",
86
--For now all categories are included.
87
--Optimally only the ones that are used should be generated.
95
"IDENT [a-zA-Z0-9'_]",
96
"%START YYINITIAL COMMENT CHAR CHARESC CHAREND STRING ESCAPED",
100
lexSymbols :: SymEnv -> String
101
lexSymbols ss = concatMap transSym ss
104
"<YYINITIAL>\"" ++ s' ++ "\" \t return " ++ r ++ ";\n"
108
restOfFlex :: Maybe String -> CF -> SymEnv -> String
109
restOfFlex inPackage cf env = concat
111
lexComments inPackage (comments cf),
113
ifC "String" strStates,
115
ifC "Double" ("<YYINITIAL>{DIGIT}+\".\"{DIGIT}+(\"e\"(\\-)?{DIGIT}+)? \t " ++ ns ++ "yylval.double_ = atof(yytext); return " ++ nsDefine inPackage "_DOUBLE_" ++ ";\n"),
116
ifC "Integer" ("<YYINITIAL>{DIGIT}+ \t " ++ ns ++ "yylval.int_ = atoi(yytext); return " ++ nsDefine inPackage "_INTEGER_" ++ ";\n"),
117
ifC "Ident" ("<YYINITIAL>{LETTER}{IDENT}* \t " ++ ns ++ "yylval.string_ = strdup(yytext); return " ++ nsDefine inPackage "_IDENT_" ++ ";\n"),
118
"\\n ++" ++ ns ++ "yy_mylinenumber ;\n",
119
"<YYINITIAL>[ \\t\\r\\n\\f] \t /* ignore white space. */;\n",
120
"<YYINITIAL>. \t return " ++ nsDefine inPackage "_ERROR_" ++ ";\n",
125
ifC cat s = if isUsedCat cf cat then s else ""
126
ns = nsString inPackage
127
userDefTokens = unlines $
128
["<YYINITIAL>" ++ printRegFlex exp ++
129
" \t " ++ ns ++ "yylval.string_ = strdup(yytext); return " ++ sName name ++ ";"
130
| (name, exp) <- tokenPragmas cf]
132
sName n = case lookup n env of
135
strStates = unlines --These handle escaped characters in Strings.
137
"<YYINITIAL>\"\\\"\" \t BEGIN STRING;",
138
"<STRING>\\\\ \t BEGIN ESCAPED;",
139
"<STRING>\\\" \t " ++ ns ++ "yylval.string_ = strdup(YY_PARSED_STRING); YY_BUFFER_RESET(); BEGIN YYINITIAL; return " ++ nsDefine inPackage "_STRING_" ++ ";",
140
"<STRING>. \t YY_BUFFER_APPEND(yytext);",
141
"<ESCAPED>n \t YY_BUFFER_APPEND(\"\\n\"); BEGIN STRING;",
142
"<ESCAPED>\\\" \t YY_BUFFER_APPEND(\"\\\"\"); BEGIN STRING ;",
143
"<ESCAPED>\\\\ \t YY_BUFFER_APPEND(\"\\\\\"); BEGIN STRING;",
144
"<ESCAPED>t \t YY_BUFFER_APPEND(\"\\t\"); BEGIN STRING;",
145
"<ESCAPED>. \t YY_BUFFER_APPEND(yytext); BEGIN STRING;"
147
chStates = unlines --These handle escaped characters in Chars.
149
"<YYINITIAL>\"'\" \tBEGIN CHAR;",
150
"<CHAR>\\\\ \t BEGIN CHARESC;",
151
"<CHAR>[^'] \t BEGIN CHAREND; " ++ ns ++ "yylval.char_ = yytext[0]; return " ++ nsDefine inPackage "_CHAR_" ++ ";",
152
"<CHARESC>n \t BEGIN CHAREND; " ++ ns ++ "yylval.char_ = '\\n'; return " ++ nsDefine inPackage "_CHAR_" ++ ";",
153
"<CHARESC>t \t BEGIN CHAREND; " ++ ns ++ "yylval.char_ = '\\t'; return " ++ nsDefine inPackage "_CHAR_" ++ ";",
154
"<CHARESC>. \t BEGIN CHAREND; " ++ ns ++ "yylval.char_ = yytext[0]; return " ++ nsDefine inPackage "_CHAR_" ++ ";",
155
"<CHAREND>\"'\" \t BEGIN YYINITIAL;"
159
"int " ++ ns ++ "initialize_lexer(FILE *inp) { yyin = inp; BEGIN YYINITIAL; }",
160
"int yywrap(void) { return 1; }"
164
lexComments :: Maybe String -> ([(String, String)], [String]) -> String
165
lexComments inPackage (m,s) =
166
(unlines (map (lexSingleComment inPackage) s))
167
++ (unlines (map (lexMultiComment inPackage) m))
169
lexSingleComment :: Maybe String -> String -> String
170
lexSingleComment inPackage c =
171
"<YYINITIAL>\"" ++ c ++ "\"[^\\n]*\\n ++" ++ nsString inPackage ++ "yy_mylinenumber ; \t /* BNFC single-line comment */;"
173
--There might be a possible bug here if a language includes 2 multi-line comments.
174
--They could possibly start a comment with one character and end it with another.
175
--However this seems rare.
176
lexMultiComment :: Maybe String -> (String, String) -> String
177
lexMultiComment inPackage (b,e) = unlines [
178
"<YYINITIAL>\"" ++ b ++ "\" \t BEGIN COMMENT;",
179
"<COMMENT>\"" ++ e ++ "\" \t BEGIN YYINITIAL;",
180
"<COMMENT>. \t /* BNFC multi-line comment */;",
181
"<COMMENT>[\\n] ++" ++ nsString inPackage ++ "yy_mylinenumber ; \t /* BNFC multi-line comment */;"
182
---- "\\n ++yy_mylinenumber ;"
186
--Helper function that escapes characters in strings
187
escapeChars :: String -> String
189
escapeChars ('\\':xs) = '\\' : ('\\' : (escapeChars xs))
190
escapeChars ('\"':xs) = '\\' : ('\"' : (escapeChars xs))
191
escapeChars (x:xs) = x : (escapeChars xs)