From e5507303cd9cea34080749924311e43b5e42b983 Mon Sep 17 00:00:00 2001 From: Pavel Shevaev Date: Thu, 25 May 2023 18:02:16 +0300 Subject: [PATCH] JSM now supports an array of base dirs --- .gitignore | 1 + jsm.inc.php | 121 ++++++++++++++++++++++++++++------------------------ 2 files changed, 67 insertions(+), 55 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e92f57 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tags diff --git a/jsm.inc.php b/jsm.inc.php index dee53d7..c0d9b39 100644 --- a/jsm.inc.php +++ b/jsm.inc.php @@ -6,7 +6,7 @@ class JSM static private $modules = array(); static private $consts = array(); - private $base_dir; + private $base_dirs = array(); private $file; private $scope_vars = array(array()); private $includes = array(); @@ -16,12 +16,17 @@ class JSM private $cur_modules = array(); private $args_parser; - function __construct($base_dir, $file) + function __construct($base_dirs/*can be a string for BC*/, $file) { //for rand constants mt_srand(); - $this->base_dir = rtrim(jsm_normalize_path($base_dir, true/*nix*/), '/'); + if(is_string($base_dirs)) + $base_dirs = array($base_dirs); + $this->base_dirs = array_map( + function($base_dir) { return rtrim(jsm_normalize_path($base_dir, true/*nix*/), '/'); }, + $base_dirs + ); $this->file = $file; $this->args_parser = new JSM_ArgsParser(); } @@ -261,9 +266,9 @@ class JSM $full_path = realpath($curr_dir . '/' . $m[1]); if(!$full_path) throw new Exception("Bad relative path '$m[1]'"); + //2) now turning the path into *nix alike - $full_path = jsm_normalize_path($full_path, true/*nix*/); - $rel_path = ltrim(str_replace($this->base_dir, '', $full_path), '/'); + $rel_path = jsm_make_rel_path($this->base_dirs, $full_path); return $rel_path; } @@ -275,8 +280,7 @@ class JSM if(!$full_path) throw new Exception("Bad relative path '$m[1]'" . ($curr_dir . '/' . $m[1])); - $full_path = jsm_normalize_path($full_path, true/*nix*/); - $rel_path = ltrim(str_replace($this->base_dir, '', $full_path), '/'); + $rel_path = jsm_make_rel_path($this->base_dirs, $full_path); $rel_path = str_replace('.conf.js', '', $rel_path); return "@$rel_path"; @@ -301,11 +305,9 @@ class JSM return $txt; } - /*private */function _includesCallback(array $m, $curr_file) + /*private */function _includesCallback($inc_path, $curr_file) { - $file_name = $m[1]; - $dir = ($file_name[0] == '/') ? $this->base_dir : dirname($curr_file); - $file = jsm_normalize_path($dir . '/' . $m[1]); + $file = jsm_resolve_inc_path($this->base_dirs, $curr_file, $inc_path); $cm = $this->_currentModule(); @@ -355,7 +357,7 @@ class JSM $self = $this; $txt = preg_replace_callback( '~<%\s*INC\s*\(\s*"([^"]+)"\s*\)\s*%>~', - function($m) use($self, $file) { return $self->_includesCallback($m, $file); }, + function($m) use($self, $file) { return $self->_includesCallback($m[1], $file); }, $txt); } @@ -373,43 +375,28 @@ class JSM return strpos($file, $ext, strlen($file) - strlen($ext) - 1) !== false; } - static function getDeps($base_dir, $file) + static function getDeps(array $base_dirs, $file) { $deps = array(); - self::_getDeps($base_dir, $file, $deps); + self::_getDeps($base_dirs, $file, $deps); return $deps; } static private function _extractDeps($txt) { $dep_files = array(); - if(preg_match_all('~<%\s*((?:INC)|(?:BHL))\s*\(\s*"([^\n]+)~', $txt, $ms)) + if(preg_match_all('~<%\s*(?:INC)\s*\(\s*"([^\n]+)~', $txt, $ms)) { - foreach($ms[1] as $idx => $type) + foreach($ms[1] as $raw_dep) { - $raw_dep = $ms[2][$idx]; - - $is_bhl = $type === "BHL"; - if($is_bhl) - { - $bhl_imps = explode(',', str_replace('"', '', $raw_dep)); - foreach($bhl_imps as $bhl_imp) - { - if($bhl_imp) - $dep_files[$bhl_imp . '.bhl'] = true; - } - } - else - { - $dep_file = substr($raw_dep, 0, strpos($raw_dep, '"')); - $dep_files[$dep_file] = false; - } + $dep_file = substr($raw_dep, 0, strpos($raw_dep, '"')); + $dep_files[$dep_file] = true; } } - return $dep_files; + return array_keys($dep_files); } - static private function _getDeps($base_dir, $file, &$deps) + static private function _getDeps(array $base_dirs, $file, &$deps) { static $cache = array(); @@ -424,36 +411,29 @@ class JSM $extracted_deps = self::_extractDeps($txt); - foreach($extracted_deps as $inc => $is_bhl) + foreach($extracted_deps as $inc) { - $dir = ($inc[0] == '/') ? $base_dir : dirname($file); - $dep_file = jsm_normalize_path($dir . "/" . $inc); - $local_deps[] = array($is_bhl, $dep_file); + $dep_file = jsm_resolve_inc_path($base_dirs, $file, $inc); + $local_deps[] = $dep_file; $cache[$file] = $local_deps; } } else $local_deps = $cache[$file]; - foreach($local_deps as $local_dep) + foreach($local_deps as $dep_file) { - list($is_bhl, $dep_file) = $local_dep; - if(in_array($dep_file, $deps)) continue; $deps[] = $dep_file; - //NOTE: bhl deps are shallow - if(!$is_bhl) + try { - try - { - self::_getDeps($base_dir, $dep_file, $deps); - } - catch(Exception $e) - { - throw new Exception("Bad deps for '$file' : " . $e->getMessage()); - } + self::_getDeps($base_dirs, $dep_file, $deps); + } + catch(Exception $e) + { + throw new Exception("Bad deps for '$file' : " . $e->getMessage()); } } } @@ -587,6 +567,7 @@ class JSM { $txt = $this->_replaceVarsWithMacro($file, $txt); + //let's exit early if there are no macro calls if(strpos($txt, '<%') === false) { $node->addChild(new JSM_TextNode($txt)); @@ -616,7 +597,7 @@ class JSM $mcall = $this->_newCall($name); if($mcall === null) - throw new Exception("No node for '$name'"); + throw new Exception("No macro for '$name'"); $this->_pushNode($mcall); } @@ -627,7 +608,7 @@ class JSM throw new Exception("Non paired macro ending"); $tmp_node = $this->_node(false/*non strict*/); if($tmp_node == null) - throw new Exception("No current node(prev. node " . $mcall->name . ")"); + throw new Exception("No current macro (prev. " . $mcall->name . ")"); $tmp_node->addChild($mcall); } else @@ -805,7 +786,7 @@ class JSM_MacroPHPNode implements JSM_MacroNode { //NOTE: making sure file containing PHP macro was actually included by this file if(!$this->is_builtin && !$jsm->_isIncluded($this->refl->getFileName())) - throw new Exception("Not found"); + throw new Exception("Macro source file was not included by config file itself: ".$this->refl->getFileName()); $named = false; $args = array(); @@ -1438,6 +1419,36 @@ function jsm_normalize_path($path, $unix=null/*null means try to guess*/) return $res; } +function jsm_resolve_inc_path(array $base_dirs, $curr_file, $inc_path) +{ + return jsm_normalize_path( + $inc_path[0] == '/' ? + jsm_make_full_path($base_dirs, $inc_path) : + dirname($curr_file) . '/' . $inc_path + ); +} + +function jsm_make_full_path(array $base_dirs, $rel_path) +{ + foreach($base_dirs as $dir) + if(is_file($dir . '/' . $rel_path)) + return $dir . '/' . $rel_path; + throw new Exception("No file for relative path '$rel_path'"); +} + +function jsm_make_rel_path(array $base_dirs, $full_path) +{ + $full_path = jsm_normalize_path($full_path, true/*nix*/); + + foreach($base_dirs as $base_dir) + { + $rel_path = str_replace($base_dir, '', $full_path); + if(strlen($rel_path) < strlen($full_path)) + return ltrim($rel_path, '/'); + } + throw new Exception("File '$full_path' not mapped to any base dir"); +} + function jsm_eval_string(JSM $jsm, $expr_str) { $expr = new JSM_Expr();