Compare commits

..
Author SHA1 Message Date
syedm 8d6f7d323e Fix minor bugs and add language detection. 2026-09-25 13:23:57 +01:00
syedm 6711ecbaee Add basic c syntax highlighting. 2026-09-14 11:25:29 +01:00
syedm 8f67be87ee Add escape in strings control to ruby parser. 2026-09-13 14:07:19 +01:00
20 changed files with 685 additions and 78 deletions
+2 -1
View File
@@ -59,4 +59,5 @@ It should support:
### TODO: ### TODO:
- Make "g" command work. - Make "g" command work.
- properly handle escapes for %q ' etc in ruby parser (rn everything is escapable.) - Switch to proper posix style Shard objects (to distinguish 1 empty line from no lines.)
- Use proper ruby binding context to keep locals across <pre>`</pre> function calls.
+2
View File
@@ -20,6 +20,8 @@ struct BEd {
internal::functions::Function eof_op; internal::functions::Function eof_op;
std::array<std::optional<internal::functions::Suffix>, 26> suffixes; std::array<std::optional<internal::functions::Suffix>, 26> suffixes;
internal::theme::Theme theme; internal::theme::Theme theme;
std::unordered_map<std::string, std::string> extention_to_language;
std::unordered_map<std::string, std::string> filename_to_language;
std::unordered_map<std::string, internal::syntax::Language *> languages; std::unordered_map<std::string, internal::syntax::Language *> languages;
internal::io::IO io; internal::io::IO io;
internal::vase::AppendStorage append{"/tmp"}; internal::vase::AppendStorage append{"/tmp"};
+2 -1
View File
@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "definitions.h" #include "definitions.h"
#include "internal/syntax/c/parser.h"
#include "internal/syntax/parser.h" #include "internal/syntax/parser.h"
#include "internal/syntax/ruby/parser.h" #include "internal/syntax/ruby/parser.h"
#include "internal/theme/theme.h" #include "internal/theme/theme.h"
@@ -32,7 +33,7 @@ struct Buffer {
virtual bool waste() = 0; virtual bool waste() = 0;
virtual uint64_t lines() = 0; virtual uint64_t lines() = 0;
virtual uint64_t bytes() = 0; virtual uint64_t bytes() = 0;
virtual void set_filename(std::filesystem::path path) = 0; virtual void set_filename(BEd &ctx, std::filesystem::path path) = 0;
virtual std::filesystem::path filename() = 0; virtual std::filesystem::path filename() = 0;
virtual void saved_hook() = 0; virtual void saved_hook() = 0;
virtual void load(BEd &ctx, vase::Shard *text) = 0; virtual void load(BEd &ctx, vase::Shard *text) = 0;
+1 -1
View File
@@ -14,7 +14,7 @@ struct ClipBuffer : Buffer {
uint64_t lines() override; uint64_t lines() override;
uint64_t bytes() override; uint64_t bytes() override;
void load(BEd &ctx, vase::Shard *text) override; void load(BEd &ctx, vase::Shard *text) override;
void set_filename(std::filesystem::path path) override; void set_filename(BEd &ctx, std::filesystem::path path) override;
std::filesystem::path filename() override; std::filesystem::path filename() override;
vase::Shard *copy(uint64_t start_line, uint64_t end_line) override; vase::Shard *copy(uint64_t start_line, uint64_t end_line) override;
void substitute( void substitute(
+1 -1
View File
@@ -34,7 +34,7 @@ struct GenericBuffer : ShardBuffer {
void saved_hook() override; void saved_hook() override;
bool waste() override; bool waste() override;
void load(BEd &ctx, vase::Shard *text) override; void load(BEd &ctx, vase::Shard *text) override;
void set_filename(std::filesystem::path path) override; void set_filename(BEd &ctx, std::filesystem::path path) override;
std::filesystem::path filename() override; std::filesystem::path filename() override;
void substitute( void substitute(
BEd &ctx, uint64_t start_line, uint64_t end_line, BEd &ctx, uint64_t start_line, uint64_t end_line,
+1 -1
View File
@@ -19,7 +19,7 @@ struct ReadonlyBuffer : ShardBuffer {
void load(BEd &, vase::Shard *) override { void load(BEd &, vase::Shard *) override {
throw ed_error("This buffer is read-only."); throw ed_error("This buffer is read-only.");
} }
void set_filename(std::filesystem::path) override {} void set_filename(BEd &, std::filesystem::path) override {}
std::filesystem::path filename() override { std::filesystem::path filename() override {
return {}; return {};
} }
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "../decl.h"
#include "pch.h"
#include "tries.h"
namespace bed::internal::syntax::c {
struct alignas(2) CState {
struct CInternalState {
uint16_t brace_level;
enum : uint8_t {
NONE,
STRING,
CHAR,
MULTI_COMMENT,
INLINE_COMMENT
} state;
uint8_t flags = 0;
};
uint8_t top;
// the stack (RubyInternalState * top)
inline CInternalState *stack() {
return (CInternalState *)(this + 1);
}
};
Language lang_c();
} // namespace bed::internal::syntax::c
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#include "decl.h"
#include "pch.h"
namespace bed::internal::syntax::c {
struct CParser {
void **v_state;
CState *state;
std::string_view line;
bool still_empty = true;
uint32_t i = 0;
CParser(void **v_state, std::string_view line)
: v_state(v_state), state((CState *)*v_state), line(line) {}
uint32_t len() const {
return line.size();
}
CState::CInternalState &current() {
return state->stack()[state->top - 1];
}
char peek(int32_t offset = 0) {
uint32_t pos = i + offset;
return pos < line.size() ? line[pos] : '\0';
}
std::string_view peek_str(uint32_t len) {
return line.substr(i, len);
}
void advance() {
++i;
}
void advance(uint32_t n) {
i += n;
}
void push_state() {
state->top++;
state = (CState *)realloc(
state,
sizeof(CState)
+ sizeof(CState::CInternalState) * state->top
);
current() = {
.brace_level = 1,
.state = CState::CInternalState::NONE,
.flags = 0
};
}
void pop_state() {
state->top--;
}
};
void c_parse(
void **v_state, std::string_view line, bool first_line,
std::vector<io::Token> *tokens, std::vector<ParseEvent> *events
);
} // namespace bed::internal::syntax::c
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "internal/trie/trie.h"
#include "pch.h"
namespace bed::internal::syntax::c {
const static std::vector<std::string> types = {
"int",
"char",
"long",
"void",
"auto"
};
const static std::vector<std::string> type_modifier = {
"signed",
"unsigned",
"long"
};
const static std::vector<std::string> math_operators = {
"+",
"-",
"*"
};
struct CTries {
trie::Trie<void> types_trie;
CTries() {
for (auto &keyword : types)
types_trie.insert(keyword);
}
};
} // namespace bed::internal::syntax::c
+2 -1
View File
@@ -24,7 +24,8 @@ struct alignas(2) RubyState {
DEF_NAME = 0b10, DEF_NAME = 0b10,
MODULE_NAME = 0b11 MODULE_NAME = 0b11
}; };
static constexpr uint8_t NEWLINE = 1 << 5; static constexpr uint8_t NEWLINE = 1 << 4;
static constexpr uint8_t ALLOW_ESCAPE = 1 << 5;
static constexpr uint8_t ALLOW_INTERPOLATION = 1 << 6; static constexpr uint8_t ALLOW_INTERPOLATION = 1 << 6;
static constexpr uint8_t EXPECTING_EXPRESSION = 1 << 7; static constexpr uint8_t EXPECTING_EXPRESSION = 1 << 7;
uint8_t flags = 0; uint8_t flags = 0;
+3
View File
@@ -6,6 +6,9 @@
#include "storage/storage.h" #include "storage/storage.h"
namespace bed::internal::vase { namespace bed::internal::vase {
// TODO: rewrite with invariant, text ends with a newline, as right now both 1 real and empty line and no text mean teh same thing,
// which causes a ton of issues.
struct Shard { struct Shard {
enum struct Kind : uint8_t { enum struct Kind : uint8_t {
Branch, Branch,
+8
View File
@@ -47,6 +47,14 @@ BEd::BEd(std::vector<std::string> args)
internal::functions::Suffix::register_suffixes(*this); internal::functions::Suffix::register_suffixes(*this);
internal::scripting::register_basic(*this); internal::scripting::register_basic(*this);
languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby()); languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby());
extention_to_language[".rb"] = "ruby";
languages["c"] = new internal::syntax::Language(internal::syntax::c::lang_c());
extention_to_language[".h"] = "c";
extention_to_language[".hh"] = "c";
extention_to_language[".h++"] = "c";
extention_to_language[".c"] = "c";
extention_to_language[".cc"] = "c";
extention_to_language[".c++"] = "c";
buffers["clip"] = new internal::buffer::ClipBuffer("clip"); buffers["clip"] = new internal::buffer::ClipBuffer("clip");
current() = {"default", 0}; current() = {"default", 0};
try { try {
+8 -2
View File
@@ -249,11 +249,17 @@ void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
ctx.prev().end = text->lines + 1; ctx.prev().end = text->lines + 1;
} }
ctx.current() = {name, lines()}; ctx.current() = {name, lines()};
syntax::Language *lang = parse.lang;
syntax::release(parse); syntax::release(parse);
parse = syntax::make_parser(root, lines(), nullptr); if (save_path.has_extension()) {
auto it = ctx.extention_to_language.find(save_path.extension().string());
if (it != ctx.extention_to_language.end())
lang = ctx.languages[it->second];
}
parse = syntax::make_parser(root, lines(), lang);
} }
void GenericBuffer::set_filename(std::filesystem::path path) { void GenericBuffer::set_filename(BEd &, std::filesystem::path path) {
save_path = path; save_path = path;
} }
+1 -1
View File
@@ -47,7 +47,7 @@ void ClipBuffer::load(BEd &ctx, vase::Shard *text) {
} }
} }
void ClipBuffer::set_filename(std::filesystem::path) {} void ClipBuffer::set_filename(BEd &, std::filesystem::path) {}
std::filesystem::path ClipBuffer::filename() { std::filesystem::path ClipBuffer::filename() {
return ""; return "";
+5 -5
View File
@@ -145,7 +145,7 @@ void Function::register_posix(BEd &ctx) {
if (std::holds_alternative<std::filesystem::path>(arg)) { if (std::holds_alternative<std::filesystem::path>(arg)) {
auto path = std::get<std::filesystem::path>(arg); auto path = std::get<std::filesystem::path>(arg);
s = vase::Shard::from_file(path); s = vase::Shard::from_file(path);
buf.set_filename(path); buf.set_filename(ctx, path);
} else if (std::holds_alternative<ShellArg>(arg)) { } else if (std::holds_alternative<ShellArg>(arg)) {
auto cmd = std::get<ShellArg>(arg).cmd; auto cmd = std::get<ShellArg>(arg).cmd;
ctx.escape_command(cmd, buf.filename().string()); ctx.escape_command(cmd, buf.filename().string());
@@ -191,7 +191,7 @@ void Function::register_posix(BEd &ctx) {
if (std::holds_alternative<std::filesystem::path>(arg)) { if (std::holds_alternative<std::filesystem::path>(arg)) {
auto path = std::get<std::filesystem::path>(arg); auto path = std::get<std::filesystem::path>(arg);
s = vase::Shard::from_file(path); s = vase::Shard::from_file(path);
buf.set_filename(path); buf.set_filename(ctx, path);
} else if (std::holds_alternative<ShellArg>(arg)) { } else if (std::holds_alternative<ShellArg>(arg)) {
auto cmd = std::get<ShellArg>(arg).cmd; auto cmd = std::get<ShellArg>(arg).cmd;
ctx.escape_command(cmd, buf.filename().string()); ctx.escape_command(cmd, buf.filename().string());
@@ -234,7 +234,7 @@ void Function::register_posix(BEd &ctx) {
auto &addr = std::get<std::string>(addr_); auto &addr = std::get<std::string>(addr_);
auto &buf = ctx.buffer(addr); auto &buf = ctx.buffer(addr);
if (std::holds_alternative<std::filesystem::path>(arg)) if (std::holds_alternative<std::filesystem::path>(arg))
buf.set_filename(std::get<std::filesystem::path>(arg)); buf.set_filename(ctx, std::get<std::filesystem::path>(arg));
else if (std::holds_alternative<ShellArg>(arg)) else if (std::holds_alternative<ShellArg>(arg))
throw ed_error("Can't save shell command as save path."); throw ed_error("Can't save shell command as save path.");
if (buf.filename().empty()) if (buf.filename().empty())
@@ -590,7 +590,7 @@ void Function::register_posix(BEd &ctx) {
auto path = std::get<std::filesystem::path>(arg); auto path = std::get<std::filesystem::path>(arg);
s = vase::Shard::from_file(path); s = vase::Shard::from_file(path);
if (buf.filename().empty()) if (buf.filename().empty())
buf.set_filename(path); buf.set_filename(ctx, path);
} else if (std::holds_alternative<ShellArg>(arg)) { } else if (std::holds_alternative<ShellArg>(arg)) {
auto cmd = std::get<ShellArg>(arg).cmd; auto cmd = std::get<ShellArg>(arg).cmd;
ctx.escape_command(cmd, buf.filename().string()); ctx.escape_command(cmd, buf.filename().string());
@@ -740,7 +740,7 @@ void Function::register_posix(BEd &ctx) {
if (std::holds_alternative<std::filesystem::path>(arg)) { if (std::holds_alternative<std::filesystem::path>(arg)) {
auto path = std::get<std::filesystem::path>(arg); auto path = std::get<std::filesystem::path>(arg);
vase::write_file(path, text); vase::write_file(path, text);
buf.set_filename(path); buf.set_filename(ctx, path);
} else if (std::holds_alternative<ShellArg>(arg)) { } else if (std::holds_alternative<ShellArg>(arg)) {
auto cmd = std::get<ShellArg>(arg).cmd; auto cmd = std::get<ShellArg>(arg).cmd;
ctx.escape_command(cmd, buf.filename().string()); ctx.escape_command(cmd, buf.filename().string());
+37
View File
@@ -0,0 +1,37 @@
#include "internal/syntax/c/parser.h"
namespace bed::internal::syntax::c {
Language lang_c() {
return Language{
.none_state = []() {
CState *st = (CState *)malloc(
sizeof(CState)
+ sizeof(CState::CInternalState)
);
st->top = 1;
st->stack()[0] = {
.brace_level = 1,
.state = CState::CInternalState::NONE,
.flags = 0
};
return st; },
.parse = c_parse,
.copy = [](void *v_i_st) {
CState *i_st = (CState *)v_i_st;
uint32_t bytes = sizeof(CState)
+ sizeof(CState::CInternalState) * i_st->top;
CState *o_st = (CState *)malloc(bytes);
memcpy(o_st, i_st, bytes);
return o_st; },
.equal = [](void *v_a_st, void *v_b_st) {
CState *a_st = (CState *)v_a_st;
uint32_t a_bytes = sizeof(CState)
+ sizeof(CState::CInternalState) * a_st->top;
CState *b_st = (CState *)v_b_st;
uint32_t b_bytes = sizeof(CState)
+ sizeof(CState::CInternalState) * b_st->top;
return a_bytes == b_bytes && memcmp(a_st, b_st, a_bytes) == 0; },
.destroy = [](void *v_st) { free(v_st); },
};
}
} // namespace bed::internal::syntax::c
+396
View File
@@ -0,0 +1,396 @@
#include "internal/syntax/c/parser.h"
namespace bed::internal::syntax::c {
inline bool is_hex(char c) {
return ('0' <= c && c <= '9')
|| ('a' <= c && c <= 'f')
|| ('A' <= c && c <= 'F');
};
inline bool identifier_start_char(char c) {
return (c & 0x80)
|| ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| c == '_';
}
inline bool identifier_char(char c) {
return (c & 0x80)
|| ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
|| c == '_';
}
void handle_multiline_comment(CParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) {
uint32_t start = p.i;
while (p.i < p.len()) {
if (p.peek() == '*' && p.peek(1) == '/') {
p.i += 2;
p.current().state = CState::CInternalState::NONE;
tokens->push_back({start, p.i, io::Token::Comment});
events->push_back(true);
return;
}
p.advance();
}
tokens->push_back({start, p.i, io::Token::Comment});
}
void handle_inline_comment(CParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *) {
uint32_t start = p.i;
p.i = p.len();
tokens->push_back({start, p.i, io::Token::Comment});
if (p.line.empty() || p.line.back() != '\\')
p.current().state = CState::CInternalState::NONE;
}
bool handle_escapes(CParser &p, std::vector<io::Token> *tokens, uint32_t &start, bool string) {
if (p.peek() != '\\')
return false;
if (string)
tokens->push_back({start, p.i, io::Token::String});
else
tokens->push_back({start, p.i, io::Token::Char});
start = p.i;
p.advance();
if (p.peek() == 'x') {
p.advance();
while (is_hex(p.peek()))
p.advance();
} else if (p.peek() == 'u') {
p.advance();
for (int i = 0; i < 4 && is_hex(p.peek()); i++)
p.advance();
} else if (p.peek() == 'U') {
p.advance();
for (int i = 0; i < 8 && is_hex(p.peek()); i++)
p.advance();
} else if ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if ('0' <= p.peek() && p.peek() <= '7')
p.advance();
if ('0' <= p.peek() && p.peek() <= '7')
p.advance();
} else {
p.advance();
}
tokens->push_back({start, p.i, io::Token::Escape});
start = p.i;
return true;
}
void handle_string(CParser &p, std::vector<io::Token> *tokens, bool string) {
uint32_t start = p.i;
while (p.i < p.len()) {
if (handle_escapes(p, tokens, start, string))
continue;
if (p.peek() == (string ? '"' : '\'')) {
p.advance();
if (string)
tokens->push_back({start, p.i, io::Token::String});
else
tokens->push_back({start, p.i, io::Token::Char});
p.current().state = CState::CInternalState::NONE;
return;
}
p.advance();
}
if (p.i >= p.len()) {
if (string)
tokens->push_back({start, p.len(), io::Token::String});
else
tokens->push_back({start, p.len(), io::Token::Char});
if (p.line.empty() || p.line.back() != '\\')
p.current().state = CState::CInternalState::NONE;
}
}
void handle_number(CParser &p, std::vector<io::Token> *tokens) {
bool integer = true;
uint32_t start = p.i;
if (p.peek() == '0') {
p.advance();
if (p.peek() == 'x' || p.peek() == 'X') {
p.advance();
if (!(is_hex(p.peek()) || p.peek() == '.'))
return;
while (is_hex(p.peek())) {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(is_hex(p.peek())))
return;
}
}
bool need_p = false;
if (p.peek() == '.') {
need_p = true;
p.advance();
}
while (is_hex(p.peek())) {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(is_hex(p.peek())))
return;
}
}
if (p.peek() == 'p' || p.peek() == 'P') {
integer = false;
p.advance();
if (p.peek() == '-' || p.peek() == '+')
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
} else if (need_p) {
return;
}
} else if (p.peek() == 'b' || p.peek() == 'B') {
p.advance();
if (p.peek() != '0' && p.peek() != '1')
return;
p.advance();
while (p.peek() == '0' || p.peek() == '1') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!(p.peek() == '0' || p.peek() == '1'))
return;
}
}
} else {
while ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '7'))
return;
}
}
if (p.peek() == '.') {
integer = false;
p.advance();
}
while ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '7'))
return;
}
}
if (p.peek() == 'e' || p.peek() == 'E') {
integer = false;
p.advance();
if (p.peek() == '-' || p.peek() == '+')
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
}
}
} else if (p.peek() == '.') {
integer = false;
p.advance();
goto after_decimal;
} else {
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
if (p.peek() == '.') {
integer = false;
p.advance();
}
after_decimal:
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
if (p.peek() == 'e' || p.peek() == 'E') {
integer = false;
p.advance();
if (p.peek() == '-' || p.peek() == '+')
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
p.advance();
while ('0' <= p.peek() && p.peek() <= '9') {
p.advance();
if (p.peek() == '\'') {
p.advance();
if (!('0' <= p.peek() && p.peek() <= '9'))
return;
}
}
}
}
if (integer) {
if (p.peek() == 'u' || p.peek() == 'U') {
p.advance();
if (p.peek() == 'w') {
p.advance();
if (p.peek() != 'b')
return;
p.advance();
}
if (p.peek() == 'W') {
p.advance();
if (p.peek() != 'B')
return;
p.advance();
}
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
} else if (p.peek() == 'l' || p.peek() == 'L') {
p.advance();
if (p.peek() == 'l' || p.peek() == 'L')
p.advance();
if (p.peek() == 'u' || p.peek() == 'U')
p.advance();
} else if (p.peek_str(2) == "wb" || p.peek_str(2) == "WB") {
p.advance(2);
} else if (p.peek() == 'f' || p.peek() == 'F' || p.peek() == 'l' || p.peek() == 'L') {
p.advance();
} else if ((p.peek() == 'd' || p.peek() == 'D')
&& (p.peek(1) == 'f' || p.peek(1) == 'F' || p.peek(1) == 'd' || p.peek(1) == 'D' || p.peek(1) == 'l' || p.peek(1) == 'L')) {
p.advance(2);
}
} else {
if (p.peek() == 'f' || p.peek() == 'F' || p.peek() == 'l' || p.peek() == 'L')
p.advance();
else if ((p.peek() == 'd' || p.peek() == 'D')
&& (p.peek(1) == 'f' || p.peek(1) == 'F' || p.peek(1) == 'd' || p.peek(1) == 'D' || p.peek(1) == 'l' || p.peek(1) == 'L'))
p.advance(2);
}
if (identifier_char(p.peek()) || p.peek() == '.')
return;
tokens->push_back({start, p.i, io::Token::Number});
}
void handle_syntax(CParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) {
if (p.peek() == '/' && p.peek(1) == '/') {
p.current().state = CState::CInternalState::INLINE_COMMENT;
return;
}
if (p.peek() == '/' && p.peek(1) == '*') {
p.current().state = CState::CInternalState::MULTI_COMMENT;
events->push_back(false);
return;
}
switch (p.peek()) {
case '"':
p.current().state = CState::CInternalState::STRING;
p.advance();
tokens->push_back({p.i - 1, p.i, io::Token::String});
return;
case '\'':
p.current().state = CState::CInternalState::CHAR;
p.advance();
tokens->push_back({p.i - 1, p.i, io::Token::Char});
return;
case 'u': {
uint32_t j = 1;
if (p.peek(j) == '8')
j++;
if (p.peek(j) == '"') {
p.current().state = CState::CInternalState::STRING;
tokens->push_back({p.i, p.i + j, io::Token::Annotation});
p.advance(j + 1);
tokens->push_back({p.i - 1, p.i, io::Token::String});
return;
} else if (p.peek(j) == '\'') {
p.current().state = CState::CInternalState::CHAR;
tokens->push_back({p.i, p.i + j, io::Token::Annotation});
p.advance(j + 1);
tokens->push_back({p.i - 1, p.i, io::Token::Char});
return;
}
goto def;
}
case 'U':
case 'L': {
if (p.peek(1) == '"') {
p.current().state = CState::CInternalState::STRING;
tokens->push_back({p.i, p.i + 1, io::Token::Annotation});
p.advance(2);
tokens->push_back({p.i - 1, p.i, io::Token::String});
return;
} else if (p.peek(1) == '\'') {
p.current().state = CState::CInternalState::CHAR;
tokens->push_back({p.i, p.i + 1, io::Token::Annotation});
p.advance(2);
tokens->push_back({p.i - 1, p.i, io::Token::Char});
return;
}
goto def;
}
default:
def:
if (('0' <= p.peek() && p.peek() <= '9') || p.peek() == '.') {
handle_number(p, tokens);
return;
}
p.advance();
}
}
void c_parse(
void **v_state,
std::string_view line,
bool,
std::vector<io::Token> *tokens,
std::vector<ParseEvent> *events
) {
CParser p(v_state, line);
while (p.i < p.len()) {
if (p.current().state == CState::CInternalState::MULTI_COMMENT) {
handle_multiline_comment(p, tokens, events);
continue;
}
if (p.current().state == CState::CInternalState::INLINE_COMMENT) {
handle_inline_comment(p, tokens, events);
return;
}
if (p.current().state == CState::CInternalState::STRING) {
handle_string(p, tokens, true);
continue;
}
if (p.current().state == CState::CInternalState::CHAR) {
handle_string(p, tokens, false);
continue;
}
handle_syntax(p, tokens, events);
continue;
}
}
} // namespace bed::internal::syntax::c
+83 -63
View File
@@ -35,73 +35,84 @@ inline uint8_t utf8_codepoint_width(unsigned char c) {
} }
bool handle_escapes(RubyParser &p, std::vector<io::Token> *tokens, uint32_t &start, bool string = true) { bool handle_escapes(RubyParser &p, std::vector<io::Token> *tokens, uint32_t &start, bool string = true) {
// TODO: properly handle escapes for %q ' etc. if (p.peek() != '\\')
if (p.peek() == '\\') { return false;
if (string) if (!(p.current().flags & RubyState::RubyInternalState::ALLOW_ESCAPE)) {
tokens->push_back({start, p.i, io::Token::String});
else
tokens->push_back({start, p.i, io::Token::Regexp});
start = p.i;
p.advance(); p.advance();
if (p.peek() == 'x') { if (p.peek() != '\'' && p.peek() != '\\')
p.advance(); return false;
if (is_hex(p.peek())) if (string)
p.advance(); tokens->push_back({start, p.i - 1, io::Token::String});
if (is_hex(p.peek())) else
p.advance(); tokens->push_back({start, p.i - 1, io::Token::Regexp});
} else if (p.peek() == 'u') { p.advance();
p.advance(); tokens->push_back({p.i - 2, p.i, io::Token::Escape});
if (p.peek() == '{') {
p.advance();
while (p.peek() != '}' && p.peek() != '\0')
p.advance();
if (p.peek() == '}')
p.advance();
} else {
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
}
} else if ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if ('0' <= p.peek() && p.peek() <= '7')
p.advance();
if ('0' <= p.peek() && p.peek() <= '7')
p.advance();
} else if (p.peek() == 'c') {
p.advance();
if (p.peek() != '\\')
p.advance();
} else if (p.peek() == 'M' || p.peek() == 'C') {
p.advance();
if (p.peek() == '-') {
p.advance();
if (p.peek() != '\\')
p.advance();
}
} else if (p.peek() == 'N') {
p.advance();
if (p.peek() == '{') {
p.advance();
while (p.peek() != '}' && p.peek() != '\0')
p.advance();
if (p.peek() == '}')
p.advance();
}
} else {
p.advance();
}
tokens->push_back({start, p.i, io::Token::Escape});
start = p.i; start = p.i;
return true; return true;
} }
return false; if (string)
}; tokens->push_back({start, p.i, io::Token::String});
else
tokens->push_back({start, p.i, io::Token::Regexp});
start = p.i;
p.advance();
if (p.peek() == 'x') {
p.advance();
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
} else if (p.peek() == 'u') {
p.advance();
if (p.peek() == '{') {
p.advance();
while (p.peek() != '}' && p.peek() != '\0')
p.advance();
if (p.peek() == '}')
p.advance();
} else {
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
if (is_hex(p.peek()))
p.advance();
}
} else if ('0' <= p.peek() && p.peek() <= '7') {
p.advance();
if ('0' <= p.peek() && p.peek() <= '7')
p.advance();
if ('0' <= p.peek() && p.peek() <= '7')
p.advance();
} else if (p.peek() == 'c') {
p.advance();
if (p.peek() != '\\')
p.advance();
} else if (p.peek() == 'M' || p.peek() == 'C') {
p.advance();
if (p.peek() == '-') {
p.advance();
if (p.peek() != '\\')
p.advance();
}
} else if (p.peek() == 'N') {
p.advance();
if (p.peek() == '{') {
p.advance();
while (p.peek() != '}' && p.peek() != '\0')
p.advance();
if (p.peek() == '}')
p.advance();
}
} else {
p.advance();
}
tokens->push_back({start, p.i, io::Token::Escape});
start = p.i;
return true;
}
bool handle_heredoc(RubyParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) { bool handle_heredoc(RubyParser &p, std::vector<io::Token> *tokens, std::vector<ParseEvent> *events) {
uint8_t *heredocs = p.state->heredocs(); uint8_t *heredocs = p.state->heredocs();
@@ -669,6 +680,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
p.current().delim_start = '\''; p.current().delim_start = '\'';
p.current().delim_end = '\''; p.current().delim_end = '\'';
p.current().flags &= ~RubyState::RubyInternalState::ALLOW_INTERPOLATION; p.current().flags &= ~RubyState::RubyInternalState::ALLOW_INTERPOLATION;
p.current().flags &= ~RubyState::RubyInternalState::ALLOW_ESCAPE;
p.advance(); p.advance();
return false; return false;
} }
@@ -679,6 +691,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
p.current().delim_start = '"'; p.current().delim_start = '"';
p.current().delim_end = '"'; p.current().delim_end = '"';
p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION; p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION;
p.current().flags |= RubyState::RubyInternalState::ALLOW_ESCAPE;
p.advance(); p.advance();
return false; return false;
} }
@@ -704,6 +717,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
char delim_start = '\0'; char delim_start = '\0';
char delim_end = '\0'; char delim_end = '\0';
bool allow_interp = true; bool allow_interp = true;
bool allow_escape = true;
int prefix_len = 1; int prefix_len = 1;
bool is_regexp = false; bool is_regexp = false;
switch (type) { switch (type) {
@@ -717,6 +731,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
case 'I': case 'I':
case 'W': case 'W':
allow_interp = true; allow_interp = true;
allow_escape = true;
prefix_len = 2; prefix_len = 2;
break; break;
case 'w': case 'w':
@@ -724,10 +739,12 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
case 'i': case 'i':
case 's': case 's':
allow_interp = false; allow_interp = false;
allow_escape = false;
prefix_len = 2; prefix_len = 2;
break; break;
default: default:
allow_interp = true; allow_interp = true;
allow_escape = true;
prefix_len = 1; prefix_len = 1;
break; break;
} }
@@ -769,6 +786,8 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
p.current().delim_end = delim_end; p.current().delim_end = delim_end;
if (allow_interp) if (allow_interp)
p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION; p.current().flags |= RubyState::RubyInternalState::ALLOW_INTERPOLATION;
if (allow_escape)
p.current().flags |= RubyState::RubyInternalState::ALLOW_ESCAPE;
p.current().lit_brace_level = 1; p.current().lit_brace_level = 1;
p.advance(prefix_len + 1); p.advance(prefix_len + 1);
return false; return false;
@@ -981,6 +1000,7 @@ bool handle_syntax(RubyParser &p, std::vector<io::Token> *tokens, std::vector<Pa
p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION; p.current().flags &= ~RubyState::RubyInternalState::EXPECTING_EXPRESSION;
tokens->push_back({p.i, p.i + j, io::Token::Function}); tokens->push_back({p.i, p.i + j, io::Token::Function});
p.advance(j); p.advance(j);
return false;
} }
uint32_t start = p.i; uint32_t start = p.i;
if (p.peek(j) == ':') { if (p.peek(j) == ':') {
+4 -1
View File
@@ -179,7 +179,10 @@ Shard *insert(AppendStorage *ap, Shard *root, Shard *text, uint64_t line) {
if (!root) { if (!root) {
if (line != 0) if (line != 0)
throw ed_error("line out of range"); throw ed_error("line out of range");
Shard::retain(text); if (text)
Shard::retain(text);
else
text = newline(ap);
return text; return text;
} }
if (line > root->lines + 1) if (line > root->lines + 1)