1- // hacker-compiler/src/main.rs - Updated Rust compiler for Hacker Lang using Cranelift.
2- // Syntax: // for deps, [ ... ] for config (ignored), > for cmds, ! for comments, @var=value for vars, *num > cmd for loops.
3- // Enhanced with syntax checking and verbose output.
4- // Compiles to native binary with putenv for vars and system for cmds.
5- // Place binary in ~/.hackeros/hacker-lang/bin/hacker-compiler.
1+ // main.rs - Hacker Lang compiler using Cranelift.
2+ // Located at ~/.hacker-lang/bin/hacker-compiler.
3+ // Handles updated syntax with conditionals and includes.
64
75use std:: env;
86use std:: fs:: { self , File } ;
@@ -18,11 +16,23 @@ use cranelift_module::{DataContext, Linkage, Module};
1816use cranelift_object:: { ObjectBuilder , ObjectModule } ;
1917use subprocess:: Exec ;
2018
21- fn parse_hacker_file ( path : & Path , verbose : bool ) -> io:: Result < ( Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > ) > {
19+ const HACKER_DIR : & str = "~/.hacker-lang" ;
20+
21+ fn expand_home ( path : & str ) -> String {
22+ if path. starts_with ( "~/" ) {
23+ if let Some ( home) = std:: env:: var_os ( "HOME" ) {
24+ return path. replacen ( "~" , home. to_str ( ) . unwrap ( ) , 1 ) ;
25+ }
26+ }
27+ path. to_string ( )
28+ }
29+
30+ fn parse_hacker_file ( path : & Path , verbose : bool ) -> io:: Result < ( Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > ) > {
2231 let file = File :: open ( path) ?;
2332 let mut deps = Vec :: new ( ) ;
2433 let mut vars = Vec :: new ( ) ;
2534 let mut cmds = Vec :: new ( ) ;
35+ let mut includes = Vec :: new ( ) ;
2636 let mut errors = Vec :: new ( ) ;
2737 let mut in_config = false ;
2838 let mut config_lines = Vec :: new ( ) ;
@@ -38,14 +48,14 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
3848
3949 if line == "[" {
4050 if in_config {
41- errors. push ( format ! ( "Line {}: Nested config section detected " , line_num) ) ;
51+ errors. push ( format ! ( "Line {}: Nested config section" , line_num) ) ;
4252 }
4353 in_config = true ;
4454 config_lines = Vec :: new ( ) ;
4555 continue ;
4656 } else if line == "]" {
4757 if !in_config {
48- errors. push ( format ! ( "Line {}: Closing ] without opening [" , line_num) ) ;
58+ errors. push ( format ! ( "Line {}: Closing ] without [" , line_num) ) ;
4959 }
5060 in_config = false ;
5161 continue ;
@@ -76,14 +86,14 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
7686 let var = line[ 1 ..eq_idx] . trim ( ) . to_string ( ) ;
7787 let value = line[ eq_idx + 1 ..] . trim ( ) . to_string ( ) ;
7888 if var. is_empty ( ) || value. is_empty ( ) {
79- errors. push ( format ! ( "Line {}: Invalid variable assignment " , line_num) ) ;
89+ errors. push ( format ! ( "Line {}: Invalid variable" , line_num) ) ;
8090 } else {
8191 vars. push ( ( var, value) ) ;
8292 }
8393 } else {
8494 errors. push ( format ! ( "Line {}: Missing = in variable" , line_num) ) ;
8595 }
86- } else if line. starts_with ( "* " ) {
96+ } else if line. starts_with ( "= " ) {
8797 let parts: Vec < String > = line[ 1 ..] . split ( '>' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
8898 if parts. len ( ) == 2 {
8999 if let Ok ( num) = parts[ 0 ] . parse :: < usize > ( ) {
@@ -106,6 +116,40 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
106116 } else {
107117 errors. push ( format ! ( "Line {}: Invalid loop syntax" , line_num) ) ;
108118 }
119+ } else if line. starts_with ( "?" ) {
120+ let parts: Vec < String > = line[ 1 ..] . split ( '>' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
121+ if parts. len ( ) == 2 {
122+ let condition = parts[ 0 ] . clone ( ) ;
123+ let cmd_parts: Vec < String > = parts[ 1 ] . split ( '!' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ;
124+ let cmd = cmd_parts[ 0 ] . clone ( ) ;
125+ if condition. is_empty ( ) || cmd. is_empty ( ) {
126+ errors. push ( format ! ( "Line {}: Invalid conditional" , line_num) ) ;
127+ } else {
128+ cmds. push ( format ! ( "if {}; then {}; fi" , condition, cmd) ) ;
129+ }
130+ } else {
131+ errors. push ( format ! ( "Line {}: Invalid conditional syntax" , line_num) ) ;
132+ }
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) ) ;
137+ } 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+ }
152+ }
109153 } else if line. starts_with ( "!" ) {
110154 // Ignore comment
111155 } else {
@@ -114,19 +158,20 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
114158 }
115159
116160 if in_config {
117- errors. push ( "File ended with unclosed config section" . to_string ( ) ) ;
161+ errors. push ( "Unclosed config section" . to_string ( ) ) ;
118162 }
119163
120164 if verbose {
121165 println ! ( "Parsed deps: {:?}" , deps) ;
122166 println ! ( "Parsed vars: {:?}" , vars) ;
123167 println ! ( "Parsed cmds: {:?}" , cmds) ;
168+ println ! ( "Parsed includes: {:?}" , includes) ;
124169 if !errors. is_empty ( ) {
125170 println ! ( "Errors: {:?}" , errors) ;
126171 }
127172 }
128173
129- Ok ( ( deps, vars, cmds, errors) )
174+ Ok ( ( deps, vars, cmds, includes , errors) )
130175}
131176
132177fn generate_check_cmd ( dep : & str ) -> String {
@@ -146,7 +191,7 @@ fn main() -> io::Result<()> {
146191 let input_path = Path :: new ( & args[ 1 ] ) ;
147192 let output_path = Path :: new ( & args[ 2 ] ) ;
148193
149- let ( mut deps, vars, mut cmds, errors) = parse_hacker_file ( input_path, verbose) ?;
194+ let ( mut deps, vars, mut cmds, _includes , errors) = parse_hacker_file ( input_path, verbose) ?;
150195 if !errors. is_empty ( ) {
151196 for err in errors {
152197 eprintln ! ( "{}" , err) ;
0 commit comments