Compare commits

..

No commits in common. "master" and "v1.0.3" have entirely different histories.

5 changed files with 129 additions and 213 deletions

View File

@ -1,29 +0,0 @@
name: Publish PHP Package
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get tag name
run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: zip and send
run: |
ls -la
apt-get update -y
apt-get install -y zip
cd ../
zip -r ${{ gitea.event.repository.name }}.zip ${{ gitea.event.repository.name }} -x '*.git*'
curl -v \
--user composer-pbl:${{ secrets.COMPOSER_PSWD }} \
--upload-file ${{ gitea.event.repository.name }}.zip \
https://git.bit5.ru/api/packages/bit/composer?version=${{ env.TAG }}

1
.gitignore vendored
View File

@ -1 +0,0 @@
tags

View File

@ -1,9 +0,0 @@
## v1.4.0
- Disallowing duplicating def macro arguments
## v1.3.3
- Adding experimental support for prefixing of included macros via <%INC("path/to", "prefix")%>
## v1.2.0
- JSM now supports an array of base dirs
- Renaming @builtin into @global for better clarity

View File

@ -6,6 +6,6 @@
"php": ">=7.4" "php": ">=7.4"
}, },
"autoload": { "autoload": {
"files": ["jsm.inc.php"] "classmap": ["jsm.inc.php"]
} }
} }

View File

@ -6,7 +6,7 @@ class JSM
static private $modules = array(); static private $modules = array();
static private $consts = array(); static private $consts = array();
private $base_dirs = array(); private $base_dir;
private $file; private $file;
private $scope_vars = array(array()); private $scope_vars = array(array());
private $includes = array(); private $includes = array();
@ -16,34 +16,29 @@ class JSM
private $cur_modules = array(); private $cur_modules = array();
private $args_parser; private $args_parser;
function __construct($base_dirs/*can be a string for BC*/, $file) function __construct($base_dir, $file)
{ {
//for rand constants //for rand constants
mt_srand(); mt_srand();
if(is_string($base_dirs)) $this->base_dir = rtrim(jsm_normalize_path($base_dir, true/*nix*/), '/');
$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->file = $file;
$this->args_parser = new JSM_ArgsParser(); $this->args_parser = new JSM_ArgsParser();
} }
function getRootFile() : string function getRootFile()
{ {
return $this->file; return $this->file;
} }
static function getModule($file) : JSM_Module function getModule($file)
{ {
if(isset(self::$modules[$file])) if(isset(self::$modules[$file]))
return self::$modules[$file]; return self::$modules[$file];
throw new Exception("Module for '$file' not found"); throw new Exception("Module for '$file' not found");
} }
function _extractModule($file) : JSM_Module function _extractModule($file)
{ {
$txt = file_get_contents($file); $txt = file_get_contents($file);
if($txt === false) if($txt === false)
@ -54,12 +49,11 @@ class JSM
$m->node = new JSM_MacroDefNode($file/*using file as name*/); $m->node = new JSM_MacroDefNode($file/*using file as name*/);
$this->_pushCurrentModule($m); $this->_pushCurrentModule($m);
$txt = self::_fixJunk($txt); $txt = $this->_fixJunk($txt);
$txt = self::_removeBlockComments($txt);
$txt = self::_removeLineComments($txt);
$txt = $this->_processIncludes($file, $txt); $txt = $this->_processIncludes($file, $txt);
$txt = $this->_removeBlockComments($txt);
$txt = $this->_removeLineComments($txt);
$txt = $this->_extractScriptDefs($file, $txt); $txt = $this->_extractScriptDefs($file, $txt);
$this->_parseDefBody($m->node, $file, $txt); $this->_parseDefBody($m->node, $file, $txt);
$this->_popCurrentModule(); $this->_popCurrentModule();
@ -80,7 +74,7 @@ class JSM
return $m; return $m;
} }
function _currentModule() : JSM_Module function _currentModule()
{ {
$m = end($this->cur_modules); $m = end($this->cur_modules);
if(!$m) if(!$m)
@ -88,13 +82,12 @@ class JSM
return $m; return $m;
} }
function getCurrentModule() : JSM_Module function getCurrentModule()
{ {
return $this->_currentModule(); return $this->_currentModule();
} }
//NOTE: returns [json, module] function process()
function process() : array
{ {
$m = $this->_extractModule($this->file); $m = $this->_extractModule($this->file);
@ -104,17 +97,9 @@ class JSM
$this->defs = $m->defs; $this->defs = $m->defs;
$this->includes = $m->includes; $this->includes = $m->includes;
$res = '';
if($m->node->raw_text_node)
{
$res = $m->node->raw_text_node->txt;
}
else
{
$this->_pushBuffer(); $this->_pushBuffer();
$m->node->call($this); $m->node->call($this);
$res = $this->_popBuffer(); $res = $this->_popBuffer();
}
if($this->buffers) if($this->buffers)
throw new Exception("Non closed buffers " . sizeof($this->buffers)); throw new Exception("Non closed buffers " . sizeof($this->buffers));
@ -243,63 +228,61 @@ class JSM
throw new Exception("Var '$name' not resolved"); throw new Exception("Var '$name' not resolved");
} }
private static function _fixJunk(string $txt) : string private function _fixJunk($txt)
{ {
//windows eol //windows eol
$txt = str_replace("\r\n", "\n", $txt); $txt = str_replace("\r\n", "\n", $txt);
return $txt; return $txt;
} }
private static function _removeBlockComments(string $txt) : string private function _removeBlockComments($txt)
{ {
if(strpos($txt, '/*') === false) if(strpos($txt, '/*') === false)
return $txt; return $txt;
$regex = '~/\*.*?\*/~s';
$txt = preg_replace_callback( $txt = preg_replace_callback(
'~/\*.*?\*/~s', $regex,
//preserve the new lines for better error reporting //preserve the new lines for better error reporting
function($m) { return str_repeat("\n", substr_count($m[0], "\n")); }, function($m) { return str_repeat("\n", substr_count($m[0], "\n")); },
$txt); $txt);
return $txt; return $txt;
} }
private static function _removeLineComments(string $txt) : string private function _removeLineComments($txt)
{ {
//TODO: it's not robust enough, note a hack for URL addresses //TODO: it's not robust enough, note a hack for URL addresses
$txt = preg_replace_callback( $txt = preg_replace("~\s*(?<!:)//.*~", "\n", $txt);
'~\s*(?<!:)//.*~',
//preserve the new lines for better error reporting
function($m) { return str_repeat("\n", substr_count($m[0], "\n")); },
$txt);
return $txt; return $txt;
} }
/*private */function _relpathCallback(array $m, string $curr_dir) : string /*private */function _relpathCallback(array $m, $curr_dir)
{ {
//1) checking if such a file exists and normalizing all .. in the path //1) checking if such a file exists and normalizing all .. in the path
$full_path = realpath($curr_dir . '/' . $m[1]); $full_path = realpath($curr_dir . '/' . $m[1]);
if(!$full_path) if(!$full_path)
throw new Exception("Bad relative path '$m[1]'"); throw new Exception("Bad relative path '$m[1]'");
//2) now turning the path into *nix alike //2) now turning the path into *nix alike
$rel_path = jsm_make_rel_path($this->base_dirs, $full_path); $full_path = jsm_normalize_path($full_path, true/*nix*/);
$rel_path = ltrim(str_replace($this->base_dir, '', $full_path), '/');
return $rel_path; return $rel_path;
} }
/*private */function _relconfCallback(array $m, string $curr_dir) : string /*private */function _relconfCallback(array $m, $curr_dir)
{ {
//1) checking if such a file exists and normalizing all .. in the path //1) checking if such a file exists and normalizing all .. in the path
$full_path = realpath($curr_dir . '/' . $m[1] . '.conf.js'); $full_path = realpath($curr_dir . '/' . $m[1] . '.conf.js');
if(!$full_path) if(!$full_path)
throw new Exception("Bad relative path '$m[1]'" . ($curr_dir . '/' . $m[1])); throw new Exception("Bad relative path '$m[1]'" . ($curr_dir . '/' . $m[1]));
$rel_path = jsm_make_rel_path($this->base_dirs, $full_path); $full_path = jsm_normalize_path($full_path, true/*nix*/);
$rel_path = ltrim(str_replace($this->base_dir, '', $full_path), '/');
$rel_path = str_replace('.conf.js', '', $rel_path); $rel_path = str_replace('.conf.js', '', $rel_path);
return "@$rel_path"; return "@$rel_path";
} }
private function _processRelpaths(string $file, string $txt) : string private function _processRelpaths($file, $txt)
{ {
//normalizing all file paths //normalizing all file paths
if(strpos($txt, './') !== false) if(strpos($txt, './') !== false)
@ -318,19 +301,21 @@ class JSM
return $txt; return $txt;
} }
/*private */function _includesCallback(string $inc_path, string $prefix, string $curr_file) : string /*private */function _includesCallback(array $m, $curr_file)
{ {
$file = jsm_resolve_inc_path($this->base_dirs, $curr_file, $inc_path); $file_name = $m[1];
$dir = ($file_name[0] == '/') ? $this->base_dir : dirname($curr_file);
$file = jsm_normalize_path($dir . '/' . $m[1]);
$cm = $this->_currentModule(); $cm = $this->_currentModule();
if(!isset(self::$modules[$file])) if(!isset(self::$modules[$file]))
{ {
if(self::_hasExtension($file, '.js')) if($this->_hasExtension($file, '.js'))
{ {
self::$modules[$file] = $this->_extractModule($file); self::$modules[$file] = $this->_extractModule($file);
} }
else if(self::_hasExtension($file, '.php')) else if($this->_hasExtension($file, '.php'))
{ {
include_once($file); include_once($file);
//special mark for PHP include //special mark for PHP include
@ -350,11 +335,8 @@ class JSM
{ {
foreach($m->includes as $f => $_) foreach($m->includes as $f => $_)
$cm->includes[$f] = 1; $cm->includes[$f] = 1;
foreach($m->defs as $n => $d)
foreach($m->defs as $_n => $d)
{ {
$n = $prefix.$_n;
if(isset($cm->defs[$n]) && $cm->defs[$n] !== $d) if(isset($cm->defs[$n]) && $cm->defs[$n] !== $d)
throw new Exception("Def '$n' is already defined in '{$cm->file}' (check {$cm->defs[$n]->file})"); throw new Exception("Def '$n' is already defined in '{$cm->file}' (check {$cm->defs[$n]->file})");
$cm->defs[$n] = $d; $cm->defs[$n] = $d;
@ -366,53 +348,68 @@ class JSM
return $need_include_macro ? "<%INC($file)%>" : ''; return $need_include_macro ? "<%INC($file)%>" : '';
} }
private function _processIncludes(string $file, string $txt) : string private function _processIncludes($file, $txt)
{ {
if(strpos($txt, 'INC') !== false) if(strpos($txt, 'INC') !== false)
{ {
$self = $this; $self = $this;
$txt = preg_replace_callback( $txt = preg_replace_callback(
'~<%\s*INC\s*\(\s*"([^"]+)"\s*(?:,\s*"([^"]+)")?\s*\)\s*%>~', '~<%\s*INC\s*\(\s*"([^"]+)"\s*\)\s*%>~',
function($m) use($self, $file) { return $self->_includesCallback($m[1], isset($m[2]) ? $m[2] : '', $file); }, function($m) use($self, $file) { return $self->_includesCallback($m, $file); },
$txt); $txt);
} }
return $txt; return $txt;
} }
function _isIncluded(string $file) : bool function _isIncluded($file)
{ {
$file = jsm_normalize_path($file); $file = jsm_normalize_path($file);
return isset($this->includes[$file]); return isset($this->includes[$file]);
} }
static function _hasExtension(string $file, string $ext) : bool function _hasExtension($file, $ext)
{ {
return strpos($file, $ext, strlen($file) - strlen($ext) - 1) !== false; return strpos($file, $ext, strlen($file) - strlen($ext) - 1) !== false;
} }
static function getDeps(array $base_dirs, string $file) : array static function getDeps($base_dir, $file)
{ {
$deps = array(); $deps = array();
self::_getDeps($base_dirs, $file, $deps); self::_getDeps($base_dir, $file, $deps);
return $deps; return $deps;
} }
static private function _extractDeps(string $txt) : array static private function _extractDeps($txt)
{ {
$dep_files = array(); $dep_files = array();
if(preg_match_all('~<%\s*INC\s*\(\s*"([^\n]+)~', $txt, $ms)) if(preg_match_all('~<%\s*((?:INC)|(?:BHL))\s*\(\s*"([^\n]+)~', $txt, $ms))
{ {
foreach($ms[1] as $raw_dep) foreach($ms[1] as $idx => $type)
{
$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_file = substr($raw_dep, 0, strpos($raw_dep, '"'));
$dep_files[$dep_file] = true; $dep_files[$dep_file] = false;
} }
} }
return array_keys($dep_files); }
return $dep_files;
} }
static private function _getDeps(array $base_dirs, string $file, array &$deps) static private function _getDeps($base_dir, $file, &$deps)
{ {
static $cache = array(); static $cache = array();
@ -427,25 +424,31 @@ class JSM
$extracted_deps = self::_extractDeps($txt); $extracted_deps = self::_extractDeps($txt);
foreach($extracted_deps as $inc) foreach($extracted_deps as $inc => $is_bhl)
{ {
$dep_file = jsm_resolve_inc_path($base_dirs, $file, $inc); $dir = ($inc[0] == '/') ? $base_dir : dirname($file);
$local_deps[] = $dep_file; $dep_file = jsm_normalize_path($dir . "/" . $inc);
$local_deps[] = array($is_bhl, $dep_file);
$cache[$file] = $local_deps; $cache[$file] = $local_deps;
} }
} }
else else
$local_deps = $cache[$file]; $local_deps = $cache[$file];
foreach($local_deps as $dep_file) foreach($local_deps as $local_dep)
{ {
list($is_bhl, $dep_file) = $local_dep;
if(in_array($dep_file, $deps)) if(in_array($dep_file, $deps))
continue; continue;
$deps[] = $dep_file; $deps[] = $dep_file;
//NOTE: bhl deps are shallow
if(!$is_bhl)
{
try try
{ {
self::_getDeps($base_dirs, $dep_file, $deps); self::_getDeps($base_dir, $dep_file, $deps);
} }
catch(Exception $e) catch(Exception $e)
{ {
@ -453,8 +456,9 @@ class JSM
} }
} }
} }
}
private function _extractDefNameAndArgs(string $full_name) : array private function _extractDefNameAndArgs($full_name)
{ {
$pos = strpos($full_name, '('); $pos = strpos($full_name, '(');
if($pos == -1) if($pos == -1)
@ -467,14 +471,14 @@ class JSM
return array($name, $args); return array($name, $args);
} }
private function _parseMacroDefArgs(string $args_str) private function _parseMacroDefArgs($args_str)
{ {
if(!$args_str) if(!$args_str)
return array(); return array();
return $this->args_parser->parseDef($args_str); return $this->args_parser->parseDef($args_str);
} }
function nodes2str($nodes_or_str) : string function nodes2str($nodes_or_str)
{ {
$str = ''; $str = '';
if(is_array($nodes_or_str)) if(is_array($nodes_or_str))
@ -491,13 +495,13 @@ class JSM
return $str; return $str;
} }
function _processMacroArgs($arg_nodes_or_str) : array function _processMacroArgs($arg_nodes_or_str)
{ {
$str = $this->nodes2str($arg_nodes_or_str); $str = $this->nodes2str($arg_nodes_or_str);
return $this->_parseMacroArgsStr($str); return $this->_parseMacroArgsStr($str);
} }
function _parseMacroArgsStr($orig_args_str) : array function _parseMacroArgsStr($orig_args_str)
{ {
static $cache = array(); static $cache = array();
@ -509,8 +513,8 @@ class JSM
if($may_cache && isset($cache[$orig_args_str])) if($may_cache && isset($cache[$orig_args_str]))
return $cache[$orig_args_str]; return $cache[$orig_args_str];
$args_str = self::_removeBlockComments($orig_args_str); $args_str = $this->_removeBlockComments($orig_args_str);
$args_str = self::_removeLineComments($args_str); $args_str = $this->_removeLineComments($args_str);
$args_str = rtrim($args_str, ','); $args_str = rtrim($args_str, ',');
$res = $this->args_parser->parseInvoke($args_str); $res = $this->args_parser->parseInvoke($args_str);
@ -521,14 +525,9 @@ class JSM
return $res; return $res;
} }
private function _extractScriptDefs(string $file, string $txt) : string private function _extractScriptDefs($file, $txt)
{ {
$strpos = strpos($txt, 'def'); if(strpos($txt, 'def') === false)
if($strpos === false)
return $txt;
//let's extra check if newline symbols preceed 'def'
if($strpos > 0 && !($txt[$strpos-1] === "\n" || $txt[$strpos-1] === "\r"))
return $txt; return $txt;
//NOTE: make it more robust //NOTE: make it more robust
@ -567,11 +566,8 @@ class JSM
return $txt; return $txt;
} }
private function _replaceVarsWithMacro(string $file, string $txt) : string private function _replaceVarsWithMacro($file, $txt)
{ {
if(strpos($txt, '$') === false)
return $txt;
//simple vals //simple vals
$txt = preg_replace( $txt = preg_replace(
'~\{(\$[a-zA-Z0-9_]+)\}~', '~\{(\$[a-zA-Z0-9_]+)\}~',
@ -587,16 +583,13 @@ class JSM
return $txt; return $txt;
} }
private function _parseDefBody(JSM_MacroDefNode $node, string $file, string $txt) private function _parseDefBody(JSM_MacroDefNode $node, $file, $txt)
{ {
$txt = $this->_replaceVarsWithMacro($file, $txt); $txt = $this->_replaceVarsWithMacro($file, $txt);
//let's exit early if there are no macro calls
if(strpos($txt, '<%') === false) if(strpos($txt, '<%') === false)
{ {
$raw_text_node = new JSM_TextNode($txt); $node->addChild(new JSM_TextNode($txt));
$node->raw_text_node = $raw_text_node;
$node->addChild($raw_text_node);
return; return;
} }
@ -623,7 +616,7 @@ class JSM
$mcall = $this->_newCall($name); $mcall = $this->_newCall($name);
if($mcall === null) if($mcall === null)
throw new Exception("No macro for '$name'"); throw new Exception("No node for '$name'");
$this->_pushNode($mcall); $this->_pushNode($mcall);
} }
@ -634,7 +627,7 @@ class JSM
throw new Exception("Non paired macro ending"); throw new Exception("Non paired macro ending");
$tmp_node = $this->_node(false/*non strict*/); $tmp_node = $this->_node(false/*non strict*/);
if($tmp_node == null) if($tmp_node == null)
throw new Exception("No current macro (prev. " . $mcall->name . ")"); throw new Exception("No current node(prev. node " . $mcall->name . ")");
$tmp_node->addChild($mcall); $tmp_node->addChild($mcall);
} }
else else
@ -652,7 +645,7 @@ class JSM
$this->_popNode(); $this->_popNode();
} }
function _newCall(string $name) function _newCall($name)
{ {
$func = "macro_ex_{$name}"; $func = "macro_ex_{$name}";
if(is_callable($func)) if(is_callable($func))
@ -665,7 +658,7 @@ class JSM
} }
} }
function _getMacro(string $name) : JSM_MacroUserNode function _getMacro($name)
{ {
if(isset($this->defs[$name])) if(isset($this->defs[$name]))
return $this->defs[$name]; return $this->defs[$name];
@ -683,23 +676,22 @@ class JSM
class JSM_Module class JSM_Module
{ {
var string $file = ''; var $file = '';
var $defs = array(); var $defs = array();
//NOTE: all includes (even those included from other modules)
var $includes = array(); var $includes = array();
var $node = null; var $node = null;
function __construct(string $file) function __construct($file)
{ {
$this->file = $file; $this->file = $file;
} }
function getIncludes() : array function getIncludes()
{ {
return $this->includes; return $this->includes;
} }
function getFile() : string function getFile()
{ {
return $this->file; return $this->file;
} }
@ -710,11 +702,6 @@ interface JSM_MacroNode
function call(JSM $jsm); function call(JSM $jsm);
} }
interface JSM_MacroUserNode extends JSM_MacroNode
{
function setUserArgs(array $args);
}
interface JSM_MacroInternalNode extends JSM_MacroNode interface JSM_MacroInternalNode extends JSM_MacroNode
{ {
function addChild(JSM_MacroNode $node); function addChild(JSM_MacroNode $node);
@ -774,14 +761,14 @@ class JSM_MacroCallNode implements JSM_MacroInternalNode
} }
} }
class JSM_MacroPHPNode implements JSM_MacroUserNode class JSM_MacroPHPNode implements JSM_MacroNode
{ {
var $func; var $func;
var $refl; var $refl;
var $user_args = ''; var $user_args = '';
var $is_global; var $is_builtin;
var $is_eval_args; //arguments will be evaluated and eval result will be passed var $is_eval_args; //arguments will be evaluated and eval result will be passed
var $is_raw_args; //arguments result string will be passed, not evaluated var $is_raw_args; //arguments result string will be passed, not evaluated
var $is_node_args; //argument nodes will be passed as is var $is_node_args; //argument nodes will be passed as is
@ -796,7 +783,7 @@ class JSM_MacroPHPNode implements JSM_MacroUserNode
$this->refl = new ReflectionFunction($php_func); $this->refl = new ReflectionFunction($php_func);
$doc = $this->refl->getDocComment(); $doc = $this->refl->getDocComment();
$this->is_global = strpos($doc, '@global') !== false; $this->is_builtin = strpos($doc, '@builtin') !== false;
$this->is_raw_args = strpos($doc, '@raw_args') !== false; $this->is_raw_args = strpos($doc, '@raw_args') !== false;
$this->is_eval_args = strpos($doc, '@eval_args') !== false; $this->is_eval_args = strpos($doc, '@eval_args') !== false;
$this->is_no_args = strpos($doc, '@no_args') !== false; $this->is_no_args = strpos($doc, '@no_args') !== false;
@ -817,9 +804,8 @@ class JSM_MacroPHPNode implements JSM_MacroUserNode
function call(JSM $jsm) function call(JSM $jsm)
{ {
//NOTE: making sure file containing PHP macro was actually included by this file //NOTE: making sure file containing PHP macro was actually included by this file
// if it's not a @global one if(!$this->is_builtin && !$jsm->_isIncluded($this->refl->getFileName()))
if(!$this->is_global && !$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; $named = false;
$args = array(); $args = array();
@ -886,14 +872,13 @@ class JSM_MacroPHPNode implements JSM_MacroUserNode
} }
} }
class JSM_MacroDefNode implements JSM_MacroInternalNode,JSM_MacroUserNode class JSM_MacroDefNode implements JSM_MacroInternalNode
{ {
var $name = ''; var $name = '';
var $file = ''; var $file = '';
var $decl_args = array(); var $decl_args = array();
var $node_args = array(); var $node_args = array();
var $children = array(); var $children = array();
var $raw_text_node = null;
function __construct($name, $decl_args = array(), $file = '') function __construct($name, $decl_args = array(), $file = '')
{ {
@ -1130,6 +1115,7 @@ class JSM_Eval
else else
{ {
throw new Exception("Unknown func " . $func); throw new Exception("Unknown func " . $func);
return false;
} }
} }
} }
@ -1351,8 +1337,8 @@ class JSM_Expr
case ' ': case "\r": case "\t": case "\n" : break; case ' ': case "\r": case "\t": case "\n" : break;
case '(': $token = array(self::T_LParen, $c); $binary = false; break; case '(': $token = array(self::T_LParen, $c); $binary = false; break;
case ')': $token = array(self::T_RParen, $c); $binary = true; break; case ')': $token = array(self::T_RParen, $c); $binary = true; break;
case '+': $token = $this->_getFuncToken(($binary ? '+' : 'u+')); $binary = false; break; case '+': $token = $this->_getFuncToken(($binary ? '+' : 'u+'), $c); $binary = false; break;
case '-': $token = $this->_getFuncToken(($binary ? '-' : 'u-')); $binary = false; break; case '-': $token = $this->_getFuncToken(($binary ? '-' : 'u-'), $c); $binary = false; break;
default: default:
//try to peek the next symbol and check if such an operator exists //try to peek the next symbol and check if such an operator exists
@ -1407,7 +1393,7 @@ function jsm_lex($string, array $tokenMap)
{ {
foreach($tokenMap as $regex => $token) foreach($tokenMap as $regex => $token)
{ {
if(preg_match($regex, $string, $matches, 0, $offset)) if(preg_match($regex, $string, $matches, null, $offset))
{ {
$tokens[] = array( $tokens[] = array(
$token, // token ID (e.g. T_FIELD_SEPARATOR) $token, // token ID (e.g. T_FIELD_SEPARATOR)
@ -1452,36 +1438,6 @@ function jsm_normalize_path($path, $unix=null/*null means try to guess*/)
return $res; 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) function jsm_eval_string(JSM $jsm, $expr_str)
{ {
$expr = new JSM_Expr(); $expr = new JSM_Expr();
@ -1595,8 +1551,6 @@ class JSM_ArgsParser
{ {
$this->skip_whitespace(); $this->skip_whitespace();
$value = $this->parse_arg_value(); $value = $this->parse_arg_value();
if(isset($out[$name]))
throw new Exception("Argument '{$name}' is already defined in def macro");
$out[$name] = $value; $out[$name] = $value;
$this->skip_whitespace(); $this->skip_whitespace();
$ch = $this->next(); $ch = $this->next();
@ -1782,11 +1736,10 @@ class JSM_ArgsParser
++$this->c; ++$this->c;
} }
$str_num = substr($this->in, $start, $this->c - $start);
if($is_float) if($is_float)
return floatval($str_num); return 1*substr($this->in, $start, $this->c - $start);
else else
return intval($str_num); return (int)(1*substr($this->in, $start, $this->c - $start));
} }
private function _error($error) private function _error($error)
@ -1827,20 +1780,22 @@ class JSM_ArgsParser
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
/** /**
* @global @raw_args * @builtin @raw_args
*/ */
function macro_INC($jsm, $file_and_prefix) function macro_DEP($jsm)
{}
/**
* @builtin @raw_args
*/
function macro_INC($jsm, $file)
{ {
$items = explode(',', $file_and_prefix);
//NOTE: we don't care about prefix here, it's handled
// during includes processing
$file = trim($items[0]);
$m = $jsm->getModule($file); $m = $jsm->getModule($file);
$m->node->call($jsm); $m->node->call($jsm);
} }
/** /**
* @global @raw_args * @builtin @raw_args
*/ */
function macro_LET($jsm, $txt_args) function macro_LET($jsm, $txt_args)
{ {
@ -1850,7 +1805,7 @@ function macro_LET($jsm, $txt_args)
} }
/** /**
* @global @raw_args * @builtin @raw_args
*/ */
function macro_VAL($jsm, $txt_args) function macro_VAL($jsm, $txt_args)
{ {
@ -1871,7 +1826,7 @@ function macro_VAL($jsm, $txt_args)
//simple version of variable echoing //simple version of variable echoing
/** /**
* @global @raw_args * @builtin @raw_args
*/ */
function macro_SVAL($jsm, $txt_args) function macro_SVAL($jsm, $txt_args)
{ {
@ -1879,7 +1834,7 @@ function macro_SVAL($jsm, $txt_args)
} }
/** /**
* @global * @builtin
*/ */
function macro_CONST($jsm, $k, $v) function macro_CONST($jsm, $k, $v)
{ {
@ -2072,7 +2027,7 @@ function macro_ex_ENDREP(JSM $jsm)
} }
/** /**
* @global @raw_args * @builtin @raw_args
*/ */
function macro_CALL($proc, $raw_args) function macro_CALL($proc, $raw_args)
{ {
@ -2084,7 +2039,7 @@ function macro_CALL($proc, $raw_args)
} }
/** /**
* @global @eval_args * @builtin @eval_args
*/ */
function macro_TRACE($jsm, array $eval_res) function macro_TRACE($jsm, array $eval_res)
{ {
@ -2092,7 +2047,7 @@ function macro_TRACE($jsm, array $eval_res)
} }
/** /**
* @global @raw_args * @builtin @raw_args
*/ */
function macro_LET_IF($jsm, $raw_args) function macro_LET_IF($jsm, $raw_args)
{ {