diff --git a/Manifest.toml b/Manifest.toml new file mode 100644 index 0000000..20f35d3 --- /dev/null +++ b/Manifest.toml @@ -0,0 +1,22 @@ +# This file is machine-generated - editing it directly is not advised + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..791f17f --- /dev/null +++ b/Project.toml @@ -0,0 +1,14 @@ +name = "IDL" +uuid = "d8fbff9c-ffdd-4f12-b08d-aa41cc43ce74" +authors = ["Bob Portmann", "Hongyang Zhou "] +version = "0.1.0" + +[deps] +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md index 346d9cd..e06c4d6 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,77 @@ # IDL interface for the Julia language -[![Build Status](https://travis-ci.org/BobPortmann/IDLCall.jl.svg?branch=master)](https://travis-ci.org/BobPortmann/IDLCall.jl) +[![Build Status](https://travis-ci.org/BobPortmann/`IDL.jl`.jl.svg?branch=master)](https://travis-ci.org/BobPortmann/`IDL.jl`.jl) -IDLCall is an interface to call IDL from the Julia language. Note that you must have a valid IDL -license to use IDL from julia. +The `IDL.jl` package is an interface for calling IDL from the Julia language. You must have a valid IDL license to use IDL from Julia. ## Installation Within Julia, use the package manager: ```julia -Pkg.clone("https://github.com/BobPortmann/IDLCall.jl.git") +Pkg.clone("https://github.com/BobPortmann/IDL.jl.git") ``` -IDLCall should find and load the IDL library automatically on Mac and Linux. It has not been -tested on Windows so please file an issue if you use Windows and want to help make it work. +`IDL.jl` should find and load the IDL library automatically on Mac and Linux. It has not been tested on Windows so please file an issue if you use Windows and want to help make it work. -IDL can be called using either the `RPC` or `Callable` interface. On windows only the `Callable` -interface is available. You can set an environmental variable `JL_IDL_TYPE` to `RPC` or `CALLABLE` -to force the use of that interface. -Alternatively you can set `ENV["JL_IDL_TYPE]` within julia before starting IDLCall. -Note that by default IDLCall uses the `RPC` interface -on Mac and Linux and `Callable` on Windows. The biggest difference between these is that: +IDL can be called using either the `RPC` or `Callable` interface. On windows only the `Callable` interface is available. You can set an environmental variable `JL_IDL_TYPE` to `RPC` or `CALLABLE` to force the use of that interface. +Alternatively you can set `ENV["JL_IDL_TYPE"]` within julia before starting `IDL.jl`. +Note that by default `IDL.jl` uses the `RPC` interface on Mac and Linux and `Callable` on Windows. The biggest difference between these is that: - `Callable` IDL runs in one program space and thus arrays can be shared between julia and IDL. In `RPC` all arrays are copied between processes. Note that I have run into issues with IDL loading DLM's while using `Callable` (e.g., NetCDF). -- IDL `RPC` is not supported on windows +- IDL `RPC` is not supported on windows. -- `Callable` is always managed by IDLCall while `RPC` can be managed by IDLCall or the user. - By managed we mean that it is opened it when you load IDLCall and closed it when you close julia. - To manage `RPC` yourself run `idlrpc` in a shell before starting IDLCall. This allows the `idlrpc` +- `Callable` is always managed by `IDL.jl` while `RPC` can be managed by `IDL.jl` or the user. + By managed we mean that it is opened it when you load `IDL.jl` and closed it when you close julia. + To manage `RPC` yourself run `idlrpc` in a shell before starting `IDL.jl`. This allows the `idlrpc` session to persist and julia can be restarted without killing the `idlrpc` process. + Currently `RPC` is always reset after the initialization of `IDL.jl`. -## Quickstart +There is an [issue](https://github.com/JuliaLang/julia/issues/7004) of opening `libidl` on Mac. -I recommend you start your code with +## Quickstart ```julia -import IDLCall -idl = IDLCall -``` -Then you can add a julia variable to the IDL process with - -``` -idl.put_var(x, "x") +using IDL ``` - -and you can retrieve variable into julia using - +You can add a Julia variable to the IDL process with +```julia +x = 1 +put_var(x, "x") ``` -x = idl.get_var("x") +and you can retrieve variable into Julia using +```julia +x = get_var("x") ``` - You can run an arbitrary chunk of code in IDL using +```julia +idl.execute("any valid idl code") +``` +Note that only primitive data types are supported at this time (e.g., structure variables are not supported yet). Also, `[;|\$]` inside quotes won't be correctly recognized. +Many convenient functions are provided: ``` -idl.execute("any valid idl code") +IDL.help +IDL.idlhelp +IDL.shell_command +IDL.reset +IDL.full_reset +IDL.dotrun ``` -Note that only primitive data types are supported at this time (e.g., structure variables -are not supported yet). + +See more examples in the [test script](test/runtests.jl) ## REPL -You can drop into an IDL REPL by typing `>` at the julia prompt. Then you can type any valid -IDL commands, including using continuation characters `$` for multi-line commands. One -experimental feature I have added is the use of `%var` will auto-magically import the julia -variable `var` into the IDL process. This works at the IDL prompt or in strings passed into the -`execute` function. +You can drop into an IDL REPL by typing `>` at the Julia prompt. Then you can type any valid IDL commands, including using continuation characters `$` for multi-line commands. One experimental feature I have added is the use of `%var` will auto-magically import the Julia variable `var` into the IDL process. This works at the IDL prompt or in strings passed into the `execute` function. -## ToDo +## Note -- Add tests +The IDL `RPC` library won't be automatically reset if you exit Julia. Therefore, currently you need to type `IDL.reset()` to cleanup the library. + +## ToDo - Make more flexible to install on all platforms diff --git a/src/IDL.jl b/src/IDL.jl new file mode 100644 index 0000000..9bc556e --- /dev/null +++ b/src/IDL.jl @@ -0,0 +1,44 @@ +module IDL + +export get_var, put_var, @get_var, @put_var, execute + +# Find IDL library directory if on Linux +if Sys.isunix() + idl_exec = chomp(read(`which idl`,String)) + if islink(idl_exec) + idl_dir = dirname(readlink(idl_exec)) + else + idl_dir = dirname(idl_exec) + end + idl_lib_dir = joinpath(idl_dir,"bin.darwin.x86_64") + const libidl_rpc = joinpath(idl_lib_dir,"libidl_rpc.dylib") + const idlrpc = joinpath(idl_dir,"idlrpc") + const idlcall = joinpath(idl_lib_dir,"libidl.dylib") +else # Windows + const idlcall = "libidl" + const idlrpc = "libidl_rpc" +end + + +jl_idl_type = get(ENV, "JL_IDL_TYPE", Sys.iswindows() ? "CALLABLE" : "RPC") + +jl_idl_type == "RPC" ? include("IDLRPC.jl") : +jl_idl_type == "CALLABLE" ? include("IDLCallable.jl") : +Sys.iswindows() ? error("JL_IDL_TYPE must be CALLABLE on windows") : +error("JL_IDL_TYPE must be RPC or CALLABLE") + +include("IDLREPL.jl") + +function __init__() + if jl_idl_type == "RPC" + rpc_init() + full_reset() # Reset IDL + elseif jl_idl_type == "CALLABLE" + callable_init() # not yet working for MaxOS and Linux + end + + # Initializing REPL + idl_repl() +end + +end diff --git a/src/IDLCall.jl b/src/IDLCall.jl deleted file mode 100644 index 1e19302..0000000 --- a/src/IDLCall.jl +++ /dev/null @@ -1,32 +0,0 @@ - -module IDLCall - -using Compat - -# Find IDL library directory if on Linux -if is_unix() - idl_dir = dirname(chomp(readstring(`which idl`))) - idl_lib_dir = chomp(readstring(`bash -c "ls -d $(idl_dir)/bin.*"`)) - const idlcall = idl_lib_dir*"/libidl" - const idlrpc = idl_lib_dir*"/libidl_rpc" -else - const idlcall = "libidl" - const idlrpc = "libidl_rpc" -end - -export init, get_var, put_var, execute, @get_var, @put_var, idl_repl - -jl_idl_type = get(ENV, "JL_IDL_TYPE", is_windows() ? "CALLABLE" : "RPC") - -jl_idl_type == "RPC" ? include("IDLRPC.jl") : -jl_idl_type == "CALLABLE" ? include("IDLCallable.jl") : -is_windows() ? error("JL_IDL_TYPE must be CALLABLE on windows") : -error("JL_IDL_TYPE must be RPC or CALLABLE") - -include("IDLREPL.jl") - -init() -idl_repl() -repl = idl_repl - -end diff --git a/src/IDLCallable.jl b/src/IDLCallable.jl index 682c3b4..96cc8b9 100644 --- a/src/IDLCallable.jl +++ b/src/IDLCallable.jl @@ -1,22 +1,21 @@ - include("idl_types.jl") include("common-funcs.jl") include("common-macros.jl") -if is_apple() +using Libdl + +if Sys.isapple() cd(idl_lib_dir) do Libdl.dlopen("libidl") end end -function init() - ecode = ccall((:IDL_Init, idlcall), Cint, (Cint, Ptr{Cint}, Ptr{Ptr{UInt8}}), - 0, C_NULL, C_NULL) - if ecode == 0 - error("IDL.init: IDL init failed") - end +function callable_init() + ecode = ccall((:IDL_Initialize, idlcall), Cint, + (Cint, Ptr{Cint}, Ptr{Ptr{UInt8}}), 0, C_NULL, C_NULL) + ecode == 0 && error("IDL.init: IDL init failed") global output_cb - ccall((:IDL_ToutPush, idlcall), Void, (Ptr{Void},), output_cb) + ccall((:IDL_ToutPush, idlcall), Nothing, (Ptr{Nothing},), output_cb) end # function execute{T<:AbstractString}(strarr::Array{T,1}) @@ -49,7 +48,7 @@ function get_output(flags::Cint, buf::Ptr{UInt8}, n::Cint) return end -output_cb = cfunction(get_output, Void, (Cint, Ptr{UInt8},Cint)) +output_cb = @cfunction(get_output, Nothing, (Cint, Ptr{UInt8},Cint)) # function exit() # # probably better to do a .full_reset instead @@ -68,17 +67,17 @@ function done_with_var(p::Ptr{UInt8}) return end -free_cb = cfunction(done_with_var, Void, (Ptr{UInt8},)) +free_cb = @cfunction(done_with_var, Nothing, (Ptr{UInt8},)) -function put_var{T,N}(arr::Array{T,N}, name::AbstractString) +function put_var(arr::Array{T,N}, name::AbstractString) where {T,N} if !isbits(eltype(arr)) || (idl_type(arr) < 0) error("IDL.put_var: only works with some vars containing bits types") end dim = zeros(Int, IDL_MAX_ARRAY_DIM) dim[1:N] = [size(arr)...] vptr = ccall((:IDL_ImportNamedArray, idlcall), Ptr{IDL_Variable}, - (Ptr{UInt8}, Cint, IDL_ARRAY_DIM, Cint, Ptr{UInt8}, IDL_ARRAY_FREE_CB , Ptr{Void}), - name, N, dim, idl_type(arr), pointer(arr), free_cb, C_NULL) + (Ptr{UInt8}, Cint, IDL_ARRAY_DIM, Cint, Ptr{UInt8}, IDL_ARRAY_FREE_CB , Ptr{Nothing}), + name, N, dim, idl_type(arr), pointer(arr), free_cb, C_NULL) if vptr == C_NULL error("IDL.put_var: failed") end @@ -89,15 +88,15 @@ end function put_var(x, name::AbstractString) # Sort of a HACK: import as one-element array and then truncate to scalar # IDL_ImportArray(int n_dim, IDL_MEMINT dim[], int type, - # UCHAR *data, IDL_ARRAY_FREE_CB free_cb, void *s) + # UCHAR *data, IDL_ARRAY_FREE_CB free_cb, Nothing *s) if !isbits(x) || (idl_type(x) < 0) error("IDL.put_var: only works with some vars containing bits types") end dim = zeros(Int, IDL_MAX_ARRAY_DIM) dim[1] = 1 - ccall((:IDL_ImportNamedArray, idlcall), Ptr{Void}, - (Ptr{UInt8}, Cint, Ptr{IDL_MEMINT}, Cint, Ptr{UInt8}, Ptr{Void}, Ptr{Void}), - name, 1, dim, idl_type(x), pointer([x]), C_NULL, C_NULL) + ccall((:IDL_ImportNamedArray, idlcall), Ptr{Nothing}, + (Ptr{UInt8}, Cint, Ptr{IDL_MEMINT}, Cint, Ptr{UInt8}, Ptr{Nothing}, Ptr{Nothing}), + name, 1, dim, idl_type(x), pointer([x]), C_NULL, C_NULL) execute("$name = $name[0]") return end diff --git a/src/IDLREPL.jl b/src/IDLREPL.jl index a23960e..ec89d52 100644 --- a/src/IDLREPL.jl +++ b/src/IDLREPL.jl @@ -1,15 +1,16 @@ -import Base: LineEdit, REPL +import REPL:respond, LineEdit, mode_keymap function idl_repl() - # Setup idl prompt prompt = LineEdit.Prompt("IDL> "; - prompt_prefix=Base.text_colors[:blue], - prompt_suffix=Base.text_colors[:white]) - + prompt_prefix=Base.text_colors[:blue], + prompt_suffix=Base.text_colors[:white]) + + !isdefined(Base, :active_repl) && return + repl = Base.active_repl - prompt.on_done = REPL.respond(repl,prompt) do line + prompt.on_done = respond(repl,prompt) do line ok2, line, msg = convert_continuations(line) if !ok2 println(msg) @@ -36,38 +37,38 @@ function idl_repl() repl.interface.modes[i_mode] = prompt end - hp = main_mode.hist hp.mode_mapping[:idl] = prompt prompt.hist = hp - - const idl_keymap = Dict{Any,Any}( - '>' => function (s,args...) - if isempty(s) - if !haskey(s.mode_state,prompt) - s.mode_state[prompt] = LineEdit.init_state(repl.t,prompt) - end - LineEdit.transition(s,prompt) - else - LineEdit.edit_insert(s,'>') - end - end) + + idl_keymap = Dict{Any,Any}( + '>' => function (s,args...) + if isempty(s) + if !haskey(s.mode_state,prompt) + s.mode_state[prompt] = LineEdit.init_state(repl.t,prompt) + end + LineEdit.transition(s,prompt) + else + LineEdit.edit_insert(s,'>') + end + end) search_prompt, skeymap = LineEdit.setup_search_keymap(hp) - mk = REPL.mode_keymap(main_mode) + mk = mode_keymap(main_mode) - b = Dict{Any,Any}[skeymap, mk, LineEdit.history_keymap, LineEdit.default_keymap, - LineEdit.escape_defaults] - prompt.keymap_dict = LineEdit.keymap(b) + b = Dict{Any,Any}[skeymap, mk, LineEdit.history_keymap, + LineEdit.default_keymap, LineEdit.escape_defaults] + prompt.keymap_dict = LineEdit.keymap(b) - main_mode.keymap_dict = LineEdit.keymap_merge(main_mode.keymap_dict, idl_keymap); + main_mode.keymap_dict = LineEdit.keymap_merge(main_mode.keymap_dict, + idl_keymap) nothing end function find_prompt_in_modes(modes, name) j = -1 for (i,mode) in enumerate(modes) - if :prompt in fieldnames(mode) && mode.prompt == name + if :prompt in fieldnames(typeof(mode)) && mode.prompt == name j = i break end diff --git a/src/IDLRPC.jl b/src/IDLRPC.jl index d412a5e..521ae8c 100644 --- a/src/IDLRPC.jl +++ b/src/IDLRPC.jl @@ -1,46 +1,31 @@ - include("idl_types.jl") include("common-funcs.jl") include("common-macros.jl") # RPC client -type RPCclient - ptr::Ptr{Void} - process::Union{Void, Base.Process} +struct RPCclient + ptr::Ptr{Nothing} + process::Union{Nothing, Base.Process} end RPCclient() = RPCclient(C_NULL, nothing) -RPCclient(ptr::Ptr{Void}) = RPCclient(ptr, nothing) +RPCclient(ptr::Ptr{Nothing}) = RPCclient(ptr, nothing) pclient = RPCclient() function rpc_init() - ccall((:IDL_RPCInit, idlrpc), Ptr{Void}, (Clong, Ptr{UInt8}), 0, C_NULL) -end - -function rpc_cleanup() - ecode = ccall((:IDL_RPCCleanup, idlrpc), Cint, (Ptr{Void}, Cint), pclient.ptr, 0) - if ecode != 1 - error("IDL.exit: failed") - end - if pclient.process != nothing - kill(pclient.process) - end - return -end - -function init() - olderr = STDERR - (rd, wr) = redirect_stderr() #Redirect error messages - ptr = rpc_init() - if ptr != C_NULL #Check if idlrpc is already running + # Initializing RPC + olderr = stderr + (rd, wr) = redirect_stderr() # Redirect error messages + ptr = ccall((:IDL_RPCInit, libidl_rpc), Ptr{Nothing}, (Clong, Ptr{UInt8}), 0, C_NULL) + if ptr != C_NULL # Check if idlrpc is already running global pclient = RPCclient(ptr) - else #Start up idlrpc - print("Initializing IDL ") - proc = spawn(`idlrpc`) + else # Start up idlrpc + println("Initializing IDL") + run(`$idlrpc`, wait=false) ptr = C_NULL cnt = 0 - while ptr == C_NULL && cnt < 60 #Allow for startup time - ptr = rpc_init() + while ptr == C_NULL && cnt < 60 # Allow for startup time + ptr = ccall((:IDL_RPCInit, libidl_rpc), Ptr{Nothing}, (Clong, Ptr{UInt8}), 0, C_NULL) cnt = cnt + 1 sleep(1) print(".") @@ -49,19 +34,28 @@ function init() ptr == C_NULL && error("IDL.init: IDLRPC init failed") global pclient = RPCclient(ptr, proc) end - capture(true) + capture(true) # Capture output from IDL redirect_stderr(olderr) - #Register cleanup function to be called at exit + # Register cleanup function to be called at exit atexit(rpc_cleanup) end +function rpc_cleanup() + ecode = ccall((:IDL_RPCCleanup, libidl_rpc), Cint, (Ptr{Nothing}, Cint), pclient.ptr, 0) + ecode != 1 && error("IDL.exit: failed") + if pclient.process != nothing + kill(pclient.process) + end + return +end + function execute_converted(str::AbstractString) # does no conversion of interpolated vars, continuation chars, or newlines - ecode = ccall((:IDL_RPCExecuteStr, idlrpc), Cint, (Ptr{Void},Ptr{UInt8}), pclient.ptr, str) + ecode = ccall((:IDL_RPCExecuteStr, libidl_rpc), Cint, (Ptr{Nothing},Ptr{UInt8}), pclient.ptr, str) if ecode != 1 # since error get printed by IDL, we just reset error state - ecode = ccall((:IDL_RPCExecuteStr, idlrpc), Cint, (Ptr{Void},Ptr{UInt8}), pclient.ptr, - "message, /RESET") + ecode = ccall((:IDL_RPCExecuteStr, libidl_rpc), Cint, (Ptr{Nothing},Ptr{UInt8}), pclient.ptr, + "message, /RESET") flush() return false end @@ -71,16 +65,14 @@ end function capture(flag::Bool) nlines = flag ? 5000 : 0 - ecode = ccall((:IDL_RPCOutputCapture, idlrpc), Cint, (Ptr{Void}, Cint), pclient.ptr, nlines) - if ecode != 1 - error("IDL.capture: IDL_RPCOutputCapture failed") - end + ecode = ccall((:IDL_RPCOutputCapture, libidl_rpc), Cint, (Ptr{Nothing}, Cint), pclient.ptr, nlines) + ecode != 1 && error("IDL.capture: IDL_RPCOutputCapture failed") return nothing end function get_output!(line_s::IDL_RPC_LINE_S) - ecode = ccall((:IDL_RPCOutputGetStr, idlrpc), Cint, (Ptr{Void},Ref{IDL_RPC_LINE_S},Cint), - pclient.ptr, line_s, 0) + ecode = ccall((:IDL_RPCOutputGetStr, libidl_rpc), Cint, (Ptr{Nothing},Ref{IDL_RPC_LINE_S},Cint), + pclient.ptr, line_s, 0) ecode == 1 ? true : false end @@ -95,28 +87,25 @@ function flush() end end -# no free_cb needed in idlrpc (I think) +# no free_cb needed in libidl_rpc (I think) free_cb = C_NULL # NOTE: Put_var makes a copy of the data in the array when it is put into idlrpc process. # This is different than callable idl where the pointer to the data is copied. # I think the difference is because callable idl runs in the same process but # idlrpc does not. Thus, no free_cb is needed in idlrpc version. -function put_var{T,N}(arr::Array{T,N}, name::AbstractString) - if !isbits(eltype(arr)) || (idl_type(arr) < 0) +function put_var(arr::Array{T,N}, name::AbstractString) where {T,N} + if !isbitstype(eltype(arr)) || (idl_type(arr) < 0) error("IDL.put_var: only works with some vars containing bits types") end dim = zeros(Int, IDL_MAX_ARRAY_DIM) dim[1:N] = [size(arr)...] - vptr = ccall((:IDL_RPCImportArray, idlrpc), Ptr{IDL_Variable}, - (Cint, IDL_ARRAY_DIM, Cint, Ptr{T}, IDL_ARRAY_FREE_CB), - N, dim, idl_type(arr), arr, free_cb) - ecode = ccall((:IDL_RPCSetVariable, idlrpc), Cint, - (Ptr{Void}, Ptr{UInt8}, Ptr{IDL_Variable}), - pclient.ptr, name, vptr) - if ecode != 1 - error("IDL.put_var: failed") - end + vptr = ccall((:IDL_RPCImportArray, libidl_rpc), Ptr{IDL_Variable}, + (Cint, IDL_ARRAY_DIM, Cint, Ptr{T}, IDL_ARRAY_FREE_CB), + N, dim, idl_type(arr), arr, free_cb) + ecode = ccall((:IDL_RPCSetVariable, libidl_rpc), Cint, + (Ptr{Nothing}, Ptr{UInt8}, Ptr{IDL_Variable}), pclient.ptr, name, vptr) + ecode != 1 && error("IDL.put_var: failed") return end @@ -140,16 +129,15 @@ function put_var(x, name::AbstractString) error("IDL.put_var: only works with some vars containing bits types") end vptr = get_vptr(name) - ccall((:IDL_RPCStoreScalar, idlrpc), Void, - (Ptr{IDL_Variable}, Cint, Ref{UInt128}), - vptr, idl_type(x), Ref{UInt128}(convert(UInt128,reinterpret(uint_size(x),x)))) - ecode = ccall((:IDL_RPCSetVariable, idlrpc), Cint, - (Ptr{Void}, Ptr{UInt8}, Ptr{IDL_Variable}), - pclient.ptr, name, vptr) + ccall((:IDL_RPCStoreScalar, libidl_rpc), Nothing, + (Ptr{IDL_Variable}, Cint, Ref{UInt128}), + vptr, idl_type(x), Ref{UInt128}(convert(UInt128,reinterpret(uint_size(x),x)))) + ecode = ccall((:IDL_RPCSetVariable, libidl_rpc), Cint, + (Ptr{Nothing}, Ptr{UInt8}, Ptr{IDL_Variable}), pclient.ptr, name, vptr) return end -function put_var{T}(x::Complex{T}, name::AbstractString) +function put_var(x::Complex{T}, name::AbstractString) where {T} T == Float32 || T == Float64 || error("IDL.put_var: only floating point complex types allowed") if T == Float64 y = (convert(UInt128, reinterpret(UInt64, imag(x))) << 64) + reinterpret(UInt64, real(x)) @@ -157,12 +145,10 @@ function put_var{T}(x::Complex{T}, name::AbstractString) y = (convert(UInt128, reinterpret(UInt32, imag(x))) << 32) + reinterpret(UInt32, real(x)) end vptr = get_vptr(name) - ccall((:IDL_RPCStoreScalar, idlrpc), Void, - (Ptr{IDL_Variable}, Cint, Ref{UInt128}), - vptr, idl_type(x), Ref{UInt128}(y)) - ecode = ccall((:IDL_RPCSetVariable, idlrpc), Cint, - (Ptr{Void}, Ptr{UInt8}, Ptr{IDL_Variable}), - pclient.ptr, name, vptr) + ccall((:IDL_RPCStoreScalar, libidl_rpc), Nothing, + (Ptr{IDL_Variable}, Cint, Ref{UInt128}), vptr, idl_type(x), Ref{UInt128}(y)) + ecode = ccall((:IDL_RPCSetVariable, libidl_rpc), Cint, + (Ptr{Nothing}, Ptr{UInt8}, Ptr{IDL_Variable}), pclient.ptr, name, vptr) return end @@ -174,38 +160,34 @@ end function put_var(str::AbstractString, name::AbstractString) idl_string = IDL_String() - ccall((:IDL_RPCStrStore, idlrpc), Void, (Ptr{IDL_String}, Ptr{Cchar}), - idl_string, str) - ecode = ccall((:IDL_RPCSetVariable, idlrpc), Cint, - (Ptr{Void}, Ptr{UInt8}, Ptr{IDL_Variable}), - pclient.ptr, name, vptr) - if ecode != 1 - error("IDL.put_var: failed") - end + ccall((:IDL_RPCStrStore, libidl_rpc), Nothing, (Ptr{IDL_String}, Ptr{Cchar}), + idl_string, str) + ecode = ccall((:IDL_RPCSetVariable, libidl_rpc), Cint, + (Ptr{Nothing}, Ptr{UInt8}, Ptr{IDL_Variable}), pclient.ptr, name, vptr) + ecode != 1 && error("IDL.put_var: failed") end function get_name(vptr::Ptr{IDL_Variable}) # not implemented for RPC - str = ccall((:IDL_RPCVarName, idlrpc), Ptr{Cchar}, (Ptr{IDL_Variable},), vptr) + str = ccall((:IDL_RPCVarName, libidl_rpc), Ptr{Cchar}, (Ptr{IDL_Variable},), vptr) return unsafe_string(str) end function get_vptr(name::AbstractString) # returns C_NULL if name not in scope - ccall((:IDL_RPCGetMainVariable, idlrpc), Ptr{IDL_Variable}, (Ptr{Void},Ptr{UInt8}), - pclient.ptr, name) + ccall((:IDL_RPCGetMainVariable, libidl_rpc), Ptr{IDL_Variable}, + (Ptr{Nothing},Ptr{UInt8}), pclient.ptr, name) end function get_var(name::AbstractString) - vptr = ccall((:IDL_RPCGetMainVariable, idlrpc), Ptr{IDL_Variable}, (Ptr{Void},Ptr{UInt8}), - pclient.ptr, name) + vptr = ccall((:IDL_RPCGetMainVariable, libidl_rpc), Ptr{IDL_Variable}, + (Ptr{Nothing},Ptr{UInt8}), pclient.ptr, name) # NOTE: IDL_RPCGetVariable never seems to return NULL in spite of docs if vptr == C_NULL error("IDL.get_var: variable $name does not exist") end var = get_var(vptr, name) # not sure if this is needed? - vptr = ccall((:IDL_RPCDeltmp, idlrpc), Void, (Ptr{IDL_Variable},), vptr) + vptr = ccall((:IDL_RPCDeltmp, libidl_rpc), Nothing, (Ptr{IDL_Variable},), vptr) var end - diff --git a/src/common-funcs.jl b/src/common-funcs.jl index 9dd602e..10a9f58 100644 --- a/src/common-funcs.jl +++ b/src/common-funcs.jl @@ -1,11 +1,10 @@ - # convienence routines help() = execute("help") help(s::AbstractString) = execute("help, "*s) idlhelp(s::AbstractString) = execute("?"*s) -idlhelp{T<:AbstractString}(strarr::Array{T,1}) = println("IDL.idlhelp: Array input not supported") +idlhelp(strarr::Array{T,1}) where {T<:AbstractString} = println("IDL.idlhelp: Array input not supported") shell_command(s::AbstractString) = println("% Shell commands not allowed in IDLRPC") -shell_command{T<:AbstractString}(strarr::Array{T,1}) = println("% Shell commands not allowed in IDLRPC") +shell_command(strarr::Array{T,1}) where {T<:AbstractString} = println("% Shell commands not allowed in IDLRPC") reset() = execute(".reset_session") full_reset() = execute(".full_reset_session") dotrun(filename::AbstractString) = execute(".run $filename") @@ -17,7 +16,7 @@ function execute(str::AbstractString) return nothing end -function execute_converted{T<:AbstractString}(strarr::Array{T,1}) +function execute_converted(strarr::Array{T,1}) where {T<:AbstractString} # does no conversion of interpolated vars, continuation chars, or newlines for str in strarr execute_converted(str) || return false @@ -42,7 +41,7 @@ function put_var_from_name(name::AbstractString, abort::Bool=true) return (ok, msg) end -function put_var{T<:AbstractString,N}(arr::Array{T,N}, name::AbstractString) +function put_var(arr::Array{T,N}, name::AbstractString) where {T<:AbstractString,N} # Sort of a HACK: do direcly since ImportNamedArray doesn't work execute("$name = strarr"*replace(string(size(arr)), ",)", ")")) for i=1:length(arr) @@ -75,8 +74,8 @@ function get_var(vptr::Ptr{IDL_Variable}, name::AbstractString="") parr = reinterpret(Ptr{IDL_Array}, convert(Int, var.buf)) idl_arr = unsafe_load(parr) pdata = reinterpret(Ptr{IDL_String}, idl_arr.data) - strarr = Array(Compat.ASCIIString, dims(idl_arr.dim, idl_arr.n_dim)) - for i=1:idl_arr.n_elts + strarr = Array{AbstractString}(dims(idl_arr.dim, idl_arr.n_dim)) + for i = 1:idl_arr.n_elts data = unsafe_load(pdata, i) strarr[i] = data.slen > 0 ? unsafe_string(data.s, Int(data.slen)) : "" end @@ -92,65 +91,66 @@ function get_var(vptr::Ptr{IDL_Variable}, name::AbstractString="") idl_arr = unsafe_load(parr) jl_t = jl_type(var.vtype) pdata = reinterpret(Ptr{jl_t}, idl_arr.data) - # not sure why this doesn't work - # arr = unsafe_wrap(Array, pdata, dims(idl_arr.dim, idl_arr.n_dim)) - arr = Array(jl_t, dims(idl_arr.dim, idl_arr.n_dim)) - for i=1:idl_arr.n_elts - arr[i] = unsafe_load(pdata, i) - end - return arr + arr = unsafe_wrap(Array, pdata, dims(idl_arr.dim, idl_arr.n_dim)) + #= + arr = Array{jl_t}(undef, dims(idl_arr.dim, idl_arr.n_dim)) + for i = 1:idl_arr.n_elts + arr[i] = unsafe_load(pdata, i) end + =# + # If you don't copy, the pointer will be freed! + return copy(arr) end +end - # Scalar value - if var.vtype == IDL_TYP_UNDEF - error("IDL.extract_from_vptr: $name: undefined variable") - elseif var.vtype == IDL_TYP_BYTE - return reinterpret(Int8, convert(UInt8, var.buf)) - elseif var.vtype == IDL_TYP_INT - return reinterpret(Int16, convert(UInt16, var.buf)) - elseif var.vtype == IDL_TYP_LONG - return reinterpret(Int32, convert(UInt32, var.buf)) - elseif var.vtype == IDL_TYP_FLOAT - return reinterpret(Float32, convert(UInt32, var.buf)) - elseif var.vtype == IDL_TYP_DOUBLE - return reinterpret(Float64, convert(UInt64, var.buf)) - elseif var.vtype == IDL_TYP_COMPLEX - return complex(reinterpret(Float32, convert(UInt32, var.buf & mask32)), - reinterpret(Float32, convert(UInt32, var.buf >> 32))) - elseif var.vtype == IDL_TYP_STRING - slen = reinterpret(Int32, convert(UInt32,var.buf & mask32)) - stype = reinterpret(Int32, convert(UInt32,(var.buf & mask64) >> 32)) - println(stype) - s = reinterpret(Ptr{Cchar}, convert(UInt64,var.buf >> 64)) - return slen > 0 ? unsafe_string(s, slen) : "" - elseif var.vtype == IDL_TYP_STRUCT - error("IDL.extract_from_vptr: $name: STRUCT not setup") - elseif var.vtype == IDL_TYP_DCOMPLEX - return complex(reinterpret(Float64, convert(UInt64, var.buf & mask64)), - reinterpret(Float64, convert(UInt64, var.buf >> 64))) - elseif var.vtype == IDL_TYP_PTR - error("IDL.extract_from_vptr: $name: PTR not setup") - elseif var.vtype == IDL_TYP_OBJREF - error("IDL.extract_from_vptr: $name: OBJREF not setup") - elseif var.vtype == IDL_TYP_UINT - return reinterpret(UInt16, convert(UInt16, var.buf)) - elseif var.vtype == IDL_TYP_ULONG - return reinterpret(UInt32, convert(UInt32, var.buf)) - elseif var.vtype == IDL_TYP_LONG64 - return reinterpret(Int64, convert(UInt64, var.buf)) - elseif var.vtype == IDL_TYP_ULONG64 - return reinterpret(UInt64, convert(UInt64, var.buf)) - end - # should be impossible to get here - error("IDL.extract_from_vptr: $name: type is not setup") +# Scalar value +if var.vtype == IDL_TYP_UNDEF + error("IDL.extract_from_vptr: $name: undefined variable") +elseif var.vtype == IDL_TYP_BYTE + return reinterpret(Int8, convert(UInt8, var.buf)) +elseif var.vtype == IDL_TYP_INT + return reinterpret(Int16, convert(UInt16, var.buf)) +elseif var.vtype == IDL_TYP_LONG + return reinterpret(Int32, convert(UInt32, var.buf)) +elseif var.vtype == IDL_TYP_FLOAT + return reinterpret(Float32, convert(UInt32, var.buf)) +elseif var.vtype == IDL_TYP_DOUBLE + return reinterpret(Float64, convert(UInt64, var.buf)) +elseif var.vtype == IDL_TYP_COMPLEX + return complex(reinterpret(Float32, convert(UInt32, var.buf & mask32)), + reinterpret(Float32, convert(UInt32, var.buf >> 32))) +elseif var.vtype == IDL_TYP_STRING + slen = reinterpret(Int32, convert(UInt32,var.buf & mask32)) + stype = reinterpret(Int32, convert(UInt32,(var.buf & mask64) >> 32)) + println(stype) + s = reinterpret(Ptr{Cchar}, convert(UInt64,var.buf >> 64)) + return slen > 0 ? unsafe_string(s, slen) : "" +elseif var.vtype == IDL_TYP_STRUCT + error("IDL.extract_from_vptr: $name: STRUCT not setup") +elseif var.vtype == IDL_TYP_DCOMPLEX + return complex(reinterpret(Float64, convert(UInt64, var.buf & mask64)), + reinterpret(Float64, convert(UInt64, var.buf >> 64))) +elseif var.vtype == IDL_TYP_PTR + error("IDL.extract_from_vptr: $name: PTR not setup") +elseif var.vtype == IDL_TYP_OBJREF + error("IDL.extract_from_vptr: $name: OBJREF not setup") +elseif var.vtype == IDL_TYP_UINT + return reinterpret(UInt16, convert(UInt16, var.buf)) +elseif var.vtype == IDL_TYP_ULONG + return reinterpret(UInt32, convert(UInt32, var.buf)) +elseif var.vtype == IDL_TYP_LONG64 + return reinterpret(Int64, convert(UInt64, var.buf)) +elseif var.vtype == IDL_TYP_ULONG64 + return reinterpret(UInt64, convert(UInt64, var.buf)) +end +# should be impossible to get here +error("IDL.extract_from_vptr: $name: type is not setup") end function inside_string(pt::Int, line::AbstractString) - for re in [r"('[^']+')", r"(\"[^\"]+\")"] + for re in (r"('[^']+')", r"(\"[^\"]+\")") for m in eachmatch(re, line) - if pt >= m.offset && - pt < m.offset+endof(m.captures[1]) + if m.offset+length(m.captures[1]) > pt ≥ m.offset return true end end @@ -160,26 +160,19 @@ end function convert_continuations(line) # remove trailing comments and continuation lines - # will remove continuation on final line which is invalid idl syntax - pt = start(line) - while (pt = first(search(line, r";|\$", pt))) > 0 - if !inside_string(pt, line) - line = replace(line, r"(;|\$).*(\n|$)", "", 1) - end - if pt < endof(line) - pt = next(line, pt)[2] - end - end + # currently has issue with [;|\$] inside quotes! + line = replace(strip(line), r"(;|\$).*(\n|$)"=>"") + return true, line, "" end function convert_newlines(line) - # separates line at newline ("\n") characters into string array + # separates line at newline ('\n') characters into string array # assumes that continuation characters are already removed # not type stable but should not matter for repl use line = chomp(line) - if in('\n', line) - line = split(line, '\n', keep=false) + if '\n' in line + line = split(line, '\n', keepempty=false) end return line end @@ -201,7 +194,7 @@ end function convert_command(line) ok, line, msg = replace_interpolated_vars(line) ok || return ok, line, msg - ok, line, msg = convert_continuations(line) + ok, line, msg = convert_continuations(line) # hyzhou: remove while testing ok || return ok, line, msg line = convert_newlines(line) return true, line, msg diff --git a/src/common-macros.jl b/src/common-macros.jl index 673f20e..d0af3af 100644 --- a/src/common-macros.jl +++ b/src/common-macros.jl @@ -1,4 +1,3 @@ - # These routines were modified from similar routines in MATLAB.jl # E.G., @mput, @mget, _mput_multi, _mget_multi, make_getvar_statement @@ -30,7 +29,7 @@ function make_getvar_statement(ex::Expr) if ex.head == :kw error("Must call @get_var without parenthesis if using statements") else - erorr("Invalid expression for @get_var: " * string(ex)) + error("Invalid expression for @get_var: " * string(ex)) end end v::Symbol = ex.args[1] @@ -39,7 +38,7 @@ function make_getvar_statement(ex::Expr) end function get_var_multi(vs::Union{Symbol, Expr}...) - # NOTE: I supressed output by adding :nothing + # supress output by adding :nothing nv = length(vs) if nv == 1 stmt = make_getvar_statement(vs[1]) @@ -56,5 +55,3 @@ end macro get_var(vs...) esc(get_var_multi(vs...)) end - -# END: routines modified from similar routines in MATLAB.jl diff --git a/src/idl_types.jl b/src/idl_types.jl index 450971e..12c5e66 100644 --- a/src/idl_types.jl +++ b/src/idl_types.jl @@ -24,14 +24,14 @@ const IDL_TYP_LONG64 = 14 const IDL_TYP_ULONG64 = 15 # translating IDL/C types to julia -typealias IDL_MEMINT Int -typealias IDL_UMEMINT UInt -typealias UCHAR Cuchar +const IDL_MEMINT = Int +const IDL_UMEMINT = UInt +const UCHAR = Cuchar # NOTE: IDL_ARRAY_DIM is fixed length array IDL_MEMINT[IDL_MAX_ARRAY_DIM] (i.e, Int[8]) -typealias IDL_ARRAY_DIM Ptr{IDL_MEMINT} -typealias IDL_ARRAY_FREE_CB Ptr{Void} -typealias IDL_FILEINT Int # possibly different on Windows -typealias IDL_STRING_SLEN_T Cint +const IDL_ARRAY_DIM = Ptr{IDL_MEMINT} +const IDL_ARRAY_FREE_CB = Ptr{Nothing} +const IDL_FILEINT = Int # possibly different on Windows +const IDL_STRING_SLEN_T = Cint const IDL_STRING_MAX_SLEN = 2147483647 # should you check this? # /***** IDL_VARIABLE flag values ********/ @@ -60,12 +60,10 @@ function idl_type(jl_t) idl_t = IDL_TYP_FLOAT elseif t == Float64 idl_t = IDL_TYP_DOUBLE - elseif t == Complex64 + elseif t == ComplexF64 idl_t = IDL_TYP_COMPLEX elseif t <: AbstractString idl_t = IDL_TYP_STRING - elseif t == Complex128 - idl_t = IDL_TYP_DCOMPLEX elseif t == UInt16 idl_t = IDL_TYP_UINT elseif t == UInt32 @@ -93,11 +91,11 @@ function jl_type(idl_t) elseif idl_t == IDL_TYP_DOUBLE jl_t = Float64 elseif idl_t == IDL_TYP_COMPLEX - jl_t = Complex64 + jl_t = ComplexF64 elseif idl_t == IDL_TYP_STRING jl_t = Compat.String - elseif idl_t == IDL_TYP_DCOMPLEX - jl_t = Complex128 + #elseif idl_t == IDL_TYP_DCOMPLEX + # jl_t = Complex128 elseif idl_t == IDL_TYP_UINT jl_t = UInt16 elseif idl_t == IDL_TYP_ULONG @@ -116,8 +114,8 @@ end #*************************************************************************************************# # some IDL types from extern.jl # sizeof(buf) is max size of IDL_ALLTYPES Union (64x2=128 bits or 16 bytes on all platforms) -typealias IDL_ALLTYPES UInt128 -immutable IDL_Variable +const IDL_ALLTYPES = UInt128 +struct IDL_Variable vtype::UCHAR flags::UCHAR flags2::UCHAR @@ -125,7 +123,7 @@ immutable IDL_Variable end # works as a fixed length array -immutable IDL_DIMS +struct IDL_DIMS d1::IDL_MEMINT d2::IDL_MEMINT d3::IDL_MEMINT @@ -139,7 +137,7 @@ end dims(d::IDL_DIMS) = (d.d1,d.d2,d.d3,d.d4,d.d5,d.d6,d.d7,d.d8) dims(d::IDL_DIMS, ndims::Integer) = (d.d1,d.d2,d.d3,d.d4,d.d5,d.d6,d.d7,d.d8)[1:ndims] -immutable IDL_Array +struct IDL_Array elt_len::IDL_MEMINT # Length of element in char units arr_len::IDL_MEMINT # Length of entire array (char) n_elts::IDL_MEMINT # total # of elements @@ -153,19 +151,19 @@ immutable IDL_Array data_guard::IDL_MEMINT # Guard longword end -immutable IDL_String +struct IDL_String slen::IDL_STRING_SLEN_T # Length of string, 0 for null stype::Cshort # type of string, static or dynamic s::Ptr{Cchar} # Addr of string - IDL_String() = new(0, 0, Base.unsafe_convert(Ptr{Cchar}, Array(Cchar, IDL_RPC_MAX_STRLEN))) + IDL_String() = new(0, 0, Base.unsafe_convert(Ptr{Cchar}, Array{Cchar}(undef, IDL_RPC_MAX_STRLEN))) end # From idl_rpc.h const IDL_RPC_MAX_STRLEN = 512 # max string length -immutable IDL_RPC_LINE_S +struct IDL_RPC_LINE_S flags::Cint buf::Ptr{Cchar} - IDL_RPC_LINE_S() = new(0, Base.unsafe_convert(Ptr{Cchar}, Array(Cchar, IDL_RPC_MAX_STRLEN))) + IDL_RPC_LINE_S() = new(0, Base.unsafe_convert(Ptr{Cchar}, Array{Cchar}(undef, IDL_RPC_MAX_STRLEN))) end const IDL_TOUT_F_STDERR = 1 # Output to stderr instead of stdout diff --git a/test/runtests.jl b/test/runtests.jl index 7f2e4c1..b83a2a2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,39 @@ -using IDLCall -using Base.Test +using IDL, Test -# write your own tests here -@test 1 == 1 +# scalar passing +execute("a = 1") +execute("a += 1") +a = get_var("a") +@test a == 2 + +# string passing +line = """ +b = '1+1 ' +c = 'hello' +""" +execute(line) +b = get_var("b") +@get_var c +@test b == "1+1 " +@test c == "hello" + +# array passing +a = [1,2,3] +put_var(a, "a") +execute("a += 1") +@get_var a +@test a == [2,3,4] + + +# call function +line = """ +PRINT, 'HELLO WORLD!' +""" +execute(line) + +# If you don't say reset(), rpc will remember everything, even in the next call! +IDL.reset() + + +# Why is this not working? +#execute("1+1")