Convert remaining old style `catch' to `with-throw-handler'
[clinton/bobotpp.git] / source / BotInterp.C
CommitLineData
cb21075d 1// BotInterp.C -*- C++ -*-
2// Copyright (c) 1998 Etienne BERNARD
46af1667 3// Copyright (C) 2002,2005,2008 Clinton Ebadi
cb21075d 4
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// (at your option) any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
ae97d6ec 17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18// 02110-1301, USA.
cb21075d 19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
3c37f9a8 24#include <algorithm>
25
cb21075d 26#include "BotInterp.H"
27
cfa82921 28#include "Bot.H"
29#include "Interp.H"
30#include "Utils.H"
31
cb21075d 32#ifdef USESCRIPTS
33
34#include <libguile.h>
35extern "C"
36{
37#include <libguile/regex-posix.h>
38}
39
2d3af8a4 40bool
41Hook::operator< (const Hook & h) const
42{
43 if (priority < h.priority)
44 {
45 return true;
46 }
47 else if (priority > h.priority)
48 {
49 return false;
50 }
51 else if (fallthru && h.fallthru)
52 {
53 return false;
54 }
55 else if (fallthru && !h.fallthru)
56 {
57 return false;
58 }
59 else if (!fallthru && h.fallthru)
60 {
61 return true;
62 }
63 else
64 {
65 // NOTE: This should never be reached
66 return false;
67 }
68}
69
cb21075d 70BotInterp::BotInterp(Bot *b, String fn)
7a9d1172 71 : bot(b), counter(0),
72 hook_mutex (true), timer_mutex (true)
cb21075d 73{
db098a03 74 logPort = scm_open_file (Utils::str2scm (fn),
75 Utils::str2scm ("a"));
76
cb21075d 77 scm_gc_protect_object(logPort);
78}
79
80void
81BotInterp::Execute(String command)
82{
83 Interp::Execute(bot, command);
84}
85
86void
87BotInterp::LoadScript(String filename)
88{
89 Interp::LoadScript(bot, filename);
90}
91
d56bdd22 92SCM
93BotInterp::ScriptLog()
cb21075d 94{
d56bdd22 95 return logPort;
cb21075d 96}
97
be3612f3 98namespace
99{
7a9d1172 100 struct HookFind
101 {
102 std::string rx;
103 std::string name;
104
105 HookFind (std::string r, std::string n)
106 : rx (r), name (n)
107 { }
108
109 bool operator() (const Hook * hook) const
110 { return hook->regex_str == rx && hook->name == name; }
111 };
be3612f3 112}
113
cb21075d 114bool
fd7440f1 115BotInterp::AddHook(int hooktype, SCM regex, SCM function, int pri, bool fall,
7a9d1172 116 std::string name)
3c37f9a8 117{
cb21075d 118 if (scm_string_p(regex) == SCM_BOOL_F)
119 return false;
7a9d1172 120
121 BotLock hook_lock (hook_mutex);
122 std::string rx = Utils::to_upper (Utils::scm2str (regex));
46af1667 123 SCM r = scm_make_regexp (regex,
124 scm_list_n (scm_variable_ref (scm_c_lookup ("regexp/icase")),
125 SCM_UNDEFINED));
7a9d1172 126 HookFind hook_find (rx, name);
127 HookList& hook_list = hooks[hooktype];
128
cb21075d 129 scm_gc_protect_object(r);
130 scm_gc_protect_object(function);
3c37f9a8 131
7a9d1172 132 HookList::iterator it = std::find_if (hook_list.begin (),
133 hook_list.end (),
134 hook_find);
135
136 if (it != hook_list.end())
137 {
138 Hook * found = *it;
be3612f3 139
7a9d1172 140 scm_gc_unprotect_object(found->function);
cb21075d 141 scm_gc_unprotect_object (r);
7a9d1172 142
143 found->function = function;
144 found->priority = pri;
145 found->fallthru = fall;
146
147 hook_list.erase (it);
148 Utils::push_sorted (hook_list, found, hook_sort_p);
149
150 return true;
151 }
152 else
153 {
154 Utils::push_sorted (hook_list,
155 new Hook(hooktype, rx, r, function, pri, fall, name),
156 hook_sort_p);
157
cb21075d 158 return true;
159 }
cb21075d 160}
161
162bool
7a9d1172 163BotInterp::RunHooks(int hooktype, std::string match, SCM args)
cb21075d 164{
3c37f9a8 165 BotLock hook_lock (hook_mutex);
166
cb21075d 167 SCM result;
cb21075d 168 wrapper_data wd;
169 wd.args = args;
3c37f9a8 170
7a9d1172 171 // We want to execute higher priority hooks first, so we start at
172 // the end of the list instead of the beginning
173
174 for (HookList::reverse_iterator it = hooks[hooktype].rbegin();
175 it != hooks[hooktype].rend();
176 ++it)
177 {
7a9d1172 178 if (scm_regexp_exec((*it)->regex, Utils::str2scm (match),
179 SCM_UNDEFINED, SCM_UNDEFINED) != SCM_BOOL_F)
180 {
7a9d1172 181 bool fallthru_p = (*it)->fallthru;
182 wd.func = (*it)->function;
8cd2bd26
CE
183 result = scm_c_with_throw_handler (SCM_BOOL_T,
184 (scm_t_catch_body)
185 Interp::LazyApplyWrapper,
186 static_cast<void *> (&wd),
187 (scm_t_catch_handler) Interp::EmptyHandler,
188 0,
189 0);
7a9d1172 190 if (!fallthru_p)
191 break;
192 }
cb21075d 193 }
7a9d1172 194
cb21075d 195 return true;
196}
197
198SCM
199BotInterp::AddTimer(int delay, SCM function)
200{
3c37f9a8 201 BotLock timer_lock (timer_mutex);
cb21075d 202 int when = time(NULL) + delay;
3c37f9a8 203
cb21075d 204 scm_gc_protect_object(function);
3c37f9a8 205
206 Timer *timer = new Timer (++counter, when, function);
66f62418 207 Utils::push_sorted (timers, timer, timer_sort_p);
3c37f9a8 208
209 return scm_from_int (counter);
210
cb21075d 211}
212
213bool
214BotInterp::DelTimer(SCM timer)
215{
3c37f9a8 216 BotLock timer_lock (timer_mutex);
cb21075d 217
3c37f9a8 218 int count = scm_to_int (timer);
219 TimerList::iterator it = timers.begin();
220 TimerList::iterator end = timers.end();
221
222 for ( ; it != end; ++it)
223 {
224 if ((*it)->count == count)
225 {
226 scm_gc_unprotect_object((*it)->function);
227 delete (*it);
228 timers.erase(it);
229
230 return true;
231 }
cb21075d 232 }
3c37f9a8 233
cb21075d 234 return false;
235}
236
237bool
238BotInterp::RunTimers(int now)
239{
3c37f9a8 240 BotLock timer_lock (timer_mutex);
cb21075d 241 struct wrapper_data wd;
46af1667 242 wd.args = scm_list_n (SCM_UNDEFINED);
cb21075d 243
3c37f9a8 244 while (!timers.empty ())
245 {
246 // Keep a stack allocated copy of the front of the timer queue
247 // just in case the timer is deleted while being executed (which
248 // is very unlikely as the only place this could occur is if the
249 // timer deleted itself)
250 Timer current_timer = *timers.front () ;
251
252 if (current_timer.when <= now)
253 {
254 wd.func = current_timer.function;
255
8cd2bd26
CE
256 scm_c_with_throw_handler (SCM_BOOL_T,
257 (scm_t_catch_body) Interp::LazyApplyWrapper,
258 (void *)&wd,
259 (scm_t_catch_handler) Interp::EmptyHandler,
260 0,
261 0);
3c37f9a8 262
263 // The timer list may have been modified by the timer
264 // callback; if it has in such a way that the first queue
265 // item has changed (adding a timer in the past) then we
266 // switch the slow path for deleting a timer
267 if (current_timer.count == timers.front()->count)
268 {
269 scm_gc_unprotect_object (current_timer.function);
270 delete timers.front ();
271 timers.pop_front ();
272 }
273 else
274 {
275 DelTimer (scm_from_int (current_timer.count));
276 }
277 }
278 else
279 {
280 break;
281 }
cb21075d 282 }
3c37f9a8 283
cb21075d 284 return true;
285}
286
287#endif