feat(tvix/eval): resolve relative path literals
Resolves relative paths (e.g. `./foo`) either relative to the location of the Nix file, or relative to the working directory if none is supplied. Change-Id: I70ec574657b221b458015117a004b6e4a9c25a30 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6185 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
		
							parent
							
								
									1f8aad0ab4
								
							
						
					
					
						commit
						6fe5e2d752
					
				
					 4 changed files with 37 additions and 10 deletions
				
			
		|  | @ -13,7 +13,7 @@ | ||||||
| //! the code in this module, `debug_assert!` has been used to catch
 | //! the code in this module, `debug_assert!` has been used to catch
 | ||||||
| //! mistakes early during development.
 | //! mistakes early during development.
 | ||||||
| 
 | 
 | ||||||
| use std::path::Path; | use std::path::{Path, PathBuf}; | ||||||
| 
 | 
 | ||||||
| use crate::chunk::Chunk; | use crate::chunk::Chunk; | ||||||
| use crate::errors::{Error, EvalResult}; | use crate::errors::{Error, EvalResult}; | ||||||
|  | @ -35,6 +35,7 @@ pub struct CompilationResult { | ||||||
| struct Compiler { | struct Compiler { | ||||||
|     chunk: Chunk, |     chunk: Chunk, | ||||||
|     warnings: Vec<EvalWarning>, |     warnings: Vec<EvalWarning>, | ||||||
|  |     root_dir: PathBuf, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Compiler { | impl Compiler { | ||||||
|  | @ -175,7 +176,11 @@ impl Compiler { | ||||||
|                 buf |                 buf | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             rnix::value::Anchor::Relative => todo!("resolve relative to file location"), |             rnix::value::Anchor::Relative => { | ||||||
|  |                 let mut buf = self.root_dir.clone(); | ||||||
|  |                 buf.push(path); | ||||||
|  |                 buf | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             // This confusingly named variant is actually
 |             // This confusingly named variant is actually
 | ||||||
|             // angle-bracket lookups, which in C++ Nix desugar
 |             // angle-bracket lookups, which in C++ Nix desugar
 | ||||||
|  | @ -624,8 +629,23 @@ impl Compiler { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn compile(ast: rnix::AST) -> EvalResult<CompilationResult> { | pub fn compile(ast: rnix::AST, location: Option<PathBuf>) -> EvalResult<CompilationResult> { | ||||||
|  |     let mut root_dir = match location { | ||||||
|  |         Some(dir) => Ok(dir), | ||||||
|  |         None => std::env::current_dir().map_err(|e| { | ||||||
|  |             Error::PathResolution(format!("could not determine current directory: {}", e)) | ||||||
|  |         }), | ||||||
|  |     }?; | ||||||
|  | 
 | ||||||
|  |     // If the path passed from the caller points to a file, the
 | ||||||
|  |     // filename itself needs to be truncated as this must point to a
 | ||||||
|  |     // directory.
 | ||||||
|  |     if root_dir.is_file() { | ||||||
|  |         root_dir.pop(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     let mut c = Compiler { |     let mut c = Compiler { | ||||||
|  |         root_dir, | ||||||
|         chunk: Chunk::default(), |         chunk: Chunk::default(), | ||||||
|         warnings: vec![], |         warnings: vec![], | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -1,8 +1,10 @@ | ||||||
|  | use std::path::PathBuf; | ||||||
|  | 
 | ||||||
| use rnix::{self, types::TypedNode}; | use rnix::{self, types::TypedNode}; | ||||||
| 
 | 
 | ||||||
| use crate::{errors::EvalResult, value::Value}; | use crate::{errors::EvalResult, value::Value}; | ||||||
| 
 | 
 | ||||||
| pub fn interpret(code: &str) -> EvalResult<Value> { | pub fn interpret(code: &str, location: Option<PathBuf>) -> EvalResult<Value> { | ||||||
|     let ast = rnix::parse(code); |     let ast = rnix::parse(code); | ||||||
| 
 | 
 | ||||||
|     let errors = ast.errors(); |     let errors = ast.errors(); | ||||||
|  | @ -14,7 +16,7 @@ pub fn interpret(code: &str) -> EvalResult<Value> { | ||||||
|         println!("{}", ast.root().dump()); |         println!("{}", ast.root().dump()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let result = crate::compiler::compile(ast)?; |     let result = crate::compiler::compile(ast, location)?; | ||||||
|     println!("code: {:?}", result.chunk); |     println!("code: {:?}", result.chunk); | ||||||
| 
 | 
 | ||||||
|     for warning in result.warnings { |     for warning in result.warnings { | ||||||
|  |  | ||||||
|  | @ -1,4 +1,8 @@ | ||||||
| use std::{env, fs, path::PathBuf, process}; | use std::{ | ||||||
|  |     env, fs, | ||||||
|  |     path::{Path, PathBuf}, | ||||||
|  |     process, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use rustyline::{error::ReadlineError, Editor}; | use rustyline::{error::ReadlineError, Editor}; | ||||||
| 
 | 
 | ||||||
|  | @ -18,8 +22,9 @@ fn main() { | ||||||
| 
 | 
 | ||||||
| fn run_file(file: &str) { | fn run_file(file: &str) { | ||||||
|     let contents = fs::read_to_string(file).expect("failed to read the input file"); |     let contents = fs::read_to_string(file).expect("failed to read the input file"); | ||||||
|  |     let path = Path::new(file).to_owned(); | ||||||
| 
 | 
 | ||||||
|     match tvix_eval::interpret(&contents) { |     match tvix_eval::interpret(&contents, Some(path)) { | ||||||
|         Ok(result) => println!("=> {} :: {}", result, result.type_of()), |         Ok(result) => println!("=> {} :: {}", result, result.type_of()), | ||||||
|         Err(err) => eprintln!("{}", err), |         Err(err) => eprintln!("{}", err), | ||||||
|     } |     } | ||||||
|  | @ -53,7 +58,7 @@ fn run_prompt() { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 match tvix_eval::interpret(&line) { |                 match tvix_eval::interpret(&line, None) { | ||||||
|                     Ok(result) => { |                     Ok(result) => { | ||||||
|                         println!("=> {} :: {}", result, result.type_of()); |                         println!("=> {} :: {}", result, result.type_of()); | ||||||
|                         rl.add_history_entry(line); |                         rl.add_history_entry(line); | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ fn eval_okay_test(code_path: &str) { | ||||||
|     let code = std::fs::read_to_string(code_path).expect("should be able to read test code"); |     let code = std::fs::read_to_string(code_path).expect("should be able to read test code"); | ||||||
|     let exp = std::fs::read_to_string(exp_path).expect("should be able to read test expectation"); |     let exp = std::fs::read_to_string(exp_path).expect("should be able to read test expectation"); | ||||||
| 
 | 
 | ||||||
|     let result = interpret(&code).expect("evaluation of eval-okay test should succeed"); |     let result = interpret(&code, None).expect("evaluation of eval-okay test should succeed"); | ||||||
|     let result_str = format!("{}", result); |     let result_str = format!("{}", result); | ||||||
| 
 | 
 | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|  | @ -27,7 +27,7 @@ fn eval_okay_test(code_path: &str) { | ||||||
| fn identity(code_path: &str) { | fn identity(code_path: &str) { | ||||||
|     let code = std::fs::read_to_string(code_path).expect("should be able to read test code"); |     let code = std::fs::read_to_string(code_path).expect("should be able to read test code"); | ||||||
| 
 | 
 | ||||||
|     let result = interpret(&code).expect("evaluation of identity test should succeed"); |     let result = interpret(&code, None).expect("evaluation of identity test should succeed"); | ||||||
|     let result_str = format!("{}", result); |     let result_str = format!("{}", result); | ||||||
| 
 | 
 | ||||||
|     assert_eq!( |     assert_eq!( | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue