1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4// ============================================================
5//
6// StringLexer.cpp
7//
8
9
10//
11// Implements the StringLexer class
12//
13// ============================================================
14
15#define DISABLE_BINDER_DEBUG_LOGGING
16
17#include "stringlexer.hpp"
18#include "utils.hpp"
19
20#include "ex.h"
21
22namespace BINDER_SPACE
23{
24 StringLexer::LEXEME_TYPE
25 StringLexer::GetNextLexeme(SString &currentString, BOOL fPermitUnescapedQuotes)
26 {
27 BOOL fIsEscaped = FALSE;
28 WCHAR wcCurrentChar = INVALID_CHARACTER;
29 BINDER_LOG_ENTER(L"StringLexer::GetNextLexeme");
30
31 // Remove any white spaces
32 do
33 {
34 wcCurrentChar = PopCharacter(&fIsEscaped);
35 }
36 while (IsWhitespace(wcCurrentChar));
37
38 // Determine lexeme type
39 LEXEME_TYPE kLexemeType = LEXEME_TYPE_INVALID;
40 if (!fIsEscaped)
41 {
42 kLexemeType = GetLexemeType(wcCurrentChar);
43
44 if (kLexemeType != LEXEME_TYPE_STRING)
45 {
46 return kLexemeType;
47 }
48 }
49
50 // First character of string lexeme; push it back
51 PushCharacter(wcCurrentChar, fIsEscaped);
52 kLexemeType = ParseString(currentString, fPermitUnescapedQuotes);
53 if (kLexemeType == LEXEME_TYPE_STRING)
54 {
55 BINDER_LOG_LEAVE_HR(L"StringLexer::GetNextLexeme(LEXEME_TYPE_STRING)", S_OK);
56 }
57 else
58 {
59 BINDER_LOG_LEAVE_HR(L"StringLexer::GetNextLexeme(LEXEME_TYPE_INVALID)",
60 S_FALSE);
61 }
62
63 return kLexemeType;
64 }
65
66 StringLexer::LEXEME_TYPE
67 StringLexer::ParseString(SString &currentString, BOOL fPermitUnescapedQuotes)
68 {
69 BOOL fIsFirstCharacter = TRUE;
70 WCHAR wcCurrentChar = INVALID_CHARACTER;
71 WCHAR wcOpeningQuote = INVALID_CHARACTER;
72
73 currentString.Clear();
74
75 // Read until we find another lexeme that's not a string character
76 for (;;)
77 {
78 BOOL fIsEscaped = FALSE;
79 wcCurrentChar = PopCharacter(&fIsEscaped);
80
81 if (wcCurrentChar == INVALID_CHARACTER)
82 {
83 // Found invalid character encoding
84 BINDER_LOG(L"StringLexer::ParseString: Invalid character encoding");
85 return LEXEME_TYPE_INVALID;
86 }
87
88 if (IsEOS(wcCurrentChar))
89 {
90 if (IsQuoteCharacter(wcOpeningQuote))
91 {
92 // EOS and unclosed quotes is an error
93 BINDER_LOG(L"StringLexer::ParseString: EOS and unclosed quotes");
94 return LEXEME_TYPE_INVALID;
95 }
96 else
97 {
98 // Reached end of input and therefore of string
99 break;
100 }
101 }
102
103 if (fIsFirstCharacter)
104 {
105 fIsFirstCharacter = FALSE;
106
107 // If first character is quote, then record its quoteness
108 if (IsQuoteCharacter(wcCurrentChar))
109 {
110 wcOpeningQuote = wcCurrentChar;
111 continue;
112 }
113 }
114
115 if (wcCurrentChar == wcOpeningQuote)
116 {
117 // We've found the closing quote for a quoted string
118 break;
119 }
120
121 if (!fPermitUnescapedQuotes && !fIsEscaped && IsQuoteCharacter(wcCurrentChar) && !IsQuoteCharacter(wcOpeningQuote))
122 {
123 // Unescaped quotes in the middle of the string are an error
124 BINDER_LOG(L"StringLexer::ParseString: Quote in the middle of a string");
125 return LEXEME_TYPE_INVALID;
126 }
127
128 if (IsSeparatorChar(wcCurrentChar) && !IsQuoteCharacter(wcOpeningQuote) && !fIsEscaped)
129 {
130 // Unescaped separator char terminates the string
131 PushCharacter(wcCurrentChar, fIsEscaped);
132 break;
133 }
134
135 // Add character to current string
136 currentString.Append(wcCurrentChar);
137 }
138
139 if (!IsQuoteCharacter(wcOpeningQuote))
140 {
141 // Remove trailing white spaces from unquoted string
142 BINDER_LOG(L"StringLexer::ParseString: Trimming string");
143 TrimTrailingWhiteSpaces(currentString);
144 }
145
146 BINDER_LOG_STRING(L"string", currentString);
147
148 return LEXEME_TYPE_STRING;
149 }
150
151 void StringLexer::TrimTrailingWhiteSpaces(SString &currentString)
152 {
153 SString::Iterator begin = currentString.Begin();
154 SString::Iterator cursor = currentString.End() - 1;
155 BOOL fFoundWhiteSpace = FALSE;
156
157 for (;;)
158 {
159 if ((cursor >= begin) && IsWhitespace(cursor[0]))
160 {
161 fFoundWhiteSpace = TRUE;
162 cursor--;
163 continue;
164 }
165 break;
166 }
167
168 if (fFoundWhiteSpace)
169 {
170 currentString.Truncate(cursor + 1);
171 }
172 }
173};
174