]> icculus.org git repositories - duncan/yast2-ruby-bindings.git/blob - src/ruby/YCP.cc
- fix builtins, scr
[duncan/yast2-ruby-bindings.git] / src / ruby / YCP.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                                                                      |
10 | ruby language support                              (C) Novell Inc.   |
11 \----------------------------------------------------------------------/
12
13 Author: Duncan Mac-Vicar <dmacvicar@suse.de>
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version
18 2 of the License, or (at your option) any later version.
19
20 */
21
22 #include <y2/Y2ComponentCreator.h>
23
24 #include <yui/YUIComponent.h>
25 #include <wfm/Y2WFMComponent.h>
26
27 #include <y2/Y2ComponentBroker.h>
28 #include <y2/Y2Namespace.h>
29 #include <y2/Y2Component.h>
30 #include <y2/Y2Function.h>
31
32 #include <ycp/pathsearch.h>
33 #include <ycp/y2log.h>
34 #include <ycp/YBlock.h>
35 #include <ycp/YExpression.h>
36 #include <ycp/YStatement.h>
37 #include <ycp/YCPValue.h>
38 #include <ycp/YCPBoolean.h>
39 #include <ycp/YCPList.h>
40 #include <ycp/YCPMap.h>
41 #include <ycp/YCPString.h>
42 #include <ycp/YCPInteger.h>
43 #include <ycp/YCPFloat.h>
44 #include <ycp/YCPElement.h>
45 #include <ycp/Import.h>
46 #include <ycp/y2log.h>
47
48 #include "ruby.h"
49
50 //#include "YRuby.h"
51 #include "RubyLogger.h"
52 #include "Y2RubyTypePath.h"
53 #include "Y2RubyTypeTerm.h"
54 #include "Y2RubyTypeConv.h"
55 #include "YRuby.h"
56
57 #define GetY2Object(obj, po) \
58     Data_Get_Struct(obj, Y2Namespace, po)
59
60 static VALUE rb_mYaST;
61 static VALUE rb_mUi;
62 static VALUE rb_mYCP;
63
64 // make the compiler happy when
65 // calling rb_define_method()
66 typedef VALUE (ruby_method)(...);
67
68 // more useful macros
69 #define RB_FINALIZER(func) ((void (*)(...))func)
70
71 // this macro saves us from typing
72 // (ruby_method*) & method_name
73 // in rb_define_method
74 #define RB_METHOD(func) ((VALUE (*)(...))func)
75
76 Y2Component *owned_uic = 0;
77 Y2Component *owned_wfmc = 0;
78 //Y2Component *owned_scr = 0;
79
80 extern "C" {
81
82 static
83 Y2Namespace *
84 getNs (const char * ns_name)
85 {
86   Import import(ns_name);  // has a static cache
87   Y2Namespace *ns = import.nameSpace();
88   if (ns == NULL)
89   {
90     y2error ("ruby call: Can't import namespace '%s'", ns_name);
91   }
92   else
93   {
94     ns->initialize ();
95   }
96   return ns;
97 }
98
99 void init_wfm()
100 {
101     y2milestone("init_wfm");
102 //     if (Y2WFMComponent::instance () == 0)
103 //     {
104       y2milestone("WFM init");
105       owned_wfmc = Y2ComponentBroker::createClient ("wfm");
106       if (owned_wfmc == 0)
107       {
108           y2error ("Cannot create WFM component");
109       }
110 //     }
111 }
112
113 // void init_scr()
114 // {
115 //     y2milestone("init_scr");
116 // //     if (Y2WFMComponent::instance () == 0)
117 // //     {
118 //       y2milestone("WFM init");
119 //       owned_scr = Y2ComponentBroker::createClient ("scr");
120 //       if (owned_scr == 0)
121 //       {
122 //           y2error ("Cannot create WFM component");
123 //       }
124 // //     }
125 // }
126
127 static VALUE
128 rb_init_ui( int argc, VALUE *argv, VALUE self )
129 {
130   const char *ui_name = "ncurses";
131
132   if (argc == 1)
133   {
134     Check_Type(argv[0], T_STRING);
135     ui_name = RSTRING(argv[0])->ptr;
136   }
137   else if (argc != 0)
138   {
139     y2error ("zero or one arguments required (ui name, default %s", ui_name);
140     return Qnil;
141   }
142
143   Y2Component *c = YUIComponent::uiComponent ();
144   if (c == 0)
145   {
146     y2debug ("UI component not created yet, creating %s", ui_name);
147
148     c = Y2ComponentBroker::createServer (ui_name);
149     if (c == 0)
150     {
151       y2error ("can't create component %s", ui_name);
152       return Qnil;
153     }
154
155     if (YUIComponent::uiComponent () == 0)
156     {
157       y2error ("component %s is not a UI", ui_name);
158       return Qnil;
159     }
160     else
161     {
162       // got it - initialize, remember
163       c->setServerOptions (0, NULL);
164       owned_uic = c;
165     }
166   }
167   else
168   {
169     y2debug ("UI component already present: %s", c->name ().c_str ());
170   }
171   return Qnil;
172 }
173
174 //forward declaration
175 YCPValue _call_ycp_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv );
176
177 /**
178  * looks a component for a namespace
179  * throws
180  */
181 static VALUE
182 ycp_module_lookup_namespace_component(VALUE self, VALUE name)
183 {
184   Y2Component *c;
185   c = Y2ComponentBroker::getNamespaceComponent(RSTRING(name)->ptr);
186   if (c == NULL)
187   {
188     y2internal("no component can provide namespace %s\n", RSTRING (name)->ptr);
189     rb_raise( rb_eRuntimeError, "no YaST component can provide namespace %s\n", RSTRING (name)->ptr);
190   }
191   y2internal("component name %s\n", c->name().c_str());
192   return Qtrue;
193 }
194
195 /*
196  tries to import a namespace and throws a NameError if
197  failed
198 */
199 static VALUE
200 ycp_module_import_namespace( VALUE self, VALUE namespace_name)
201 {
202   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr);
203   if (ns == NULL)
204   {
205     rb_raise( rb_eNameError, "component cannot import namespace '%s'", RSTRING (namespace_name)->ptr);
206     return Qnil;
207   }
208   else
209   {
210     y2internal("namespace created from %s\n", ns->filename().c_str());
211   }
212   return Qtrue;
213 }
214
215 static VALUE
216 ycp_module_import( VALUE self, VALUE name)
217 {
218   ycp_module_lookup_namespace_component(self,name);
219   return ycp_module_import_namespace(self,name);
220 }
221
222 /**
223  * iterates all symbols in a namespace and yields the
224  * symbol name and category
225  */
226 static VALUE
227 ycp_module_each_symbol(VALUE self, VALUE namespace_name)
228 {
229   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr);
230   if (ns == NULL)
231   {
232     rb_raise( rb_eRuntimeError, "error getting namespace '%s'", RSTRING (namespace_name)->ptr );
233     return Qnil;
234   }
235   else
236   {
237     y2internal("got namespace from %s\n", ns->filename().c_str());
238   }
239
240   for (unsigned int i=0; i < ns->symbolCount(); ++i)
241   {
242     SymbolEntryPtr s = ns->symbolEntry(i);
243     VALUE arr = rb_ary_new();
244     rb_ary_push(arr, rb_str_new2(s->name()));
245     rb_ary_push(arr, ID2SYM(rb_intern(s->catString().c_str())));
246     rb_yield(arr);
247   }
248   return Qnil;
249 }
250
251 /**
252  * helper method
253  */
254 static bool _yield_symbol_entry(const SymbolEntry & s)
255 {
256   VALUE arr = rb_ary_new();
257   rb_ary_push(arr, rb_str_new2(s.name()));
258   rb_ary_push(arr, ID2SYM(rb_intern(s.catString().c_str())));
259   rb_yield(arr);
260   return true;
261 }
262
263 /** 
264  * helper for each_builtin_symbol
265  */
266 static const SymbolEntry * __symbol = 0L;
267 static string __name;
268
269 static
270 bool __find_symbol(const SymbolEntry & s)
271 {
272   if (s.name() == __name)
273   {
274     __symbol = &s;
275     return false;
276   }
277   return true;
278 }
279
280 /**
281  * iterates all symbols in a namespace and yields the
282  * symbol name and category
283  */
284 static VALUE
285 ycp_module_each_builtin_symbol(VALUE self, VALUE name)
286 {
287   extern StaticDeclaration static_declarations;
288   __name = RSTRING(name)->ptr;
289   static_declarations.symbolTable()->forEach(__find_symbol);
290
291   const SymbolEntry *se = __symbol;
292   if (se == NULL)
293   {
294     y2error ("no such builtin '%s'", RSTRING(name)->ptr);
295     rb_raise( rb_eRuntimeError, "no YCP builtin %s\n", RSTRING(name)->ptr);
296   }
297
298   // convert to a YSymbol to access child symbols
299   const YSymbolEntry *ys = dynamic_cast<const YSymbolEntry *>(se);
300   if ( ys )
301   {
302     if ( ys->table() ) ys->table()->forEach(_yield_symbol_entry);
303   }
304   return Qnil;
305 }
306
307 static VALUE
308 ycp_module_each_builtin(VALUE self)
309 {
310   extern StaticDeclaration static_declarations;
311   static_declarations.symbolTable()->forEach(_yield_symbol_entry);
312   return Qnil;
313 }
314
315 /**
316  * Forwards a ruby call to the namespace
317  * First argument is the namespace
318  * then function name and arguments
319  */
320 static VALUE
321 ycp_module_call_ycp_function(int argc, VALUE *argv, VALUE self)
322 {
323   y2internal("Dynamic Proxy: [%d] params\n", argc);
324   VALUE symbol = argv[1];
325   VALUE namespace_name = argv[0];
326
327   // the func name (1st argument, is a symbol
328   // lets convert it to string
329   VALUE symbol_str = rb_funcall(symbol, rb_intern("to_s"), 0);
330   y2internal("Dynamic Proxy: [%s::%s] with [%d] params\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc);
331
332   //Check_Type(argv[0], T_STRING);
333   //y2internal(RSTRING (symbol)->ptr);
334   //Data_Get_Struct( self, class Y2Namespace, ns );
335   //ns = gNameSpaces[self];
336
337   // get the name of the module
338   //VALUE namespace_name = rb_funcall(self, rb_intern("name"), 0);
339
340   ycp_module_lookup_namespace_component(self, namespace_name);
341
342   // import the namespace
343   //Y2Namespace *ns = c->import(RSTRING (namespace_name)->ptr);
344   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr);
345   if (ns == NULL)
346   {
347     rb_raise( rb_eRuntimeError, "Component cannot import namespace '%s' for symbol '%s'", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr );
348     return Qnil;
349   }
350   else
351   {
352     y2internal("Namespace created from %s\n", ns->filename().c_str());
353   }
354
355   y2internal("Namespace %s initialized\n", RSTRING (namespace_name)->ptr);
356
357   TableEntry *sym_te = ns->table()->find (RSTRING(symbol_str)->ptr);
358
359   if (sym_te == NULL)
360   {
361     y2error ("No such symbol %s::%s", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
362     rb_raise( rb_eNameError, "YCP symbol '%s' not found in namespace '%s'", RSTRING(symbol_str)->ptr, RSTRING (namespace_name)->ptr );
363     return Qnil;
364   }
365
366   if (sym_te->sentry ()->isVariable () ||
367       sym_te->sentry ()->isReference ())
368   {
369     y2internal ("Variable or reference %s\n", RSTRING(symbol_str)->ptr);
370     // set the variable
371     //ret_yv = YCP_getset_variable (aTHX_ ns_name, sym_te->sentry (), args);
372   }
373   else
374   { // no indent yet
375     Y2Function* call = ns->createFunctionCall (RSTRING(symbol_str)->ptr, 0 /*Type::fromSignature("list<string>()")*/);
376
377     if (call == NULL)
378     {
379       y2internal ("cannot create function call %s\n", RSTRING(symbol_str)->ptr);
380       rb_raise( rb_eRuntimeError, "can't create call to %s::%s", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
381     }
382
383     // add the parameters
384     for (int i=2; i < argc; i++)
385     {
386       YCPValue v = rbvalue_2_ycpvalue(argv[i]);
387       call->appendParameter (v);
388     }
389     call->finishParameters ();
390
391     YCPValue res = call->evaluateCall ();
392     delete call;
393     y2internal ("call succeded\n");
394     //y2internal ("Result: %i\n", res->asList()->size());
395     return ycpvalue_2_rbvalue(res);
396   }
397   return Qnil;
398 }
399
400 YCPValue _call_ycp_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv )
401 {
402   // access directly the statically declared builtins
403   extern StaticDeclaration static_declarations;
404
405   string qualified_name_s = module_name + "::" + func_name;
406   const char *qualified_name = qualified_name_s.c_str ();
407
408   y2milestone("qualified name '%s', %d args", qualified_name, argc);
409   
410   declaration_t *bi_dt = static_declarations.findDeclaration (qualified_name);
411   if (bi_dt == NULL)
412   {
413     y2error ("no such builtin '%s'", qualified_name);
414     rb_raise( rb_eRuntimeError, "no YCP builtin %s\n", RSTRING(qualified_name)->ptr);
415     return YCPNull ();
416   }
417   y2milestone("builtin '%s' found.", module_name.c_str());
418   // construct a builtin call using the proper overloaded builtin
419   YEBuiltin *bi_call = new YEBuiltin (bi_dt);
420
421   // attach the parameters:
422
423   // we would like to know the destination type so that we could
424   // convert eg a Ruby scalar to a YCP symbol, but because the
425   // builtins may be overloaded, let's say we want Any
426   // maybe a special exceptional hack to make Path for the 1st argument?
427   // go through the actual parameters
428   int j;
429   for (j = 0; j < argc; ++j)
430   {
431     // convert the value according to the expected type:
432     constTypePtr param_tp = (j == 0)? Type::Path : Type::Any;
433     
434     // convert the first argument to a path
435     YCPValue param_v;
436     // HACK SCR path conversion
437     if ( (j == 0) && (module_name == "SCR") )
438       param_v = rbvalue_2_ycppath(argv[j]);
439     else
440       param_v = rbvalue_2_ycpvalue(argv[j] /*, param_tp */);
441     
442     if (param_v.isNull ())
443     {
444       // an error has already been reported, now refine it.
445       // Can't know parameter name?
446       y2error ("... when passing parameter #%u to builtin %s",
447         j, qualified_name);
448       return YCPNull ();
449     }
450     // Such YConsts without a specific type produce invalid
451     // bytecode. (Which is OK here)
452     // The actual parameter's YCode becomes owned by the function call?
453     YConst *param_c = new YConst (YCode::ycConstant, param_v);
454     // for attaching the parameter, must get the real type so that it matches
455     constTypePtr act_param_tp = Type::vt2type (param_v->valuetype ());
456     // Attach the parameter
457     // Returns NULL if OK, Type::Error if excessive argument
458     // Other errors (bad code, bad type) shouldn't happen
459     constTypePtr err_tp = bi_call->attachParameter (param_c, act_param_tp);
460     if (err_tp != NULL)
461     {
462         if (err_tp->isError ())
463         {
464           // TODO really need to know the place in Ruby code
465           // where we were called from.
466           y2error ("Excessive parameter to builtin %s", qualified_name);
467         }
468         else
469         {
470           y2internal ("attachParameter returned %s", err_tp->toString ().c_str ());
471         }
472         return YCPNull ();
473     }
474   } // for each actual parameter
475
476   // now must check if we got fewer parameters than needed
477   // or there was another error while resolving the overload
478   constTypePtr err_tp = bi_call->finalize (RubyLogger::instance ());
479   if (err_tp != NULL)
480   {
481     // apparently the error was already reported?
482     y2error ("Error type %s when finalizing builtin %s",
483     err_tp->toString ().c_str (), qualified_name);
484     return YCPNull ();
485   }
486
487   // go call it now!
488   y2debug ("Ruby is calling builtin %s", qualified_name);
489   YCPValue ret_yv = bi_call->evaluate (false /* no const subexpr elim */);
490   delete bi_call;
491
492   return ret_yv;
493 }
494
495 VALUE
496 ycp_module_call_ycp_builtin( int argc, VALUE *argv, VALUE self )
497 {
498   VALUE symbol = argv[1];
499   VALUE namespace_name = argv[0];
500
501   // the func name (1st argument, is a symbol
502   // lets convert it to string
503   VALUE symbol_str = rb_funcall(symbol, rb_intern("to_s"), 0);
504
505   y2internal("builtin proxy: [%s::%s] is a builtin call with %d params\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc);
506   YCPValue res;
507   res = _call_ycp_builtin( RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc-2, argv+2);
508   return ycpvalue_2_rbvalue(res);
509 }
510 //y2_logger_helper
511
512 //y2_logger (level, comp, file, line, function, "%s", message);
513
514 static VALUE
515 rb_y2_logger( int argc, VALUE *argv, VALUE self )
516 {
517   Check_Type(argv[0], T_FIXNUM);
518   Check_Type(argv[1], T_STRING);
519   Check_Type(argv[2], T_STRING);
520   Check_Type(argv[3], T_FIXNUM);
521   Check_Type(argv[4], T_STRING);
522
523   int i;
524   for ( i = 5; i < argc; i++)
525   {
526     Check_Type(argv[i], T_STRING);
527   }
528   y2_logger((loglevel_t)NUM2INT(argv[0]),RSTRING(argv[1])->ptr,RSTRING(argv[2])->ptr,NUM2INT(argv[3]),"",RSTRING(argv[5])->ptr);
529   return Qnil;
530 }
531
532 } //extern C
533
534 extern "C"
535 {
536   void
537   Init_yastx()
538   {
539     YCPPathSearch::initialize ();
540     init_wfm();
541   
542     for ( list<string>::const_iterator it = YCPPathSearch::searchListBegin (YCPPathSearch::Module); it != YCPPathSearch::searchListEnd (YCPPathSearch::Module) ; ++it )
543     {
544       y2internal("search path %s\n", (*it).c_str() );
545     }
546
547     rb_mYaST = rb_define_module("YaST");
548
549     //rb_mYCP = rb_define_module_under(rb_mYaST, "YCP");
550     rb_mYCP = rb_define_module("YCP");
551     rb_define_singleton_method( rb_mYCP, "import", RB_METHOD(ycp_module_import), 1);
552     rb_define_singleton_method( rb_mYCP, "call_ycp_function", RB_METHOD(ycp_module_call_ycp_function), -1);
553     rb_define_singleton_method( rb_mYCP, "call_ycp_builtin", RB_METHOD(ycp_module_call_ycp_builtin), -1);
554
555     rb_define_singleton_method( rb_mYCP, "each_symbol", RB_METHOD(ycp_module_each_symbol), 1);
556     rb_define_singleton_method( rb_mYCP, "each_builtin_symbol", RB_METHOD(ycp_module_each_builtin_symbol), 1);
557     rb_define_singleton_method( rb_mYCP, "each_builtin", RB_METHOD(ycp_module_each_builtin), 0);
558
559     rb_mUi = rb_define_module_under(rb_mYCP, "Ui");
560     rb_define_singleton_method( rb_mUi, "init", RB_METHOD(rb_init_ui), -1);
561     
562     
563     rb_define_method( rb_mYaST, "y2_logger", RB_METHOD(rb_y2_logger), -1);
564
565     ryast_path_init(rb_mYaST);
566     ryast_term_init(rb_mYaST);
567   }
568 }
569