#include "bed.h" #include "internal/parser/parser.h" namespace bed::internal::parser { void Parser::operation() { if (peek() == '\0') { command->function = &bed.no_op; return; } uint64_t len = bed.functions.longest_match(peek_str()); if (len == 0) throw ed_error("Function not found."); functions::Function *function = bed.functions.get_ptr(peek_str(len)); token(io::Token::Function); advance(len); end_token(); command->function = function; char suffix = '\0'; switch (command->function->argument_kind) { case functions::Function::ArgumentKind::None: break; case functions::Function::ArgumentKind::Number: skip_ws(); command->argument = offset(); break; case functions::Function::ArgumentKind::Mark: if (('a' <= peek() && peek() <= 'z') || ('A' <= peek() && peek() <= 'Z')) command->argument = peek(); else throw ed_error("Valid mark needed."); token(io::Token::Mark); advance(); end_token(); break; case functions::Function::ArgumentKind::Any: command->argument = std::string(peek_str()); token(io::Token::Data); advance(peek_str().size()); end_token(); break; case functions::Function::ArgumentKind::Global: { char delim; std::string val; switch (peek()) { case '\0': throw ed_error("Command needs a delimited value."); case '{': { advance(); delim = '}'; uint16_t j = 0; while (peek(j) != '}' && peek(j) != '\0') { if (peek(j) == '\\') j++; j++; } switch (peek(j)) { case '\0': throw ed_error("Unterminated {"); case '}': val = peek_str(j); break; } advance(j + 1); } break; case '<': { advance(); delim = '<'; uint16_t j = 0; while (peek(j) != '>' || peek(j) != '\0') { if (peek(j) == '\\') j++; j++; } switch (peek(j)) { case '\0': throw ed_error("Unterminated <"); case '>': val = peek_str(j); break; } advance(j + 1); } break; case '^': case '~': advance(); delim = '^'; break; default: delim = peek(); advance(); uint64_t j = 0; while (true) { if (peek(j) == '\0') break; if (peek(j) == delim) break; else if (peek(j) == '\\') j += 2; else if (peek(j) == '[') while (peek(j) != '\0' && cmd[i] != ']') j++; else j++; } val = peek_str(j); advance(j + 1); } command->argument = functions::Function::GlobalArg(delim, std::move(val)); } break; case functions::Function::ArgumentKind::File: if (!(peek() == '\t' || peek() == ' ' || peek() == '\0')) throw ed_error("Incorrect file input usage."); skip_ws(); switch (peek()) { case '!': token(io::Token::Error); advance(); end_token(); token(io::Token::Shell); command->argument = functions::Function::ShellArg(std::string(peek_str())); advance(peek_str().size()); end_token(); break; case '\0': command->argument = std::monostate(); break; default: token(io::Token::File); command->argument = std::filesystem::path(peek_str()); advance(peek_str().size()); end_token(); break; } break; case functions::Function::ArgumentKind::Line: command->argument = buffer::Line(); addresses(command->argument_addresses); break; case functions::Function::ArgumentKind::Range: command->argument = buffer::Range(); addresses(command->argument_addresses); break; case functions::Function::ArgumentKind::Regex: { char delim = peek(); if (delim == '\0') throw ed_error("regex expected"); token(io::Token::Regexp); advance(); uint16_t j = 0; while (true) { if (peek(j) == '\0') throw ed_error("Unterminated regex"); if (peek(j) == delim) break; else if (peek(j) == '\\') j += 2; else if (peek(j) == '[') while (peek(j) != '\0' && peek(j) != ']') j++; else j++; } std::string expression(peek_str(j)); advance(j + 1); j = 0; while (true) { if (peek(j) == '\0') break; if (peek(j) == delim) break; else if (peek(j) == '\\') j += 2; else if (peek(j) == '[') while (peek(j) != '\0' && peek(j) != ']') j++; else j++; } std::string replacement(peek_str(j)); std::string options; if (peek(j) != '\0') { advance(j + 1); } else { advance(j); options = "p"; } end_token(); token(io::Token::Suffix); if (peek() != '\0') { options = std::string(peek_str()); advance(peek_str().size()); } end_token(); std::erase_if(options, [&](char c) { if (bed.suffixes[c - 'a'].has_value()) { suffix = c; return true; } return false; }); command->argument = functions::Function::RegexArg(expression, replacement, options); } break; case functions::Function::ArgumentKind::Ruby: { command->argument = functions::Function::RubyArg(std::string(peek_str())); auto *ruby_parser = bed.languages["ruby"]; void *state = ruby_parser->none_state(); std::vector events; std::vector tokens_; ruby_parser->parse(&state, peek_str(), false, &tokens_, &events); for (auto &token : tokens_) { token.start += i; token.end += i; tokens->push_back(token); } ruby_parser->destroy(state); advance(peek_str().size()); } break; case functions::Function::ArgumentKind::Shell: command->argument = functions::Function::ShellArg(std::string(peek_str())); token(io::Token::Shell); advance(peek_str().size()); end_token(); break; } if (!suffix) { suffix = peek(); token(io::Token::Suffix); advance(); end_token(); } if (suffix) { auto &s = bed.suffixes[suffix - 'a']; if (s.has_value()) command->suffix = &s.value(); else throw ed_error("Invalid suffix."); } } } // namespace bed::internal::parser