add minimal Zed syntax highlighting for Ferro (#2)

* add minimal Ferro tree-sitter grammar

* add temporary tree-sitter generation workflow

* generate Ferro tree-sitter parser

* drop custom grammar in favor of Rust grammar reuse

* remove temporary tree-sitter generation workflow

* add Zed extension manifest for Ferro

* configure Ferro files for Zed

* add Ferro syntax highlighting queries

* remove generated custom tree-sitter parser

* remove generated custom tree-sitter node types

* tighten Ferro Zed highlight queries

* add minimal Ferro tree-sitter grammar

* point Zed at Ferro grammar

* refresh Ferro highlights

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
정시원
2026-08-16 18:50:19 +09:00
committed by GitHub
co-authored by github-actions[bot]
parent deafd27939
commit 83ce26c209
7 changed files with 2051 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
id = "ferro"
name = "Ferro"
version = "0.0.1"
schema_version = 1
authors = ["sebastianrcnt"]
description = "Ferro syntax highlighting for Zed"
repository = "https://github.com/sebastianrcnt/doslang"
[grammars.ferro]
repository = "https://github.com/sebastianrcnt/doslang"
rev = "40316bb34f5acbaf34979cdf2dce296ae752d81d"
+6
View File
@@ -0,0 +1,6 @@
name = "Ferro"
grammar = "ferro"
path_suffixes = ["fe"]
line_comments = ["// "]
tab_size = 4
hard_tabs = false
@@ -0,0 +1,12 @@
; Ferro syntax highlighting for Zed.
(line_comment) @comment
(block_comment) @comment
(string_literal) @string
(char_literal) @string
(integer_literal) @number
(builtin) @function.builtin
(builtin_type) @type.builtin
(keyword) @keyword
(operator) @operator
(punctuation) @punctuation.delimiter
+102
View File
@@ -0,0 +1,102 @@
// Minimal Tree-sitter grammar for Ferro syntax highlighting in Zed.
// This intentionally models lexical structure rather than full Ferro semantics.
module.exports = grammar({
name: "ferro",
extras: $ => [
/[\s\uFEFF\u2060\u200B]/,
],
rules: {
source_file: $ => repeat(choice(
$.line_comment,
$.block_comment,
$.string_literal,
$.char_literal,
$.integer_literal,
$.builtin,
$.builtin_type,
$.keyword,
$.identifier,
$.operator,
$.punctuation,
)),
line_comment: _ => token(seq("//", /[^\n]*/)),
// Ferro block comments may nest. Keeping this rule recursive makes syntax
// highlighting follow the compiler lexer instead of flattening nested /* */.
block_comment: $ => seq(
"/*",
repeat(choice(
$.block_comment,
/[^*/]+/,
/\*[^/]/,
/\/[^*]/,
)),
"*/",
),
string_literal: _ => token(seq(
'"',
repeat(choice(
/[^"\\\n\r]/,
/\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[nrt\\'"0])/,
)),
'"',
)),
char_literal: _ => token(seq(
"'",
choice(
/[^'\\\n\r]/,
/\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[nrt\\'"0])/,
),
"'",
)),
integer_literal: _ => token(prec(2, choice(
/0[xX][0-9A-Fa-f_]+/,
/0[bB][01_]+/,
/0[oO][0-7_]+/,
/[0-9][0-9_]*/,
))),
builtin: _ => token(prec(3, /@[A-Za-z_][A-Za-z0-9_]*/)),
builtin_type: _ => token(prec(3, choice(
"i8", "i16", "i32",
"u8", "u16", "u32",
"usize", "isize",
"bool", "char", "void", "str",
))),
keyword: _ => token(prec(3, choice(
"unit", "import", "pub", "fn", "struct", "packed", "enum", "error",
"const", "static", "var", "let", "mut",
"if", "else", "while", "for", "in", "match",
"return", "break", "continue", "defer",
"unsafe", "critical", "shared", "atomic", "comptime", "asm",
"try", "catch", "orelse", "as", "extern",
"interrupt", "interrupt_safe", "far",
"true", "false", "null", "undefined",
"self", "Self", "type",
"and", "or", "not",
))),
identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/,
operator: _ => token(choice(
"<<=", ">>=",
"+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=",
"==", "!=", "<=", ">=", "<<", ">>", "->", "=>", "..",
"+%", "-%", "*%",
"+", "-", "*", "/", "%", "=", "<", ">", "&", "|", "^", "~", "!", "?",
)),
punctuation: _ => token(choice(
"(", ")", "{", "}", "[", "]", ",", ";", ":", ".",
)),
},
});
+121
View File
@@ -0,0 +1,121 @@
[
{
"type": "block_comment",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "block_comment",
"named": true
}
]
}
},
{
"type": "source_file",
"named": true,
"root": true,
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "block_comment",
"named": true
},
{
"type": "builtin",
"named": true
},
{
"type": "builtin_type",
"named": true
},
{
"type": "char_literal",
"named": true
},
{
"type": "identifier",
"named": true
},
{
"type": "integer_literal",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "line_comment",
"named": true
},
{
"type": "operator",
"named": true
},
{
"type": "punctuation",
"named": true
},
{
"type": "string_literal",
"named": true
}
]
}
},
{
"type": "*/",
"named": false
},
{
"type": "/*",
"named": false
},
{
"type": "builtin",
"named": true
},
{
"type": "builtin_type",
"named": true
},
{
"type": "char_literal",
"named": true
},
{
"type": "identifier",
"named": true
},
{
"type": "integer_literal",
"named": true
},
{
"type": "keyword",
"named": true
},
{
"type": "line_comment",
"named": true
},
{
"type": "operator",
"named": true
},
{
"type": "punctuation",
"named": true
},
{
"type": "string_literal",
"named": true
}
]
+1744
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
#ifndef TREE_SITTER_PARSER_H_
#define TREE_SITTER_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define ts_builtin_sym_error ((TSSymbol)-1)
#define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
#ifndef TREE_SITTER_API_H_
typedef uint16_t TSStateId;
typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage;
typedef struct TSLanguageMetadata { uint8_t major_version; uint8_t minor_version; uint8_t patch_version; } TSLanguageMetadata;
#endif
typedef struct { TSFieldId field_id; uint8_t child_index; bool inherited; } TSFieldMapEntry;
typedef struct { uint16_t index; uint16_t length; } TSMapSlice;
typedef struct { bool visible; bool named; bool supertype; } TSSymbolMetadata;
typedef struct TSLexer TSLexer;
struct TSLexer { int32_t lookahead; TSSymbol result_symbol; void (*advance)(TSLexer *, bool); void (*mark_end)(TSLexer *); uint32_t (*get_column)(TSLexer *); bool (*is_at_included_range_start)(const TSLexer *); bool (*eof)(const TSLexer *); void (*log)(const TSLexer *, const char *, ...); };
typedef enum { TSParseActionTypeShift, TSParseActionTypeReduce, TSParseActionTypeAccept, TSParseActionTypeRecover } TSParseActionType;
typedef union { struct { uint8_t type; TSStateId state; bool extra; bool repetition; } shift; struct { uint8_t type; uint8_t child_count; TSSymbol symbol; int16_t dynamic_precedence; uint16_t production_id; } reduce; uint8_t type; } TSParseAction;
typedef struct { uint16_t lex_state; uint16_t external_lex_state; } TSLexMode;
typedef struct { uint16_t lex_state; uint16_t external_lex_state; uint16_t reserved_word_set_id; } TSLexerMode;
typedef union { TSParseAction action; struct { uint8_t count; bool reusable; } entry; } TSParseActionEntry;
typedef struct { int32_t start; int32_t end; } TSCharacterRange;
struct TSLanguage { uint32_t abi_version; uint32_t symbol_count; uint32_t alias_count; uint32_t token_count; uint32_t external_token_count; uint32_t state_count; uint32_t large_state_count; uint32_t production_id_count; uint32_t field_count; uint16_t max_alias_sequence_length; const uint16_t *parse_table; const uint16_t *small_parse_table; const uint32_t *small_parse_table_map; const TSParseActionEntry *parse_actions; const char * const *symbol_names; const char * const *field_names; const TSMapSlice *field_map_slices; const TSFieldMapEntry *field_map_entries; const TSSymbolMetadata *symbol_metadata; const TSSymbol *public_symbol_map; const uint16_t *alias_map; const TSSymbol *alias_sequences; const TSLexerMode *lex_modes; bool (*lex_fn)(TSLexer *, TSStateId); bool (*keyword_lex_fn)(TSLexer *, TSStateId); TSSymbol keyword_capture_token; struct { const bool *states; const TSSymbol *symbol_map; void *(*create)(void); void (*destroy)(void *); bool (*scan)(void *, TSLexer *, const bool *); unsigned (*serialize)(void *, char *); void (*deserialize)(void *, const char *, unsigned); } external_scanner; const TSStateId *primary_state_ids; const char *name; const TSSymbol *reserved_words; uint16_t max_reserved_word_set_size; uint32_t supertype_count; const TSSymbol *supertype_symbols; const TSMapSlice *supertype_map_slices; const TSSymbol *supertype_map_entries; TSLanguageMetadata metadata; };
static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { uint32_t index=0,size=len; while(size>1){uint32_t half=size/2,mid=index+half; const TSCharacterRange *r=&ranges[mid]; if(lookahead>=r->start&&lookahead<=r->end)return true; else if(lookahead>r->end)index=mid; size-=half;} const TSCharacterRange *r=&ranges[index]; return lookahead>=r->start&&lookahead<=r->end; }
#ifdef _MSC_VER
#define UNUSED __pragma(warning(suppress : 4101))
#else
#define UNUSED __attribute__((unused))
#endif
#define START_LEXER() bool result=false; bool skip=false; UNUSED bool eof=false; int32_t lookahead; goto start; next_state: lexer->advance(lexer,skip); start: skip=false; lookahead=lexer->lookahead;
#define ADVANCE(state_value) { state=state_value; goto next_state; }
#define ADVANCE_MAP(...) { static const uint16_t map[]={__VA_ARGS__}; for(uint32_t i=0;i<sizeof(map)/sizeof(map[0]);i+=2){if(map[i]==lookahead){state=map[i+1];goto next_state;}} }
#define SKIP(state_value) { skip=true; state=state_value; goto next_state; }
#define ACCEPT_TOKEN(symbol_value) result=true; lexer->result_symbol=symbol_value; lexer->mark_end(lexer);
#define END_STATE() return result;
#define SMALL_STATE(id) ((id)-LARGE_STATE_COUNT)
#define STATE(id) id
#define ACTIONS(id) id
#define SHIFT(state_value) {{.shift={.type=TSParseActionTypeShift,.state=(state_value)}}}
#define SHIFT_REPEAT(state_value) {{.shift={.type=TSParseActionTypeShift,.state=(state_value),.repetition=true}}}
#define SHIFT_EXTRA() {{.shift={.type=TSParseActionTypeShift,.extra=true}}}
#define REDUCE(symbol_name,children,precedence,prod_id) {{.reduce={.type=TSParseActionTypeReduce,.symbol=symbol_name,.child_count=children,.dynamic_precedence=precedence,.production_id=prod_id},}}
#define RECOVER() {{.type=TSParseActionTypeRecover}}
#define ACCEPT_INPUT() {{.type=TSParseActionTypeAccept}}
#ifdef __cplusplus
}
#endif
#endif