Add basic c syntax highlighting.
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 ¤t() {
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -47,6 +47,7 @@ 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());
|
||||||
|
languages["c"] = new internal::syntax::Language(internal::syntax::c::lang_c());
|
||||||
buffers["clip"] = new internal::buffer::ClipBuffer("clip");
|
buffers["clip"] = new internal::buffer::ClipBuffer("clip");
|
||||||
current() = {"default", 0};
|
current() = {"default", 0};
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
#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_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') {
|
||||||
|
// TODO: add floats/doubles
|
||||||
|
uint32_t start = p.i;
|
||||||
|
if (p.peek() == '0') {
|
||||||
|
p.advance();
|
||||||
|
if (p.peek() == 'x' || p.peek() == 'X') {
|
||||||
|
p.advance();
|
||||||
|
while (is_hex(p.peek())) {
|
||||||
|
p.advance();
|
||||||
|
if (p.peek() == '\'') {
|
||||||
|
p.advance();
|
||||||
|
if (!(is_hex(p.peek())))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (p.peek() == 'b' || p.peek() == 'B') {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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() == '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 (identifier_char(p.peek())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tokens->push_back({start, p.i, io::Token::Number});
|
||||||
|
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
|
||||||
@@ -112,7 +112,7 @@ bool handle_escapes(RubyParser &p, std::vector<io::Token> *tokens, uint32_t &sta
|
|||||||
tokens->push_back({start, p.i, io::Token::Escape});
|
tokens->push_back({start, p.i, io::Token::Escape});
|
||||||
start = p.i;
|
start = p.i;
|
||||||
return true;
|
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();
|
||||||
|
|||||||
Reference in New Issue
Block a user