Serenity Operating System
at master 448 lines 14 kB view raw
1/* 2 * Copyright (c) 2021, Daniel Bertalan <dani@danielbertalan.dev> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/GenericLexer.h> 9#include <AK/HashTable.h> 10#include <AK/OwnPtr.h> 11#include <AK/SourceGenerator.h> 12#include <AK/StringBuilder.h> 13#include <AK/Types.h> 14#include <LibCore/ArgsParser.h> 15#include <LibCore/File.h> 16#include <LibMain/Main.h> 17#include <ctype.h> 18 19struct Range { 20 int begin; 21 int end; 22}; 23 24struct StateTransition { 25 Optional<DeprecatedString> new_state; 26 Optional<DeprecatedString> action; 27}; 28 29struct MatchedAction { 30 Range range; 31 StateTransition action; 32}; 33 34struct State { 35 DeprecatedString name; 36 Vector<MatchedAction> actions; 37 Optional<DeprecatedString> entry_action; 38 Optional<DeprecatedString> exit_action; 39}; 40 41struct StateMachine { 42 DeprecatedString name; 43 DeprecatedString initial_state; 44 Vector<State> states; 45 Optional<State> anywhere; 46 Optional<DeprecatedString> namespaces; 47}; 48 49static OwnPtr<StateMachine> 50parse_state_machine(StringView input) 51{ 52 auto state_machine = make<StateMachine>(); 53 GenericLexer lexer(input); 54 55 auto consume_whitespace = [&] { 56 bool consumed = true; 57 while (consumed) { 58 consumed = lexer.consume_while(isspace).length() > 0; 59 if (lexer.consume_specific("//")) { 60 lexer.consume_line(); 61 consumed = true; 62 } 63 } 64 }; 65 66 auto consume_identifier = [&] { 67 consume_whitespace(); 68 return lexer.consume_while([](char c) { return isalnum(c) || c == '_'; }); 69 }; 70 71 auto get_hex_value = [&](char c) { 72 if (isdigit(c)) 73 return c - '0'; 74 else 75 return c - 'a' + 10; 76 }; 77 78 auto consume_number = [&] { 79 int num = 0; 80 consume_whitespace(); 81 if (lexer.consume_specific("0x")) { 82 auto hex_digits = lexer.consume_while([](char c) { 83 if (isdigit(c)) return true; 84 else { 85 c = tolower(c); 86 return (c >= 'a' && c <= 'f'); 87 } }); 88 for (auto c : hex_digits) 89 num = 16 * num + get_hex_value(c); 90 } else { 91 lexer.consume_specific('\''); 92 if (lexer.next_is('\\')) { 93 num = (int)lexer.consume_escaped_character('\\'); 94 } else { 95 num = lexer.consume_until('\'').to_int().value(); 96 lexer.ignore(); 97 } 98 lexer.consume_specific('\''); 99 } 100 return num; 101 }; 102 103 auto consume_condition = [&] { 104 Range condition; 105 consume_whitespace(); 106 if (lexer.consume_specific('[')) { 107 consume_whitespace(); 108 condition.begin = consume_number(); 109 consume_whitespace(); 110 lexer.consume_specific(".."); 111 consume_whitespace(); 112 condition.end = consume_number(); 113 consume_whitespace(); 114 lexer.consume_specific(']'); 115 } else { 116 auto num = consume_number(); 117 condition.begin = num; 118 condition.end = num; 119 } 120 return condition; 121 }; 122 123 auto consume_action = [&]() { 124 StateTransition action; 125 consume_whitespace(); 126 lexer.consume_specific("=>"); 127 consume_whitespace(); 128 lexer.consume_specific('('); 129 consume_whitespace(); 130 if (!lexer.consume_specific("_")) 131 action.new_state = consume_identifier(); 132 consume_whitespace(); 133 lexer.consume_specific(','); 134 consume_whitespace(); 135 if (!lexer.consume_specific("_")) 136 action.action = consume_identifier(); 137 consume_whitespace(); 138 lexer.consume_specific(')'); 139 return action; 140 }; 141 142 auto consume_state_description 143 = [&] { 144 State state; 145 consume_whitespace(); 146 state.name = consume_identifier(); 147 consume_whitespace(); 148 consume_whitespace(); 149 lexer.consume_specific('{'); 150 for (;;) { 151 consume_whitespace(); 152 if (lexer.consume_specific('}')) { 153 break; 154 } 155 if (lexer.consume_specific("@entry")) { 156 consume_whitespace(); 157 state.entry_action = consume_identifier(); 158 } else if (lexer.consume_specific("@exit")) { 159 consume_whitespace(); 160 state.exit_action = consume_identifier(); 161 } else if (lexer.next_is('@')) { 162 auto directive = consume_identifier().to_deprecated_string(); 163 fprintf(stderr, "Unimplemented @ directive %s\n", directive.characters()); 164 exit(1); 165 } else { 166 MatchedAction matched_action; 167 matched_action.range = consume_condition(); 168 matched_action.action = consume_action(); 169 state.actions.append(matched_action); 170 } 171 } 172 return state; 173 }; 174 175 while (!lexer.is_eof()) { 176 consume_whitespace(); 177 if (lexer.is_eof()) 178 break; 179 if (lexer.consume_specific("@namespace")) { 180 consume_whitespace(); 181 state_machine->namespaces = lexer.consume_while([](char c) { return isalpha(c) || c == ':'; }); 182 } else if (lexer.consume_specific("@begin")) { 183 consume_whitespace(); 184 state_machine->initial_state = consume_identifier(); 185 } else if (lexer.consume_specific("@name")) { 186 consume_whitespace(); 187 state_machine->name = consume_identifier(); 188 } else if (lexer.next_is("@anywhere")) { 189 lexer.consume_specific('@'); 190 state_machine->anywhere = consume_state_description(); 191 } else if (lexer.consume_specific('@')) { 192 auto directive = consume_identifier().to_deprecated_string(); 193 fprintf(stderr, "Unimplemented @ directive %s\n", directive.characters()); 194 exit(1); 195 } else { 196 auto description = consume_state_description(); 197 state_machine->states.append(description); 198 } 199 } 200 201 if (state_machine->initial_state.is_empty()) { 202 fprintf(stderr, "Missing @begin directive\n"); 203 exit(1); 204 } else if (state_machine->name.is_empty()) { 205 fprintf(stderr, "Missing @name directive\n"); 206 exit(1); 207 } 208 209 if (state_machine->anywhere.has_value()) { 210 state_machine->anywhere.value().name = "_Anywhere"; 211 } 212 return state_machine; 213} 214 215void output_header(StateMachine const&, SourceGenerator&); 216 217ErrorOr<int> serenity_main(Main::Arguments arguments) 218{ 219 Core::ArgsParser args_parser; 220 StringView path; 221 args_parser.add_positional_argument(path, "Path to parser description", "input", Core::ArgsParser::Required::Yes); 222 args_parser.parse(arguments); 223 224 auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read)); 225 auto content = TRY(file->read_until_eof()); 226 auto state_machine = parse_state_machine(content); 227 228 StringBuilder builder; 229 SourceGenerator generator { builder }; 230 output_header(*state_machine, generator); 231 outln("{}", generator.as_string_view()); 232 return 0; 233} 234 235HashTable<DeprecatedString> actions(StateMachine const& machine) 236{ 237 HashTable<DeprecatedString> table; 238 239 auto do_state = [&](State const& state) { 240 if (state.entry_action.has_value()) 241 table.set(state.entry_action.value()); 242 if (state.exit_action.has_value()) 243 table.set(state.exit_action.value()); 244 for (auto action : state.actions) { 245 if (action.action.action.has_value()) 246 table.set(action.action.action.value()); 247 } 248 }; 249 for (auto state : machine.states) { 250 do_state(state); 251 } 252 if (machine.anywhere.has_value()) 253 do_state(machine.anywhere.value()); 254 return table; 255} 256 257void generate_lookup_table(StateMachine const& machine, SourceGenerator& generator) 258{ 259 generator.append(R"~~~( 260 static constexpr StateTransition STATE_TRANSITION_TABLE[][256] = { 261)~~~"); 262 263 auto generate_for_state = [&](State const& s) { 264 auto table_generator = generator.fork(); 265 table_generator.set("active_state", s.name); 266 table_generator.append("/* @active_state@ */ { "); 267 VERIFY(!s.name.is_empty()); 268 Vector<StateTransition> row; 269 for (int i = 0; i < 256; i++) 270 row.append({ s.name, "_Ignore" }); 271 for (auto action : s.actions) { 272 for (int range_element = action.range.begin; range_element <= action.range.end; range_element++) { 273 row[range_element] = { action.action.new_state, action.action.action }; 274 } 275 } 276 for (int i = 0; i < 256; ++i) { 277 auto cell_generator = table_generator.fork(); 278 cell_generator.set("cell_new_state", row[i].new_state.value_or(s.name)); 279 cell_generator.set("cell_action", row[i].action.value_or("_Ignore")); 280 cell_generator.append(" {State::@cell_new_state@, Action::@cell_action@}, "); 281 } 282 table_generator.append("},\n"); 283 }; 284 if (machine.anywhere.has_value()) { 285 generate_for_state(machine.anywhere.value()); 286 } 287 for (auto s : machine.states) { 288 generate_for_state(s); 289 } 290 generator.append(R"~~~( 291 }; 292)~~~"); 293} 294 295void output_header(StateMachine const& machine, SourceGenerator& generator) 296{ 297 generator.set("class_name", machine.name); 298 generator.set("initial_state", machine.initial_state); 299 generator.set("state_count", DeprecatedString::number(machine.states.size() + 1)); 300 301 generator.append(R"~~~( 302#pragma once 303 304#include <AK/Function.h> 305#include <AK/Platform.h> 306#include <AK/Types.h> 307 )~~~"); 308 if (machine.namespaces.has_value()) { 309 generator.set("namespace", machine.namespaces.value()); 310 generator.append(R"~~~( 311namespace @namespace@ { 312)~~~"); 313 } 314 generator.append(R"~~~( 315class @class_name@ { 316public: 317 enum class Action : u8 { 318 _Ignore, 319)~~~"); 320 for (auto a : actions(machine)) { 321 if (a.is_empty()) 322 continue; 323 auto action_generator = generator.fork(); 324 action_generator.set("action.name", a); 325 action_generator.append(R"~~~( 326 @action.name@, 327 )~~~"); 328 } 329 330 generator.append(R"~~~( 331 }; // end Action 332 333 using Handler = Function<void(Action, u8)>; 334 335 @class_name@(Handler handler) 336 : m_handler(move(handler)) 337 { 338 } 339 340 void advance(u8 byte) 341 { 342 auto next_state = lookup_state_transition(byte); 343 bool state_will_change = next_state.new_state != m_state && next_state.new_state != State::_Anywhere; 344 345 // only run exit directive if state is being changed 346 if (state_will_change) { 347 switch (m_state) { 348)~~~"); 349 for (auto s : machine.states) { 350 auto state_generator = generator.fork(); 351 if (s.exit_action.has_value()) { 352 state_generator.set("state_name", s.name); 353 state_generator.set("action", s.exit_action.value()); 354 state_generator.append(R"~~~( 355 case State::@state_name@: 356 m_handler(Action::@action@, byte); 357 break; 358)~~~"); 359 } 360 } 361 generator.append(R"~~~( 362 default: 363 break; 364 } 365 } 366 367 if (next_state.action != Action::_Ignore) 368 m_handler(next_state.action, byte); 369 m_state = next_state.new_state; 370 371 // only run entry directive if state is being changed 372 if (state_will_change) 373 { 374 switch (next_state.new_state) 375 { 376)~~~"); 377 for (auto state : machine.states) { 378 auto state_generator = generator.fork(); 379 if (state.entry_action.has_value()) { 380 state_generator.set("state_name", state.name); 381 state_generator.set("action", state.entry_action.value()); 382 state_generator.append(R"~~~( 383 case State::@state_name@: 384 m_handler(Action::@action@, byte); 385 break; 386)~~~"); 387 } 388 } 389 generator.append(R"~~~( 390 default: 391 break; 392 } 393 } 394 } 395 396private: 397 enum class State : u8 { 398 _Anywhere, 399)~~~"); 400 401 for (auto s : machine.states) { 402 auto state_generator = generator.fork(); 403 state_generator.set("state.name", s.name); 404 state_generator.append(R"~~~( 405 @state.name@, 406)~~~"); 407 } 408 generator.append(R"~~~( 409 }; // end State 410 411 struct StateTransition { 412 State new_state; 413 Action action; 414 }; 415 416 State m_state { State::@initial_state@ }; 417 418 Handler m_handler; 419 420 ALWAYS_INLINE StateTransition lookup_state_transition(u8 byte) 421 { 422 VERIFY((u8)m_state < @state_count@); 423)~~~"); 424 if (machine.anywhere.has_value()) { 425 generator.append(R"~~~( 426 auto anywhere_state = STATE_TRANSITION_TABLE[0][byte]; 427 if (anywhere_state.new_state != State::_Anywhere || anywhere_state.action != Action::_Ignore) 428 return anywhere_state; 429 else 430)~~~"); 431 } 432 generator.append(R"~~~( 433 return STATE_TRANSITION_TABLE[(u8)m_state][byte]; 434 } 435)~~~"); 436 437 auto table_generator = generator.fork(); 438 generate_lookup_table(machine, table_generator); 439 generator.append(R"~~~( 440}; // end @class_name@ 441)~~~"); 442 443 if (machine.namespaces.has_value()) { 444 generator.append(R"~~~( 445} // end namespace 446)~~~"); 447 } 448}