diff --git a/include/internal/buffer/decl.h b/include/internal/buffer/decl.h index d3dfc30..8438698 100644 --- a/include/internal/buffer/decl.h +++ b/include/internal/buffer/decl.h @@ -1,6 +1,7 @@ #pragma once #include "definitions.h" +#include "internal/syntax/c/parser.h" #include "internal/syntax/parser.h" #include "internal/syntax/ruby/parser.h" #include "internal/theme/theme.h" diff --git a/include/internal/syntax/c/decl.h b/include/internal/syntax/c/decl.h new file mode 100644 index 0000000..2628414 --- /dev/null +++ b/include/internal/syntax/c/decl.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 diff --git a/include/internal/syntax/c/parser.h b/include/internal/syntax/c/parser.h new file mode 100644 index 0000000..29b8feb --- /dev/null +++ b/include/internal/syntax/c/parser.h @@ -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 *tokens, std::vector *events +); +} // namespace bed::internal::syntax::c diff --git a/include/internal/syntax/c/tries.h b/include/internal/syntax/c/tries.h new file mode 100644 index 0000000..100cd0f --- /dev/null +++ b/include/internal/syntax/c/tries.h @@ -0,0 +1,34 @@ +#pragma once + +#include "internal/trie/trie.h" +#include "pch.h" + +namespace bed::internal::syntax::c { +const static std::vector types = { + "int", + "char", + "long", + "void", + "auto" +}; + +const static std::vector type_modifier = { + "signed", + "unsigned", + "long" +}; + +const static std::vector math_operators = { + "+", + "-", + "*" +}; + +struct CTries { + trie::Trie types_trie; + CTries() { + for (auto &keyword : types) + types_trie.insert(keyword); + } +}; +} // namespace bed::internal::syntax::c diff --git a/src/bed/bed.cc b/src/bed/bed.cc index 850b62f..3995e8b 100644 --- a/src/bed/bed.cc +++ b/src/bed/bed.cc @@ -47,6 +47,7 @@ BEd::BEd(std::vector args) internal::functions::Suffix::register_suffixes(*this); internal::scripting::register_basic(*this); 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"); current() = {"default", 0}; try { diff --git a/src/internal/syntax/c/core.cc b/src/internal/syntax/c/core.cc new file mode 100644 index 0000000..d221c0c --- /dev/null +++ b/src/internal/syntax/c/core.cc @@ -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 diff --git a/src/internal/syntax/c/parse.cc b/src/internal/syntax/c/parse.cc new file mode 100644 index 0000000..e27de8d --- /dev/null +++ b/src/internal/syntax/c/parse.cc @@ -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 *tokens, std::vector *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 *tokens, std::vector *) { + 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 *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 *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 *tokens, std::vector *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 *tokens, + std::vector *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 diff --git a/src/internal/syntax/ruby/parse.cc b/src/internal/syntax/ruby/parse.cc index fba8ee6..ea7433e 100644 --- a/src/internal/syntax/ruby/parse.cc +++ b/src/internal/syntax/ruby/parse.cc @@ -112,7 +112,7 @@ bool handle_escapes(RubyParser &p, std::vector *tokens, uint32_t &sta tokens->push_back({start, p.i, io::Token::Escape}); start = p.i; return true; -}; +} bool handle_heredoc(RubyParser &p, std::vector *tokens, std::vector *events) { uint8_t *heredocs = p.state->heredocs();