diff --git a/src/compiler/GF/Interactive.hs b/src/compiler/GF/Interactive.hs index 4e15768be..dd11f6f48 100644 --- a/src/compiler/GF/Interactive.hs +++ b/src/compiler/GF/Interactive.hs @@ -525,7 +525,7 @@ wordCompletion gfenv (left,right) = do -> Haskeline.completeFilename (left,right) CmplIdent _ pref -> case pgfenv gfenv of - (_,Just pgf,_) -> ret (length pref) [Haskeline.simpleCompletion name | name <- functions pgf, isPrefixOf pref name] + (_,Just pgf,_) -> ret (length pref) [Haskeline.simpleCompletion name | name <- functionsByPrefix pgf pref] _ -> ret (length pref) [] _ -> ret 0 [] where diff --git a/src/runtime/c/pgf/namespace.h b/src/runtime/c/pgf/namespace.h index a5cd0ea96..92b72b82e 100644 --- a/src/runtime/c/pgf/namespace.h +++ b/src/runtime/c/pgf/namespace.h @@ -627,6 +627,32 @@ void namespace_iter(Namespace map, PgfItor* itor, PgfExn *err) return; } +template +void namespace_iter_prefix(Namespace map, PgfText *prefix, PgfItor* itor, PgfExn *err) +{ + if (map == 0) + return; + + int cmp = textcmp_prefix(prefix,&map->value->name); + if (cmp < 0) + namespace_iter_prefix(map->left, prefix, itor, err); + else if (cmp > 0) + namespace_iter_prefix(map->right, prefix, itor, err); + else { + namespace_iter_prefix(map->left, prefix, itor, err); + if (err->type != PGF_EXN_NONE) + return; + + itor->fn(itor, &map->value->name, map->value.as_object(), err); + if (err->type != PGF_EXN_NONE) + return; + + namespace_iter_prefix(map->right, prefix, itor, err); + if (err->type != PGF_EXN_NONE) + return; + } +} + template Namespace namespace_map(Namespace map, std::function(ref)> f) { diff --git a/src/runtime/c/pgf/pgf.cxx b/src/runtime/c/pgf/pgf.cxx index bca9f9bda..1c877602e 100644 --- a/src/runtime/c/pgf/pgf.cxx +++ b/src/runtime/c/pgf/pgf.cxx @@ -487,6 +487,19 @@ void pgf_iter_functions(PgfDB *db, PgfRevision revision, } PGF_API_END } +PGF_API +void pgf_iter_functions_by_prefix(PgfDB *db, PgfRevision revision, + PgfText *prefix, PgfItor *itor, PgfExn *err) +{ + PGF_API_BEGIN { + DB_scope scope(db, READER_SCOPE); + ref pgf = db->revision2pgf(revision); + + pgf_exn_clear(err); + namespace_iter_prefix(pgf->abstract.funs, prefix, itor, err); + } PGF_API_END +} + PGF_API void pgf_iter_functions_by_cat(PgfDB *db, PgfRevision revision, PgfText *cat, PgfItor *itor, PgfExn *err) diff --git a/src/runtime/c/pgf/pgf.h b/src/runtime/c/pgf/pgf.h index 9ab9f733f..a1ccccd0d 100644 --- a/src/runtime/c/pgf/pgf.h +++ b/src/runtime/c/pgf/pgf.h @@ -336,6 +336,10 @@ PGF_API_DECL void pgf_iter_functions(PgfDB *db, PgfRevision revision, PgfItor *itor, PgfExn *err); +PGF_API_DECL +void pgf_iter_functions_by_prefix(PgfDB *db, PgfRevision revision, + PgfText *prefix, PgfItor *itor, PgfExn *err); + PGF_API_DECL void pgf_iter_functions_by_cat(PgfDB *db, PgfRevision revision, PgfText *cat, PgfItor *itor, PgfExn *err); diff --git a/src/runtime/c/pgf/text.cxx b/src/runtime/c/pgf/text.cxx index 63a8b98eb..dec691cb6 100644 --- a/src/runtime/c/pgf/text.cxx +++ b/src/runtime/c/pgf/text.cxx @@ -16,6 +16,22 @@ int textcmp(PgfText *t1, PgfText *t2) } } +PGF_INTERNAL +int textcmp_prefix(PgfText *t1, PgfText *t2) +{ + for (size_t i = 0; ; i++) { + if (i >= t1->size) + return 0; + if (i >= t2->size) + return 1; + + if (t1->text[i] > t2->text[i]) + return 1; + else if (t1->text[i] < t2->text[i]) + return -1; + } +} + PGF_INTERNAL void texticmp(PgfText *t1, PgfText *t2, int res[2]) { diff --git a/src/runtime/c/pgf/text.h b/src/runtime/c/pgf/text.h index 919bea8e4..9f0752b80 100644 --- a/src/runtime/c/pgf/text.h +++ b/src/runtime/c/pgf/text.h @@ -5,6 +5,10 @@ PGF_INTERNAL_DECL int textcmp(PgfText *t1, PgfText *t2); +/* The same as textcmp but returns 0 if t1 is a prefix of t2. */ +PGF_INTERNAL_DECL +int textcmp_prefix(PgfText *t1, PgfText *t2); + /* Performs both case-insensitive and case-sensitive comparison. * The first element in res contains the result from * the case-insensitive comparison. The second the result diff --git a/src/runtime/haskell/PGF2.hsc b/src/runtime/haskell/PGF2.hsc index 9d256d2f3..42fb96105 100644 --- a/src/runtime/haskell/PGF2.hsc +++ b/src/runtime/haskell/PGF2.hsc @@ -24,7 +24,7 @@ module PGF2 (-- * PGF Cat,categories,categoryContext,categoryProbability, -- ** Functions - Fun, functions, functionsByCat, + Fun, functions, functionsByPrefix, functionsByCat, functionType, functionIsConstructor, functionProbability, -- ** Expressions @@ -1094,6 +1094,26 @@ functions p = name <- peekText key writeIORef ref $ (name : names) +-- | List of all functions whose names start with a given prefix +functionsByPrefix :: PGF -> String -> [Fun] +functionsByPrefix p prefix = + unsafePerformIO $ do + ref <- newIORef [] + (withText prefix $ \c_prefix -> + allocaBytes (#size PgfItor) $ \itor -> + bracket (wrapItorCallback (getFunctions ref)) freeHaskellFunPtr $ \fptr -> + withForeignPtr (a_revision p) $ \c_revision -> do + (#poke PgfItor, fn) itor fptr + withPgfExn "functions" (pgf_iter_functions_by_prefix (a_db p) c_revision c_prefix itor) + fs <- readIORef ref + return (reverse fs)) + where + getFunctions :: IORef [String] -> ItorCallback + getFunctions ref itor key _ exn = do + names <- readIORef ref + name <- peekText key + writeIORef ref $ (name : names) + -- | List of all functions defined in the abstract syntax functionsByCat :: PGF -> Cat -> [Fun] functionsByCat p cat = diff --git a/src/runtime/haskell/PGF2/FFI.hsc b/src/runtime/haskell/PGF2/FFI.hsc index a6fd70044..d1f338a0e 100644 --- a/src/runtime/haskell/PGF2/FFI.hsc +++ b/src/runtime/haskell/PGF2/FFI.hsc @@ -173,6 +173,8 @@ foreign import ccall pgf_category_prob :: Ptr PgfDB -> Ptr PGF -> Ptr PgfText -> foreign import ccall pgf_iter_functions :: Ptr PgfDB -> Ptr PGF -> Ptr PgfItor -> Ptr PgfExn -> IO () +foreign import ccall pgf_iter_functions_by_prefix :: Ptr PgfDB -> Ptr PGF -> Ptr PgfText -> Ptr PgfItor -> Ptr PgfExn -> IO () + foreign import ccall pgf_iter_functions_by_cat :: Ptr PgfDB -> Ptr PGF -> Ptr PgfText -> Ptr PgfItor -> Ptr PgfExn -> IO () foreign import ccall pgf_function_type :: Ptr PgfDB -> Ptr PGF -> Ptr PgfText -> Ptr PgfUnmarshaller -> Ptr PgfExn -> IO (StablePtr Type)