35
36:- module(md_eval,
37 [ swish_provides/1 38 ]). 39:- use_module(library(modules)). 40:- use_module(library(apply)). 41:- use_module(library(lists)). 42:- use_module(library(debug)). 43:- use_module(library(option)). 44:- use_module(library(occurs)). 45:- use_module(library(settings)). 46:- use_module(library(error)). 47:- use_module(library(pldoc/doc_wiki)). 48:- use_module(library(dcg/basics)). 49:- use_module(library(time)). 50
51:- use_module(config). 52
53:- multifile
54 provides/1. 55
56:- setting(time_limit, number, 10,
57 "Timit limit for evaluating a ```{eval} cell"). 58
70
81
82eval_dom(DOM0, DOM, Options) :-
83 contains_eval(DOM0),
84 !,
85 in_temporary_module(
86 Module,
87 prepare(Module),
88 md_eval(Module, Options, DOM0, DOM, 0, _)).
89
90prepare(Module) :-
91 setting(swish:program_space, SpaceLimit),
92 set_module(Module:program_space(SpaceLimit)),
93 delete_import_module(Module, user),
94 add_import_module(Module, swish, start).
95
97
98md_eval(Module, Options, Pre, Evaluated, I0, I) :-
99 pre(Pre, eval, Code),
100 !,
101 eval(Module, I0, Code, Evaluated, Options),
102 I is I0 + 1.
103md_eval(Module, Options, Compound, Evaluated, I0, I) :-
104 compound(Compound),
105 !,
106 compound_name_arguments(Compound, Name, Args0),
107 foldl(md_eval(Module, Options), Args0, Args, I0, I),
108 compound_name_arguments(Evaluated, Name, Args).
109md_eval(_, _, DOM, DOM, I, I) :-
110 !.
111
112:- meta_predicate
113 call_collect_messages(0, -). 114
115eval(Module, I, Code, div(class(eval), Evaluated), Options) :-
116 option(time_limit(Limit), Options, 10),
117 catch(( call_collect_messages(
118 call_with_time_limit(
119 Limit,
120 do_eval(Module, I, Code, Evaluated0, Options)),
121 Messages),
122 append(Evaluated0, Messages, Evaluated)
123 ),
124 Error,
125 failed(Error, Evaluated)).
126
127do_eval(Module, I, Code, [div(class(output), DOM)], _Options) :-
128 debug(md(eval), 'Evaluating ~p', [Code]),
129 format(atom(Id), 'eval://~w-~w', [Module, I]),
130 with_output_to(
131 codes(Codes),
132 setup_call_cleanup(
133 open_string(Code, In),
134 load_files(Module:Id,
135 [ stream(In),
136 sandboxed(true)
137 ]),
138 close(In))),
139 eval_to_dom(Codes, DOM).
140
141eval_to_dom(Codes, DOM) :-
142 phrase(is_html, Codes),
143 E = error(_,_),
144 catch(setup_call_cleanup(
145 open_string(Codes, In),
146 load_html(In, DOM, []),
147 close(In)),
148 E, fail),
149 !.
150eval_to_dom(Codes, DOM) :-
151 wiki_codes_to_dom(Codes, [], DOM).
152
153is_html -->
154 blanks, "<", tag(Tag),
155 string(_),
156 "</", tag(Tag), ">", blanks.
157
158tag([H|T]) -->
159 alpha(H),
160 alphas(T).
161
162alpha(H) -->
163 [H],
164 { between(0'a, 0'z, H) }.
165
166alphas([H|T]) -->
167 alpha(H),
168 !,
169 alphas(T).
170alphas([]) -->
171 [].
172
173contains_eval(DOM) :-
174 sub_term(Pre, DOM),
175 nonvar(Pre),
176 pre(Pre, eval, _),
177 !.
178
179pre(pre(Attrs, Text), Ext, Text) :-
180 atomic(Text),
181 is_list(Attrs),
182 ground(Attrs),
183 memberchk(ext(Ext), Attrs).
184
185:- thread_local
186 saved_message/1. 187
188failed(Error, [div(class(error), Message)]) :-
189 message_to_string(Error, Message).
190
191call_collect_messages(Goal, Messages) :-
192 setup_call_cleanup(
193 asserta((user:thread_message_hook(Term, Kind, Lines) :-
194 save_message(Term, Kind, Lines)), Ref),
195 Goal,
196 collect_messages(Ref, Messages)).
197
198save_message(_Term, Kind, Lines) :-
199 kind_prefix(Kind, Prefix),
200 with_output_to(
201 string(Msg),
202 print_message_lines(current_output, Prefix, Lines)),
203 assertz(saved_message(pre(class([eval,Kind]), Msg))).
204
205kind_prefix(error, '% ERROR: ').
206kind_prefix(warning, '% Warning: ').
207
208collect_messages(Ref, Messages) :-
209 erase(Ref),
210 findall(Msg, retract(saved_message(Msg)), Messages).
211
212
213 216
227
228swish_provides(plugin(Plugin)) :-
229 swish_has_plugin(Plugin).
230swish_provides(Term) :-
231 provides(Term).
232
233
238
239swish_has_plugin(Name) :-
240 var(Name), !,
241 distinct(Dir,
242 absolute_file_name(
243 config_enabled(.),
244 Dir,
245 [ solutions(all),
246 file_type(directory)
247 ])),
248 directory_files(Dir, Files),
249 member(File, Files),
250 directory_file_path(Dir, File, Source),
251 source_file(Source),
252 file_name_extension(Name, _, File).
253swish_has_plugin(Name) :-
254 must_be(atom, Name),
255 absolute_file_name(
256 config_enabled(Name),
257 File,
258 [ solutions(all),
259 file_type(prolog)
260 ]),
261 source_file(File),
262 !.
263
264sandbox:safe_primitive(md_eval:swish_provides(_)).
265
266
267 270
271:- multifile
272 swish_markdown:dom_expansion/2. 273
274swish_markdown:dom_expansion(DOM0, DOM) :-
275 setting(time_limit, Limit),
276 Limit > 0,
277 eval_dom(DOM0, DOM, [time_limit(Limit)])