lbp_completion.cpp
Go to the documentation of this file.
1 /*
2 ********************************************************************
3 ********************************************************************
4 ********************************************************************
5 
6  Lua completer, derived from Lua advanced readline
7  support patch, with original copyright al follows:
8 
9  Copyright (C) 2004-2006 Mike Pall.
10  Same license as Lua. See lua.h.
11 
12  The below version is adapted to go with SWIG generated
13  __index functions (as opposed to the perhaps more common
14  __index tables). The interface was changed to a more
15  C++ like style, to make a better match with applications
16  like DESTool. The current implementation is preliminary
17  and requires some clean-up.
18 
19  This file is included by lbp_function.cpp and the functions
20  are called by the LuaState methods for completion.
21 
22  Thomas Moor, 2011.
23 
24 ********************************************************************
25 ********************************************************************
26 ********************************************************************
27 */
28 
29 
30 // Lua keywords
31 static const char *const faudes_rl_keywords[] = {
32  "and", "break", "do", "else", "elseif", "end", "false",
33  "for", "function", "if", "in", "local", "nil", "not", "or",
34  "repeat", "return", "then", "true", "until", "while", NULL
35 };
36 
37 // Test identifier
38 static bool faudes_rl_valididentifier(const std::string& str) {
39  std::string::const_iterator sit=str.begin();
40  if(sit==str.end()) return false;
41  if(!(isalpha(*sit) || *sit=='_')) return false;
42  for(++sit; sit!=str.end(); ++sit)
43  if(!(isalnum(*sit) || *sit=='_')) return true;
44  return true;
45 }
46 
47 // Append compound prefix + string + suffix to list and maintain common prefix.
48 static void faudes_rl_dmadd(std::list< std::string > & mlist, const std::string& prefix, const std::string& str, const std::string& suffix) {
49  // iterate suffxes
50  if(suffix.size()>1) {
51  for(unsigned int i=0;i<suffix.size();i++)
52  faudes_rl_dmadd(mlist,prefix,str,std::string(1,suffix.at(i)));
53  return;
54  }
55  // build compound
56  std::string comp;
57  comp=prefix + str + suffix;
58  // initialize: matching prefix equals first entry
59  if(mlist.size()==0) {
60  mlist.push_back(comp);
61  return;
62  }
63  // initialize: matching prefix equals first entry
64  if(mlist.size()==1) {
65  mlist.push_back(*mlist.begin());
66  mlist.push_back(comp);
67  }
68  // figure maximal matching prefix
69  std::string& match=*mlist.begin();
70  std::string::iterator mit, cit;
71  mit=match.begin();
72  cit=comp.begin();
73  while(mit!=match.end() && cit!=comp.end()) {
74  if(*mit!=*cit) break;
75  mit++; cit++;
76  }
77  // adjust matching prefix in list
78  if(mit!=match.end())
79  match.erase(mit,match.end());
80  // append to list
81  mlist.push_back(comp);
82 }
83 
84 // Get __index field of metatable of object on top of stack
85 // -- return 1 for ok and leave __index field on stack
86 // -- return 0 for error, nothing on stack
87 static int faudes_rl_getmetaindex(lua_State *L) {
88  // object has no metatable, error
89  if(!lua_getmetatable(L, -1))
90  { lua_pop(L, 1); return 0; } // stack:
91  lua_replace(L, -2); // stack: metatable
92  // get the __ index field
93  lua_pushstring(L, "__index");
94  lua_rawget(L, -2); // stack: metatable,__index
95  lua_replace(L, -2); // stack: __index
96  // invald result, error
97  if(lua_isnil(L,-1) || lua_rawequal(L, -1, -2))
98  { lua_pop(L, 1); return 0; } // stack:
99  return 1; // stack: __index
100 }
101 
102 // Get .fn fields of metatable of object on top of stack.
103 // -- the SWIG __index function retrieves methods from the .fn attribute
104 // -- allways puts a table on the stack, empty on error
105 static void faudes_rl_getmetafn(lua_State *L) {
106  // object has no metatable: return empty table
107  if(!lua_getmetatable(L, -1))
108  { lua_pop(L, 1); lua_newtable(L); return; }
109  lua_replace(L, -2); // stack: metatable
110  // get .fn
111  lua_pushstring(L, ".fn");
112  lua_rawget(L, -2); // stack: metatable,.fn
113  lua_replace(L, -2); // stack: .fn
114  if(!lua_istable(L, -1))
115  { lua_pop(L, 1); lua_newtable(L); return; }
116 }
117 
118 // Get .get field of metatable of object on top of stack.
119 // -- the SWIG __index function retrieves member access from the .get attribute
120 // -- allways puts a table on the stack, empty on error
121 static void faudes_rl_getmetaget(lua_State *L) {
122  // object has no metatable: just pop the object
123  if(!lua_getmetatable(L, -1))
124  { lua_pop(L, 1); lua_newtable(L); return; }
125  lua_replace(L, -2); // stack: metatable
126  // get .get
127  lua_pushstring(L, ".get");
128  lua_rawget(L, -2); // stack: metatable,.get
129  lua_replace(L, -2); // stack: .get
130  if(!lua_istable(L, -1))
131  { lua_pop(L, 1); lua_newtable(L); return; }
132 }
133 
134 // Get __index/.fn/.get field of metatable of object on top of stack.
135 // -- allways puts a table on stack, empty of error
136 // -- returns 1 to indicate ussage of SWIG .get table
137 static int faudes_rl_getmeta(lua_State *L, bool has_colon) {
138  // try __index first
139  lua_pushvalue(L,-1); // stack: obj,obj
140  if(faudes_rl_getmetaindex(L)) {
141  // is it a table // stack: obj,_index
142  if(lua_istable(L,-1)) {
143  // is it non-empty? // stack: obj,_index
144  lua_pushnil(L);
145  if(lua_next(L, -2)) {
146  lua_pop(L, 2); // stack: obj,_index
147  lua_replace(L, -2); // stack: _index
148  return 0;
149  }
150  }
151  lua_pop(L,1); // stack: obj1
152  } // stack: obj
153  // colon indicates method, so we use .fn
154  if(has_colon) {
155  faudes_rl_getmetafn(L); // stack: .fn
156  return 0;
157  }
158  // absence of colon indicates member, so we use .get
159  faudes_rl_getmetaget(L); // stack: .get
160  return 1;
161 
162 }
163 
164 
165 // Get field from object on top of stack (without calling metamethods)
166 static int faudes_rl_getfield(lua_State *L, const char *s, size_t n) {
167  int i = 20; // max loop count to limit infinite metatable loops. */
168  do {
169  if(lua_istable(L, -1)) { // if obj is a table, try to get the requested field
170  lua_pushlstring(L, s, n);
171  lua_rawget(L, -2); // stack: obj,field
172  if(!lua_isnil(L, -1)) // ok, got the field
173  {lua_replace(L, -2); return 1;}
174  lua_pop(L, 1); // stack: obj
175  }
176  if(!faudes_rl_getmetaindex(L)) break; // stack: _index
177  } while (--i > 0);
178  lua_pop(L, 1);
179  return 0;
180 } /* 1: obj -- val, 0: obj -- */
181 
182 
183 // actual completer
184 static std::list< std::string > faudes_rl_complete(lua_State *L, const std::string& word) {
185 
186  // C++ style interface
187  std::list< std::string > mlist;
188  const char* text= word.c_str();
189  int start=0;
190  int end=word.size();
191 
192  // other locals
193  const char *s;
194  size_t i, n, dot, loop;
195  int colon;
196  int savetop;
197  bool used_swig_get=false;
198 
199  // bail ot on text that cannot complete to an identifier
200  if (!(text[0] == '\0' || isalpha(text[0]) || text[0] == '_')) return mlist;
201 
202  // recird top of stack
203  savetop = lua_gettop(L);
204 
205  // figure the right most complete field in "word" and
206  // -- leave the table of globals on the stack, if there is no complete field
207  lua_pushvalue(L, LUA_GLOBALSINDEX);
208  for (n = (size_t)(end-start), i = dot = 0, colon=-1; i < n; i++)
209  if (text[i] == '.' || text[i] == ':') {
210  if (!faudes_rl_getfield(L, text+dot, i-dot))
211  { lua_settop(L, savetop); return mlist; } // error
212  dot = i+1; // Points to first char after dot/colon.
213  if(text[i] == ':') colon=dot; // record whether we have seen a colon
214  }
215 
216  // Append all matches against keywords if there is no dot/colon.
217  if (dot == 0)
218  for (i = 0; (s = faudes_rl_keywords[i]) != NULL; i++)
219  if(!strncmp(s, text, n)) faudes_rl_dmadd(mlist, "", std::string(s), " ");
220 
221  // Append all valid matches from all tables/metatables.
222  loop = 0; // Avoid infinite metatable loops.
223  do {
224  if(lua_istable(L, -1) &&
225  (loop == 0 || !lua_rawequal(L, -1, LUA_GLOBALSINDEX)))
226  for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
227  if (lua_type(L, -2) == LUA_TSTRING) {
228  s = lua_tostring(L, -2);
229  // Only match names starting with '_' if explicitly requested.
230  if (!strncmp(s, text+dot, n-dot) && faudes_rl_valididentifier(s) &&
231  (*s != '_' || text[dot] == '_')) {
232  std::string suf = " "; // Default suffix is a space.
233  switch (lua_type(L, -1)) {
234  case LUA_TTABLE: suf = ":."; break;
235  case LUA_TFUNCTION: if(!used_swig_get) suf = "("; break;
236  case LUA_TUSERDATA:
237  if (lua_getmetatable(L, -1)) { lua_pop(L, 1); suf = ":."; }
238  break;
239  }
240  faudes_rl_dmadd(mlist, std::string(text,dot), std::string(s), suf);
241  }
242  }
243  used_swig_get = faudes_rl_getmeta(L,colon>0);
244  } while (++loop < 20);
245 
246  // Fix stack
247  lua_settop(L, savetop);
248 
249  // done
250  return mlist;
251 }
252 
static int faudes_rl_getmetaindex(lua_State *L)
static std::list< std::string > faudes_rl_complete(lua_State *L, const std::string &word)
static int faudes_rl_getfield(lua_State *L, const char *s, size_t n)
static void faudes_rl_getmetafn(lua_State *L)
static int faudes_rl_getmeta(lua_State *L, bool has_colon)
static const char *const faudes_rl_keywords[]
static void faudes_rl_dmadd(std::list< std::string > &mlist, const std::string &prefix, const std::string &str, const std::string &suffix)
static bool faudes_rl_valididentifier(const std::string &str)
static void faudes_rl_getmetaget(lua_State *L)

libFAUDES 2.32b --- 2024.03.01 --- c++ api documentaion by doxygen