feat: add extern let declarations for JS FFI
Add support for `extern let name: Type` and `extern let name: Type = "jsName"` syntax for declaring external JavaScript values. This follows the same pattern as extern fn across all compiler passes: parser, typechecker, interpreter (runtime error placeholder), JS backend (emits JS name directly without mangling), formatter, linter, modules, and symbol table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -238,7 +238,7 @@ impl Parser {
|
||||
|
||||
match self.peek_kind() {
|
||||
TokenKind::Fn => Ok(Declaration::Function(self.parse_function_decl(visibility, doc)?)),
|
||||
TokenKind::Extern => Ok(Declaration::ExternFn(self.parse_extern_fn_decl(visibility, doc)?)),
|
||||
TokenKind::Extern => self.parse_extern_decl(visibility, doc),
|
||||
TokenKind::Effect => Ok(Declaration::Effect(self.parse_effect_decl(doc)?)),
|
||||
TokenKind::Handler => Ok(Declaration::Handler(self.parse_handler_decl()?)),
|
||||
TokenKind::Type => Ok(Declaration::Type(self.parse_type_decl(visibility, doc)?)),
|
||||
@@ -324,6 +324,58 @@ impl Parser {
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse extern declaration: dispatch to extern fn or extern let
|
||||
fn parse_extern_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<Declaration, ParseError> {
|
||||
// Peek past 'extern' to see if it's 'fn' or 'let'
|
||||
if self.pos + 1 < self.tokens.len() {
|
||||
match &self.tokens[self.pos + 1].kind {
|
||||
TokenKind::Fn => Ok(Declaration::ExternFn(self.parse_extern_fn_decl(visibility, doc)?)),
|
||||
TokenKind::Let => Ok(Declaration::ExternLet(self.parse_extern_let_decl(visibility, doc)?)),
|
||||
_ => Err(self.error("Expected 'fn' or 'let' after 'extern'")),
|
||||
}
|
||||
} else {
|
||||
Err(self.error("Expected 'fn' or 'let' after 'extern'"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse extern let declaration: extern let name: Type = "jsName"
|
||||
fn parse_extern_let_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<ExternLetDecl, ParseError> {
|
||||
let start = self.current_span();
|
||||
self.expect(TokenKind::Extern)?;
|
||||
self.expect(TokenKind::Let)?;
|
||||
|
||||
let name = self.parse_ident()?;
|
||||
|
||||
// Type annotation
|
||||
self.expect(TokenKind::Colon)?;
|
||||
let typ = self.parse_type()?;
|
||||
|
||||
// Optional JS name override: = "jsName"
|
||||
let js_name = if self.check(TokenKind::Eq) {
|
||||
self.advance();
|
||||
match self.peek_kind() {
|
||||
TokenKind::String(s) => {
|
||||
let name = s.clone();
|
||||
self.advance();
|
||||
Some(name)
|
||||
}
|
||||
_ => return Err(self.error("Expected string literal for JS name in extern let")),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let span = start.merge(self.previous_span());
|
||||
Ok(ExternLetDecl {
|
||||
visibility,
|
||||
doc,
|
||||
name,
|
||||
typ,
|
||||
js_name,
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse extern function declaration: extern fn name<T>(params): ReturnType = "jsName"
|
||||
fn parse_extern_fn_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<ExternFnDecl, ParseError> {
|
||||
let start = self.current_span();
|
||||
|
||||
Reference in New Issue
Block a user