File: scripting.rst

package info (click to toggle)
libgnatcoll 1.7gpl2015-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 17,280 kB
  • ctags: 1,124
  • sloc: ada: 134,072; python: 4,017; cpp: 1,397; ansic: 1,234; makefile: 368; sh: 152; xml: 31; sql: 6
file content (1249 lines) | stat: -rw-r--r-- 51,244 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
.. _Embedding_script_languages:

***************************************
**Scripts**: Embedding script languages
***************************************

In a lot of contexts, you want to give the possibility to users to extend
your application. This can be done in several ways: define an Ada API from
which they can build dynamically loadable modules, provide the whole source
code to your application and let users recompile it, interface with a simpler
scripting languages,...

Dynamically loadable modules can be loaded on demand, as their name indicate.
However, they generally require a relatively complex environment to build,
and are somewhat less portable. But when your users are familiar with Ada,
they provide a programming environment in which they are comfortable.
As usual, changing the module requires recompilation, re-installation,...

Providing the source code to your application is generally even more
complex for users. This requires an even more complex setup, your application
is generally too big for users to dive into, and modifications done by one
users are hard to provide to other users, or will be lost when you
distribute a new version of your application.

The third solution is to embed one or more scripting languages in your
application, and export some functions to it. This often requires your users
to learn a new language, but these languages are generally relatively simple,
and since they are interpreted they are easier to learn in an interactive
console. The resulting scripts can easily be redistributed to other users or
even distributed with future versions of your application.

The module in GNATColl helps you implement the third solution. It was
used extensively in the GPS programming environment for its python interface.

|Tip| Each of the scripting language is optional

This module can be compiled with any of these languages as an optional
dependency (except for the shell language, which is always built-in, but is
extremely minimal, and doesn't have to be loaded at run time anyway).
If the necessary libraries are found on the system, GNATColl will
be build with support for the corresponding language, but your application
can chose at run time whether or not to activate the support for a specific
language.

|Tip| Optional support is provided for the *gtk+* library.

Likewise, extensions are provided if the gtk+ libraries were found on your
system. These provide a number of Ada subprograms that help interface with
code using this library, and help export the corresponding classes.
This support for gtk+ is also optional, and you can still build
GNATColl even if gtk+ wasn't installed on your system (or if your
application is text-only, in which case you likely do not want to depend
at link time on graphical libraries).

.. index:: test driver
.. index:: testing your application

|Tip| Use a scripting language to provide an automatic testing framework for
your application.

The GPS environment uses python command for its *automatic test suite*,
including graphical tests such as pressing on a button, selecting a
menu,...

.. _Supported_languages:

Supported languages
===================

The module provides built-in support for several scripting languages, and
other languages can "easily" be added. Your application does not change
when new languages are added, since the interface to export subprograms
and classes to the scripting languages is language-neutral, and will
automatically export to all known scripting languages.

Support is provided for the following languages:

*Shell*
  This is a very simple-minded scripting language, which doesn't provide
  flow-control instructions (:ref:`The_Shell_language`).

*Python*
  Python (`http://www.python.org <http://www.python.org>`_) is an advanced scripting language
  that comes with an extensive library. It is fully object-oriented
  (:ref:`The_Python_language`).


.. _The_Shell_language:

The Shell language
------------------

The shell language was initially developed in the context of the GPS
programming environment, as a way to embed scripting commands in XML
configuration files.

In this language, you can execute any of the commands exported by the
application, passing any number of arguments they need. Arguments to function
calls can, but need not, be quoted. Quoting is only mandatory when they
contain spaces, newline characters, or double-quotes ('"'). To quote an
argument, surround it by double-quotes, and precede each double-quote it
contains by a backslash character. Another way of quoting is similar to
what python provides, which is to triple-quote the argument, i.e. surround it
by '"""' on each side. In such a case, any special character (in particular
other double-quotes or backslashes) lose their special meaning and are just
taken as part of the argument. This is in particular useful when you do not
know in advance the contents of the argument you are quoting::

  Shell> function_name arg1 "arg 2" """arg 3"""
  
Commands are executed as if on a stack machine: the result of a command is
pushed on the stack, and later commands can reference it using `%`
following by a number. By default, the number of previous results that are
kept is set to 9, and this can only be changed by modifying the source code
for GNATColl. The return values are also modified by commands executed
internally by your application, and that might have no visible output from
the user's point of view. As a result, you should never assume you know
what `%1`,... contain unless you just executed a command in the
same script::

  Shell> function_name arg1
  Shell> function2_name %1

In particular, the `%1` syntax is used when emulating object-oriented
programming in the shell. A method of a class is just a particular function
that contains a '.' in its name, and whose first implicit argument is the
instance on which it applies. This instance is generally the result of
calling a constructor in an earlier call. Assuming, for instance, that we
have exported a class "Base" to the shell from our Ada core, we could use
the following code::

  Shell> Base arg1 arg2
  Shell> Base.method %1 arg1 arg2
  
to create an instance and call one of its methods.
Of course, the shell is not the best language for object-oriented programming,
and better languages should be used instead.

When an instance has associated properties (which you can export from Ada
using `Set_Property`), you access the properties by prefixing its name
with "@"::
  
  Shell> Base arg1 arg2   # Build new instance
  Shell> @id %1           # Access its "id" field
  Shell> @id %1 5         # Set its "id" field
  

Some commands are automatically added to the shell when this scripting
language is added to the application. These are

.. index:: Function load

`Function load (file)`
  Loads the content of `file` from the disk, and execute each of its lines as
  a Shell command. This can for instance be used to load scripts when your
  application is loaded

.. index:: Function echo

`Function echo (arg...)`
  This function takes any number of argument, and prints them in the console
  associated with the language. By default, when in an interactive console, the
  output of commands is automatically printed to the console. But when you
  execute a script through `load` above, you need to explicitly call
  `echo` to make some output visible.

.. index:: Function clear_cache

`Function clear_cache`
  This frees the memory used to store the output of previous commands. Calling
  `%1` afterward will not make sense until further commands are executed.

.. _The_Python_language:

The Python language
-------------------

.. highlight:: python
.. index:: Python

Python is an interpreted, object-oriented language. See
`http://www.python.org <http://www.python.org>`_ for more information, including tutorials, on
this language.

|Note| Python support is optional in GNATColl. If it hasn't been installed
on your system, GNATColl will be compiled without it, but that
will not impact applications using GNATColl, since the same packages
(and the same API therein) are provided in both cases. Of course, if python
support wasn't compiled in, these packages will do nothing.

.. index:: GNATCOLL.Python
.. index:: gnatcoll-python.ads

In addition to the API common to all languages (:ref:`Scripts_API`),
GNATColl also comes with a low-level interface to the python
library. This interface is available in the :file:`GNATCOLL.Python` package.
In general, it is much simpler to use the common API rather than this
specialized one, though, since otherwise you will need to take care of lots
of details like memory management, conversion to and from python types,...

|Tip| All functions exported to python are available in a specific namespace

All functions exported to python through GNATColl are available in
a single python module, whose name you must specify when adding support
for python. This is done to avoid namespace pollution. You can further
organize the subprograms through python classes to provide more logical
namespaces.

As in Ada, python lets you use named parameters in subprogram calls,
and thus let's you change the order of arguments on the command line.
This is fully supported by GNATColl, although your callbacks will
need to specify the name of the parameters for this to work fine::

  >>> func_name (arg1, arg2)
  >>> func_name (arg2=arg2, arg1=arg1)`

Some commands and types are always exported by GNATColl, since they
are needed by most application, or even internally by GNATColl
itself.

.. index::  Exception Unexpected_Exception

`Exception Unexpected_Exception`

.. index:: Exception Exception

`Exception Exception`

.. index:: Exception Missing_Arguments

`Exception Missing_Arguments`

.. index:: Exception Invalid_Argument

`Exception Invalid_Argument`
  A number of exceptions are added automatically, so that the internal
  state of your application is reflected in python. These are raised on
  unexpected uncaught Ada exceptions, when your callbacks return explicit
  errors, or when a function call is missing some arguments.

.. index:: Function exec_in_console

`Function exec_in_console command`
  This function can be used in your script when you need to modify the
  contents of the python interpreter itself.

  When you run a python script, all its commands (including the global
  variables) are within the context of the script. Therefore, you cannot
  affect variables which are used for instance in the rest of your
  application or in the python console. With this function, `command`
  will be executed as if it had been typed in the python console::

    exec_in_console ("sys.ps1 = 'foo'")
    	=> foo>  # Prompt was changed in the console

.. index:: pygtk

PyGtk is a python extension that provides an interface to the popular
gtk+ library. It gives access to a host of functions for writing graphical
interfaces from python. GNATColl interfaces nicely with this extension
if it is found.

|Note| PyGtk support is also optional. It will be activated in your application
if the four following conditions are met: Python was detected on your system,
PyGtk was also detected when GNATColl is built, PyGtk is detected
dynamically when your application is launched and your code is calling the
`Init_PyGtk_Support` function

When PyGtk is detected, you can add the following method to any of the
classes you export to python:

.. index:: AnyClass pywidget

`AnyClass pywidget`
  This function returns an instance of a PyGtk class corresponding to the
  graphical object represented by `AnyClass`. In general, it makes sense when
  `AnyClass` is bound, in your Ada code, to a GtkAda object. As a result, the
  same graphical element visible to the user on the screen is available from
  three different programming languages: C, Ada and Python. All three can
  manipulate it in the same way

.. _Classes_exported_to_all_languages:

Classes exported to all languages
---------------------------------

In addition to the functions exported by each specific scripting language,
as described above, GNATColl exports the following to all the
scripting languages. These are exported when your Ada code calls the
Ada procedure `GNATCOLL.Scripts.Register_Standard_Classes`, which should
done after you have loaded all the scripting languages.

.. index:: Class Console

`Class Console`
  `Console` is a name that you can chose yourself when you call the
  above Ada procedure. It will be assumed to be `Console` in the rest
  of this document.

  This class provides an interface to consoles. A console is an input/output
  area in your application (whether it is a text area in a graphical
  application, or simply standard text I/O in text mode). In particular,
  the python standard output streams `sys.stdin`, `sys.stdout`
  and `sys.stderr` are redirected to an instance of that class. If you
  want to see python's error messages or usual output in your application,
  you must register that class, and define a default console for your
  scripting language through calls to
  `GNATCOLL.Scripts.Set_Default_Console`.

  You can later add new methods to this class, which would be specific to your
  application. Or you can derive this class into a new class to achieve a
  similar goal.

.. index:: Console.write

`Console.write(text)`
  This method writes `text` to the console associated with the class
  instance. See the examples delivered with GNATColl for examples on
  how to create a graphical window and make it into a `Console`.

.. index:: Console.clear

`Console.clear()`
  Clears the contents of the console.

.. index:: Console.flush

`Console.flush()`
  Does nothing currently, but is needed for compatibility with python.
  Output through `Console` instances is not buffered anyway.

.. index:: Console.isatty

`Console.isatty(): Boolean`
  Whether the console is a pseudo-terminal. This is always wrong in the
  case of GNATColl.

.. index:: Console.read

`Console.read([size]): string`
  Reads at most `size` bytes from the console, and returns the resulting
  string.

.. index:: Console.readline

`Console.readline([size]): string`
  Reads at most `size` lines from the console, and returns them as a single
  string.

.. _Scripts_API:

Scripts API
===========

This section will give an overview of the API used in the scripts module.
The reference documentation for this API is in the source files themselves. In
particular, each :file:`.ads` file fully documents all its public API.

As described above, GNATColl contains several levels of API. In
particular, it provides a low-level interface to python, in the packages
`GNATCOLL.Python`. This interface is used by the rest of GNATColl,
but is likely too low-level to really be convenient in your applications,
since you need to take care of memory management and type conversions by
yourself.

Instead, GNATColl provides a language-neutral Ada API. Using this
API, it is transparent for your application whether you are talking to the
Shell, to python, or to another language integrated in GNATColl.
The code remains exactly the same, and new scripting languages can be added
in later releases of GNATColl without requiring a change in your
application. This flexibility is central to the design of GNATColl.

In exchange for that flexibility, however, there are language-specific
features that cannot be performed through the GNATColl API. At
present, this includes for instance exporting functions that return hash
tables. But GNATColl doesn't try to export the greatest set of
features common to all languages. On the contrary, it tries to fully
support all the languages, and provide reasonable fallback for languages
that do not support that feature. For instance, named parameters (which
are a part of the python language) are fully supported, although the
shell language doesn't support them. But that's an implementation detail
transparent to your own application.

Likewise, your application might decide to always load the python
scripting language. If GNATColl wasn't compiled with python support,
the corresponding Ada function still exists (and thus your code still
compiles), although of course it does nothing. But since the rest of the
code is independent of python, this is totally transparent for your
application.

|Tip| GNATColl comes with some examples, which you can use
as a reference when building your own application.
See the :file:`<prefix>/share/examples/gnatcoll` directory.

Interfacing your application with the scripting module is a multistep
process:

* You *must* **initialize** GNATColl and decide which features
  to load
* You *can* create an **interactive console** for the various
  languages, so that users can perform experiments interactively. This
  is optional, and you could decide to keep the scripting language has a
  hidden implementation detail (or just for automatic testing purposes
  for instance)
* You *can* **export** some classes and methods.
  This is optional, but it doesn't really make sense to just embed a
  scripting language and export nothing to it. In such a case, you might
  as well spawn a separate executable.
* You *can* load **start up scripts** or plug-ins that users have
  written to extend your application.

.. _Initializing_the_scripting_module:

Initializing the scripting module
---------------------------------

GNATColl must be initialized properly in order to provide added
value to your application. This cannot be done automatically simply by
depending on the library, since this initialization requires multiple-step
that must be done at specific moments in the initialization of your whole
application.

This initialization does not depend on whether you have build support
for python or for gtk+ in GNATColl. The same packages and subprograms
are available in all cases, and therefore you do not need conditional
compilation in your application to support the various cases.

.. _Create_the_scripts_repository:

Create the scripts repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The type `GNATCOLL.Scripts.Scripts_Repository` will contain various
variables common to all the scripting languages, as well as a list of the
languages that were activated. This is the starting point for all other
types, since from there you have access to everything. You will have only
one variable of this type in your application, but it should generally be
available from all the code that interfaces with the scripting language.

Like the rest of GNATColl, this is a tagged type, which you can
extend in your own code. For instance, the GPS programming environment is
organized as a kernel and several optional modules. The kernel provides
the core functionality of GPS, and should be available from most functions
that interface with the scripting languages. Since these functions have
very specific profiles, we cannot pass additional arguments to them. One
way to work around this limitation is to store the additional arguments
(in this case a pointer to the kernel) in a class derived from
`Scripts_Repository_Data`.

.. highlight:: ada

As a result, the code would look like::

  with GNATCOLL.Scripts;
  Repo : Scripts_Repository := new Scripts_Repository_Record;

or, in the more complex case of GPS described above::

  type Kernel_Scripts_Repository is new
     Scripts_Repository_Data with record
        Kernel : ...;
  end record;
  Repo : Scripts_Repository := new Kernel_Scripts_Repository'
     (Scripts_Repository_Data with Kernel => ...);

.. _Loading_the_scripting_language:

Loading the scripting language
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The next step is to decide which scripting languages should be made
available to users. This must be done before any function is exported,
since only functions exported after a language has been loaded will be
made available in that language.

|Note| If for instance python support was build into GNATColl, and
if you decide not to make it available to users, your application will
still be linked with :file:`libpython`. It is therefore recommended although
not mandatory to only build those languages that you will use.

This is done through a simple call to one or more subprograms. The following
example registers both the shell and python languages::

  with GNATCOLL.Scripts.Python;
  with GNATCOLL.Scripts.Shell;
  Register_Shell_Scripting (Repo);
  Register_Python_Scripting (Repo, "MyModule");

.. index:: Procedure Register_Shell_Scripting

`Procedure Register_Shell_Scripting (Repo)`
  This adds support for the shell language. Any class or function that is
  now exported through GNATColl will be made available in the shell

.. index:: Procedure Register_Python_Scripting

`Procedure Register_Python_Scripting (Repo, Module_Name)`
  This adds support for the python language. Any class or function exported
  from now on will be made available in python, in the module specified
  by `Module_Name`

.. _Exporting_standard_classes:

Exporting standard classes
^^^^^^^^^^^^^^^^^^^^^^^^^^

To be fully functional, GNATColl requires some predefined classes
to be exported to all languages (:ref:`Classes_exported_to_all_languages`).
For instance, the `Console` class is needed for proper interactive with
the consoles associated with each language.

These classes are created with the following code::

  Register_Standard_Classes (Repo, "Console");

This must be done only after all the scripting languages were loaded in the
previous step, since otherwise the new classes would not be visible in the
other languages.

.. index:: Procedure Register_Standard_Classes

`Procedure Register_Standard_Classes(Repo,Console_Class)`
  The second parameter `Console_Class` is the name of the class that
  is bound to a console, and thus provides input/output support. You can chose
  this name so that it matches the classes you intend to export later on from
  your application.

.. _Creating_interactive_consoles:

Creating interactive consoles
-----------------------------

The goal of the scripting module in GNATColl is to work both in
text-only applications and graphical applications that use the gtk+ toolkit.
However, in both cases applications will need a way to capture the output
of scripting languages and display them to the user (at least for errors, to
help debugging scripts), and possibly emulate input when a script is waiting
for such input.

GNATColl solved this problem by using an abstract class
`GNATCOLL.Scripts.Virtual_Console_Record` that defines an API for these
consoles. This API is used throughout `GNATCOLL.Scripts` whenever input or
output has to be performed.

|Tip| The :file:`examples/` directory in the GNATColl package
shows how to implement a console in text mode and in graphical mode.

If you want to provide feedback or interact with users, you will need to
provide an actual implementation for these `Virtual_Console`, specific
to your application. This could be a graphical text window, or based on
`Ada.Text_IO`. The full API is fully documented in
:file:`gnatcoll-scripts.ads`, but here is a list of the main subprograms that
need to be overriden.

.. index:: Virtual_Console.Insert_Text

`Virtual_Console.Insert_Text (Txt)`

.. index:: Virtual_Console.Insert_Log

`Virtual_Console.Insert_Log (Txt)`

.. index:: Virtual_Console.Insert_Error

`Virtual_Console.Insert_Error (Txt)`
  These are the various methods for doing output. Error messages could for
  instance be printed in a different color. Log messages should in general
  be directed elsewhere, and not be made visible to users unless in special
  debugging modes.

.. index:: Virtual_Console.Insert_Prompt

`Virtual_Console.Insert_Prompt (Txt)`
  This method must display a prompt so that the user knows input is expected.
  Graphical consoles will in general need to remember where the prompt ended
  so that they also know where the user input starts

.. index:: Virtual_Console.Set_As_Default_Console

`Virtual_Console.Set_As_Default_Console (Script)`
  This method is called when the console becomes the default console for
  a scripting language. They should in general keep a pointer on that
  language, so that when the user presses :kbd:`enter` they know which language
  must execute the command

.. index:: Virtual_Console.Read 

`Virtual_Console.Read (Size, Whole_Line) : String`
  Read either several characters or whole lines from the console. This is
  called when the user scripts read from their stdin.

.. index:: Virtual_Console.Set_Data_Primitive

`Virtual_Console.Set_Data_Primitive (Instance)`

.. index:: Virtual_Console.Get_Instance

`Virtual_Console.Get_Instance : Console`
  These two methods are responsible for storing an instance of `Console`
  into a `GNATCOLL.Scripts.Class_Instance`. Such an instance is
  what the user
  manipulates from his scripting language. But when he executes a method, the
  Ada callback must know how to get the associated `Virtual_Console`
  back to perform actual operations on it.

  These methods are implemented using one of the `GNATCOLL.Scripts.Set_Data`
  and `GNATCOLL.Scripts.Get_Data` operations when in text mode, or possibly
  `GNATCOLL.Scripts.Gtkada.Set_Data` and
  `GNATCOLL.Scripts.Gtkada.Get_Data`
  when manipulating graphical GtkAda objects.

There are lots of small details to take into account when writing a
graphical console. The example in :file:`examples/gtkconsole.ads`
should provide a good starting point. However, it doesn't handle things
like history of commands, preventing the user from moving the cursor
to previous lines,... which are all small details that need to be right
for the user to feel comfortable with the console.

.. highlight:: ada

Once you have created one or more of these console, you can set them as
the default console for each of the scripting languages. This way, any
input/output done by scripts in this language will interact with that
console, instead of being discarded. This is done through code similar
to::

  Console := GtkConsole.Create (...);
  Set_Default_Console
    (Lookup_Scripting_Language (Repo, "python"),
     Virtual_Console (Console));

Creating a new instance of `Console`, although allowed, will by
default create an unusable console. Indeed, depending on your application,
you might want to create a new window, reuse an existing one, or do many
other things when the user does::

  c = Console()

As a result, GNATColl does not try to guess the correct behavior,
and thus does not export a constructor for the console. So in the above
python code, the default python constructor is used. But this constructor
does not associate `c` with any actual `Virtual_Console`, and
thus any call to a method of `c` will result in an error.

To make it possible for users to create their own consoles, you need to
export a `Constructor_Method` (see below) for the `Console`
class. In addition to your own processing, this constructor needs also to
call::
  
     declare
        Inst : constant Class_Instance := Nth_Arg (Data, 1);
     begin
        C := new My_Console_Record;  --  or your own type
        GNATCOLL.Scripts.Set_Data (Inst, C);
     end

.. _Exporting_classes_and_methods:

Exporting classes and methods
-----------------------------

Once all scripting languages have been loaded, you can start exporting
new classes and functions to all the scripting languages. It is important
to realize that through a single Ada call, they are exported to all loaded
scripting languages, without further work required on your part.

.. _Classes_diagram:

Classes diagram
^^^^^^^^^^^^^^^

The following diagram shows the dependencies between the major data types
defined in :file:`GNATCOLL.Scripts`. Most of these are abstract classes that
are implemented by the various scripting languages. Here is a brief description
of the role of each type:

.. index:: class diagram, script module

.. image:: classes.png

.. index:: Class Scripts_Repository

`Class Scripts_Repository`
  As we have seen before, this is a type of which there is a single instance
  in your whole application, and whose main role is to give access to each
  of the scripting languages (`Lookup_Scripting_Language` function), and
  to make it possible to register each exported function only once (it then
  takes care of exporting it to each scripting language).

.. index:: Class Scripting_Language

`Class Scripting_Language`
  Instances of this type represent a specific language. It provides various
  operations to export subprograms, execute commands, create the other types
  described below,... There should exists a single instance of this class per
  supported language.

  This class interacts with the script interpreter (for instance python), and
  all code executed in python goes through this type, which then executes your
  Ada callbacks to perform the actual operation.

  It is also associated with a default console, as described above, so that
  all input and output of the scripts can be made visible to the user.

.. index:: Class Callback_Data

`Class Callback_Data`
  This type is an opaque tagged type that provides a language-independent
  interface to the scripting language. It gives for instance access to the
  various parameters passed to your subprogram (`Nth_Arg` functions),
  allows you to set the return value (`Set_Return_Value` procedure),
  or raise exceptions (`Set_Error_Msg` procedure),...

.. index:: Record Class_Type

`Record Class_Type`
  This type is not tagged, and cannot be extended. It basically represents a
  class in any of the scripting languages, and is used to create new instances
  of that class from Ada.

.. index:: Class Class_Instance

`Class Class_Instance`
  A class instance represents a specific instance of a class. In general,
  such an instance is strongly bound to an instance of an Ada type. For
  instance, if you have a `Foo` type in your application that you wish
  to export, you would create a `Class_Type` called "Foo", and then the
  user can create as many instances as he wants of that class, each of which
  is associated with different values of `Foo` in Ada.

  Another more specific example is the predefined `Console` class. As
  we have seen before, this is a `Virtual_Console` in Ada. You could
  for instance have two graphical windows in your application, each of which
  is a `Virtual_Console`. In the scripting language, this is exported
  as a class named `Console`. The user can create two
  instances of those, each of which is associated with one of your graphical
  windows. This way, executing `Console.write` on these instances would
  print the string on their respective graphical window.

  .. highlight:: python

  Some scripting languages, in particular python, allow you to store any
  data within the class instances. In the example above, the user could for
  instance store the time stamp of the last output in each of the instances.
  It is therefore important that, as much as possible, you always return the
  same `Class_Instance` for a given Ada object. See the following
  python example::

    myconsole = Console ("title") # Create new console
    myconsole.mydata = "20060619"  # Any data, really
    myconsole = Console ("title2")  # Create another window
    myconsole = Console ("title") # Must be same as first, 
    print myconsole.mydata  # so that this prints "20060619"

.. index:: Class Instance_Property

`Class Instance_Property`
  As we have seen above, a `Class_Instance` is associated in general with
  an Ada object. This `Instance_Property` tagged type should be extended
  for each Ada type you want to be able to store in a `Class_Instance`.
  You can then use the `Set_Data` and `Get_Data` methods of the
  `Class_Instance` to get and retrieve that associated Ada object.

.. index:: Class Subprogram_Record

`Class Subprogram_Record`
  This class represents a callback in the scripting language, that is some
  code that can be executed when some conditions are met.

  The exact semantic here depends on each of the programming languages. For
  instance, if you are programming in python, this is the name of a python
  method to execute. If you are programming in shell, this is any shell code.

  .. highlight:: python

  The idea here is to blend in as smoothly as possible with the usual constructs
  of each language. For instance, in python one would prefer to write the
  second line rather than the third::

    def on_exit():
       pass
    set_on_exit_callback(on_exit)   # Yes, python style
    set_on_exit_callback("on_exit") # No

  The last line (using a string as a parameter) would be extremely unusual
  in python, and would for instance force you to qualify the subprogram name
  with the name of its namespace (there would be no implicit namespace
  resolution).

  To support this special type of parameters, the `Subprogram_Record`
  type was created in Ada.

Although the exact way they are all these types are created is largely
irrelevant to your specific application in general, it might be useful for you
to override part of the types to provide more advanced features. For instance,
GPS redefines its own Shell language, that has basically the same behavior as
the Shell language described above but whose `Subprogram_Record` in fact
execute internal GPS actions rather than any shell code.

.. _Exporting_functions:

Exporting functions
^^^^^^^^^^^^^^^^^^^

.. highlight:: ada

All functions that you export to the scripting languages will result in a
call to an Ada subprogram from your own application. This subprogram must
have the following profile::

  procedure Handler
     (Data    : in out Callback_Data'Class;
      Command : String);

The first parameter `Data` gives you access to the parameters of the
subprogram as passed from the scripting language, and the second parameter
`Command` is the name of the command to execute. The idea behind this
second parameter is that a single Ada procedure might handle several
different script function (for instance because they require common actions
to be performed).

.. index:: Register_Command

`Register_Command (Repo,Command,Min_Args,Max_Args,Handler)`
  Each of the shell functions is then exported through a call to
  `Register_Command`. In its simplest form, this procedure takes the
  following arguments. `Repo` is the scripts repository, so that the
  command is exported to all the scripting languages. `Command` is the
  name of the command. `Min_Args` and `Max_Args` are the minimum and
  maximum number of arguments. Most language allow option parameters, and
  this is how you specify them. `Handler` is the Ada procedure to call
  to execute the command.

Here is a simple example. It implements a function called `Add`, which
takes two integers in parameter, and returns their sum::

  Arg1_C : aliased constant String := "arg1";
  Arg2_C : aliased constant String := "arg2";

  procedure Sum
     (Data : in out Callback_Data'Class;
      Command : String)
  is
     Arg1, Arg2 : Integer;
  begin
     Name_Parameters ((1 => Arg1_C'Access, 2 => Arg2_C'Access));
     Arg1 := Nth_Arg (Data, 1);
     Arg2 := Nth_Arg (Data, 2);
     Set_Return_Value (Data, Arg1 + Arg2);
  end Sum;

  Register_Command (Repo, "sum", 2, 2, Sum'Access);

This is not the most useful function to export! Still, it illustrates a
number of important concepts.

Automatic parameters types
~~~~~~~~~~~~~~~~~~~~~~~~~~

When the command is registered, the number of arguments is specified.
This means that GNATColl will check on its own whether the right
number of arguments is provided. But the type of these arguments is not
specified. Instead, your callback should proceed as if they were correct,
and try to retrieve them through one of the numerous `Nth_Arg`
functions. In the example above, we assume they are integer. But if one of
them was passed as a string, an exception would be raised and sent back to
the scripting language to display a proper error message to the user. You
have nothing special to do here.

Support for named parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Some languages (especially python) support named parameters, ie parameters
can be specified in any order on the command line, as long as they are
properly identified (very similar to Ada's own capabilities). In the example
above, the call to `Name_Parameters` is really optional, but adds this
support for your own functions as well. You just have to specify the name
of the parameters, and GNATColl will then ensure that when you
call `Nth_Arg` the parameter number 1 is really "arg1".
For scripting languages that do not support named parameters, this has no
effect.

Your code can then perform as complex a code as needed, and finally
return a value (or not) to the scripting language, through a call to
`Set_Return_Value`.

.. highlight:: python

After the above code has been executed, your users can go to the python
console and type for instance::

  from MyModule import *    # MyModule is the name we declared above
  print sum (1,2)
  	=> 3
  print sum ()
  	=> Error:  Wrong number of parameters
  print sum ("1", 2)
  	=> Error:  Parameter 1 should be an integer
  print sum (arg2=2, arg1=1)
  	=> 3

.. _Exporting_classes:

Exporting classes
^^^^^^^^^^^^^^^^^

Whenever you want to make an Ada type accessible through the scripting
languages, you should export it as a class. For object-oriented languages,
this would map to the appropriate concept. For other languages, this provides
a namespace, so that each method of the class now takes an additional first
parameter which is the instance of the class, and the name of the method is
prefixed by the class name.

.. highlight:: ada

Creating a new class is done through a call to `New_Class`, as shown
in the example below::

  MyClass : Class_Type;
  MyClass := GNATCOLL.Scripts.New_Class (Repo, "MyClass");

At this stage, nothing is visible in the scripting language, but all the
required setup has been done internally so that you can now add methods to
this class.

You can then register the class methods in the same way that you registered
functions. An additional parameter `Class` exists for
`Register_Command`. A method is really just a standard function that
has an implicit first parameter which is a `Class_Instance`. This
extra parameter should not be taken into account in `Min_Args` and
`Max_Args`. You can also declare the method as a static method, ie
one that doesn't take this extra implicit parameter, and basically just
uses the class as a namespace.

Some special method names are available. In particular,
`Constructor_Method` should be used for the constructor of a class.
It is a method that receives, as its first argument, a class instance that
has just been created. It should associate that instance with the Ada
object it represents.

.. highlight:: python

Here is a simple example that exports a class. Each instance of this class
is associated with a string, passed in parameter to the constructor. The
class has a single method `print`, which prints its string parameter
prefixed by the instance's string. To start with, here is a python example
on what we want to achieve::

  c1 = MyClass ("prefix1")
  c1.print ("foo")
  	=> "prefix1 foo"
  c2 = MyClass ()  # Using a default prefix
  c2.print ("foo")
  	=> "default foo"

.. highlight:: ada

Here is the corresponding Ada code::

  with GNATCOLL.Scripts.Impl;
  procedure Handler
     (Data : **in out** Callback_Data'Class; Command : String)
  is
     Inst : Class_Instance := Nth_Arg (Data, 1, MyClass);
  begin
     if Command = Constructor_Method then
       Set_Data (Inst, MyClass, Nth_Arg (Data, 2, "default"));
     elsif Command = "print" then
       Insert_Text
          (Get_Script (Data), null,
           String'(Get_Data (Inst)) & " " & Nth_Arg (Data, 2));
     end if;
  end Handler;

  Register_Command
    (Repo, Constructor_Method, 0, 1, Handler'Access, MyClass);
  Register_Command
    (Repo, "print", 1, 1, Handler'Access, MyClass);

This example also demonstrates a few concepts: the constructor is declared
as a method that takes one optional argument. The default value is in
fact passed in the call to `Nth_Arg` and is set to "default".
In the handler, we know there is always a first argument which is the
instance on which the method applies. The implementation for the
constructor stores the prefix in the instance itself, so that several
instances can have different prefixes (we can't use global variables,
of course, since we don't know in advance how many instances will exist).
The implementation for `print` inserts code in the default console
for the script (we could of course use `Put_Line` or any other way
to output data), and computes the string to output by concatenating the
instance's prefix and the parameter to `print`.

Note that `Set_Data` and `Get_Data` take the class in parameter,
in addition to the class instance. This is needed for proper handling of
multiple inheritance: say we have a class `C` that extends two classes
`A` and `B`. The Ada code that deals with `A` associates an
integer with the class instance, whereas the code that deals with `B`
associates a string. Now, if you have an instance of `C` but call a
method inherited from `A`, and if `Get_Data` didn't specify the
class, there would be a risk that a string would be returned instead of the
expected integer. In fact, the proper solution here is that both `A`
and `B` store their preferred data at the same time in the instances,
but only fetch the one they actually need. Therefore instances of `C`
are associated with two datas.

Here is a more advanced example that shows how to export an Ada object. Let's
assume we have the following Ada type that we want to make available to
scripts::

  type MyType is record
     Field : Integer;
  end record;

As you can see, this is not a tagged type, but could certainly be. There is
of course no procedure `Set_Data` in :file:`GNATCOLL.Scripts` that enables
us to store `MyType` in a `Class_Instance`. This example shows how
to write such a procedure. The rest of the code would be similar to the
first example, with a constructor that calls `Set_Data`, and methods
that call `Get_Data`::

  type MyPropsR is new Instance_Property_Record with record
     Val : MyType;
  end record;
  type MyProps is access all MyPropsR'Class;

  procedure Set_Data
    (Inst : Class_Instance; Val : MyType)
  is
  begin
    Set_Data (Inst, Get_Name (MyClass), MyPropsR'(Val => Val));
  end Set_Data;

  function Get_Data (Inst : Class_Instance) return MyType is
     Data : MyProps := MyProps (Instance_Property'
        (Get_Data (Inst, Get_Name (MyClass))));
  begin
     return Data.Val;
  end Get_Data;

Several aspects worth noting in this example. Each data is associated with
a name, not a class as in the previous example. That's in fact the same
thing, and mostly for historical reasons. We have to create our own
instance of `Instance_Property_Record` to store the data, but the
implementation presents no special difficulty. In fact, we don't absolutely
need to create `Set_Data` and `Get_Data` and could do everything
inline in the method implementation, but it is cleaner this way and easier
to reuse.

GNATColl is fully responsible for managing the lifetime of the
data associated with the class instances and you can override the procedure
`Destroy` if you need special memory management.

.. _Reusing_class_instances:

Reusing class instances
^^^^^^^^^^^^^^^^^^^^^^^

We mentioned above that it is more convenient for users of your exported
classes if you always return the same class instance for the same Ada
object (for instance a graphical window should always be associated with
the same class instance), so that users can associate their own internal
data with them.

GNATColl provides a few types to facilitate this. In passing, it
is worth noting that in fact the Ada objects will be associated with a
single instance *per scripting language*, but each language has its
own instance. Data is not magically transferred from python to shell!

There are two cases to distinguish here:

* The Ada object derives from a GtkAda object

  In such a case, the package :file:`GNATCOLL.Scripts.GtkAda` provides three
  procedures that automatically associate the instance with the object,
  and can return the class instance associated with any given GtkAda
  object, or can return the GtkAda object stored in the instance. There is
  nothing else to do that to call `Set_Data` as we have seen above.
  See below for a brief discussion on the Factory design pattern. The internal
  handling is complex, since python
  for instance has ref-counted types, and so does gtk+. For the memory to
  be correctly freed when no longer needed, GNATColl must properly
  takes care of these reference counting. The result is that the class
  instance will never be destroyed while the gtk+ object exists, but the
  gtk+ object might be destroyed while the class instance still exists (in
  which case no further operation on that instance is possible).

* The Ada object does not derive from a GtkAda object

  In such a case, you should store the list of associated instances with
  your object. The type `GNATCOLL.Scripts.Instance_List_Access` is meant for
  that purpose, and provides two `Set` and `Get` primitives
  to retrieve existing instances.

The final aspect to consider here is how to return existing instances.
This cannot be done from the constructor method, since when it is called
it has already received the created instance (this is forced by python, and
was done the same for other languages for compatibility reasons).
There are two ways to work around that limitation:

* Static `get` methods

  .. highlight:: python

  With each of your classes, you can export a static method generally called
  `get` that takes in parameter a way to identify an existing instance,
  and either return it or create a new one. It is also recommended to disable
  the constructor, ie force it to raise an error. Let's examine the python
  code as it would be used::

    ed = Editor ("file.adb")  # constructor
    	=> Error, cannot construct instances
    ed = Editor.get ("file.adb")
    	=> Create a new instance
    ed2 = Editor.get ("file.adb")
    	=> Return existing instance
    ed == ed2
    	=> True	
    
  .. highlight:: ada

  The corresponding Ada code would be something like::

    type MyType is record
       Val : Integer;
       Inst : Instance_List_Access;
    end record;
    type MyTypeAccess is access all MyType;
    procedure Handler
      (Data : in out Callback_Data'Class; Cmd : String)
    is
       Inst : Class_Instance;
       Tmp  : MyTypeAccess;
    begin
       if Cmd = Constructor_Method then
         Set_Error_Msg (Data, "cannot construct instances");
       elsif Cmd = "get" then
         Tmp := check_if_exists (Nth_Arg (Data, 1));
         if Tmp = null then
            Tmp := create_new_mytype (Nth_Arg (Data, 1));
            Tmp.Inst := new Instance_List;
         end if;
         Inst := Get (Tmp.Inst.all, Get_Script (Data));
         if Inst = No_Class_Instance then
            Inst := New_Instance (Get_Script (Data), MyClass);
            Set (Tmp.Inst.all, Get_Script (Data), Inst);
            Set_Data (Inst, Tmp);
         end if;
         Set_Return_Value (Data, Inst);
       end if;
    end Handler;

* Factory classes

  The standard way to do this in python, which applies to other languages
  as well, is to use the Factory design pattern. For this, we need to
  create one class (`MyClassImpl`) and one factory
  function (`MyClass`).

  .. highlight:: python

  The python code now looks like::

    ed = MyClass ("file.adb")  # Create new instance
    	=> ed is of type MyClassImpl
    ed = MyClass ("file.adb")  # return same instance
    ed.do_something()
    

  It is important to realize that in the call above, we are not calling
  the constructor of a class, but a function. At the Ada level, the function
  has basically the same implementation as the one we gave for `get`
  above. But the python code looks nicer because we do not have these
  additional `.get()` calls. The name of the class `MyClassImpl`
  doesn't appear anywhere in the python code, so this is mostly transparent.

  However, if you have more than one scripting language, in particular for
  the shell, the code looks less nice in this case::

    MyClass "file.adb"
    	=>  <MyClassImpl_Instance_0x12345>
    MyClassImpl.do_something %1
    
  and the new name of the class is visible in the method call.


.. _Executing_startup_scripts:

Executing startup scripts
-------------------------

The final step in starting up your application is to load extensions or
plug-ins written in one of the scripting languages.

There is not much to be said here, except that you should use the
`GNATCOLL.Scripts.Execute_File` procedure to do so.

.. _Debugging_scripts:

Multithreading applications and scripts
---------------------------------------

Python itself is not thread-safe. So a single thread can call the python C API
at a time. To enforce this, the python interpreter provides a global
interpreter lock, which you must acquire before calling the C API, and release
when you are done. To simulate multitasking, the python interpreter will in
fact release and reacquire the lock every 100 micro-instructions (opcodes in
the python virtual machine), to give a chance to run to other tasks. So this is
preemptive multitasking.

The threads that are created in Ada that do not need access to python do not
need any special handling. However, those that need access to python must make
a special function call before they first call the python C API, so that python
can create a thread-specific data for them.

`GNATCOLL.Scripts.Python` contains a number of subprograms to interact with the
global interpreter lock of the python engine. The initialization of your
application needs to do two extra calls::

     Register_Python_Scripting (...);
     Initialize_Threads_Support;   --  Also acquires the lock
     Begin_Allow_Threads;          --  Releases the lock

Whenever a task needs to execute python commands (or basically use any
subprogram from `GNATCOLL.Scripts`, it needs to do the following::

     Ensure_Thread_State;   --  Block all python threads
     ...  access to python C API as usual
     Begin_Allow_Threads;   --  Let other python threads run

In some cases, the simplest is to get the lock at the beginning of the task,
and release it when done. This assumes the task executes fast enough. In other
cases, you will need finer grain control over the lock.

Debugging scripts
-----------------

GNATColl provides a convenient hook to debug your script. By default,
a script (python for instance) will call your Ada callback, which might
raise errors. Most of the time, the error should indeed be reported to the
user, and you can thus raise a standard exception, or call
`Set_Error_Msg`.

But if you wish to know which script was executing the command, it is
generally not doable. You can however activate a trace
(:ref:`Logging_information`) called `"PYTHON.TB"` (for "traceback"), which will
output the name of the command that is being executed, as well as the
full traceback within the python scripts. This will help you locate which
script is raising an exception.