1+ use std:: { fs, path:: Path } ;
2+
3+ use clvmr:: { Allocator , serde:: node_from_bytes} ;
14use indexmap:: IndexMap ;
25use log:: debug;
36use rue_ast:: { AstFunctionItem , AstNode } ;
4- use rue_diagnostic:: DiagnosticKind ;
5- use rue_hir:: { Declaration , FunctionKind , FunctionSymbol , ParameterSymbol , Symbol , SymbolId , Test } ;
7+ use rue_diagnostic:: { DiagnosticKind , SourceKind } ;
8+ use rue_hir:: {
9+ Declaration , FunctionKind , FunctionSymbol , HirId , ParameterSymbol , Symbol , SymbolId , Test ,
10+ } ;
11+ use rue_parser:: SyntaxToken ;
612use rue_types:: { FunctionType , Type } ;
713
814use crate :: {
915 Compiler , CompletionContext , SyntaxItemKind , compile_block, compile_generic_parameters,
10- compile_type, create_binding,
16+ compile_type, const_eval :: decode_node , create_binding,
1117} ;
1218
1319pub fn declare_function ( ctx : & mut Compiler , function : & AstFunctionItem ) -> SymbolId {
@@ -102,6 +108,12 @@ pub fn declare_function(ctx: &mut Compiler, function: &AstFunctionItem) -> Symbo
102108 ret : return_type,
103109 } ) ) ;
104110
111+ if function. source_path ( ) . is_some ( ) {
112+ for & parameter in parameters. values ( ) {
113+ ctx. reference ( Declaration :: Symbol ( parameter) , None ) ;
114+ }
115+ }
116+
105117 let name = function. name ( ) . map ( |name| ctx. local_name ( & name) ) ;
106118
107119 * ctx. symbol_mut ( symbol) = Symbol :: Function ( FunctionSymbol {
@@ -115,6 +127,8 @@ pub fn declare_function(ctx: &mut Compiler, function: &AstFunctionItem) -> Symbo
115127 body,
116128 kind : if function. inline ( ) . is_some ( ) {
117129 FunctionKind :: Inline
130+ } else if function. source_path ( ) . is_some ( ) {
131+ FunctionKind :: External
118132 } else if function. extern_kw ( ) . is_some ( ) {
119133 FunctionKind :: Sequential
120134 } else {
@@ -172,7 +186,9 @@ pub fn compile_function(ctx: &mut Compiler, function: &AstFunctionItem, symbol:
172186 ctx. pop_declaration ( ) ;
173187 }
174188
175- let resolved_body = if let Some ( body) = function. body ( ) {
189+ let resolved_body = if let Some ( source_path) = function. source_path ( ) {
190+ compile_external_function ( ctx, & source_path)
191+ } else if let Some ( body) = function. body ( ) {
176192 let value = compile_block (
177193 ctx,
178194 & body,
@@ -181,10 +197,10 @@ pub fn compile_function(ctx: &mut Compiler, function: &AstFunctionItem, symbol:
181197 function. return_type ( ) . is_some ( ) ,
182198 ) ;
183199 ctx. assign_type ( body. syntax ( ) , value. ty , return_type) ;
184- value
200+ value. hir
185201 } else {
186202 debug ! ( "Unresolved function body" ) ;
187- ctx. builtins ( ) . unresolved . clone ( )
203+ ctx. builtins ( ) . unresolved . hir
188204 } ;
189205
190206 ctx. pop_scope ( range. end ( ) ) ;
@@ -193,7 +209,97 @@ pub fn compile_function(ctx: &mut Compiler, function: &AstFunctionItem, symbol:
193209 unreachable ! ( ) ;
194210 } ;
195211
196- * body = resolved_body. hir ;
212+ * body = resolved_body;
197213
198214 ctx. pop_declaration ( ) ;
199215}
216+
217+ fn compile_external_function ( ctx : & mut Compiler , source_path : & SyntaxToken ) -> HirId {
218+ let unresolved = ctx. builtins ( ) . unresolved . hir ;
219+ let path_text = source_path
220+ . text ( )
221+ . strip_prefix ( '"' )
222+ . and_then ( |path| path. strip_suffix ( '"' ) )
223+ . unwrap_or ( source_path. text ( ) ) ;
224+ let relative_path = Path :: new ( path_text) ;
225+
226+ if relative_path. is_absolute ( ) {
227+ ctx. diagnostic ( source_path, DiagnosticKind :: AbsoluteExternalPath ) ;
228+ return unresolved;
229+ }
230+
231+ if relative_path
232+ . extension ( )
233+ . is_none_or ( |extension| extension != "hex" )
234+ {
235+ ctx. diagnostic ( source_path, DiagnosticKind :: InvalidExternalExtension ) ;
236+ return unresolved;
237+ }
238+
239+ let SourceKind :: File ( source_file) = & ctx. source ( ) . kind else {
240+ ctx. diagnostic ( source_path, DiagnosticKind :: ExternalFromNonFileSource ) ;
241+ return unresolved;
242+ } ;
243+ let Some ( parent) = Path :: new ( source_file) . parent ( ) else {
244+ ctx. diagnostic ( source_path, DiagnosticKind :: ExternalFromNonFileSource ) ;
245+ return unresolved;
246+ } ;
247+ let unresolved_path = parent. join ( relative_path) ;
248+ let resolved_path = match unresolved_path. canonicalize ( ) {
249+ Ok ( path) => path,
250+ Err ( error) => {
251+ ctx. diagnostic (
252+ source_path,
253+ DiagnosticKind :: ExternalFileRead ( format ! ( "{path_text}: {error}" ) ) ,
254+ ) ;
255+ return unresolved;
256+ }
257+ } ;
258+
259+ let ( bytes, should_cache) = if let Some ( bytes) = ctx. external_program ( & resolved_path) {
260+ ( bytes. to_vec ( ) , false )
261+ } else {
262+ let contents = match fs:: read_to_string ( & resolved_path) {
263+ Ok ( contents) => contents,
264+ Err ( error) => {
265+ ctx. diagnostic (
266+ source_path,
267+ DiagnosticKind :: ExternalFileRead ( format ! ( "{path_text}: {error}" ) ) ,
268+ ) ;
269+ return unresolved;
270+ }
271+ } ;
272+ let hex = contents
273+ . chars ( )
274+ . filter ( |character| !character. is_ascii_whitespace ( ) )
275+ . collect :: < String > ( ) ;
276+ let bytes = match hex:: decode ( hex) {
277+ Ok ( bytes) => bytes,
278+ Err ( error) => {
279+ ctx. diagnostic (
280+ source_path,
281+ DiagnosticKind :: InvalidExternalHex ( error. to_string ( ) ) ,
282+ ) ;
283+ return unresolved;
284+ }
285+ } ;
286+ ( bytes, true )
287+ } ;
288+
289+ let mut allocator = Allocator :: new ( ) ;
290+ let program = match node_from_bytes ( & mut allocator, & bytes) {
291+ Ok ( program) => program,
292+ Err ( error) => {
293+ ctx. diagnostic (
294+ source_path,
295+ DiagnosticKind :: InvalidExternalClvm ( error. to_string ( ) ) ,
296+ ) ;
297+ return unresolved;
298+ }
299+ } ;
300+ if should_cache {
301+ ctx. cache_external_program ( resolved_path, bytes) ;
302+ }
303+ let hir = decode_node ( ctx, & allocator, program) ;
304+ ctx. alloc_hir ( hir)
305+ }
0 commit comments