So what exactly does Julia do with the statement using Foo if you don't have package Foo installed? As I understood Julia starts searching JULIA_LOAD_PATH, but how?
At the root level of JULIA_LOAD_PATH there must be a directory named Foo.jl where the Foo part may be case insensitive and the .jl suffix is optional?
And within this Foo.jl directory there must be a source file name Foo.jl with a module Foo?
using implicitly calls require which indirectly calls find_in_path:
function find_in_path(name::AbstractString, wd = pwd())
isabspath(name) && return name
base = name
# this is why `.jl` suffix is optional
if endswith(name,".jl")
base = name[1:end-3]
else
name = string(base,".jl")
end
if wd !== nothing
isfile(joinpath(wd,name)) && return joinpath(wd,name)
end
for prefix in [Pkg.dir(); LOAD_PATH]
path = joinpath(prefix, name)
isfile(path) && return abspath(path)
path = joinpath(prefix, base, "src", name)
isfile(path) && return abspath(path)
path = joinpath(prefix, name, "src", name)
isfile(path) && return abspath(path)
end
return nothing
end
The source code above shows that there is no additional manipulation on name, which means the Foo part should be case sensitive(Currently depend on the filesystem, see the comment below). And the directory name is unnecessary to be compatible with your file name, it can be anything as long as the directory is in your LOAD_PATH.
Related
I have a script written in Lua 5.1 that imports third-party module and calls some functions from it. I would like to get a list of function calls from a module with their arguments (when they are known before execution).
So, I need to write another script which takes the source code of my first script, parses it, and extracts information from its code.
Consider the minimal example.
I have the following module:
local mod = {}
function mod.foo(a, ...)
print(a, ...)
end
return mod
And the following driver code:
local M = require "mod"
M.foo('a', 1)
M.foo('b')
What is the better way to retrieve the data with the "use" occurrences of the M.foo function?
Ideally, I would like to get the information with the name of the function being called and the values of its arguments. From the example code above, it would be enough to get the mapping like this: {'foo': [('a', 1), ('b')]}.
I'm not sure if Lua has functions for reflection to retrieve this information. So probably I'll need to use one of the existing parsers for Lua to get the complete AST and find the function calls I'm interested in.
Any other suggestions?
If you can not modify the files, you can read the files into a strings then parse mod file and find all functions in it, then use that information to parse the target file for all uses of the mod library
functions = {}
for func in modFile:gmatch("function mod%.(%w+)") do
functions[func] = {}
end
for func, call in targetFile:gmatch("M%.(%w+)%(([^%)]+)%)") do
args = {}
for arg in string.gmatch(call, "([^,]+)") do
table.insert(args, arg)
end
table.insert(functions[func], args)
end
Resulting table can then be serialized
['foo'] = {{"'a'", " 1"}, {"'b'"}}
3 possible gotchas:
M is not a very unique name and could vary possibly match unintended function calls to another library.
This example does not handle if there is a function call made inside the arg list. e.g. myfunc(getStuff(), true)
The resulting table does not know the typing of the args so they are all save as strings representations.
If modifying the target file is an option you can create a wrapper around your required module
function log(mod)
local calls = {}
local wrapper = {
__index = function(_, k)
if mod[k] then
return function(...)
calls[k] = calls[k] or {}
table.insert(calls[k], {...})
return mod[k](...)
end
end
end,
}
return setmetatable({},wrapper), calls
end
then you use this function like so.
local M, calls = log(require("mod"))
M.foo('a', 1)
M.foo('b')
If your module is not just functions you would need to handle that in the wrapper, this wrapper assumes all indexes are a function.
after all your calls you can serialize the calls table to get the history of all the calls made. For the example code the table looks like
{
['foo'] = {{'a', 1}, {'b'}}
}
The following function prints the directories and files within a path, I want it to go into the directories and sub directories by using a recursive function. This causes a stackoverflow error. It only works if doesn't call "RecursiveSearch" func but only prints out directories and files but not subdirectories.
extends Node
func RecursiveSearch(dir):
var path = ("res://")
var err = dir.open(path)
if err != OK:
print("error occurred")
return
dir.list_dir_begin() # points to the first one, true ignores special directory entries (.) and (..)
var name = dir.get_next() # retrieves the firdt file or dir
while name != "":
if dir.current_is_dir(): # test if it's a dir type
print("dir : " , name)
RecursiveSearch(dir)
elif !dir.current_is_dir():
print("file : " , name)
else:
break
name = dir.get_next() # points to the next dir or file
dir.list_dir_end()
func _ready():
var dir = Directory.new()
RecursiveSearch(dir)
Directory.list_dir_begin() only lists the immediate children of a directory. To recurse into a subdirectory, you will need to create a new Directory object for the subdirectory, and call your RecursiveSearch on that.
As it stands, your function calls itself with its own argument. It will do the exact same thing--call itself with the same argument--again and again until it hits the recursion limit.
This expression eval(Meta.parse("begin $(code)\nend")) would eval Julia code with include resolved relative to the file eval... is defined in.
How to change it so that it would use another directory? Something like
eval(Meta.parse("begin $(code)\nend"), resolve_include_relative_to=somepath)
Or, if that's not possible - relative to current directory (like REPL)?
UPDATE
Possible solution - replacing relative paths with absolute
function fix_include(code::String, relative_path::String)::String
code = replace(code, r"include\(\"\./(.*?)\"\)" => s"include(\"__relative_path__/\1\")")
code = replace(code, r"__relative_path__" => relative_path)
code
end
eval(Meta.parse("begin $(fix_include(code, relative_path))\nend")
Use case:
I'm evaluating snippets of string code, sometimes they contain include statement with relative paths and they resolved against wrong path. I want to explicitly specify tell it what path should be used for resolution. Or at the very least always use the current directory '.', not the directory where the file with the eval(xxx) line defined ./lib/runner.jl.
This function should do the trick (include is relative to the path in task-local storage, as kinda indicated by the docstring):
function eval_at(code; path = "none", mod = Main)
tls = task_local_storage()
hassource = haskey(tls, :SOURCE_PATH)
hassource && (path′ = tls[:SOURCE_PATH])
# setting this is enough for `include` to be correct
tls[:SOURCE_PATH] = path
try
# let's use the three-arg `include_string` here to make sure `#__FILE__`
# etc resolve correctly
return include_string(mod, code, path)
finally
hassource ?
(tls[:SOURCE_PATH] = path′) :
delete!(tls, :SOURCE_PATH)
end
end
Example usage:
julia> pwd()
"/home/pfitzseb/Documents"
julia> isfile("test.jl")
false
julia> include("test.jl")
ERROR: could not open file /home/pfitzseb/Documents/test.jl
julia> eval_at("""include("test.jl")""", path = "/home/pfitzseb/foo.jl")
Main.LogT
julia> eval_at("""#__FILE__""", path = "/home/pfitzseb/foo.jl")
"/home/pfitzseb/foo.jl"
It is not clear what exactly you want to do but for "do something in a folder" situations usually cd() do ... end syntax works great.
code = quote
cd("c:/temp") do
println("do something in $(pwd())")
#do something more
end
end
And now use it
julia> eval(code)
do something in c:\temp
Depending in your scenario you might consider using macros to manipulate code blocks that do something in a directory. Below is a simple example not offering more functionality than cd ... do ... end statement but of course it can be extended:
macro doinfolder(folder, what)
isa(what, Expr) || error("what needs to be some expression")
quote
cd($folder) do
$what
#other useful code injections can occur here...
end
end
end
And now use it
julia> #doinfolder "C:\\temp" pwd()
"C:\\temp"
Will also work with more complex code structures
julia> #doinfolder "C:\\temp" begin
pwd()
end
"C:\\temp"
My deep copy code:
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, deepcopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
I'm trying to implement this to oop using self, but couldn't get it to work, here is what I've tried so far
function block:deepcopy()
local orig_type = type(self)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, self, nil do
copy[self:deepcopy(orig_key)] = deepcopy(orig_value)
end
setmetatable(copy, self:deeepcopy(getmetatable(self)))
else
copy = orig
end
return copy
In the OOP version of the function, self:deepcopy(something) with the method syntax (colon) doesn't do what you want it to. It is equivalent to self.deepcopy(self, something); the second argument something is ignored and you just end up trying to re-copy the same self over and over until there's a stack overflow. You have to do self.deepcopy(something) with a dot to pass something as the self argument (the argument that is copied).
Calling self.deepcopy inside the definition of the deepcopy method assumes that every subtable has a self.deepcopy function. If not, you will get an "attempt to call a nil value" error. But you could do this if you want every subtable to have its own version of deepcopy that is used when copying the immediate children of that table (keys, values, metatable). For instance, you could have a subtable whose deepcopy method does not copy the metatable. Here is the basic version where the subtable has the same deepcopy method:
local block = {}
function block:deepcopy()
if type(self) == 'table' then
local copy = {}
for key, value in pairs(self) do
copy[self.deepcopy(key)] = self.deepcopy(value)
end
return setmetatable(copy, self.deepcopy(getmetatable(self)))
else
return self
end
end
block.a = { a = 10, deepcopy = block.deepcopy }
block:deepcopy() -- works
block.a = { a = 10 }
block:deepcopy() -- error: "attempt to call a nil value (field 'deepcopy')"
But you don't need to rewrite the function at all to use it in object-oriented style. Try using your first definition of deepcopy. Do object.deepcopy = deepcopy, and then call object:deepcopy() and you will get a copy of the object.
I have to copy an entire project folder inside the MarkLogic server and instead of doing it manually I decided to do it with a recursive function, but is becoming the worst idea I have ever had. I'm having problems with the transactions and with the syntax but being new I don't find a true way to solve it. Here's my code, thank you for the help!
import module namespace dls = "http://marklogic.com/xdmp/dls" at "/MarkLogic/dls.xqy";
declare option xdmp:set-transaction-mode "update";
declare function local:recursive-copy($filesystem as xs:string, $uri as xs:string)
{
for $e in xdmp:filesystem-directory($filesystem)/dir:entry
return
if($e/dir:type/text() = "file")
then dls:document-insert-and-manage($e/dir:filename, fn:false(), $e/dir:pathname)
else
(
xdmp:directory-create(concat(concat($uri, data($e/dir:filename)), "/")),
local:recursive-copy($e/dir:pathname, $uri)
)
};
let $filesystemfolder := 'C:\Users\WB523152\Downloads\expath-ml-console-0.4.0\src'
let $uri := "/expath_console/"
return local:recursive-copy($filesystemfolder, $uri)
MLCP would have been nice to use. However, here is my version:
declare option xdmp:set-transaction-mode "update";
declare variable $prefix-replace := ('C:/', '/expath_console/');
declare function local:recursive-copy($filesystem as xs:string){
for $e in xdmp:filesystem-directory($filesystem)/dir:entry
return
if($e/dir:type/text() = "file")
then
let $source := $e/dir:pathname/text()
let $dest := fn:replace($source, $prefix-replace[1], $prefix-replace[2])
let $_ := xdmp:document-insert($source,
<options xmlns="xdmp:document-load">
<uri>{$dest}</uri>
</options>)
return <record>
<from>{$source}</from>
<to>{$dest}</to>
</record>
else
local:recursive-copy($e/dir:pathname)
};
let $filesystemfolder := 'C:\Temp'
return <results>{local:recursive-copy($filesystemfolder)}</results>
Please note the following:
I changed my sample to the C:\Temp dir
The output is XML only because by convention I try to do this in case I want to analyze results. It is actually how I found the error related to conflicting updates.
I chose to define a simple prefix replace on the URIs
I saw no need for DLS in your description
I saw no need for the explicit creation of directories in your use case
The reason you were getting conflicting updates because you were using just the filename as the URI. Across the whole directory structure, these names were not unique - hence the conflicting update on double inserts of same URI.
This is not solid code:
You would have to ensure that a URI is valid. Not all filesystem paths/names are OK for a URI, so you would want to test for this and escape chars if needed.
Large filesystems would time-out, so spawning in batches may be useful.
A an example, I might gather the list of docs as in my XML and then process that list by spawning a new task for every 100 documents. This could be accomplished by a simple loop over xdmp:spawn-function or using a library such as taskbot by #mblakele