@@ -30,6 +30,7 @@ pub enum Stmt {
3030 else_body : Vec < Stmt > ,
3131 } ,
3232 Return ( Expr ) ,
33+ Call ( String , Vec < Expr > ) ,
3334}
3435
3536#[ derive( Debug , Clone , PartialEq ) ]
@@ -52,17 +53,29 @@ pub enum Expr {
5253
5354/// Parse an XPL file into a Program AST
5455pub fn parse_file ( path : & str ) -> Result < Program , XplError > {
55- let file = File :: open ( path) ?;
56- let root = Element :: parse ( file) ?;
56+ let file = File :: open ( path) . map_err ( |e| XplError :: Io {
57+ source : e,
58+ file : path. to_string ( ) ,
59+ } ) ?;
60+ let root = Element :: parse ( file) . map_err ( |e| XplError :: Xml {
61+ source : e,
62+ file : path. to_string ( ) ,
63+ } ) ?;
5764 let mut functions = HashMap :: new ( ) ;
5865 // Process include only for program roots (to load libs)
5966 if root. name == "program" {
6067 if let Some ( include_list) = root. attributes . get ( "include" ) {
61- let base = std:: path:: Path :: new ( path)
68+ let script_dir = std:: path:: Path :: new ( path)
6269 . parent ( )
6370 . unwrap_or_else ( || std:: path:: Path :: new ( "." ) ) ;
6471 for inc in include_list. split ( ',' ) . map ( |s| s. trim ( ) ) {
65- let inc_path = base. join ( inc) ;
72+ // try script-relative first, then workspace-relative
73+ let rel_path = script_dir. join ( inc) ;
74+ let inc_path = if rel_path. exists ( ) {
75+ rel_path
76+ } else {
77+ std:: path:: Path :: new ( inc) . to_path_buf ( )
78+ } ;
6679 let included = parse_file ( inc_path. to_str ( ) . unwrap ( ) ) ?;
6780 functions. extend ( included. functions ) ;
6881 }
@@ -88,8 +101,14 @@ pub fn parse_file(path: &str) -> Result<Program, XplError> {
88101 for stmt_node in & body_elem. children {
89102 if let XMLNode :: Element ( stmt_elem) = stmt_node {
90103 match stmt_elem. name . as_str ( ) {
104+ "call" => {
105+ // standalone call statement
106+ let expr = parse_expr ( stmt_elem) ?;
107+ if let Expr :: Call ( name, args) = expr {
108+ body. push ( Stmt :: Call ( name, args) ) ;
109+ }
110+ }
91111 "return" => {
92- // parse return expression
93112 let expr = if let Some ( XMLNode :: Element ( e) ) =
94113 stmt_elem. children . get ( 0 )
95114 {
@@ -101,10 +120,14 @@ pub fn parse_file(path: &str) -> Result<Program, XplError> {
101120 body. push ( Stmt :: Return ( expr) ) ;
102121 }
103122 "if" => {
104- // parse condition
105123 let cond_elem =
106124 stmt_elem. get_child ( "condition" ) . ok_or_else ( || {
107- XplError :: Semantic ( "Missing condition" . to_string ( ) )
125+ XplError :: Semantic {
126+ msg : "Missing condition" . to_string ( ) ,
127+ file : path. to_string ( ) ,
128+ line : 0 ,
129+ col : 0 ,
130+ }
108131 } ) ?;
109132 // either inner element or text
110133 let cond_expr = if let Some ( XMLNode :: Element ( e) ) =
@@ -126,7 +149,12 @@ pub fn parse_file(path: &str) -> Result<Program, XplError> {
126149 // then block
127150 let then_elem =
128151 stmt_elem. get_child ( "then" ) . ok_or_else ( || {
129- XplError :: Semantic ( "Missing then block" . to_string ( ) )
152+ XplError :: Semantic {
153+ msg : "Missing then block" . to_string ( ) ,
154+ file : path. to_string ( ) ,
155+ line : 0 ,
156+ col : 0 ,
157+ }
130158 } ) ?;
131159 let mut then_body = Vec :: new ( ) ;
132160 for then_node in & then_elem. children {
@@ -154,7 +182,12 @@ pub fn parse_file(path: &str) -> Result<Program, XplError> {
154182 // else block
155183 let else_elem =
156184 stmt_elem. get_child ( "else" ) . ok_or_else ( || {
157- XplError :: Semantic ( "Missing else block" . to_string ( ) )
185+ XplError :: Semantic {
186+ msg : "Missing else block" . to_string ( ) ,
187+ file : path. to_string ( ) ,
188+ line : 0 ,
189+ col : 0 ,
190+ }
158191 } ) ?;
159192 let mut else_body = Vec :: new ( ) ;
160193 for else_node in & else_elem. children {
0 commit comments