1- // main.rs - Hacker Lang compiler using Cranelift.
2- // Located at ~/.hacker-lang/bin/hacker-compiler.
3- // Handles updated syntax with conditionals and includes.
4-
51use std:: env;
62use std:: fs:: { self , File } ;
73use std:: io:: { self , BufRead , Write } ;
84use std:: path:: Path ;
95use std:: process;
106
117use cranelift:: prelude:: * ;
8+ use cranelift_codegen:: ir:: Function ;
129use cranelift_codegen:: isa;
1310use cranelift_codegen:: settings;
1411use cranelift_frontend:: { FunctionBuilder , FunctionBuilderContext } ;
1512use cranelift_module:: { DataContext , Linkage , Module } ;
1613use cranelift_object:: { ObjectBuilder , ObjectModule } ;
17- use subprocess:: Exec ;
14+ use subprocess:: { Exec , PopenError } ;
1815
1916const HACKER_DIR : & str = "~/.hacker-lang" ;
2017
@@ -27,9 +24,10 @@ fn expand_home(path: &str) -> String {
2724 path. to_string ( )
2825}
2926
30- fn parse_hacker_file ( path : & Path , verbose : bool ) -> io:: Result < ( Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > ) > {
27+ fn parse_hacker_file ( path : & Path , verbose : bool ) -> io:: Result < ( Vec < String > , Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > ) > {
3128 let file = File :: open ( path) ?;
3229 let mut deps = Vec :: new ( ) ;
30+ let mut libs = Vec :: new ( ) ;
3331 let mut vars = Vec :: new ( ) ;
3432 let mut cmds = Vec :: new ( ) ;
3533 let mut includes = Vec :: new ( ) ;
@@ -69,10 +67,32 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
6967 if line. starts_with ( "//" ) {
7068 let dep = line[ 2 ..] . trim ( ) . to_string ( ) ;
7169 if dep. is_empty ( ) {
72- errors. push ( format ! ( "Line {}: Empty dependency" , line_num) ) ;
70+ errors. push ( format ! ( "Line {}: Empty system dependency" , line_num) ) ;
7371 } else {
7472 deps. push ( dep) ;
7573 }
74+ } else if line. starts_with ( "#" ) {
75+ let lib = line[ 1 ..] . trim ( ) . to_string ( ) ;
76+ if lib. is_empty ( ) {
77+ errors. push ( format ! ( "Line {}: Empty library/include" , line_num) ) ;
78+ } else {
79+ let lib_path_str = expand_home ( & format ! ( "{}/libs/{}/main.hacker" , HACKER_DIR , lib) ) ;
80+ let lib_path = Path :: new ( & lib_path_str) ;
81+ if lib_path. exists ( ) {
82+ includes. push ( lib. clone ( ) ) ;
83+ let ( sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_errors) = parse_hacker_file ( lib_path, verbose) ?;
84+ deps. extend ( sub_deps) ;
85+ libs. extend ( sub_libs) ;
86+ vars. extend ( sub_vars) ;
87+ cmds. extend ( sub_cmds) ;
88+ includes. extend ( sub_includes) ;
89+ for err in sub_errors {
90+ errors. push ( format ! ( "In {}: {}" , lib, err) ) ;
91+ }
92+ } else {
93+ libs. push ( lib) ;
94+ }
95+ }
7696 } else if line. starts_with ( ">" ) {
7797 let parts: Vec < String > = line[ 1 ..] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
7898 let cmd = parts[ 0 ] . clone ( ) ;
@@ -130,25 +150,13 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
130150 } else {
131151 errors. push ( format ! ( "Line {}: Invalid conditional syntax" , line_num) ) ;
132152 }
133- } else if line. starts_with ( "#" ) {
134- let lib = line[ 1 ..] . trim ( ) . to_string ( ) ;
135- if lib. is_empty ( ) {
136- errors. push ( format ! ( "Line {}: Empty include" , line_num) ) ;
153+ } else if line. starts_with ( "&" ) {
154+ let parts: Vec < String > = line[ 1 ..] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
155+ let cmd = parts[ 0 ] . clone ( ) ;
156+ if cmd. is_empty ( ) {
157+ errors. push ( format ! ( "Line {}: Empty background command" , line_num) ) ;
137158 } else {
138- let lib_path = Path :: new ( & expand_home ( & format ! ( "{}/libs/{}.hacker" , HACKER_DIR , lib) ) ) ;
139- if lib_path. exists ( ) {
140- includes. push ( lib) ;
141- let ( sub_deps, sub_vars, sub_cmds, sub_includes, sub_errors) = parse_hacker_file ( lib_path, verbose) ?;
142- deps. extend ( sub_deps) ;
143- vars. extend ( sub_vars) ;
144- cmds. extend ( sub_cmds) ;
145- includes. extend ( sub_includes) ;
146- for err in sub_errors {
147- errors. push ( format ! ( "In {}: {}" , lib, err) ) ;
148- }
149- } else {
150- errors. push ( format ! ( "Line {}: Library {} not found" , line_num, lib) ) ;
151- }
159+ cmds. push ( format ! ( "{} &" , cmd) ) ;
152160 }
153161 } else if line. starts_with ( "!" ) {
154162 // Ignore comment
@@ -162,16 +170,17 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
162170 }
163171
164172 if verbose {
165- println ! ( "Parsed deps: {:?}" , deps) ;
166- println ! ( "Parsed vars: {:?}" , vars) ;
167- println ! ( "Parsed cmds: {:?}" , cmds) ;
168- println ! ( "Parsed includes: {:?}" , includes) ;
173+ println ! ( "System Deps: {:?}" , deps) ;
174+ println ! ( "Custom Libs: {:?}" , libs) ;
175+ println ! ( "Vars: {:?}" , vars) ;
176+ println ! ( "Cmds: {:?}" , cmds) ;
177+ println ! ( "Includes: {:?}" , includes) ;
169178 if !errors. is_empty ( ) {
170179 println ! ( "Errors: {:?}" , errors) ;
171180 }
172181 }
173182
174- Ok ( ( deps, vars, cmds, includes, errors) )
183+ Ok ( ( deps, libs , vars, cmds, includes, errors) )
175184}
176185
177186fn generate_check_cmd ( dep : & str ) -> String {
@@ -191,7 +200,7 @@ fn main() -> io::Result<()> {
191200 let input_path = Path :: new ( & args[ 1 ] ) ;
192201 let output_path = Path :: new ( & args[ 2 ] ) ;
193202
194- let ( mut deps, vars, mut cmds, _includes, errors) = parse_hacker_file ( input_path, verbose) ?;
203+ let ( mut deps, _libs , vars, mut cmds, _includes, errors) = parse_hacker_file ( input_path, verbose) ?;
195204 if !errors. is_empty ( ) {
196205 for err in errors {
197206 eprintln ! ( "{}" , err) ;
@@ -213,7 +222,7 @@ fn main() -> io::Result<()> {
213222 let isa_builder = isa:: lookup ( triple) . expect ( "Host not supported" ) ;
214223 let isa = isa_builder. finish ( flags) . expect ( "ISA build failed" ) ;
215224
216- let builder = ObjectBuilder :: new ( isa, output_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . as_bytes ( ) . to_vec ( ) , cranelift_module:: default_libcall_names ( ) ) . unwrap ( ) ;
225+ let builder = ObjectBuilder :: new ( isa, output_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . to_vec ( ) , cranelift_module:: default_libcall_names ( ) ) . expect ( "Failed to create ObjectBuilder" ) ;
217226 let mut module = ObjectModule :: new ( builder) ;
218227
219228 let pointer_type = module. target_config ( ) . pointer_type ( ) ;
@@ -287,14 +296,13 @@ fn main() -> io::Result<()> {
287296 builder. finalize ( ) ;
288297
289298 module. define_function ( main_id, & mut ctx) . unwrap ( ) ;
290- module. finalize_definitions ( ) ;
291- let obj = module. object . finish ( ) ;
299+ let obj = module. finish ( ) ;
292300
293301 let temp_obj_path = output_path. with_extension ( "o" ) ;
294302 let mut file = File :: create ( & temp_obj_path) ?;
295303 file. write_all ( & obj) ?;
296304
297- let status = Exec :: shell ( format ! ( "gcc -o {} {}" , output_path. display( ) , temp_obj_path. display( ) ) ) . join ( ) ?;
305+ let status = Exec :: shell ( format ! ( "gcc -o {} {}" , output_path. display( ) , temp_obj_path. display( ) ) ) . join ( ) . map_err ( | e : PopenError | io :: Error :: new ( io :: ErrorKind :: Other , e . to_string ( ) ) ) ?;
298306 if !status. success ( ) {
299307 return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "Linking failed" ) ) ;
300308 }
0 commit comments