1719e22d4SMarek Sokolowski //===-- ResourceScriptToken.cpp ---------------------------------*- C++-*-===//
2719e22d4SMarek Sokolowski //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6719e22d4SMarek Sokolowski //
7719e22d4SMarek Sokolowski //===---------------------------------------------------------------------===//
8719e22d4SMarek Sokolowski //
9719e22d4SMarek Sokolowski // This file implements an interface defined in ResourceScriptToken.h.
10719e22d4SMarek Sokolowski // In particular, it defines an .rc script tokenizer.
11719e22d4SMarek Sokolowski //
12719e22d4SMarek Sokolowski //===---------------------------------------------------------------------===//
13719e22d4SMarek Sokolowski
14719e22d4SMarek Sokolowski #include "ResourceScriptToken.h"
15d10c995bSSam McCall #include "llvm/ADT/StringExtras.h"
16719e22d4SMarek Sokolowski #include "llvm/Support/raw_ostream.h"
17719e22d4SMarek Sokolowski
18719e22d4SMarek Sokolowski #include <algorithm>
19719e22d4SMarek Sokolowski #include <cassert>
20719e22d4SMarek Sokolowski #include <cctype>
21719e22d4SMarek Sokolowski #include <cstdlib>
22719e22d4SMarek Sokolowski #include <utility>
23719e22d4SMarek Sokolowski
24719e22d4SMarek Sokolowski using namespace llvm;
25719e22d4SMarek Sokolowski
26719e22d4SMarek Sokolowski using Kind = RCToken::Kind;
27719e22d4SMarek Sokolowski
28719e22d4SMarek Sokolowski // Checks if Representation is a correct description of an RC integer.
29719e22d4SMarek Sokolowski // It should be a 32-bit unsigned integer, either decimal, octal (0[0-7]+),
30719e22d4SMarek Sokolowski // or hexadecimal (0x[0-9a-f]+). It might be followed by a single 'L'
31719e22d4SMarek Sokolowski // character (that is the difference between our representation and
32719e22d4SMarek Sokolowski // StringRef's one). If Representation is correct, 'true' is returned and
33719e22d4SMarek Sokolowski // the return value is put back in Num.
rcGetAsInteger(StringRef Representation,uint32_t & Num)34719e22d4SMarek Sokolowski static bool rcGetAsInteger(StringRef Representation, uint32_t &Num) {
35719e22d4SMarek Sokolowski size_t Length = Representation.size();
36719e22d4SMarek Sokolowski if (Length == 0)
37719e22d4SMarek Sokolowski return false;
38719e22d4SMarek Sokolowski // Strip the last 'L' if unnecessary.
39719e22d4SMarek Sokolowski if (std::toupper(Representation.back()) == 'L')
40719e22d4SMarek Sokolowski Representation = Representation.drop_back(1);
41719e22d4SMarek Sokolowski
42719e22d4SMarek Sokolowski return !Representation.getAsInteger<uint32_t>(0, Num);
43719e22d4SMarek Sokolowski }
44719e22d4SMarek Sokolowski
RCToken(RCToken::Kind RCTokenKind,StringRef Value)45719e22d4SMarek Sokolowski RCToken::RCToken(RCToken::Kind RCTokenKind, StringRef Value)
46719e22d4SMarek Sokolowski : TokenKind(RCTokenKind), TokenValue(Value) {}
47719e22d4SMarek Sokolowski
intValue() const48719e22d4SMarek Sokolowski uint32_t RCToken::intValue() const {
49719e22d4SMarek Sokolowski assert(TokenKind == Kind::Int);
50719e22d4SMarek Sokolowski // We assume that the token already is a correct integer (checked by
51719e22d4SMarek Sokolowski // rcGetAsInteger).
52719e22d4SMarek Sokolowski uint32_t Result;
53719e22d4SMarek Sokolowski bool IsSuccess = rcGetAsInteger(TokenValue, Result);
54719e22d4SMarek Sokolowski assert(IsSuccess);
55719e22d4SMarek Sokolowski (void)IsSuccess; // Silence the compiler warning when -DNDEBUG flag is on.
56719e22d4SMarek Sokolowski return Result;
57719e22d4SMarek Sokolowski }
58719e22d4SMarek Sokolowski
isLongInt() const5907bc04ffSZachary Turner bool RCToken::isLongInt() const {
6007bc04ffSZachary Turner return TokenKind == Kind::Int && std::toupper(TokenValue.back()) == 'L';
6107bc04ffSZachary Turner }
6207bc04ffSZachary Turner
value() const63719e22d4SMarek Sokolowski StringRef RCToken::value() const { return TokenValue; }
64719e22d4SMarek Sokolowski
kind() const65719e22d4SMarek Sokolowski Kind RCToken::kind() const { return TokenKind; }
66719e22d4SMarek Sokolowski
isBinaryOp() const677e89ee7fSMarek Sokolowski bool RCToken::isBinaryOp() const {
687e89ee7fSMarek Sokolowski switch (TokenKind) {
697e89ee7fSMarek Sokolowski case Kind::Plus:
707e89ee7fSMarek Sokolowski case Kind::Minus:
717e89ee7fSMarek Sokolowski case Kind::Pipe:
727e89ee7fSMarek Sokolowski case Kind::Amp:
737e89ee7fSMarek Sokolowski return true;
747e89ee7fSMarek Sokolowski default:
757e89ee7fSMarek Sokolowski return false;
767e89ee7fSMarek Sokolowski }
777e89ee7fSMarek Sokolowski }
787e89ee7fSMarek Sokolowski
getStringError(const Twine & message)79719e22d4SMarek Sokolowski static Error getStringError(const Twine &message) {
80719e22d4SMarek Sokolowski return make_error<StringError>("Error parsing file: " + message,
81719e22d4SMarek Sokolowski inconvertibleErrorCode());
82719e22d4SMarek Sokolowski }
83719e22d4SMarek Sokolowski
84719e22d4SMarek Sokolowski namespace {
85719e22d4SMarek Sokolowski
86719e22d4SMarek Sokolowski class Tokenizer {
87719e22d4SMarek Sokolowski public:
Tokenizer(StringRef Input)8899762872SSimon Pilgrim Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()), Pos(0) {}
89719e22d4SMarek Sokolowski
90719e22d4SMarek Sokolowski Expected<std::vector<RCToken>> run();
91719e22d4SMarek Sokolowski
92719e22d4SMarek Sokolowski private:
93719e22d4SMarek Sokolowski // All 'advancing' methods return boolean values; if they're equal to false,
94719e22d4SMarek Sokolowski // the stream has ended or failed.
95719e22d4SMarek Sokolowski bool advance(size_t Amount = 1);
96719e22d4SMarek Sokolowski bool skipWhitespaces();
97719e22d4SMarek Sokolowski
98719e22d4SMarek Sokolowski // Consumes a token. If any problem occurred, a non-empty Error is returned.
99719e22d4SMarek Sokolowski Error consumeToken(const Kind TokenKind);
100719e22d4SMarek Sokolowski
101719e22d4SMarek Sokolowski // Check if tokenizer is about to read FollowingChars.
102719e22d4SMarek Sokolowski bool willNowRead(StringRef FollowingChars) const;
103719e22d4SMarek Sokolowski
104719e22d4SMarek Sokolowski // Check if tokenizer can start reading an identifier at current position.
105719e22d4SMarek Sokolowski // The original tool did non specify the rules to determine what is a correct
106719e22d4SMarek Sokolowski // identifier. We assume they should follow the C convention:
107b04d84c0SBenjamin Kramer // [a-zA-Z_][a-zA-Z0-9_]*.
108719e22d4SMarek Sokolowski bool canStartIdentifier() const;
109719e22d4SMarek Sokolowski // Check if tokenizer can continue reading an identifier.
110719e22d4SMarek Sokolowski bool canContinueIdentifier() const;
111719e22d4SMarek Sokolowski
112719e22d4SMarek Sokolowski // Check if tokenizer can start reading an integer.
113719e22d4SMarek Sokolowski // A correct integer always starts with a 0-9 digit,
114719e22d4SMarek Sokolowski // can contain characters 0-9A-Fa-f (digits),
115719e22d4SMarek Sokolowski // Ll (marking the integer is 32-bit), Xx (marking the representation
116719e22d4SMarek Sokolowski // is hexadecimal). As some kind of separator should come after the
117719e22d4SMarek Sokolowski // integer, we can consume the integer until a non-alphanumeric
118719e22d4SMarek Sokolowski // character.
119719e22d4SMarek Sokolowski bool canStartInt() const;
120719e22d4SMarek Sokolowski bool canContinueInt() const;
121719e22d4SMarek Sokolowski
122719e22d4SMarek Sokolowski bool canStartString() const;
123719e22d4SMarek Sokolowski
124bd3a9dbaSZachary Turner // Check if tokenizer can start reading a single line comment (e.g. a comment
125bd3a9dbaSZachary Turner // that begins with '//')
126bd3a9dbaSZachary Turner bool canStartLineComment() const;
127bd3a9dbaSZachary Turner
128bd3a9dbaSZachary Turner // Check if tokenizer can start or finish reading a block comment (e.g. a
129bd3a9dbaSZachary Turner // comment that begins with '/*' and ends with '*/')
130bd3a9dbaSZachary Turner bool canStartBlockComment() const;
131bd3a9dbaSZachary Turner
132bd3a9dbaSZachary Turner // Throw away all remaining characters on the current line.
133bd3a9dbaSZachary Turner void skipCurrentLine();
134bd3a9dbaSZachary Turner
135719e22d4SMarek Sokolowski bool streamEof() const;
136719e22d4SMarek Sokolowski
137719e22d4SMarek Sokolowski // Classify the token that is about to be read from the current position.
138719e22d4SMarek Sokolowski Kind classifyCurrentToken() const;
139719e22d4SMarek Sokolowski
140719e22d4SMarek Sokolowski // Process the Kind::Identifier token - check if it is
141719e22d4SMarek Sokolowski // an identifier describing a block start or end.
142719e22d4SMarek Sokolowski void processIdentifier(RCToken &token) const;
143719e22d4SMarek Sokolowski
144719e22d4SMarek Sokolowski StringRef Data;
145719e22d4SMarek Sokolowski size_t DataLength, Pos;
146719e22d4SMarek Sokolowski };
147719e22d4SMarek Sokolowski
skipCurrentLine()148bd3a9dbaSZachary Turner void Tokenizer::skipCurrentLine() {
149bd3a9dbaSZachary Turner Pos = Data.find_first_of("\r\n", Pos);
150bd3a9dbaSZachary Turner Pos = Data.find_first_not_of("\r\n", Pos);
151bd3a9dbaSZachary Turner
152bd3a9dbaSZachary Turner if (Pos == StringRef::npos)
153bd3a9dbaSZachary Turner Pos = DataLength;
154bd3a9dbaSZachary Turner }
155bd3a9dbaSZachary Turner
run()156719e22d4SMarek Sokolowski Expected<std::vector<RCToken>> Tokenizer::run() {
157719e22d4SMarek Sokolowski Pos = 0;
158719e22d4SMarek Sokolowski std::vector<RCToken> Result;
159719e22d4SMarek Sokolowski
160719e22d4SMarek Sokolowski // Consume an optional UTF-8 Byte Order Mark.
161719e22d4SMarek Sokolowski if (willNowRead("\xef\xbb\xbf"))
162719e22d4SMarek Sokolowski advance(3);
163719e22d4SMarek Sokolowski
164719e22d4SMarek Sokolowski while (!streamEof()) {
165719e22d4SMarek Sokolowski if (!skipWhitespaces())
166719e22d4SMarek Sokolowski break;
167719e22d4SMarek Sokolowski
168719e22d4SMarek Sokolowski Kind TokenKind = classifyCurrentToken();
169719e22d4SMarek Sokolowski if (TokenKind == Kind::Invalid)
170719e22d4SMarek Sokolowski return getStringError("Invalid token found at position " + Twine(Pos));
171719e22d4SMarek Sokolowski
172719e22d4SMarek Sokolowski const size_t TokenStart = Pos;
173719e22d4SMarek Sokolowski if (Error TokenError = consumeToken(TokenKind))
174c55cf4afSBill Wendling return std::move(TokenError);
175719e22d4SMarek Sokolowski
176bd3a9dbaSZachary Turner // Comments are just deleted, don't bother saving them.
177bd3a9dbaSZachary Turner if (TokenKind == Kind::LineComment || TokenKind == Kind::StartComment)
178bd3a9dbaSZachary Turner continue;
179bd3a9dbaSZachary Turner
180719e22d4SMarek Sokolowski RCToken Token(TokenKind, Data.take_front(Pos).drop_front(TokenStart));
181719e22d4SMarek Sokolowski if (TokenKind == Kind::Identifier) {
182719e22d4SMarek Sokolowski processIdentifier(Token);
183719e22d4SMarek Sokolowski } else if (TokenKind == Kind::Int) {
184719e22d4SMarek Sokolowski uint32_t TokenInt;
185719e22d4SMarek Sokolowski if (!rcGetAsInteger(Token.value(), TokenInt)) {
186719e22d4SMarek Sokolowski // The integer has incorrect format or cannot be represented in
187719e22d4SMarek Sokolowski // a 32-bit integer.
188719e22d4SMarek Sokolowski return getStringError("Integer invalid or too large: " +
189719e22d4SMarek Sokolowski Token.value().str());
190719e22d4SMarek Sokolowski }
191719e22d4SMarek Sokolowski }
192719e22d4SMarek Sokolowski
193719e22d4SMarek Sokolowski Result.push_back(Token);
194719e22d4SMarek Sokolowski }
195719e22d4SMarek Sokolowski
196719e22d4SMarek Sokolowski return Result;
197719e22d4SMarek Sokolowski }
198719e22d4SMarek Sokolowski
advance(size_t Amount)199719e22d4SMarek Sokolowski bool Tokenizer::advance(size_t Amount) {
200719e22d4SMarek Sokolowski Pos += Amount;
201719e22d4SMarek Sokolowski return !streamEof();
202719e22d4SMarek Sokolowski }
203719e22d4SMarek Sokolowski
skipWhitespaces()204719e22d4SMarek Sokolowski bool Tokenizer::skipWhitespaces() {
205d10c995bSSam McCall while (!streamEof() && isSpace(Data[Pos]))
206719e22d4SMarek Sokolowski advance();
207719e22d4SMarek Sokolowski return !streamEof();
208719e22d4SMarek Sokolowski }
209719e22d4SMarek Sokolowski
consumeToken(const Kind TokenKind)210719e22d4SMarek Sokolowski Error Tokenizer::consumeToken(const Kind TokenKind) {
211719e22d4SMarek Sokolowski switch (TokenKind) {
212719e22d4SMarek Sokolowski // One-character token consumption.
213719e22d4SMarek Sokolowski #define TOKEN(Name)
214719e22d4SMarek Sokolowski #define SHORT_TOKEN(Name, Ch) case Kind::Name:
215b961d29bSDavid Blaikie #include "ResourceScriptTokenList.def"
216719e22d4SMarek Sokolowski advance();
217719e22d4SMarek Sokolowski return Error::success();
218719e22d4SMarek Sokolowski
219bd3a9dbaSZachary Turner case Kind::LineComment:
220bd3a9dbaSZachary Turner advance(2);
221bd3a9dbaSZachary Turner skipCurrentLine();
222bd3a9dbaSZachary Turner return Error::success();
223bd3a9dbaSZachary Turner
224bd3a9dbaSZachary Turner case Kind::StartComment: {
225bd3a9dbaSZachary Turner advance(2);
226bd3a9dbaSZachary Turner auto EndPos = Data.find("*/", Pos);
227bd3a9dbaSZachary Turner if (EndPos == StringRef::npos)
228bd3a9dbaSZachary Turner return getStringError(
229bd3a9dbaSZachary Turner "Unclosed multi-line comment beginning at position " + Twine(Pos));
230bd3a9dbaSZachary Turner advance(EndPos - Pos);
231bd3a9dbaSZachary Turner advance(2);
232bd3a9dbaSZachary Turner return Error::success();
233bd3a9dbaSZachary Turner }
234719e22d4SMarek Sokolowski case Kind::Identifier:
235719e22d4SMarek Sokolowski while (!streamEof() && canContinueIdentifier())
236719e22d4SMarek Sokolowski advance();
237719e22d4SMarek Sokolowski return Error::success();
238719e22d4SMarek Sokolowski
239719e22d4SMarek Sokolowski case Kind::Int:
240719e22d4SMarek Sokolowski while (!streamEof() && canContinueInt())
241719e22d4SMarek Sokolowski advance();
242719e22d4SMarek Sokolowski return Error::success();
243719e22d4SMarek Sokolowski
244719e22d4SMarek Sokolowski case Kind::String:
245719e22d4SMarek Sokolowski // Consume the preceding 'L', if there is any.
246719e22d4SMarek Sokolowski if (std::toupper(Data[Pos]) == 'L')
247719e22d4SMarek Sokolowski advance();
248719e22d4SMarek Sokolowski // Consume the double-quote.
249719e22d4SMarek Sokolowski advance();
250719e22d4SMarek Sokolowski
251719e22d4SMarek Sokolowski // Consume the characters until the end of the file, line or string.
252719e22d4SMarek Sokolowski while (true) {
253719e22d4SMarek Sokolowski if (streamEof()) {
254719e22d4SMarek Sokolowski return getStringError("Unterminated string literal.");
255719e22d4SMarek Sokolowski } else if (Data[Pos] == '"') {
256719e22d4SMarek Sokolowski // Consume the ending double-quote.
257719e22d4SMarek Sokolowski advance();
258a92eb33aSZachary Turner // However, if another '"' follows this double-quote, the string didn't
259a92eb33aSZachary Turner // end and we just included '"' into the string.
260a92eb33aSZachary Turner if (!willNowRead("\""))
261719e22d4SMarek Sokolowski return Error::success();
262719e22d4SMarek Sokolowski } else if (Data[Pos] == '\n') {
263719e22d4SMarek Sokolowski return getStringError("String literal not terminated in the line.");
264719e22d4SMarek Sokolowski }
265719e22d4SMarek Sokolowski
266719e22d4SMarek Sokolowski advance();
267719e22d4SMarek Sokolowski }
268719e22d4SMarek Sokolowski
269719e22d4SMarek Sokolowski case Kind::Invalid:
270719e22d4SMarek Sokolowski assert(false && "Cannot consume an invalid token.");
271719e22d4SMarek Sokolowski }
272d0c5bfa2SMarek Sokolowski
273c3e546feSSimon Pilgrim llvm_unreachable("Unknown RCToken::Kind");
274719e22d4SMarek Sokolowski }
275719e22d4SMarek Sokolowski
willNowRead(StringRef FollowingChars) const276719e22d4SMarek Sokolowski bool Tokenizer::willNowRead(StringRef FollowingChars) const {
277719e22d4SMarek Sokolowski return Data.drop_front(Pos).startswith(FollowingChars);
278719e22d4SMarek Sokolowski }
279719e22d4SMarek Sokolowski
canStartIdentifier() const280719e22d4SMarek Sokolowski bool Tokenizer::canStartIdentifier() const {
281719e22d4SMarek Sokolowski assert(!streamEof());
282719e22d4SMarek Sokolowski
283719e22d4SMarek Sokolowski const char CurChar = Data[Pos];
2844021cee9SMartin Storsjo return std::isalpha(CurChar) || CurChar == '_' || CurChar == '.';
285719e22d4SMarek Sokolowski }
286719e22d4SMarek Sokolowski
canContinueIdentifier() const287719e22d4SMarek Sokolowski bool Tokenizer::canContinueIdentifier() const {
288719e22d4SMarek Sokolowski assert(!streamEof());
289719e22d4SMarek Sokolowski const char CurChar = Data[Pos];
2904021cee9SMartin Storsjo return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' ||
291*0a1683f8SMartin Storsjö CurChar == '/' || CurChar == '\\' || CurChar == '-';
292719e22d4SMarek Sokolowski }
293719e22d4SMarek Sokolowski
canStartInt() const294719e22d4SMarek Sokolowski bool Tokenizer::canStartInt() const {
295719e22d4SMarek Sokolowski assert(!streamEof());
296719e22d4SMarek Sokolowski return std::isdigit(Data[Pos]);
297719e22d4SMarek Sokolowski }
298719e22d4SMarek Sokolowski
canStartBlockComment() const299bd3a9dbaSZachary Turner bool Tokenizer::canStartBlockComment() const {
300bd3a9dbaSZachary Turner assert(!streamEof());
301bd3a9dbaSZachary Turner return Data.drop_front(Pos).startswith("/*");
302bd3a9dbaSZachary Turner }
303bd3a9dbaSZachary Turner
canStartLineComment() const304bd3a9dbaSZachary Turner bool Tokenizer::canStartLineComment() const {
305bd3a9dbaSZachary Turner assert(!streamEof());
306bd3a9dbaSZachary Turner return Data.drop_front(Pos).startswith("//");
307bd3a9dbaSZachary Turner }
308bd3a9dbaSZachary Turner
canContinueInt() const309719e22d4SMarek Sokolowski bool Tokenizer::canContinueInt() const {
310719e22d4SMarek Sokolowski assert(!streamEof());
311719e22d4SMarek Sokolowski return std::isalnum(Data[Pos]);
312719e22d4SMarek Sokolowski }
313719e22d4SMarek Sokolowski
canStartString() const314719e22d4SMarek Sokolowski bool Tokenizer::canStartString() const {
315719e22d4SMarek Sokolowski return willNowRead("\"") || willNowRead("L\"") || willNowRead("l\"");
316719e22d4SMarek Sokolowski }
317719e22d4SMarek Sokolowski
streamEof() const318719e22d4SMarek Sokolowski bool Tokenizer::streamEof() const { return Pos == DataLength; }
319719e22d4SMarek Sokolowski
classifyCurrentToken() const320719e22d4SMarek Sokolowski Kind Tokenizer::classifyCurrentToken() const {
321bd3a9dbaSZachary Turner if (canStartBlockComment())
322bd3a9dbaSZachary Turner return Kind::StartComment;
323bd3a9dbaSZachary Turner if (canStartLineComment())
324bd3a9dbaSZachary Turner return Kind::LineComment;
325bd3a9dbaSZachary Turner
326719e22d4SMarek Sokolowski if (canStartInt())
327719e22d4SMarek Sokolowski return Kind::Int;
328719e22d4SMarek Sokolowski if (canStartString())
329719e22d4SMarek Sokolowski return Kind::String;
330719e22d4SMarek Sokolowski // BEGIN and END are at this point of lexing recognized as identifiers.
331719e22d4SMarek Sokolowski if (canStartIdentifier())
332719e22d4SMarek Sokolowski return Kind::Identifier;
333719e22d4SMarek Sokolowski
334719e22d4SMarek Sokolowski const char CurChar = Data[Pos];
335719e22d4SMarek Sokolowski
336719e22d4SMarek Sokolowski switch (CurChar) {
337719e22d4SMarek Sokolowski // One-character token classification.
338719e22d4SMarek Sokolowski #define TOKEN(Name)
339719e22d4SMarek Sokolowski #define SHORT_TOKEN(Name, Ch) \
340719e22d4SMarek Sokolowski case Ch: \
341719e22d4SMarek Sokolowski return Kind::Name;
342b961d29bSDavid Blaikie #include "ResourceScriptTokenList.def"
343719e22d4SMarek Sokolowski
344719e22d4SMarek Sokolowski default:
345719e22d4SMarek Sokolowski return Kind::Invalid;
346719e22d4SMarek Sokolowski }
347719e22d4SMarek Sokolowski }
348719e22d4SMarek Sokolowski
processIdentifier(RCToken & Token) const349719e22d4SMarek Sokolowski void Tokenizer::processIdentifier(RCToken &Token) const {
350719e22d4SMarek Sokolowski assert(Token.kind() == Kind::Identifier);
351719e22d4SMarek Sokolowski StringRef Name = Token.value();
352719e22d4SMarek Sokolowski
35342f74e82SMartin Storsjö if (Name.equals_insensitive("begin"))
354719e22d4SMarek Sokolowski Token = RCToken(Kind::BlockBegin, Name);
35542f74e82SMartin Storsjö else if (Name.equals_insensitive("end"))
356719e22d4SMarek Sokolowski Token = RCToken(Kind::BlockEnd, Name);
357719e22d4SMarek Sokolowski }
358719e22d4SMarek Sokolowski
359719e22d4SMarek Sokolowski } // anonymous namespace
360719e22d4SMarek Sokolowski
361719e22d4SMarek Sokolowski namespace llvm {
362719e22d4SMarek Sokolowski
tokenizeRC(StringRef Input)363719e22d4SMarek Sokolowski Expected<std::vector<RCToken>> tokenizeRC(StringRef Input) {
364719e22d4SMarek Sokolowski return Tokenizer(Input).run();
365719e22d4SMarek Sokolowski }
366719e22d4SMarek Sokolowski
367719e22d4SMarek Sokolowski } // namespace llvm
368