Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <hpactor/cli/lexer.hpp>
16 : : #include <cctype>
17 : :
18 : : namespace hpactor {
19 : : namespace cli {
20 : :
21 : 2 : std::string Lexer::unescape(const std::string& s) {
22 : 2 : std::string out;
23 : 2 : out.reserve(s.size());
24 : 2 : bool escape = false;
25 : 25 : for (char c : s) {
26 : 23 : if (escape) {
27 : 1 : switch (c) {
28 : 1 : case 'n': out += '\n'; break;
29 : 0 : case 't': out += '\t'; break;
30 : 0 : case '\\': out += '\\'; break;
31 : 0 : case '"': out += '"'; break;
32 : 0 : default: out += '\\'; out += c; break;
33 : : }
34 : 1 : escape = false;
35 : 22 : } else if (c == '\\') {
36 : 1 : escape = true;
37 : : } else {
38 : 21 : out += c;
39 : : }
40 : : }
41 : 2 : return out;
42 : : }
43 : :
44 : 20 : std::vector<Token> Lexer::tokenize(const std::string& input) {
45 : 20 : std::vector<Token> tokens;
46 : 20 : size_t i = 0;
47 : 20 : size_t n = input.size();
48 : :
49 : 89 : while (i < n) {
50 : : // Skip whitespace
51 : 113 : while (i < n && std::isspace(static_cast<unsigned char>(input[i]))) ++i;
52 : 70 : if (i >= n) break;
53 : :
54 : 69 : Token tok;
55 : :
56 : : // Leading slash is a keyword "/"
57 : 69 : if (input[i] == '/' && (tokens.empty() || tokens.back().type == TokenType::Eof)) {
58 : 17 : tok.type = TokenType::Keyword;
59 : 17 : tok.value = "/";
60 : 17 : ++i;
61 : 17 : tokens.push_back(std::move(tok));
62 : 17 : continue;
63 : : }
64 : :
65 : : // Flag (--flag or --flag value)
66 : 52 : if (input[i] == '-' && i + 1 < n && input[i + 1] == '-') {
67 : 9 : i += 2; // skip --
68 : 9 : size_t start = i;
69 : 67 : while (i < n && !std::isspace(static_cast<unsigned char>(input[i]))) ++i;
70 : 9 : std::string flag_name = input.substr(start, i - start);
71 : :
72 : : // Peek ahead: if the next token is not a flag, it's a flag argument
73 : 9 : size_t j = i;
74 : 17 : while (j < n && std::isspace(static_cast<unsigned char>(input[j]))) ++j;
75 : 9 : bool next_is_flag = (j < n && input[j] == '-' && j + 1 < n && input[j + 1] == '-');
76 : 9 : if (j < n && !next_is_flag) {
77 : : // Next token is the argument
78 : 6 : i = j; // skip whitespace
79 : 6 : size_t arg_start = i;
80 : 6 : if (input[i] == '"') {
81 : 0 : ++i; // skip opening quote
82 : 0 : arg_start = i;
83 : 0 : while (i < n && input[i] != '"') {
84 : 0 : if (input[i] == '\\') ++i;
85 : 0 : ++i;
86 : : }
87 : 0 : std::string arg = unescape(input.substr(arg_start, i - arg_start));
88 : 0 : ++i; // skip closing quote
89 : 0 : tok.type = TokenType::FlagWithArg;
90 : 0 : tok.value = std::move(flag_name);
91 : 0 : tok.arg = std::move(arg);
92 : 0 : } else {
93 : 39 : while (i < n && !std::isspace(static_cast<unsigned char>(input[i]))) ++i;
94 : 6 : tok.type = TokenType::FlagWithArg;
95 : 6 : tok.value = std::move(flag_name);
96 : 6 : tok.arg = input.substr(arg_start, i - arg_start);
97 : : }
98 : 6 : } else {
99 : 3 : tok.type = TokenType::Flag;
100 : 3 : tok.value = std::move(flag_name);
101 : : }
102 : 9 : tokens.push_back(std::move(tok));
103 : 9 : continue;
104 : 9 : }
105 : :
106 : : // Quoted parameter
107 : 43 : if (input[i] == '"') {
108 : 2 : ++i; // skip opening quote
109 : 2 : size_t start = i;
110 : 24 : while (i < n && input[i] != '"') {
111 : 22 : if (input[i] == '\\') ++i;
112 : 22 : ++i;
113 : : }
114 : 2 : tok.type = TokenType::Parameter;
115 : 2 : tok.value = unescape(input.substr(start, i - start));
116 : 2 : ++i; // skip closing quote
117 : 2 : tokens.push_back(std::move(tok));
118 : 2 : continue;
119 : 2 : }
120 : :
121 : : // Regular keyword or parameter
122 : : {
123 : 41 : size_t start = i;
124 : 231 : while (i < n && !std::isspace(static_cast<unsigned char>(input[i]))) ++i;
125 : 41 : tok.type = TokenType::Keyword;
126 : 41 : tok.value = input.substr(start, i - start);
127 : 41 : tokens.push_back(std::move(tok));
128 : : }
129 : 69 : }
130 : :
131 : 40 : tokens.push_back(Token{TokenType::Eof, "", std::nullopt});
132 : 20 : return tokens;
133 : : }
134 : :
135 : : } // namespace cli
136 : : } // namespace hpactor
|