Skip to content

Commit

Permalink
Updated help-entry storage. Fixed apropos.
Browse files Browse the repository at this point in the history
Test are passing/failing somewhat eratically, probably due to the
WeakObjectIdDict.
  • Loading branch information
mauro3 committed Oct 5, 2014
1 parent 83138a7 commit ba9b6e4
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 198 deletions.
45 changes: 16 additions & 29 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -763,15 +763,6 @@ length(t::WeakKeyDict) = length(t.ht)
# TODO:
# - take care of hash collisions when tidying up WeakRef(nothing)

const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__

import Base: KeyIterator, ValueIterator, haskey, get, getkey, delete!,
pop!, empty!, filter!, setindex!, getindex, similar,
sizehint, length, filter, isempty, start, next, done,
keys, values, _tablesz, skip_deleted, serialize, deserialize, serialize_type,
convert


type WeakObjectIdDict{K,V} <: Associative{K,V}
slots::Array{Uint8,1}
keys::Array{WeakRef,1}
Expand Down Expand Up @@ -851,9 +842,15 @@ function deserialize{K,V}(s, T::Type{WeakObjectIdDict{K,V}})
return t
end

# have two hashindex. For setindex:
hashindex(::WeakObjectIdDict, key::WeakRef, sz) = error("asdf") #(int(hash(object_id(key.value))) & (sz-1)) + 1
# For getindex:
# use these functions to access the h.keys array. Does conversion to
# and from WeakRef.
gkey(h::WeakObjectIdDict, ind) = h.keys[ind].value

skey!(h::WeakObjectIdDict, val, ind) = (h.keys[ind] = WeakRef(val))
skey!(ar::Array{WeakRef,1}, val, ind) = (ar[ind] = WeakRef(val))

numslots(h::WeakObjectIdDict) = length(h.slots)

hashindex(::WeakObjectIdDict, key, sz) = (int(hash(object_id(key))) & (sz-1)) + 1

isslotempty(h::WeakObjectIdDict, i::Int) = h.slots[i] == 0x0
Expand Down Expand Up @@ -889,7 +886,7 @@ function rehash{K,V}(h::WeakObjectIdDict{K,V}, newsz)

for i = 1:sz
if olds[i] == 0x1
k = getkey(h, i)
k = gkey(h, i)
if k==wnothing #&& i!=indexnothing_old # TODO: do something about hash collisions!
# a gc'ed immutable was here
continue
Expand All @@ -900,7 +897,7 @@ function rehash{K,V}(h::WeakObjectIdDict{K,V}, newsz)
index = (index & (newsz-1)) + 1
end
slots[index] = 0x1
setkey!(keys, k, index)
skey!(keys, k, index)
vals[index] = v
count += 1

Expand Down Expand Up @@ -942,16 +939,6 @@ function empty!{K,V}(h::WeakObjectIdDict{K,V})
return h
end

# use these functions to access the h.keys array. Does conversion to
# and from WeakRef.
getkey(h::WeakObjectIdDict, ind) = h.keys[ind].value
getkey(ar::Array{WeakRef,1}, val, ind) = ar[ind].value

setkey!(h::WeakObjectIdDict, val, ind) = (h.keys[ind] = WeakRef(val))
setkey!(ar::Array{WeakRef,1}, val, ind) = (ar[ind] = WeakRef(val))

numslots(h::WeakObjectIdDict) = length(h.slots)

# get the index where a key is stored, or -1 if not present
function ht_keyindex{K,V}(h::WeakObjectIdDict{K,V}, key)
sz = numslots(h)
Expand All @@ -963,7 +950,7 @@ function ht_keyindex{K,V}(h::WeakObjectIdDict{K,V}, key)
if isslotempty(h,index)
break
end
if !isslotmissing(h,index) && isequal(key, getkey(h, index))
if !isslotmissing(h,index) && isequal(key, gkey(h, index))
return index
end

Expand Down Expand Up @@ -998,7 +985,7 @@ function ht_keyindex2{K,V}(h::WeakObjectIdDict{K,V}, key)
# in case "key" already exists in a later collided slot.
avail = -index
end
elseif isequal(key, getkey(h, index))
elseif isequal(key, gkey(h, index))
return index
end

Expand All @@ -1016,7 +1003,7 @@ end

function _setindex!(h::WeakObjectIdDict, v, key, index)
h.slots[index] = 0x1
setkey!(h, key, index)
skey!(h, key, index)
h.vals[index] = v
h.count += 1

Expand All @@ -1030,7 +1017,7 @@ end

function weak_key_delete!(t::WeakObjectIdDict, k)
# when a weak key is finalized, remove from dictionary if it is still there
wk = getkey(t, k, secret_table_token) # getkey returns the WeakRef.value
wk = getkey(t, k, secret_table_token) # gkey returns the WeakRef.value
if !is(wk,secret_table_token) && is(wk, k)
delete!(t, k)
end
Expand All @@ -1050,7 +1037,7 @@ function setindex!{K,V}(h::WeakObjectIdDict{K,V}, v0, key0)
index = ht_keyindex2(h, key0)

if index > 0
setkey!(h, key0, index)
skey!(h, key0, index)
h.vals[index] = v
else
_setindex!(h, v, key0, -index)
Expand Down
105 changes: 54 additions & 51 deletions base/help.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,69 @@ import Base.WeakObjectIdDict
# - switch off when not running interactively (but what about tests?)
# - make @doc work for methods
# - make @doc work for modules
# - make APROPOS_DICT = WeakObjectIdDict(), currently this make julia very unstable
# - make an APROPOS_DICT mapping keywords to objects


typealias HelpDict Dict{Symbol,Any}
# keys
# :desc : help text
# :mod : module which contains this object/concept/keyword (if applicable)
type HelpEntry
desc::String # description
mod::Union(Module,String,Nothing) # module of entry. This is useful for type instances
# for which figuring out the defining module is hard.
HelpEntry() = new("", nothing)
HelpEntry(desc) = new(desc, nothing)
HelpEntry(desc, mod) = new(desc, mod)
end

# internal data
NOOBJ_DICT = Dict{String, HelpDict}() # To keep documentation for non-object entities, like keywords, ccall, &&, ||, ", ', etc.
NOOBJ_DICT = Dict{String, HelpEntry}() # To keep documentation for non-object entities, like keywords, ccall, &&, ||, ", ', etc.
# And also for macros as they cannot be used as dict-keys directly.
APROPOS_DICT = ObjectIdDict() # this is used in apropos search. Maps objects to their help text [:desc].
APROPOS_DICT[NOOBJ_DICT] = Dict{String, String}() # to hold the apropos for stuff in NOOBJ_DICT
INIT_OLD_HELP = true # flag

# low level functions
##
function hasdoc(obj)
if hasmeta(obj, :doc)
true
elseif haskey(NOOBJ_DICT, obj)
if hasmeta(obj, :doc) || haskey(NOOBJ_DICT, obj)
true
else
false
end
end

function getdoc(obj)
doc = getdoc(obj, nothing)
doc==nothing ? error("No documentation associated with $obj") : doc
end
function getdoc(obj, default)
doc = getmeta(obj, :doc, nothing)
if doc!=nothing
doc[:doc]
if hasmeta(obj, :doc)
getmeta(obj, :doc)
elseif haskey(NOOBJ_DICT, obj)
NOOBJ_DICT[obj]
else
default
end
end
function getdoc(obj)
doc = getdoc(obj, nothing)
doc==nothing ? error("No documentation associated with $obj") : doc
function getdoc!(obj; string_into_meta=false)
# If string_into_meta==true then add the doc to the metadata of the
# string-object, otherwise is goes into the NOOBJ_DICT (default).
if hasdoc(obj)
return getdoc(obj)
else
he = HelpEntry()
setdoc!(obj, he; string_into_meta=string_into_meta)
return he
end
end

function setdoc!(obj, doc::HelpDict; string_into_meta=false)

function setdoc!(obj, doc::HelpEntry; string_into_meta=false)
# If string_into_meta==true then add the doc to the metadata of the
# string-object, otherwise is goes into the NOOBJ_DICT (default).
if !isa(obj, String) || string_into_meta
setmeta!(obj, :doc, doc)
APROPOS_DICT[obj] = doc[:desc]
else
NOOBJ_DICT[obj] = doc
APROPOS_DICT[NOOBJ_DICT][obj] = doc[:desc]
end
nothing
end

# making docs from helpdb.jl
Expand All @@ -82,8 +94,8 @@ function init_help()
INIT_OLD_HELP = false
println("Loading help data...")
helpdb = evalfile(helpdb_filename())
for hd in helpdb
mod_,obj,desc = hd
for he in helpdb
mod_,obj,desc = he
# split objects up between what goes into META and what
# goes into NOOBJ_DICT
if obj[1]=='@' # a macro
Expand All @@ -105,14 +117,9 @@ function init_help()
catch
mod_ = mod_ # keep as string
end
if !hasdoc(obj) # do not overwrite existing doc
hdnew = HelpDict()
hdnew[:desc]= desc; hdnew[:mod]=mod_;
setdoc!(obj, hdnew)
else # append to it
hdnew = getdoc(obj)
hdnew[:desc] *= "\n$(string(mod_)).$desc"
end
henew = getdoc!(obj)
henew.desc *= desc
henew.mod = mod_
end
end
end
Expand All @@ -121,10 +128,7 @@ end
##
function doc(obj, docstr::String; mod=nothing, string_into_meta=false)
# the module cannot be set automatically with this function, use the macro instead
hd = HelpDict()
hd[:desc] = docstr
hd[:mod] = mod
setdoc!(obj, hd; string_into_meta=string_into_meta)
setdoc!(obj, HelpEntry(docstr, mod); string_into_meta=string_into_meta)
end

macro doc(args...)
Expand All @@ -134,8 +138,6 @@ macro doc(args...)
"""
if length(args)==2
docstr, obj = args
elseif length(args)==3
docstr, obj = args
else
error(errmsg)
end
Expand Down Expand Up @@ -223,9 +225,9 @@ end

function help(io::IO, obj)
init_help()
docdict = getdoc(obj, nothing)
if docdict!=nothing
out = _decor_help_desc(obj, docdict[:mod], docdict[:desc])
if hasdoc(obj)
he = getdoc(obj)
out = _decor_help_desc(obj, he.mod, he.desc)
print(io, out)
else
if isa(obj, DataType) # try to print something generic:
Expand Down Expand Up @@ -259,27 +261,28 @@ help(args...) = help(STDOUT, args...)

apropos(s::String) = apropos(STDOUT, s)
function apropos(io::IO, txt::String)
# This could be done better.
init_help()
n = 0
r = Regex("\\Q$txt", Base.PCRE.CASELESS)
for (obj, desc) in APROPOS_DICT
if isequal(obj,NOOBJ_DICT)
for (objj, desc) in obj
if ismatch(r, string(obj)) || ismatch(r, desc)
println(io, "Object: \"$objj\"")
n = 1
end
end
else
println(io, "The string '$txt' appears in the documentation of following objects:")
for (obj, cont) in Base._META
if hasdoc(obj)
desc = getdoc(obj).desc
if ismatch(r, string(obj)) || ismatch(r, desc)
println(io, "Object: \"$obj\"")
n = 1
println(io, string(obj))
end
end
n+=1
end
for (obj, cont) in NOOBJ_DICT
desc = cont.desc
if ismatch(r, obj) || ismatch(r, desc)
println(io, obj)
end
n+=1
end
if n == 0
println(io, "No help information found.")
println(io, "Non occurrence found.")
end
end

Expand Down
44 changes: 24 additions & 20 deletions base/metadata.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,46 @@
# Each metadata entry is just a dict Any=>Any:
typealias MetaData Dict{Any,Any}

# The Dict for _META: ideally this would be a typed, weak-key
# ObjectIdDict. --> make one...

# The Dict for META:
typealias MetaDict WeakObjectIdDict{Any, MetaData}
#typealias MetaDict WeakKeyDict{Any, MetaData} # Problem: does not work for immutables
#typealias MetaDict Dict{Any, MetaData} # Problem: if a mutable changes the hash changes
#typealias MetaDict ObjectIdDict
typealias MetaDict WeakObjectIdDict{Any, MetaData}
#typealias MetaDict ObjectIdDict # Problem: does not free references

META = MetaDict() # this holds all the metadata

const secret_default = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5903__
## metadata interface:
# (If defining type which also holds metadata, implement the "#*"
# methods for it)

_META = MetaDict() # this holds all the metadata
hasmeta(obj) = haskey(META, obj) #*
hasmeta(obj, key) = hasmeta(obj) && haskey(getmeta(obj), key)

getmeta(obj) = _META[obj]
getmeta(obj, key) = _META[obj][key]
getmeta(obj) = META[obj] #*
getmeta(obj, key) = getmeta(obj)[key]
function getmeta(obj, key, default)
out = get(_META, obj, secret_default)
out==secret_default ? default : out
if hasmeta(obj)
return get!(getmeta(obj), key, default)
else
default
end
end

getmeta!(obj) = get!(_META, obj, MetaData())
getmeta!(obj) = get!(META, obj, MetaData()) #*
function getmeta!(obj, key, default)
md = getmeta!(obj)
return get!(md, key, default)
end

setmeta!(obj, md::MetaData) = ( _META[obj] = md )
setmeta!(obj, md::MetaData) = ( META[obj] = md ) #*
function setmeta!(obj, key, value)
md = getmeta!(obj)
md[key] = value
end

hasmeta(obj) = haskey(_META, obj)
hasmeta(obj, key) = hasmeta(obj) && haskey(getmeta(obj), key)

# note: this only clears the central Base._META dict, meta-storage in
# This only clears the central Base.META dict, meta-storage in
# used-defined types will not be affected:
emptymeta!() = (global _META = MetaDict(); nothing)
deletemeta!(obj) = (delete!(_META, obj); nothing)
deletemeta!(obj, key) = (delete!(_META[obj], key); nothing)
emptymeta!() = (global META = MetaDict(); nothing)

deletemeta!(obj) = (delete!(META, obj); nothing) #*
deletemeta!(obj, key) = (delete!(getmeta(obj), key); nothing)
Loading

0 comments on commit ba9b6e4

Please sign in to comment.