Gambas Scripting

Introduction

The Gambas Scripter is a text based front end for the Gambas interpreter. The applications/Scripts are simple text files editable by any Linux editor. These scripts are an excellent way to write scripts where BASH does not provide the complex data types and data handling required by the programmer. Gambas script's mime type are recognized by the OS in the same way BASH or DASH scripts are recognized.

Scripter is well integrated into the Linux shell environment with env[<env variable>] giving direct access to the shell environment variables.

Scripter does not require the complex interface provided by the full Gambas IDE. In fact, the IDE is not required at all to use Gambas scripts.

Scripter is an excellent alternative to BASH for implementing complex system management procedures quickly. Bringing the full breadth of a well implemented programming language to the programmer. This allows scripts to use graphic and text base interfaces.

Gambas scripts are optimized as they are compiled, and the compiled script is cached and recompiled only if the script is changed. This vastly improves system performance and response when running Gambas scripts.

Scripter can also be used to compile and execute the more traditional Gambas project.

Sinds 3.16

Scripter may also be used as a component to a Gambas Application allowing the application to dynamically load or create text based scripts which can be directly loaded and executed by the application. The loaded script will have access to the applications classes and public class variables.

Note:
  Throughout this document Scripter and gbs3 are used interchangeably.
  The actual Linux executable is gbs3

Scripter - How it works

The Scripter(gbs3) works by: creating a temporary project from your script, compiling it, and running it.

Sinds 3.16

The Gambas Scripter gbs3 can also directly execute a project without the use of the IDE. The compiled project is cached so that it is executed immediately if you run the project again. The Scripter can also be used to generate a component which can be loaded and used at runtime.

Example: directly execute a script file
> gbs3 ~/myscripts/script1.gbs
> ./script1.gbs

Sinds 3.16

Example: directly execute a project
> gbs3 ~/myprojects/project1

Creating a Script

To create a Gambas script file, make it executable and write this preamble at the beginning:

#!/usr/bin/env gbs3

The Scripter can execute free-form programs with ease. To achieve this, Scripter will collect your free-form commands into a Main() subroutine automatically while maintaining the integrity of subroutines/structures/class separately.

#!/usr/bin/env gbs3

sub PrintBye()
    print "Bye"
end

dim sHello as string = "Hello"
Print sHello
PrintBye()

The Gambas script can also be written like a module in a common Gambas project, the interpreter automatically executes a Main() subroutine when included in the script.

TIP: Scripts execute more quickly if your code includes a Sub Main() subroutine rather than using the free-form method of writing a script. This is due to the Scripter not having to collect the free-form code and create a Main() subroutine.

Sinds 3.17
A script will return the script name from application.name rather than the temporary file name created when compiling the script.

#!/usr/bin/env gbs3

Public Sub Main()
    DoPrintHello()
End

Private Sub DoPrintHello()
    Print "Hello"
End

The code can be organized with private and public subs and functions, and constants and variables just like in Gambas modules inside Gambas projects.

Installing Scripter - gbs3

Scripter is not installed by default when the IDE is installed onto your system. It must explicitly be installed before it is available. It needs to be installed through your distributions package manager.
For example on Mint/Ubuntu the following command could be used
 sudo apt install gambas3-gb-scripter
See your distribution package manager for specifics.

To use Scripter it is not required to have the Gambas3 IDE installed.
Scripter can be installed standalone. When installed Scripter will cause the gambas-devel package containing the compiler linker and archiver to be installed and also the gb.pcre component if they are not already present on the system.

When installed standalone, the user will be required to individually install any components needed by executed scripts.

Defining Classes and Structures within a Gambas script

Unlike a Gambas project, Scripts support Classes and Structures in a script-specific way.

Classes are defined inline within your script code, structures must be public and defined before any reference to them in the script.

Defining a class inline

To define a class the following format is used:

 #!/usr/bin/env gbs3

 Public sMyVar1 as string

 Public oMyClass as new MyClass

 class MyClass
   sub sub1()
     print "hello"
   end
 end class

 oMyClass.sub1()

A Class's data and functions are defined in the same way as in the IDE except you must add the Class and End Class statement. Notice that the class does not have to be defined before it is used.

Defining a structure inline

Structures, which are a special case of class, must be defined before any reference to that structure. All structures must be declared as Public and are Global.

Examples of defining Structures

 #!/usr/bin/env gbs3
 Public sMyVar1 as string
 Public oMystruct as new MyStruct

 Public Struct MyStruct
  sText as string
 end struct

 oMystruct.sText = "hello"

The above example will fail!

The correct method is

 #!/usr/bin/env gbs3
 Public sMyVar1 as string
 public struct MyStruct
   sText as string
 end struct

 Public oMystruct as new MyStruct
 oMystruct.sText = "hello"

Executing a Gambas script

There are two ways to execute a Gambas script

You may create a non-executable file as in the example below.

From a file named MyScript.gbs containing:

Public Sub Main()
    DoPrintHello()
End

Private Sub DoPrintHello()
    Print "Hello"
End

This may be executed with:

gbs3 MyScript.gbs

An alternative to this, and most commonly used, is to provide the command inside the file and make it executable.
From the file myscript.gbs containing the following:

 #!/usr/bin/env gbs3

 Public Sub Main()
    DoPrintHello()
 End

 Private Sub DoPrintHello()
    Print "Hello"
 End

If the file is made executable with

chmod 755 myscript.gbs

Then it can be executed with the simple command:

./myscript.gbs

from the current directory. If we want to make this a command for general personal use it can be copied to:

 ~/bin

or for more general use to:

 /usr/bin

Passing Arguments To The Script

There is an ARGS[] array which contains arguments passed by command line.

#!/usr/bin/env gbs3

Dim arg as String
Dim i as Integer

for each arg in ARGS
    Print "Argument Nr. " & i & " = " & arg
    Inc i
next

See that position 0 of ARGS is used by Gambas itself, it contains the path to the cached compiled project:

$ ./test.gbs ding dong
Argument Nr. 0 = /tmp/gambas.1000/script-cache/b1826db433d3855de7e021ca9ad34b87/test.gbs
New for 3.15.3 Arg 0 now reports the name of the script correctly as test.gbs
Argument Nr. 1 = ding
Argument Nr. 2 = dong

Note: It is possible to get the name of your original script using file.name(args[0])

Using Scripter gbs3 as a component in your project

Sinds 3.16
It is possible to use Scripter gbs3 as a component in your project. Scripter provides three interfaces:

The first is for dynamically loading plugins and is defined as follows:

 '' This allows integration of scripter into an application that uses plugins
 '' the call would be ScripterPlugin(MyScript,"-b -c etc these are other parameters")
 '' This returns the instantiated class of the plugin script

 ScripterPlugin(ScriptPath As String, Optional CompileParameters As String[] = [],
                PluginNewParameters As Variant[] = Null) As Variant

 ScripterPlugin.fromString(ScriptName As String,ScriptText as string,
                           Optional CompileParameters As String[] = [],
                           PluginNewParameters As Variant[] = Null) As Variant

 ScripterPlugin.SetVerbose()                                                        'Turns on verbose information

The second allows the execution of a script from text and return its output

 ScripterExecute(ScriptName as string,ScriptText As String,
                 Optional CompileParameters As String[] = [],
                 PluginNewParameters As Variant[]) as variant

and the third is for executing a script directly returning the exit code and is defined as follows:

'' execute a script and return the exit code
'' aArgs[0] being the script name

Scriptmain.ScripterMain(aArgs As String[]) As Integer

When plugin Compile Fails using ScripterPlugin/ScripterExecute

Sinds 3.16

When a plugin load fails the result returned will be null the return code from the compiler is available from the class static variables :

ScripterPlugin error access:

   Static Public CompileResult As Integer = 0                 '' Result return by the gambas compiler
   Static Public CompileErrorText As String = ""              '' will contain the list of errors found

   ScripterPlugin.CompileErrorText
   ScripterPlugin.CompileResult

ScripterExecute error access:

   Static Public $sLastError As String = ""
   ScripterExecute.$sLastError

Loading the gbs3 component

Sinds 3.16
To include gbs3 as a component in your application the following code is required

dim Scripter,ScriptMain,ScriptExecute as object
component.load(system.find("gbs3"))
Scripter = class.load("ScripterPlugin").new()
ScriptExecute = class.load("ScripterExecute").new()
Scriptmain = class.load("ScriptMain").new()

once this is done it is just a matter of calling the interface

dim myplugin1 as object = Scripter("~/myplugins/MyPlugin1")
myPlugin1()

dim myplugin2 as object = Scripter.fromString("MyClass","print \"Hello World\"")
myPlugin2()

or to execute a text script and get the output
dim result as variant
result = ScriptExecute("MyClass2","Print \"hello world\"\nPrint \"This is also returned\"")

or to execute a script by passing the command line parameters returning the exit code for example:

dim result as integer
result = ScriptMain.ScripterMain(["-v","-f","~/myscripts/MyScript1.gbs","Script parm1","Parm2"])

Creating Plugins using Scripter --plugin

Sinds 3.16

Scripter can create plugins that can be used by other scripts or Gambas applications after being built using the --plugin or -p option. The term plugin is synonymous with the term component throughout this document.

Plugins are created in a user-selected directory and are also executed from and cached in this directory, unlike a normal script, in which executables are created in the temp directory and are executed from there. This allows plugins to persist after the Scripter exits.

When a plugin script is cached it will not be recompiled Scripter will return with no error.

If no output destination is specified, then the plugin is created in the:
  ~/.local/lib/gambas directory.

Plugins may be loaded from there using the component.load() function.

Scripts being used as plugins are generated with the entry point defined as
      with a class of the same name as the base name of the script
      _Call(...) as variant
by default.

A Scripter generated plugin source may look like this for a plugin script

from:

print "hello"

to:

Class MyPlugin1
  public sub _Call(...) as variant
   print "hello"
  end
end class

So inside your script the parameters are accessed using the param gambas class. It is also possible to define the entry point yourself with fixed parameters.

_Call(counter as integer) as integer

from:

public sub _Call(count as integer) as integer
      print count
end

to:

class MyPlugin1
  public sub _Call(count as integer) as integer
      print count
  end
end class

Example: Building an App that uses plugins

Sinds 3.16

   Define some simple plugins/components for testing

   ~/myscripts/MyPlugin1.gbs:
      print "The Parameter was",param[0]
      return "This was done"

   ~/myscripts/MyPlugin2.gbs:
      print "Number of parameters passed=",param.count
      return "So was this"

   ~/myscripts/MyPlugin3.gbs:
     public sub _Call(counter as integer) as integer
       for i as integer = 0 to counter
         print i;
       next
       return i
     end

   Execute gbs3 to create a component plugin.
     > gbs3 --plugin ~/myscripts/MyPlugin1.gbs ~/myplugins
     > gbs3 --plugin ~/myscripts/MyPlugin2.gbs ~/myplugins
     > gbs3 --plugin ~/myscripts/MyPlugin3.gbs ~/myplugins

   Now in the script that requires the plugin, they can be loaded as in the following script:
     #!/usr/bin/env gbs3
     dim MyPlugin1,MyPlugin2,MyPlugin3 as object

     ' Make sure we have the latest version, assume all compiles are perfect..
     exec ["gbs3","--plugin","~/myscripts/MyPlugin1.gbs","~/myplugins"] 'if already in cache and no change then just returns
     exec ["gbs3","--plugin","~/myscripts/MyPlugin2.gbs","~/myplugins"]
     exec ["gbs3","--plugin","~/myscripts/MyPlugin3.gbs","~/myplugins"]

     ' Load the plugins
     component.load(User.home &/ "/myplugins/MyPlugin1")
     MyPlugin1 = class.load("MyPlugin1").new()
     component.load(User.home &/ "/myplugins/MyPlugin2")
     MyPlugin2 = class.load("MyPlugin2").new()
     component.load(User.home &/ "/myplugins/MyPlugin3")
     MyPlugin3 = class.load("MyPlugin3").new()

     ' Now lets access and call the plugin
     print MyPlugin1("some parameter")
     print MyPlugin2("one","two","three")
     print MyPlugin3(100)

This ability is most useful in an application which creates the script on the fly and then incorporates it into the application with access to all application classes.

    #!/usr/bin/env gbs3
    dim thenewone as object
    file.save("~/myscripts/thenewone","For i as integer = 0 to 100\nprint i;;\nnext\nprint classes.count")
    exec ["gbs3","--plugin","~/myscripts/thenewone","~/myplugins"]
    component.load(User.home &/ "/myplugins/thenewone")
    thenewone = class.load("thenewone").new()
    thenewone()

Example: Combining gbs3 as a component and using script based plugins

Sinds 3.16

Now in this example if we combine using gbs3 as a component and using plugins we have the following:

#!/usr/bin/env -S gbs3 -a
dim MyPlugin1,MyPlugin2,MyPlugin3,thenewone as object
dim Scripter as object
component.load(system.find("gbs3"))

Scripter = class.load("ScripterPlugin").new()

MyPlugin1 = Scripter("~/myscripts/MyPlugin1.gbs")
MyPlugin2 = Scripter("~/myscripts/MyPlugin2.gbs")
MyPlugin3 = Scripter("~/myscripts/MyPlugin3.gbs")

TheNewOne = Scripter.fromString("TheNewOne","For i as integer = 0 to 100\nprint i;;\nnext\nreturn classes.count")

' Now lets access and call the plugins
print MyPlugin1("some parameter")
print MyPlugin2("one","two","three")
print MyPlugin3(100)
print thenewone()


Filtering Script Text for plugins

Sinds 3.16

When using loading or generating script based plugins within an application it is possible to filter the text in the script in order to restrict access to classes or functions in that script.

This can be accomplished by creating a filter table and passing it to the Scripter class.

The filter table is a collection that is defined as follows:

    dim filter as collection = [ "Error message 1":"regular expression to match as error check",
                                 "Error message 2": "regular expression 2",
                                 .... etc ]
Each entry in the table defines and error set and the regular expression that would cause that error.

So to prevent some action or access in a script it may have defined:
           dim filter as collection = ["Print statement not allowed":"(?:^|\\W)print(?:$|\\W)",    ' yes this is a very simple example no printing!
                                       "Write statement not allowed":"(?:^|\\W)write(?:$|\\W)",    ' don't allow a write
                                       "Access forbidden classes":"^.* classes[ .]"]               ' prevent accessing the classes[]

This filter may be activated by calling:
         ScriptSetKeywordFilter(filter)

Scripter will print the error text and line number within the script and then halt the building of the plugin.

     

Components and Libraries - use

Gambas Components and Libraries can be used with the command USE

Including Other Gambas Script Files - #include

The Include keyword has been replaced with #Include preprocessor keyword
Sinds 3.16
For backwards compatibility Include will continue to be support until the next release. Programmers should convert all code to use #Include before that release.

Other Gambas script files can be included with the command INCLUDE or #INCLUDE

Using the Gambas Jit feature with scripts

It is possible to Compile and execute your scripts more quickly in some cases using the Gambas Just in Time Compiler feature. These featured may be enabled with the: -f fast option - use the jit and -u Unsafe option - remove any validation checks from code.

There are Three ways to invoke this with a script the first is from the command line

gbs3 -f -u MyScript

Or as part of the #! in your script:

#!/usr/bin/env -S gbs3 -f -u

Public Sub Main()
    DoPrintHello()
End

Private Sub DoPrintHello()
    Print "Hello"
End

Or simply at the beginning of your script:

#!/usr/bin/env gbs3
Fast Unsafe
Public Sub Main()
    DoPrintHello()
End

Private Sub DoPrintHello()
    Print "Hello"
End

Script Naming Convention

By convention gambas scripts will have the .gbs extension, but this is not a requirement your script may be named anything with almost any extension or no extention at all.

The script is actually recognized by the shell at execution time by the:

#!/usr/bin/env gbs3

as the first line of your script.

Using Command Line Scripts with -e option

It is possible to execute a quick Gambas3 script from the command line with the format:

 gbs3 -e  "dim i as integer:for i = 0 to 100 : print i: next"

Note that it is possible to have multiple statements separated by the : character.

 gbs3 -e  'dim b as collection = ["a":"b"]:dim i as integer:for i = 0 to 100 : print i: next'

If you need to pass a string from command line then you will need to use ' not " to surround the program.

In bash and sh etc. the command line script should be encased in ".." or '..'

Using Short form Variable declaration on command line

NOTE: This feature is no longer supported
Sinds 3.16

When writing a command line script it is possible to add a short hand for the definition of variables. The above example could be rewritten as:

 gbs3 -e -p "for {i}counter = 0 to 100 : print counter: next"

With the -p or preprocess option when any variable name is preceded by a {} and contains one of the following supported variable types.
"i": "Integer" "s": "String" "f": "Float"
"d": "Date" "b": "Boolean" "v": "Variant"
"o": "Object" "l": "Long" "p": "Pointer"

When this notation is used then gbs3 will generate the correct dim statement for the variable.

#Script keyword

Sinds 3.16
This keyword is used to add information to your script, which will be used by Scripter to construct the Gambas project from the inline source file. The format of this keyword is
  #Script keyword=value
Currently the following keywords are supported
KeyWord Description Content
TYPE type of project Defaults to " " normal | can be Library, Component
VERSION project version number Defaults to "0.0.1"
DESCRIPTION Description of the project Any text
MAINTAINER person maintaining app name of person
AUTHOR or AUTHORS Who Wrote This Coma separated list
VENDOR Vendor name Defaults to "Ordinary"
LICENSE Type of license Defaults to "General Public License 2.0"
TITLE Text of project title Defaults to "Gambas Script"
NAME Name of script Defaults to Script file basename
STARTUP defines the class/module to start.
Scripter uses this name to identify/create the default class or module.
When the script/Plugin executes this will be the class-_Call()/module-main() that is started.
Defaults to Scripts = "MMAIN",
Plugins = BaseName(file name without extension)
HEADER The first line of the generated project file Defaults to "# Gambas Project File 3.0"

Converting a Script to a project with --convert-script

Sinds 3.16
Using the --convert-script parameter with gbs3 will allow the user to create a project directory from an existing script. There are some limitations to this conversion. In general most gambas source file type are supported by the script conversion. Form, Connection, WebPage, WebForm file formats are very Gambas specific, so be careful with these file formats

The project is created in the specified directory and the directory has the same basename as the script file

Example

gbs3 --convert-script ~/myscripts/project1.gbs ~/myprojects will take the script project1.gbs and create the project directory ~/myprojects/project1

Converting a project to a script with --convert-project

Sinds 3.16
Using the --convert-project parameter with gbs3 will allow the user to create a script file from an existing project. There are some limitations to this conversion. In general most gambas source file type are supported by the project conversion. Form, Connection, WebPage, WebForm file formats are very Gambas specific, so be careful with these file formats.

The output script file is placed in the defined directory and is named the same as the project directory with the .gbs extension

Example

gbs3 --convert-project ~/myprojects/project1 ~/myscripts will take as input the project1 directory and output a script ~/myscripts/project1.gbs

Supported source file types

Sinds 3.16
In General for use only Classes and main module are defined. in most cases the other supported types are only used by the automated project to script conversions.

The following definitions are supported for script files
Class className
....
End Class

Module ModuleName
 ...
end Module

The following Types are experimental:

Form FormName
...
End Form

Connection ConnectionName
...
End Connection

WebForm FormName
...
End WebForm

WebPage PageName
...
End WebPage

Executing web forms and web apps from command line

Sinds 3.16
Web form and web app scripts me be launched using the following bash command line.

These are limited to simple display functions as scripts.

                 ./TestWebApp.gbs > ~/webpage1.html && firefox ~/webpage1.html
                 ./TestWebForm.gbs > ~/webpage1.html && firefox ~/webpage1.html

To Use a Web App correctly goto Gambas Web App

Getting a list of components installed and available on your system with -l Component

Sinds 3.15
To get the list displayed on the console then execute Scripter with -l parameter

  gbs3 -l Component

Getting a list of libraries installed and available on your system with -l Library

Sinds 3.15
To get the list display on the console then execute Scripter with -l Library

  gbs3 -l Library

Scripter command line args

Sinds 3.16
Usage:
   gbs3 [options][--] [<script file> | <project directory> | - ]
   gbs3 --convert-project <input project directory> <output script directory>
   gbs3 --convert-script  <input script> <output project directory>
   gbs3 --plugin <input script> [<output plugin cache directory>]

Options: -b --buildonly process and compile the script without executing it -c --nocache force the script compilation (do not check cache) -e execute the source code provided by the command line ( : separator ) -g --debug add debugging information to application -f --fast use just-in-time compiler -h --help display this help -l --list Display the list of available -l component or -l library -L --license display license -p --plugin Compile the script, no main, define the class name as script base name entry point _Call(...) -S --strict fail if Public or Sub are defined without a main function -t --trace turn on tracing option during execution -T --terse-listing only print a very terse error report on compile errors -u --use force component loading (comp1,comp2...) -U --unsafe allows jit compiler to generate unsafe faster code -v --verbose be verbose -V --version display version -w --warnings display warnings during compilation --convert-project convert a simple project to a script --convert-script convert a simple script to a project -- stop any further option processing - read the script from standard input

-b --buildonly

Sinds 3.16
Process and compile the script without executing it. This will also generate the cache version if successfully compiled. So when the script is executed it directly executes the Gambas executable previously generated for the script. An application can execute Scripter with this option verify the compile completed without error then execute the script with very little penalty in performance.

-c --nocache

Forces the script compilation (do not check cache)

-e

Execute the source code provided by the command line ( : separator )

-g --debug

Add debugging information to the generated Gambas application executable

-f --fast, -U --unsafe

use just-in-time compiler, -U allows jit compiler to generate unsafe faster code

-S --strict

Sinds 3.16
Fail if Public or Sub are defined without a main for scripts or _Call for plugins

-T --terse-listing

Sinds 3.16
Only print a very terse error report on compile error. Normally the Scripter will print a full listing of the offending file and mark the line and location of the error. In this mode it will only display the offending line and mark the error location on the line.

-u --use

Force component loading (comp1,comp2...)

-v --verbose

Be Verbose. When enabled the Scripter prints information about the creation of the project and final Executable file. It will also display a start of execution message with a start time stamp. When the script ends execution it will display an end of execution message with an end time stamp. It will then display the total execution time of the script.

-w --warnings

Display warnings during Script compilation and project build activities.

-l library

Display the list of libraries accessible by your script

-l component

Display the list of components accessible by your script