From 9370fbe17e40c006e560adb16ec49cead53bf6a7 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 13 Aug 2018 23:11:35 -0500 Subject: [PATCH] FEATURE: Shared cache for all built ucg files. --- .gitignore | 1 - Cargo.lock | 308 ++++++++++++++++++++++++++++++++++++++ examples/shared.ucg | 1 + examples/test.ucg | 6 +- src/build/assets.rs | 68 +++++++++ src/build/compile_test.rs | 7 +- src/build/mod.rs | 120 ++++++++------- src/build/test.rs | 105 ++++++------- src/convert/exec.rs | 12 +- src/main.rs | 8 +- 10 files changed, 520 insertions(+), 116 deletions(-) create mode 100644 Cargo.lock create mode 100644 examples/shared.ucg create mode 100644 src/build/assets.rs diff --git a/.gitignore b/.gitignore index 965ad77..dc7a101 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ target -Cargo.lock .vscode/settings.json diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6640c86 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,308 @@ +[[package]] +name = "ansi_term" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "atty" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bencher" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cc" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cpuprofiler" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dbghelp-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "error-chain" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.42" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nom" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nom_locate" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_json" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "term_size" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ucg" +version = "0.1.5" +dependencies = [ + "bencher 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cpuprofiler 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" +"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" +"checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f" +"checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e" +"checksum bencher 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "49ec142f5768efb5b7622aebc3fdbdbb8950a4b9ba996393cb76ef7466e8747d" +"checksum cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efe5c877e17a9c717a0bf3613b2709f723202c4e4675cc8f12926ded29bcb17e" +"checksum clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3451e409013178663435d6f15fdb212f14ee4424a3d74f979d081d0a66b6f1f2" +"checksum cpuprofiler 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "33f07976bb6821459632d7a18d97ccca005cb5c552f251f822c7c1781c1d7035" +"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum error-chain 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5c82c815138e278b8dcdeffc49f27ea6ffb528403e9dea4194f2e3dd40b143" +"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" +"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" +"checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" +"checksum nom_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49b1c61eff39ab6b91ccedfc62aff196eae066d88355b4fe3e4100c23168f0df" +"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" +"checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95" +"checksum serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "eb40600c756f02d7ea34943626cefa85732fdae5f95b90b31f9797b3c526d1e6" +"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" +"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8e08afc40ae3459e4838f303e465aa50d823df8d7f83ca88108f6d3afe7edd" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/examples/shared.ucg b/examples/shared.ucg new file mode 100644 index 0000000..c5451f6 --- /dev/null +++ b/examples/shared.ucg @@ -0,0 +1 @@ +let port = 3306; \ No newline at end of file diff --git a/examples/test.ucg b/examples/test.ucg index 1d1bdc8..e944055 100644 --- a/examples/test.ucg +++ b/examples/test.ucg @@ -1,3 +1,5 @@ +import "shared.ucg" as shared; + // A few constants. let dbhost1 = "db1.prod.net"; let dbhost2 = "db2.prod.net"; @@ -11,8 +13,8 @@ let mk_db_conn = macro (host, port, db) => { conn_string = "@:@/@" % (host, port, db) }; -let db_conn1 = mk_db_conn(dbhost1, 3306, dbname); -let db_conn2 = mk_db_conn(dbhost2, 3306, dbname); +let db_conn1 = mk_db_conn(dbhost1, shared.port, dbname); +let db_conn2 = mk_db_conn(dbhost2, shared.port, dbname); // We have two database connections in a list let db_conn_list = [db_conn1, db_conn2]; diff --git a/src/build/assets.rs b/src/build/assets.rs new file mode 100644 index 0000000..0c6686d --- /dev/null +++ b/src/build/assets.rs @@ -0,0 +1,68 @@ +// Copyright 2017 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The asset cache for the ucg compiler. + +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; +use std::rc::Rc; +use std::result; + +use super::Val; + +pub type Result = result::Result; + +/// Defines the cach interface for a UCG build. It has functions to retrieve +/// An asset for a referenced ucg file if it exists as well as to stash +/// an asset for a built ucg file. +/// +/// All methods with a path do path canonicalization. As a result the path +/// is expected to exist on the filesystem. If the path does not exist on +/// the local filesystem then the Cache may return an error. +pub trait Cache { + fn has_path(&self, path: &PathBuf) -> Result; + fn get(&self, path: &PathBuf) -> Result>>; + fn stash(&mut self, path: PathBuf, asset: Rc) -> Result<()>; +} + +pub struct MemoryCache { + map: HashMap>, +} + +impl MemoryCache { + pub fn new() -> Self { + MemoryCache { + map: HashMap::new(), + } + } +} + +impl Cache for MemoryCache { + fn has_path(&self, path: &PathBuf) -> Result { + let new_path = try!(path.canonicalize()); + Ok(self.map.contains_key(&new_path)) + } + + fn get(&self, path: &PathBuf) -> Result>> { + let new_path = try!(path.canonicalize()); + Ok(self.map.get(&new_path).map(|v| v.clone())) + } + + fn stash(&mut self, path: PathBuf, asset: Rc) -> Result<()> { + let new_path = try!(path.canonicalize()); + self.map.insert(new_path, asset); + Ok(()) + } +} diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs index eb17887..6180f9a 100644 --- a/src/build/compile_test.rs +++ b/src/build/compile_test.rs @@ -12,10 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::cell::RefCell; +use std::rc::Rc; + +use super::assets::MemoryCache; use super::Builder; fn assert_build(input: &str) { - let mut b = Builder::new(""); + let cache = MemoryCache::new(); + let mut b = Builder::new("", Rc::new(RefCell::new(cache))); b.enable_validate_mode(); b.eval_string(input).unwrap(); if !b.assert_collector.success { diff --git a/src/build/mod.rs b/src/build/mod.rs index 26e08e2..b20a69d 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -13,8 +13,9 @@ // limitations under the License. //! The build stage of the ucg compiler. +use std::cell::RefCell; use std::collections::hash_map::Entry; -use std::collections::{HashMap, HashSet, VecDeque}; +use std::collections::{HashMap, VecDeque}; use std::convert::From; use std::env; use std::error::Error; @@ -33,11 +34,14 @@ use format; use parse::parse; use tokenizer::Span; +pub mod assets; + impl MacroDef { /// Expands a ucg Macro using the given arguments into a new Tuple. pub fn eval( &self, root: PathBuf, + cache: Rc>, env: Rc, mut args: Vec>, ) -> Result, Rc)>, Box> { @@ -61,7 +65,7 @@ impl MacroDef { for (i, arg) in args.drain(0..).enumerate() { scope.entry(self.argdefs[i].clone()).or_insert(arg.clone()); } - let b = Builder::new_with_env_and_scope(root, scope, env); + let b = Builder::new_with_env_and_scope(root, cache, scope, env); let mut result: Vec<(Positioned, Rc)> = Vec::new(); for &(ref key, ref expr) in self.fields.iter() { // We clone the expressions here because this macro may be consumed @@ -299,13 +303,18 @@ pub struct Builder { validate_mode: bool, assert_collector: AssertCollector, env: Rc, - /// assets are other parsed files from import statements. They - /// are keyed by the normalized import path. This acts as a cache + // NOTE(jwall): We use interior mutability here because we need + // our asset cache to be shared by multiple different sub-builders. + // We use Rc to handle the reference counting for us and we use + // RefCell to give us interior mutability. This sacrifices our + // compile time memory safety for runtime checks. However it's + // acceptable in this case since I can't figure out a better way to + // handle it. + /// The assets are other parsed files from import statements. They + /// are keyed by the canonicalized import path. This acts as a cache /// so multiple imports of the same file don't have to be parsed /// multiple times. - assets: ValueMap, - // List of file paths we have already parsed. - files: HashSet, + assets: Rc>, /// build_output is our built output. build_output: ValueMap, /// last is the result of the last statement. @@ -377,20 +386,25 @@ impl Builder { } /// Constructs a new Builder. - pub fn new>(root: P) -> Self { - Self::new_with_scope(root, HashMap::new()) + pub fn new>(root: P, cache: Rc>) -> Self { + Self::new_with_scope(root, cache, HashMap::new()) } /// Constructs a new Builder with a provided scope. - pub fn new_with_scope>(root: P, scope: ValueMap) -> Self { + pub fn new_with_scope>( + root: P, + cache: Rc>, + scope: ValueMap, + ) -> Self { let env_vars: Vec<(Positioned, Rc)> = env::vars() .map(|t| (Positioned::new(t.0, 0, 0), Rc::new(t.1.into()))) .collect(); - Self::new_with_env_and_scope(root, scope, Rc::new(Val::Tuple(env_vars))) + Self::new_with_env_and_scope(root, cache, scope, Rc::new(Val::Tuple(env_vars))) } pub fn new_with_env_and_scope>( root: P, + cache: Rc>, scope: ValueMap, env: Rc, ) -> Self { @@ -403,8 +417,7 @@ impl Builder { failures: String::new(), }, env: env, - assets: HashMap::new(), - files: HashSet::new(), + assets: cache, build_output: scope, out_lock: None, last: None, @@ -460,6 +473,7 @@ impl Builder { /// Builds a ucg file at the named path. pub fn build_file(&mut self, name: &str) -> BuildResult { + eprintln!("building ucg file {}", name); let mut f = try!(File::open(name)); let mut s = String::new(); try!(f.read_to_string(&mut s)); @@ -469,40 +483,23 @@ impl Builder { fn build_import(&mut self, def: &ImportDef) -> Result, Box> { let sym = &def.name; - let positioned_sym = sym.into(); let mut normalized = self.root.to_path_buf(); normalized.push(&def.path.fragment); - let key = normalized.to_str().unwrap().to_string(); - if !self.files.contains(&key) { - // Only parse the file once on import. - if self.assets.get(&positioned_sym).is_none() { - // FIXME(jwall): We should be sharing our assets collection. - let mut b = Self::new(normalized); - try!(b.build_file(&def.path.fragment)); - let fields: Vec<(Positioned, Rc)> = b.build_output.drain().collect(); - let result = Rc::new(Val::Tuple(fields)); - self.assets.entry(positioned_sym).or_insert(result.clone()); - self.files.insert(def.path.fragment.clone()); - return Ok(result); - } else { - return Ok(self.assets.get(&positioned_sym).unwrap().clone()); - } - } else { - return match self.assets.get(&positioned_sym) { - None => { - // some kind of error here I think. - Err(Box::new(error::Error::new( - format!( - "Unknown Error processing import in file: {}", - self.root.to_string_lossy() - ), - error::ErrorType::Unsupported, - def.name.pos.clone(), - ))) - } - Some(val) => Ok(val.clone()), - }; + eprintln!("processing import for {}", normalized.to_string_lossy()); + // Only parse the file once on import. + let mut shared_assets = self.assets.borrow_mut(); + if try!(shared_assets.get(&normalized)).is_some() { + return Ok(try!(shared_assets.get(&normalized)).unwrap().clone()); } + let mut b = Self::new(normalized.clone(), self.assets.clone()); + let filepath = normalized.to_str().unwrap().clone(); + try!(b.build_file(filepath)); + let fields: Vec<(Positioned, Rc)> = b.build_output.drain().collect(); + let result = Rc::new(Val::Tuple(fields)); + //eprintln!("storing sym {:?} results {:?} ", sym, result) + self.build_output.insert(sym.into(), result.clone()); + try!(shared_assets.stash(normalized.clone(), result.clone())); + return Ok(result); } fn build_let(&mut self, def: &LetDef) -> Result, Box> { @@ -512,7 +509,7 @@ impl Builder { Entry::Occupied(e) => { return Err(Box::new(error::Error::new( format!( - "Let binding \ + "Binding \ for {:?} already \ exists in file: {}", e.key(), @@ -560,9 +557,6 @@ impl Builder { if self.build_output.contains_key(sym) { return Some(self.build_output[sym].clone()); } - if self.assets.contains_key(sym) { - return Some(self.assets[sym].clone()); - } None } @@ -822,9 +816,11 @@ impl Builder { left: Rc, right: Rc, ) -> Result, Box> { - Ok(Rc::new(Val::Boolean(try!( - left.equal(right.as_ref(), &self.root.to_string_lossy(), pos.clone()) - )))) + Ok(Rc::new(Val::Boolean(try!(left.equal( + right.as_ref(), + &self.root.to_string_lossy(), + pos.clone() + ))))) } fn do_not_deep_equal( @@ -833,9 +829,11 @@ impl Builder { left: Rc, right: Rc, ) -> Result, Box> { - Ok(Rc::new(Val::Boolean(!try!( - left.equal(right.as_ref(), &self.root.to_string_lossy(), pos.clone()) - )))) + Ok(Rc::new(Val::Boolean(!try!(left.equal( + right.as_ref(), + &self.root.to_string_lossy(), + pos.clone() + ))))) } fn do_gt(&self, pos: &Position, left: Rc, right: Rc) -> Result, Box> { @@ -1064,7 +1062,12 @@ impl Builder { for arg in args.iter() { argvals.push(try!(self.eval_expr(arg))); } - let fields = try!(m.eval(self.root.clone(), self.env.clone(), argvals)); + let fields = try!(m.eval( + self.root.clone(), + self.assets.clone(), + self.env.clone(), + argvals + )); return Ok(Rc::new(Val::Tuple(fields))); } Err(Box::new(error::Error::new( @@ -1127,7 +1130,12 @@ impl Builder { let mut out = Vec::new(); for expr in l.iter() { let argvals = vec![try!(self.eval_expr(expr))]; - let fields = try!(macdef.eval(self.root.clone(), self.env.clone(), argvals)); + let fields = try!(macdef.eval( + self.root.clone(), + self.assets.clone(), + self.env.clone(), + argvals + )); if let Some(v) = Self::find_in_fieldlist(&def.field, &fields) { match def.typ { ListOpType::Map => { diff --git a/src/build/test.rs b/src/build/test.rs index 9af089e..bec7237 100644 --- a/src/build/test.rs +++ b/src/build/test.rs @@ -11,10 +11,12 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +use super::assets::MemoryCache; use super::{Builder, CallDef, MacroDef, SelectDef, Val}; use ast::*; use std; +use std::cell::RefCell; use std::rc::Rc; fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, b: Builder) { @@ -25,7 +27,8 @@ fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, b: Builder) { #[test] fn test_eval_div_expr() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![ ( @@ -54,7 +57,8 @@ fn test_eval_div_expr() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_div_expr_fail() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -71,7 +75,8 @@ fn test_eval_div_expr_fail() { #[test] fn test_eval_mul_expr() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![ ( @@ -100,7 +105,8 @@ fn test_eval_mul_expr() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_mul_expr_fail() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -117,7 +123,8 @@ fn test_eval_mul_expr_fail() { #[test] fn test_eval_subtract_expr() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![ ( @@ -146,7 +153,8 @@ fn test_eval_subtract_expr() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_subtract_expr_fail() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -163,7 +171,8 @@ fn test_eval_subtract_expr_fail() { #[test] fn test_eval_add_expr() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![ ( @@ -235,7 +244,8 @@ fn test_eval_add_expr() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_add_expr_fail() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -327,7 +337,10 @@ fn test_eval_nested_tuple() { )]), ), ], - Builder::new(std::env::current_dir().unwrap()), + Builder::new( + std::env::current_dir().unwrap(), + Rc::new(RefCell::new(MemoryCache::new())), + ), ); } @@ -362,13 +375,17 @@ fn test_eval_simple_expr() { )]), ), ], - Builder::new(std::env::current_dir().unwrap()), + Builder::new( + std::env::current_dir().unwrap(), + Rc::new(RefCell::new(MemoryCache::new())), + ), ); } #[test] fn test_eval_simple_lookup_expr() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("var1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Int(1))); @@ -383,7 +400,8 @@ fn test_eval_simple_lookup_expr() { #[test] fn test_eval_simple_lookup_error() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("var1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Int(1))); @@ -393,7 +411,8 @@ fn test_eval_simple_lookup_error() { #[test] fn test_eval_selector_expr() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("var1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Tuple(vec![( @@ -457,7 +476,8 @@ fn test_eval_selector_expr() { #[test] fn test_eval_selector_list_expr() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("var1".to_string(), 1, 1)) .or_insert(Rc::new(Val::List(vec![ @@ -484,7 +504,8 @@ fn test_eval_selector_list_expr() { #[test] #[should_panic(expected = "Unable to find tpl1")] fn test_expr_copy_no_such_tuple() { - let b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let b = Builder::new(std::env::current_dir().unwrap(), cache); test_expr_to_val( vec![( Expression::Copy(CopyDef { @@ -501,7 +522,8 @@ fn test_expr_copy_no_such_tuple() { #[test] #[should_panic(expected = "Expected Tuple got Int(1)")] fn test_expr_copy_not_a_tuple() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("tpl1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Int(1))); @@ -521,7 +543,8 @@ fn test_expr_copy_not_a_tuple() { #[test] #[should_panic(expected = "Expected type Integer for field fld1 but got String")] fn test_expr_copy_field_type_error() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("tpl1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Tuple(vec![( @@ -549,7 +572,8 @@ fn test_expr_copy_field_type_error() { #[test] fn test_expr_copy() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("tpl1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Tuple(vec![( @@ -620,7 +644,8 @@ fn test_expr_copy() { #[test] fn test_macro_call() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("tstmac".to_string(), 1, 0)) .or_insert(Rc::new(Val::Macro(MacroDef { @@ -654,7 +679,8 @@ fn test_macro_call() { #[test] #[should_panic(expected = "Unable to find arg1")] fn test_macro_hermetic() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("arg1".to_string(), 1, 0)) .or_insert(Rc::new(Val::Str("bar".to_string()))); @@ -690,7 +716,8 @@ fn test_macro_hermetic() { #[test] fn test_select_expr() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("foo".to_string(), 1, 0)) .or_insert(Rc::new(Val::Str("bar".to_string()))); @@ -752,7 +779,8 @@ fn test_select_expr() { #[test] #[should_panic(expected = "Expected String but got Integer in Select expression")] fn test_select_expr_not_a_string() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.build_output .entry(value_node!("foo".to_string(), 1, 0)) .or_insert(Rc::new(Val::Int(4))); @@ -785,7 +813,8 @@ fn test_select_expr_not_a_string() { #[test] fn test_let_statement() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = MemoryCache::new(); + let mut b = Builder::new("", Rc::new(RefCell::new(cache))); let stmt = Statement::Let(LetDef { name: make_tok!("foo", 1, 1), value: Expression::Simple(Value::Str(value_node!("bar".to_string(), 1, 1))), @@ -802,35 +831,9 @@ fn test_let_statement() { #[test] fn test_build_file_string() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); b.eval_string("let foo = 1;").unwrap(); let key = value_node!("foo".to_string(), 1, 0); assert!(b.build_output.contains_key(&key)); } - -#[test] -fn test_asset_symbol_lookups() { - let mut b = Builder::new(std::env::current_dir().unwrap()); - b.assets - .entry(value_node!("foo".to_string(), 1, 0)) - .or_insert(Rc::new(Val::Tuple(vec![( - value_node!("bar".to_string(), 1, 0), - Rc::new(Val::Tuple(vec![( - value_node!("quux".to_string(), 1, 0), - Rc::new(Val::Int(1)), - )])), - )]))); - test_expr_to_val( - vec![( - Expression::Simple(Value::Symbol(value_node!("foo".to_string(), 1, 1))), - Val::Tuple(vec![( - value_node!("bar".to_string(), 1, 0), - Rc::new(Val::Tuple(vec![( - value_node!("quux".to_string(), 1, 0), - Rc::new(Val::Int(1)), - )])), - )]), - )], - b, - ); -} diff --git a/src/convert/exec.rs b/src/convert/exec.rs index 5d53ec6..a58db92 100644 --- a/src/convert/exec.rs +++ b/src/convert/exec.rs @@ -190,14 +190,18 @@ impl Converter for ExecConverter { #[cfg(test)] mod exec_test { use super::*; + use build::assets::MemoryCache; use build::Builder; use convert::traits::Converter; + use std; + use std::cell::RefCell; use std::io::Cursor; #[test] fn convert_just_command_test() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let conv = ExecConverter::new(); b.eval_string( "let script = { @@ -216,7 +220,8 @@ mod exec_test { #[test] fn convert_command_with_env_test() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let conv = ExecConverter::new(); b.eval_string( "let script = { @@ -242,7 +247,8 @@ mod exec_test { #[test] fn convert_command_with_arg_test() { - let mut b = Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut b = Builder::new(std::env::current_dir().unwrap(), cache); let conv = ExecConverter::new(); b.eval_string( "let script = { diff --git a/src/main.rs b/src/main.rs index e0c6ca9..dd9b457 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ extern crate clap; extern crate ucglib; +use std::cell::RefCell; use std::fs::File; use std::io; use std::path::PathBuf; @@ -22,6 +23,7 @@ use std::process; use std::rc::Rc; use ucglib::build; +use ucglib::build::assets::MemoryCache; use ucglib::build::Val; use ucglib::convert::traits; use ucglib::convert::ConverterRunner; @@ -63,7 +65,8 @@ fn main() { let sym = matches.value_of("sym"); let target = matches.value_of("target").unwrap(); let root = PathBuf::from(file); - let mut builder = build::Builder::new(root); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut builder = build::Builder::new(root.parent().unwrap(), cache); match ConverterRunner::new(target) { Ok(converter) => { let result = builder.build_file(file); @@ -94,7 +97,8 @@ fn main() { } } else if let Some(matches) = app.subcommand_matches("validate") { let file = matches.value_of("INPUT").unwrap(); - let mut builder = build::Builder::new(std::env::current_dir().unwrap()); + let cache = Rc::new(RefCell::new(MemoryCache::new())); + let mut builder = build::Builder::new(std::env::current_dir().unwrap(), cache); builder.enable_validate_mode(); builder.build_file(file).unwrap(); println!("File Validates");