Implement syntax highlighting
- Create a generic incremental syntax highlighting system. - Implement a ruby syntax highlighter for it.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "../decl.h"
|
||||
#include "pch.h"
|
||||
#include "tries.h"
|
||||
|
||||
namespace bed::internal::syntax::ruby {
|
||||
struct alignas(2) RubyState {
|
||||
struct RubyInternalState {
|
||||
uint16_t brace_level;
|
||||
uint16_t lit_brace_level;
|
||||
enum : uint8_t {
|
||||
NONE,
|
||||
STRING,
|
||||
REGEXP,
|
||||
HEREDOC,
|
||||
COMMENT,
|
||||
END
|
||||
} state;
|
||||
static constexpr const uint8_t EXPECTING_EXPRESSION = 0b00010000;
|
||||
static constexpr const uint8_t ALLOW_INTERPOLATION = 0b00000001;
|
||||
uint8_t flags;
|
||||
char delim_start;
|
||||
char delim_end;
|
||||
};
|
||||
|
||||
struct Heredocs {
|
||||
// header masks
|
||||
static constexpr const uint8_t ALLOW_INDENTATION = 0b10000000;
|
||||
static constexpr const uint8_t ALLOW_INTERPOLATION = 0b01000000;
|
||||
static constexpr const uint8_t LEN_MASK = 0b00111111;
|
||||
// the rest will be len number of bytes with the actual name.
|
||||
};
|
||||
|
||||
uint8_t top;
|
||||
uint8_t docs;
|
||||
// the stack (RubyInternalState * top)
|
||||
// the docs queue (docs)
|
||||
|
||||
inline RubyInternalState *stack() {
|
||||
return (RubyInternalState *)(this + 1);
|
||||
}
|
||||
|
||||
inline uint8_t *heredocs() {
|
||||
return (uint8_t *)(stack() + top);
|
||||
}
|
||||
};
|
||||
|
||||
Language lang_ruby();
|
||||
} // namespace bed::internal::syntax::ruby
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include "decl.h"
|
||||
#include "pch.h"
|
||||
|
||||
namespace bed::internal::syntax::ruby {
|
||||
struct RubyParser {
|
||||
void **v_state;
|
||||
RubyState *state;
|
||||
std::string_view line;
|
||||
uint32_t i = 0;
|
||||
bool heredoc_start_line = false;
|
||||
|
||||
RubyParser(void **v_state, std::string_view line)
|
||||
: v_state(v_state), state((RubyState *)*v_state), line(line) {}
|
||||
|
||||
uint32_t len() const {
|
||||
return line.size();
|
||||
}
|
||||
|
||||
RubyState::RubyInternalState ¤t() {
|
||||
return state->stack()[state->top - 1];
|
||||
}
|
||||
|
||||
char peek(uint32_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++;
|
||||
*v_state = (RubyState *)realloc(
|
||||
*v_state,
|
||||
sizeof(RubyState)
|
||||
+ sizeof(RubyState::RubyInternalState) * state->top
|
||||
+ state->docs
|
||||
);
|
||||
state = (RubyState *)*v_state;
|
||||
memmove(
|
||||
state->heredocs(),
|
||||
state->stack() + state->top - 1,
|
||||
state->docs
|
||||
);
|
||||
current() = {
|
||||
.brace_level = 1,
|
||||
.lit_brace_level = 0,
|
||||
.state = RubyState::RubyInternalState::NONE,
|
||||
.flags = 0,
|
||||
.delim_start = '\0',
|
||||
.delim_end = '\0'
|
||||
};
|
||||
}
|
||||
|
||||
void pop_state() {
|
||||
state->top--;
|
||||
memmove(
|
||||
state->heredocs(),
|
||||
state->stack() + state->top + 1,
|
||||
state->docs
|
||||
);
|
||||
}
|
||||
|
||||
void enqueue_doc(uint8_t header, std::string_view name) {
|
||||
uint32_t bytes = 1 + name.size();
|
||||
uint32_t old_docs = state->docs;
|
||||
state->docs += bytes;
|
||||
*v_state = (RubyState *)realloc(
|
||||
*v_state,
|
||||
sizeof(RubyState)
|
||||
+ sizeof(RubyState::RubyInternalState) * state->top
|
||||
+ state->docs
|
||||
);
|
||||
state = (RubyState *)*v_state;
|
||||
uint8_t *heredocs = state->heredocs();
|
||||
heredocs[old_docs] = header;
|
||||
memcpy(heredocs + old_docs + 1, name.data(), name.size());
|
||||
}
|
||||
|
||||
bool dequeue_doc(uint8_t heredoc_len) {
|
||||
uint32_t bytes = heredoc_len + 1;
|
||||
uint8_t *heredocs = state->heredocs();
|
||||
if (state->docs -= bytes)
|
||||
memmove(heredocs, heredocs + bytes, state->docs);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void ruby_parse(void **v_state, std::string_view line, bool first_line, std::vector<Token> *);
|
||||
} // namespace bed::internal::syntax::ruby
|
||||
@@ -0,0 +1,336 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal/trie/trie.h"
|
||||
#include "pch.h"
|
||||
|
||||
namespace bed::internal::syntax::ruby {
|
||||
const static std::vector<std::string> types = {
|
||||
"BasicObject",
|
||||
"Object",
|
||||
"NilClass",
|
||||
"TrueClass",
|
||||
"FalseClass",
|
||||
"Integer",
|
||||
"Fixnum",
|
||||
"Bignum",
|
||||
"Float",
|
||||
"Rational",
|
||||
"Complex",
|
||||
"Numeric",
|
||||
"String",
|
||||
"Symbol",
|
||||
"Array",
|
||||
"Hash",
|
||||
"Range",
|
||||
"Regexp",
|
||||
"Struct",
|
||||
"Enumarator",
|
||||
"Enumerable",
|
||||
"Time",
|
||||
"Date",
|
||||
"IO",
|
||||
"File",
|
||||
"Dir",
|
||||
"Thread",
|
||||
"Proc",
|
||||
"Method",
|
||||
"Module",
|
||||
"Class",
|
||||
"Mutex",
|
||||
"ConditionVariable",
|
||||
"MatchData",
|
||||
"Encoding",
|
||||
"Fiber",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> builtins = {
|
||||
"ARGF",
|
||||
"ARGV",
|
||||
"ENV",
|
||||
"STDIN",
|
||||
"STDOUT",
|
||||
"STDERR",
|
||||
"DATA",
|
||||
"TOPLEVEL_BINDING",
|
||||
"RUBY_PLATFORM",
|
||||
"RUBY_VERSION",
|
||||
"RUBY_RELEASE_DATE",
|
||||
"RUBY_PATCHLEVEL",
|
||||
"RUBY_ENGINE",
|
||||
"__LINE__",
|
||||
"__FILE__",
|
||||
"__ENCODING__",
|
||||
"__dir__",
|
||||
"__callee__",
|
||||
"__method__",
|
||||
"__id__",
|
||||
"__send__",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> methods = {
|
||||
"abort",
|
||||
"at_exit",
|
||||
"binding",
|
||||
"block_given?",
|
||||
"caller",
|
||||
"catch",
|
||||
"chomp",
|
||||
"chomp!",
|
||||
"chop",
|
||||
"chop!",
|
||||
"eval",
|
||||
"exec",
|
||||
"exit",
|
||||
"exit!",
|
||||
"fail",
|
||||
"fork",
|
||||
"format",
|
||||
"gets",
|
||||
"global_variables",
|
||||
"gsub",
|
||||
"gsub!",
|
||||
"iterator?",
|
||||
"lambda",
|
||||
"load",
|
||||
"loop",
|
||||
"open",
|
||||
"print",
|
||||
"printf",
|
||||
"proc",
|
||||
"putc",
|
||||
"puts",
|
||||
"raise",
|
||||
"rand",
|
||||
"readline",
|
||||
"readlines",
|
||||
"require",
|
||||
"require_relative",
|
||||
"select",
|
||||
"sleep",
|
||||
"spawn",
|
||||
"split",
|
||||
"sprintf",
|
||||
"srand",
|
||||
"sub",
|
||||
"sub!",
|
||||
"syscall",
|
||||
"system",
|
||||
"test",
|
||||
"throw",
|
||||
"trace_var",
|
||||
"trap",
|
||||
"untrace_var",
|
||||
"attr",
|
||||
"attr_reader",
|
||||
"attr_writer",
|
||||
"attr_accessor",
|
||||
"class_variable_get",
|
||||
"class_variable_set",
|
||||
"define_method",
|
||||
"instance_variable_get",
|
||||
"instance_variable_set",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
"public_class_method",
|
||||
"module_function",
|
||||
"remove_method",
|
||||
"undef_method",
|
||||
"method",
|
||||
"methods",
|
||||
"singleton_methods",
|
||||
"private_methods",
|
||||
"protected_methods",
|
||||
"public_methods",
|
||||
"send",
|
||||
"extend",
|
||||
"include",
|
||||
"prepend",
|
||||
"clone",
|
||||
"dup",
|
||||
"freeze",
|
||||
"taint",
|
||||
"untaint",
|
||||
"trust",
|
||||
"untrust",
|
||||
"untaint?",
|
||||
"trust?",
|
||||
"each",
|
||||
"each_with_index",
|
||||
"each_with_object",
|
||||
"map",
|
||||
"collect",
|
||||
"select",
|
||||
"reject",
|
||||
"reduce",
|
||||
"inject",
|
||||
"find",
|
||||
"detect",
|
||||
"all?",
|
||||
"any?",
|
||||
"none?",
|
||||
"one?",
|
||||
"count",
|
||||
"cycle",
|
||||
"drop",
|
||||
"drop_while",
|
||||
"take",
|
||||
"take_while",
|
||||
"chunk",
|
||||
"chunk_while",
|
||||
"group_by",
|
||||
"partition",
|
||||
"slice_before",
|
||||
"slice_after",
|
||||
"nil?",
|
||||
"is_a?",
|
||||
"kind_of?",
|
||||
"instance_of?",
|
||||
"respond_to?",
|
||||
"equal?",
|
||||
"object_id",
|
||||
"class",
|
||||
"singleton_class",
|
||||
"clone",
|
||||
"freeze",
|
||||
"tap",
|
||||
"then",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> errors = {
|
||||
"Error",
|
||||
"Exception",
|
||||
"SignalException",
|
||||
"Interrupt",
|
||||
"StopIteration",
|
||||
"Errno",
|
||||
"SystemExit",
|
||||
"fatal",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> base_keywords = {
|
||||
"class",
|
||||
"module",
|
||||
"begin",
|
||||
"end",
|
||||
"else",
|
||||
"rescue",
|
||||
"ensure",
|
||||
"do",
|
||||
"when",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> expecting_keywords = {
|
||||
"if",
|
||||
"elsif",
|
||||
"case",
|
||||
"for",
|
||||
"while",
|
||||
"until",
|
||||
"unless",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> operator_keywords = {
|
||||
"alias",
|
||||
"BEGIN",
|
||||
"break",
|
||||
"catch",
|
||||
"defined?",
|
||||
"in",
|
||||
"next",
|
||||
"redo",
|
||||
"rescue",
|
||||
"retry",
|
||||
"super",
|
||||
"self",
|
||||
"nil",
|
||||
"undef",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> expecting_operators = {
|
||||
"and",
|
||||
"return",
|
||||
"not",
|
||||
"yield",
|
||||
"or",
|
||||
};
|
||||
|
||||
const static std::vector<std::string> operators = {
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"%",
|
||||
"**",
|
||||
"==",
|
||||
"!=",
|
||||
"===",
|
||||
"<=>",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<=",
|
||||
"&&",
|
||||
"||",
|
||||
"!",
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"~",
|
||||
"<<",
|
||||
">>",
|
||||
"=",
|
||||
"+=",
|
||||
"-=",
|
||||
"*=",
|
||||
"/=",
|
||||
"%=",
|
||||
"**=",
|
||||
"&=",
|
||||
"|=",
|
||||
"^=",
|
||||
"<<=",
|
||||
">>=",
|
||||
"..",
|
||||
"...",
|
||||
"===",
|
||||
"=",
|
||||
"=>",
|
||||
"&",
|
||||
"`",
|
||||
"->",
|
||||
"=~",
|
||||
};
|
||||
|
||||
struct RubyTries {
|
||||
trie::Trie<void> base_keywords_trie;
|
||||
trie::Trie<void> expecting_keywords_trie;
|
||||
trie::Trie<void> operator_keywords_trie;
|
||||
trie::Trie<void> expecting_operators_trie;
|
||||
trie::Trie<void> operator_trie;
|
||||
trie::Trie<void> types_trie;
|
||||
trie::Trie<void> builtins_trie;
|
||||
trie::Trie<void> methods_trie;
|
||||
trie::Trie<void> errors_trie;
|
||||
RubyTries() {
|
||||
for (auto &keyword : base_keywords)
|
||||
base_keywords_trie.insert(keyword);
|
||||
for (auto &keyword : expecting_keywords)
|
||||
expecting_keywords_trie.insert(keyword);
|
||||
for (auto &keyword : operator_keywords)
|
||||
operator_keywords_trie.insert(keyword);
|
||||
for (auto &keyword : expecting_operators)
|
||||
expecting_operators_trie.insert(keyword);
|
||||
for (auto &keyword : operators)
|
||||
operator_trie.insert(keyword);
|
||||
for (auto &keyword : types)
|
||||
types_trie.insert(keyword);
|
||||
for (auto &keyword : builtins)
|
||||
builtins_trie.insert(keyword);
|
||||
for (auto &keyword : methods)
|
||||
methods_trie.insert(keyword);
|
||||
for (auto &keyword : errors)
|
||||
errors_trie.insert(keyword);
|
||||
}
|
||||
};
|
||||
} // namespace bed::internal::syntax::ruby
|
||||
Reference in New Issue
Block a user