Fix style.
[grantlee:saroengelss-grantlee.git] / templates / lib / lexer.cpp
1 /*
2   This file is part of the Grantlee template system.
3
4   Copyright (c) 2009,2010,2011 Stephen Kelly <steveire@gmail.com>
5
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public
8   License as published by the Free Software Foundation; either version
9   2.1 of the Licence, or (at your option) any later version.
10
11   This library 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 GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "lexer_p.h"
23
24 using namespace Grantlee;
25
26 typedef State<TextProcessingMachine::Type> TextProcessingState;
27 typedef TextProcessingMachine::Transition TextProcessingTransition;
28
29 typedef LexerObject<TextProcessingState, NullTest, MarksClearer> ChurningState;
30 typedef LexerObject<TextProcessingState, NullTest, TokenFinalizer> FinalizeTokenState;
31 typedef LexerObject<TextProcessingTransition, NullTest, TokenFinalizer> EofHandler;
32 typedef LexerObject<TextProcessingTransition, NullTest, TokenFinalizerWithTrimming> EofHandlerWithTrimming;
33
34 typedef CharacterTransition<'{'> MaybeTemplateSyntaxHandler;
35
36 typedef CharacterTransition<'%', MarkStartSyntax> TagStartHandler;
37 typedef CharacterTransition<'#', MarkStartSyntax> CommentStartHandler;
38 typedef CharacterTransition<'%'> TagEndHandler;
39 typedef CharacterTransition<'#'> CommentEndHandler;
40 typedef CharacterTransition<'{', MarkStartSyntax> BeginValueHandler;
41 typedef CharacterTransition<'}'> MaybeEndValueHandler;
42 typedef CharacterTransition<'\n', MarkNewline> NewlineHandler;
43 typedef CharacterTransition<'}', MarkEndSyntax> EndTemplateSyntaxHandler;
44 typedef NegateCharacterTransition<'}'> NotEndTemplateSyntaxHandler;
45
46 typedef LexerObject<TextProcessingTransition,
47             Negate<
48                 OrTest<CharacterTest<'{'>,
49                 OrTest<CharacterTest<'#'>,
50                        CharacterTest<'%'> > > > > NotBeginTemplateSyntaxHandler;
51
52 typedef LexerObject<TextProcessingTransition,
53             Negate<
54                 OrTest<CharacterTest<'{'>,
55                 OrTest<CharacterTest<'#'>,
56                 OrTest<CharacterTest<'%'>,
57                        CharacterTest<'\n'> > > > > > NotBeginTemplateSyntaxOrNewlineHandler;
58
59 typedef LexerObject<TextProcessingTransition,
60             Negate<
61                 OrTest<CharacterTest<'#'>,
62                 OrTest<CharacterTest<'%'>,
63                        CharacterTest<'\n'> > > > > NotTagCommentOrNewlineHandler;
64
65 typedef LexerObject<TextProcessingTransition,
66             Negate<
67                 OrTest<IsSpace,
68                        CharacterTest<'{'> > > > NonWhitespaceLineTextHandler;
69
70 typedef LexerObject<TextProcessingTransition,
71             AndTest<Negate<CharacterTest<'\n'> >,
72                     IsSpace> > WhitespaceNonNewlineHandler;
73
74 typedef LexerObject<TextProcessingTransition,
75             Negate<
76                 OrTest<CharacterTest<'{'>,
77                        IsSpace> >,
78             TokenFinalizer> FinalizingLineTextHandler;
79
80 typedef CharacterTransition<'\n', TokenFinalizerWithTrimmingAndNewline> SyntaxBoundaryNewlineHandler;
81 typedef CharacterTransition<'{', FinalizeAndMarkStartSyntax> SyntaxBoundaryHandler;
82
83 template<typename Transition>
84 void addTransition( TextProcessingState *source, Lexer *lexer, TextProcessingState *target )
85 {
86   Transition *tr = new Transition( lexer, source );
87   tr->setTargetState( target );
88 }
89
90 TextProcessingMachine* createMachine( Lexer *lexer, Lexer::TrimType type )
91 {
92   TextProcessingMachine *machine = new TextProcessingMachine;
93
94   TextProcessingState *notFinished = new TextProcessingState( machine );
95   TextProcessingState *finished = new TextProcessingState( machine );
96   machine->setInitialState( notFinished );
97
98   TextProcessingState *processingText = new ChurningState( lexer, notFinished );
99   TextProcessingState *processingPostNewline = new TextProcessingState( notFinished );
100   TextProcessingState *processingBeginTemplateSyntax = new TextProcessingState( notFinished );
101   TextProcessingState *processingTag = new TextProcessingState( notFinished );
102   TextProcessingState *processingComment = new TextProcessingState( notFinished );
103   TextProcessingState *processingValue = new TextProcessingState( notFinished );
104   TextProcessingState *maybeProcessingValue = new TextProcessingState( notFinished );
105   TextProcessingState *processingEndTag = new TextProcessingState( notFinished );
106   TextProcessingState *processingEndComment = new TextProcessingState( notFinished );
107   TextProcessingState *processingEndValue = new TextProcessingState( notFinished );
108   TextProcessingState *processingPostTemplateSyntax;
109
110   if ( type == Lexer::SmartTrim )
111     processingPostTemplateSyntax = new TextProcessingState( notFinished );
112   else
113     processingPostTemplateSyntax = new FinalizeTokenState( lexer, notFinished );
114   TextProcessingState *processingPostTemplateSyntaxWhitespace = new TextProcessingState( notFinished );
115
116   if ( type == Lexer::SmartTrim )
117     notFinished->setInitialState( processingPostNewline );
118   else
119     notFinished->setInitialState( processingText );
120
121   if ( type == Lexer::SmartTrim ) {
122     addTransition<NewlineHandler>(               processingText, lexer, processingPostNewline );
123
124     addTransition<NewlineHandler>(               processingPostNewline, lexer, processingPostNewline );
125     addTransition<MaybeTemplateSyntaxHandler>(   processingPostNewline, lexer, processingBeginTemplateSyntax );
126     addTransition<NonWhitespaceLineTextHandler>( processingPostNewline, lexer, processingText );
127   }
128   addTransition<MaybeTemplateSyntaxHandler>( processingText, lexer, processingBeginTemplateSyntax );
129
130   addTransition<TagStartHandler>(                    processingBeginTemplateSyntax, lexer, processingTag );
131   addTransition<CommentStartHandler>(                processingBeginTemplateSyntax, lexer, processingComment );
132   addTransition<BeginValueHandler>(             processingBeginTemplateSyntax, lexer, maybeProcessingValue );
133
134   if ( type == Lexer::SmartTrim ) {
135     addTransition<NotBeginTemplateSyntaxOrNewlineHandler>( processingBeginTemplateSyntax, lexer, processingText );
136     addTransition<NewlineHandler>(                processingBeginTemplateSyntax, lexer, processingPostNewline );
137   } else {
138     addTransition<NotBeginTemplateSyntaxHandler>( processingBeginTemplateSyntax, lexer, processingText );
139   }
140
141   addTransition<NewlineHandler>( processingTag, lexer, type == Lexer::SmartTrim ? processingPostNewline : processingText );
142   addTransition<TagEndHandler>(     processingTag, lexer, processingEndTag );
143
144   addTransition<NewlineHandler>( processingComment, lexer, type == Lexer::SmartTrim ? processingPostNewline : processingText );
145   addTransition<CommentEndHandler>( processingComment, lexer, processingEndComment );
146
147   addTransition<TagStartHandler>(                    maybeProcessingValue, lexer, processingTag );
148   addTransition<CommentStartHandler>(                maybeProcessingValue, lexer, processingComment );
149   addTransition<NotTagCommentOrNewlineHandler>(       maybeProcessingValue, lexer, processingValue );
150   addTransition<NewlineHandler>(       maybeProcessingValue, lexer, type == Lexer::SmartTrim ? processingPostNewline : processingText );
151
152   addTransition<NewlineHandler>(       processingValue, lexer, type == Lexer::SmartTrim ? processingPostNewline : processingText );
153   addTransition<MaybeEndValueHandler>( processingValue, lexer, processingEndValue );
154
155   addTransition<NewlineHandler>(              processingEndTag, lexer, processingPostNewline );
156   addTransition<NotEndTemplateSyntaxHandler>( processingEndTag, lexer, processingTag );
157   addTransition<EndTemplateSyntaxHandler>(    processingEndTag, lexer, processingPostTemplateSyntax );
158
159   addTransition<NewlineHandler>(              processingEndComment, lexer, processingPostNewline );
160   addTransition<NotEndTemplateSyntaxHandler>( processingEndComment, lexer, processingComment );
161   addTransition<EndTemplateSyntaxHandler>(    processingEndComment, lexer, processingPostTemplateSyntax );
162
163   addTransition<NewlineHandler>(              processingEndValue, lexer, processingPostNewline );
164   addTransition<NotEndTemplateSyntaxHandler>( processingEndValue, lexer, processingValue );
165   addTransition<EndTemplateSyntaxHandler>(    processingEndValue, lexer, processingPostTemplateSyntax );
166
167   if ( type != Lexer::SmartTrim ) {
168     processingPostTemplateSyntax->setUnconditionalTransition( processingText );
169   } else {
170     addTransition<SyntaxBoundaryNewlineHandler>( processingPostTemplateSyntax, lexer, processingPostNewline );
171     addTransition<WhitespaceNonNewlineHandler>(  processingPostTemplateSyntax, lexer, processingPostTemplateSyntaxWhitespace );
172     addTransition<FinalizingLineTextHandler>(    processingPostTemplateSyntax, lexer, processingText );
173     addTransition<SyntaxBoundaryHandler>(        processingPostTemplateSyntax, lexer, processingBeginTemplateSyntax );
174
175     // NOTE: We only have to transition to this if there was whitespace before the opening tag.
176     // Maybe store that in an external state property?
177     // Actually, this may be a bug if we try to finalize with trimming and there is no leading whitespace.
178     addTransition<SyntaxBoundaryNewlineHandler>( processingPostTemplateSyntaxWhitespace, lexer, processingPostNewline );
179     addTransition<FinalizingLineTextHandler>(    processingPostTemplateSyntaxWhitespace, lexer, processingText );
180     addTransition<SyntaxBoundaryHandler>(        processingPostTemplateSyntaxWhitespace, lexer, processingBeginTemplateSyntax );
181   }
182
183   {
184     EofHandler *handler = new EofHandler( lexer, notFinished );
185     handler->setTargetState( finished );
186     notFinished->setEndTransition( handler );
187   }
188
189   if ( type == Lexer::SmartTrim ) {
190     {
191       EofHandlerWithTrimming *handler = new EofHandlerWithTrimming( lexer, processingPostTemplateSyntaxWhitespace );
192       handler->setTargetState( finished );
193       processingPostTemplateSyntaxWhitespace->setEndTransition( handler );
194     }
195     {
196       EofHandlerWithTrimming *handler = new EofHandlerWithTrimming( lexer, processingPostTemplateSyntax );
197       handler->setTargetState( finished );
198       processingPostTemplateSyntax->setEndTransition( handler );
199     }
200   }
201   return machine;
202 }
203
204 Lexer::Lexer( const QString &templateString )
205   : m_templateString( templateString )
206 {
207
208 }
209
210 Lexer::~Lexer()
211 {
212 }
213
214 void Lexer::clearMarkers()
215 {
216   m_startSyntaxPosition = -1;
217   m_endSyntaxPosition = -1;
218   m_newlinePosition = -1;
219 }
220
221 void Lexer::reset()
222 {
223   m_tokenList.clear();
224   m_lineCount = 0;
225   m_upto = 0;
226   m_processedUpto = 0;
227   clearMarkers();
228 }
229
230 QList<Token> Lexer::tokenize(TrimType type)
231 {
232   TextProcessingMachine* machine = createMachine( this, type );
233
234   machine->start();
235
236   QString::const_iterator it = m_templateString.constBegin();
237   const QString::const_iterator end = m_templateString.constEnd();
238
239   reset();
240   for ( ; it != end; ++it, ++m_upto )
241     machine->processCharacter( it );
242
243   machine->finished();
244
245   machine->stop();
246
247   delete machine;
248
249   return m_tokenList;
250 }
251
252 void Lexer::markStartSyntax()
253 {
254   m_startSyntaxPosition = m_upto;
255 }
256
257 void Lexer::markEndSyntax()
258 {
259   m_endSyntaxPosition = m_upto + 1;
260 }
261
262 void Lexer::markNewline()
263 {
264   m_newlinePosition = m_upto;
265   ++m_lineCount;
266 }
267
268 void Lexer::finalizeToken()
269 {
270   int nextPosition = m_upto;
271   const bool validSyntax = m_endSyntaxPosition > m_startSyntaxPosition && ( m_startSyntaxPosition >= m_processedUpto );
272
273   if ( validSyntax && m_startSyntaxPosition >= 0 ) {
274     nextPosition = m_startSyntaxPosition - 1;
275   }
276   finalizeToken( nextPosition, validSyntax );
277 }
278
279 void Lexer::finalizeTokenWithTrimmedWhitespace()
280 {
281   int nextPosition = m_upto;
282   const bool validSyntax = m_endSyntaxPosition > m_startSyntaxPosition;
283   if ( validSyntax && m_startSyntaxPosition > 0 ) {
284     if ( m_newlinePosition >= 0 && m_newlinePosition >= m_processedUpto )
285       nextPosition = qMin( m_startSyntaxPosition - 1, m_newlinePosition );
286     else
287       nextPosition = m_startSyntaxPosition - 1;
288   }
289   finalizeToken( nextPosition, validSyntax );
290 }
291
292 void Lexer::finalizeToken( int nextPosition, bool processSyntax )
293 {
294   {
295     Token token;
296     token.content = m_templateString.mid( m_processedUpto, nextPosition - m_processedUpto );
297     token.tokenType = TextToken;
298     token.linenumber = m_lineCount;
299     m_tokenList.append( token );
300   }
301
302   m_processedUpto = nextPosition;
303
304   if ( !processSyntax )
305     return;
306
307   m_processedUpto = m_endSyntaxPosition;
308
309   const QChar differentiator = *( m_templateString.constData() + m_startSyntaxPosition );
310   if ( differentiator == QLatin1Char( '#' ) )
311     return;
312
313   Token syntaxToken;
314   syntaxToken.content = m_templateString.mid( m_startSyntaxPosition + 1, m_endSyntaxPosition - m_startSyntaxPosition - 3 ).trimmed();
315   syntaxToken.linenumber = m_lineCount;
316
317   if ( differentiator == QLatin1Char( '{' ) ) {
318     syntaxToken.tokenType = VariableToken;
319   } else {
320     Q_ASSERT( differentiator == QLatin1Char( '%' ) );
321     syntaxToken.tokenType = BlockToken;
322   }
323   m_tokenList.append( syntaxToken );
324 }