Make-green half of the cadence step started in A1. Wires the
`#selector` directive end-to-end:
- Lexer token `hash_selector` at src/token.zig + lookup row in
src/lexer.zig.
- AST field `selector_override: ?[]const u8 = null` on
`ForeignMethodDecl` (src/ast.zig).
- Parser block in src/parser.zig that mirrors
`#jni_method_descriptor` — both occupy the same slot after the
optional `-> ReturnType` and before the body/terminator. Not
mutually exclusive at parse time.
- LSP semantic-token list (src/lsp/server.zig) updated.
- Lowering: `deriveObjcSelector` returns
`{ sel, keyword_count, is_override }`. When `is_override` is true,
the selector string is the user's literal and `keyword_count` is
the colon count in that literal. Both `lowerObjcMethodCall` and
`lowerObjcStaticCall` use the result.
Diagnostic policy when override colon-count ≠ call arity:
- Default mangling path: stays an error (`.err`). The user can fix
the sx-side name to produce the right keyword count.
- Override path: downgrades to a warning (`.warn`). Rationale:
Obj-C's `objc_msgSend` doesn't validate colon-vs-arg the way JNI's
`GetMethodID` validates the descriptor — the runtime dispatches
regardless and the wrong-arity case becomes silent calling-
convention corruption. The compiler is the last line of defense
for this typo class, but the warning preserves the override's
escape-hatch character (deliberate mismatches still proceed).
Snapshot for `examples/ffi-objc-dsl-06-selector-override.sx` flips
from the pre-3.2 parser-error to working output:
static override non-null: true
The mismatch diagnostic text in
`examples/ffi-objc-dsl-04-mismatch.sx`'s snapshot is updated to
drop the "once that lands (3.2)" phrasing now that 3.2 is here.
165/165 example tests.
256 lines
6.8 KiB
Zig
256 lines
6.8 KiB
Zig
pub const Tag = enum {
|
|
// Literals
|
|
int_literal,
|
|
float_literal,
|
|
string_literal,
|
|
raw_string_literal,
|
|
|
|
// Identifiers and keywords
|
|
identifier,
|
|
kw_if,
|
|
kw_else,
|
|
kw_then,
|
|
kw_true,
|
|
kw_false,
|
|
kw_enum,
|
|
kw_case,
|
|
kw_break,
|
|
kw_continue,
|
|
kw_while,
|
|
kw_for,
|
|
kw_return,
|
|
kw_defer,
|
|
kw_f32,
|
|
kw_f64,
|
|
kw_struct,
|
|
kw_union,
|
|
kw_xx,
|
|
kw_and,
|
|
kw_or,
|
|
kw_Type, // Type (metatype keyword)
|
|
kw_null, // null
|
|
kw_push, // push
|
|
kw_ufcs, // ufcs
|
|
kw_in, // in
|
|
kw_closure, // closure
|
|
kw_protocol, // protocol
|
|
kw_impl, // impl
|
|
kw_Self, // Self (in protocol declarations)
|
|
kw_inline, // inline (compile-time if/for/while)
|
|
kw_callconv, // callconv (calling convention annotation)
|
|
|
|
// Symbols
|
|
colon, // :
|
|
colon_colon, // ::
|
|
colon_equal, // :=
|
|
semicolon, // ;
|
|
comma, // ,
|
|
dot, // .
|
|
dot_dot, // ..
|
|
dollar, // $
|
|
|
|
// Operators
|
|
plus, // +
|
|
minus, // -
|
|
star, // *
|
|
slash, // /
|
|
equal, // =
|
|
equal_equal, // ==
|
|
bang, // !
|
|
bang_equal, // !=
|
|
less, // <
|
|
less_equal, // <=
|
|
greater, // >
|
|
greater_equal, // >=
|
|
plus_equal, // +=
|
|
minus_equal, // -=
|
|
star_equal, // *=
|
|
slash_equal, // /=
|
|
percent, // %
|
|
percent_equal, // %=
|
|
ampersand, // &
|
|
ampersand_equal, // &=
|
|
at, // @
|
|
pipe, // |
|
|
pipe_equal, // |=
|
|
pipe_arrow, // |>
|
|
caret, // ^
|
|
caret_equal, // ^=
|
|
question, // ?
|
|
question_question, // ??
|
|
question_dot, // ?.
|
|
tilde, // ~
|
|
less_less, // <<
|
|
less_less_equal, // <<=
|
|
greater_greater, // >>
|
|
greater_greater_equal, // >>=
|
|
|
|
// Delimiters
|
|
l_paren, // (
|
|
r_paren, // )
|
|
l_brace, // {
|
|
r_brace, // }
|
|
l_bracket, // [
|
|
r_bracket, // ]
|
|
|
|
// Arrows
|
|
arrow, // ->
|
|
fat_arrow, // =>
|
|
|
|
// Directives
|
|
hash_run, // #run
|
|
hash_import, // #import
|
|
hash_insert, // #insert
|
|
hash_builtin, // #builtin
|
|
hash_compiler, // #compiler
|
|
hash_foreign, // #foreign
|
|
hash_library, // #library
|
|
hash_framework, // #framework
|
|
hash_using, // #using
|
|
hash_include, // #include (inside #import c { ... })
|
|
hash_source, // #source (inside #import c { ... })
|
|
hash_define, // #define (inside #import c { ... })
|
|
hash_flags, // #flags (inside #import c { ... })
|
|
hash_inline, // #inline (protocol layout modifier)
|
|
hash_objc_call, // #objc_call(T)(recv, "sel:", args...)
|
|
hash_jni_call, // #jni_call(T)(env, target, "name", "(Sig)R", args...)
|
|
hash_jni_static_call, // #jni_static_call(T)(class, "name", "(Sig)R", args...)
|
|
hash_jni_class, // Foo :: #jni_class("java/path/Foo") { ...body... }
|
|
hash_jni_interface, // Foo :: #jni_interface("java/path/IFoo") { ...body... }
|
|
hash_objc_class, // Foo :: #objc_class("ObjcName") { ...body... }
|
|
hash_objc_protocol, // Foo :: #objc_protocol("ObjcProto") { ...body... }
|
|
hash_swift_class, // Foo :: #swift_class("Module.Type") { ...body... }
|
|
hash_swift_struct, // Foo :: #swift_struct("Module.Type") { ...body... }
|
|
hash_swift_protocol, // Foo :: #swift_protocol("Module.Proto") { ...body... }
|
|
hash_extends, // `#extends Alias;` inside a foreign-class body
|
|
hash_implements, // `#implements Alias;` inside a foreign-class body
|
|
hash_jni_method_descriptor, // `#jni_method_descriptor("(Sig)Ret")` per-method JNI descriptor override
|
|
hash_selector, // `#selector("explicit:string")` per-method Obj-C selector override (Phase 3.2)
|
|
hash_jni_env, // `#jni_env(env) { body }` block-form env-scoping intrinsic
|
|
hash_jni_main, // `#jni_main #jni_class(...) { ... }` — class is the launchable Android Activity
|
|
triple_minus, // ---
|
|
|
|
// Special
|
|
eof,
|
|
invalid,
|
|
|
|
pub fn lexeme(tag: Tag) ?[]const u8 {
|
|
return switch (tag) {
|
|
.colon => ":",
|
|
.colon_colon => "::",
|
|
.colon_equal => ":=",
|
|
.semicolon => ";",
|
|
.comma => ",",
|
|
.dot => ".",
|
|
.dot_dot => "..",
|
|
.dollar => "$",
|
|
.plus => "+",
|
|
.minus => "-",
|
|
.star => "*",
|
|
.slash => "/",
|
|
.equal => "=",
|
|
.equal_equal => "==",
|
|
.bang => "!",
|
|
.bang_equal => "!=",
|
|
.less => "<",
|
|
.less_equal => "<=",
|
|
.greater => ">",
|
|
.greater_equal => ">=",
|
|
.plus_equal => "+=",
|
|
.minus_equal => "-=",
|
|
.star_equal => "*=",
|
|
.slash_equal => "/=",
|
|
.percent => "%",
|
|
.percent_equal => "%=",
|
|
.ampersand => "&",
|
|
.ampersand_equal => "&=",
|
|
.at => "@",
|
|
.pipe => "|",
|
|
.pipe_equal => "|=",
|
|
.pipe_arrow => "|>",
|
|
.caret => "^",
|
|
.caret_equal => "^=",
|
|
.question => "?",
|
|
.question_question => "??",
|
|
.question_dot => "?.",
|
|
.tilde => "~",
|
|
.less_less => "<<",
|
|
.less_less_equal => "<<=",
|
|
.greater_greater => ">>",
|
|
.greater_greater_equal => ">>=",
|
|
.kw_null => "null",
|
|
.l_paren => "(",
|
|
.r_paren => ")",
|
|
.l_brace => "{",
|
|
.r_brace => "}",
|
|
.l_bracket => "[",
|
|
.r_bracket => "]",
|
|
.arrow => "->",
|
|
.fat_arrow => "=>",
|
|
.triple_minus => "---",
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
pub fn isTypeKeyword(tag: Tag) bool {
|
|
return switch (tag) {
|
|
.kw_f32, .kw_f64, .kw_Type, .kw_Self => true,
|
|
else => false,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const Token = struct {
|
|
tag: Tag,
|
|
loc: Loc,
|
|
|
|
pub const Loc = struct {
|
|
start: u32,
|
|
end: u32,
|
|
};
|
|
|
|
pub fn slice(self: Token, source: []const u8) []const u8 {
|
|
return source[self.loc.start..self.loc.end];
|
|
}
|
|
};
|
|
|
|
pub const keywords = std.StaticStringMap(Tag).initComptime(.{
|
|
.{ "if", .kw_if },
|
|
.{ "else", .kw_else },
|
|
.{ "then", .kw_then },
|
|
.{ "true", .kw_true },
|
|
.{ "false", .kw_false },
|
|
.{ "enum", .kw_enum },
|
|
.{ "case", .kw_case },
|
|
.{ "break", .kw_break },
|
|
.{ "continue", .kw_continue },
|
|
.{ "while", .kw_while },
|
|
.{ "for", .kw_for },
|
|
.{ "return", .kw_return },
|
|
.{ "defer", .kw_defer },
|
|
.{ "f32", .kw_f32 },
|
|
.{ "f64", .kw_f64 },
|
|
.{ "struct", .kw_struct },
|
|
.{ "union", .kw_union },
|
|
.{ "xx", .kw_xx },
|
|
.{ "and", .kw_and },
|
|
.{ "or", .kw_or },
|
|
.{ "Type", .kw_Type },
|
|
.{ "null", .kw_null },
|
|
.{ "push", .kw_push },
|
|
.{ "ufcs", .kw_ufcs },
|
|
.{ "in", .kw_in },
|
|
.{ "closure", .kw_closure },
|
|
.{ "protocol", .kw_protocol },
|
|
.{ "impl", .kw_impl },
|
|
.{ "Self", .kw_Self },
|
|
.{ "inline", .kw_inline },
|
|
.{ "callconv", .kw_callconv },
|
|
});
|
|
|
|
pub fn getKeyword(bytes: []const u8) ?Tag {
|
|
return keywords.get(bytes);
|
|
}
|
|
|
|
const std = @import("std");
|