12Module

12.1Module as Environment

A module is a kind of environment and capable of containing variables and functions inside it. You can use module() function that takes a block procedure containing expressions of variable and function assignments. Below is an example:

foo = module {
    var:public = 'hello'
    func() = { /* body */ }
}

Then, you can call functions and read/modify variables in the module with a member accessing operator . specifying the module on its left.

foo.func()
println(foo.var)

By default, functions defined in a module are marked as public and are accessible from outside. On the other hand, variables in a module are marked as private and would cause an error for an access from outer scope. You have to put :public attribute in a variable assignment to make it public.

You can use modules to isolate variables and functions from the current scope by giving them an independent name space. But its main purpose is to provide a mechanism to load external files that extend the language's capability.

12.2Importing Module File

Gura language has a policy that the interpreter itself should provide functions that are less dependent on external libraries, operating systems and hardware specifications. So, variety of functions such as handling regular expressions, image processing and GUI are realized by dynamically loadable files called module files.

There are two types of module files: script module file and binary module file.

Module File Suffix Content
script module file .gura a usual Gura script file
binary module file .gurd a dynamic link library that has been compiled from C++ source code

A process of loading a module file and registering its properties to the current environment is called "import". You can use import() function in your script to import a module like below:

import(re)

This loads a module file re.gurd and creates a module re in the current scope. After importing, functions like re.match() and re.sub() that the module provides become available.

You can import module properties into the current scope by specifying their symbols in a block of import() function.

import(re) { match, sub }

Then, you can call these functions like match() instead of re.match(). Specifying * in the block will import all of the module properties into the current scope.

import(re) { * }

Usually, this is not a recommended manner because there's a risk that symbols in a module conflict with ones that already exist. However, it may be a practical way to import some modules like opengl, which guarantees all the properties have distinguishable symbols.

You can also import modules at the timing launching the interpreter by specifying a command line option -i with module names. Below is an example that imports a module re before parsing the script file foo.gura.

$ gura -i re foo.gura

You can specify multiple module names by separating them with a comma character.

$ gura -i re,http,png foo.gura

Under Windows, the interpreter searches module files in the following path, where GURA_VERSION and GURA_DIR represent the interpreter's version and the path name in which the program has been installed respectively.

  1. Current directory.
  2. Directories specified by -I option in the command line.
  3. Directories specified by environment variable GURAPATH.
  4. Directory: %LOCALAPPDATA%\Gura\GURA_VERSION\module.
  5. Directory: GURA_DIR\module.
  6. Directory: GURA_DIR\module\site.

Under Linux, the interpreter searces module files in the following path.

  1. Current directory.
  2. Directories specified by -I option in the command line.
  3. Directories specified by environment variable GURAPATH.
  4. Directory: $HOME/.gura/GURA_VERSION/module.
  5. Directory: /usr/lib/gura/GURA_VERSION/module.
  6. Directory: /usr/lib/gura/GURA_VERSION/module/site.

A variable sys.path is assigned with a list that contains path names to search module files. You can add path names into the list while a script is running.

12.3Creating Module File

Any script file can be a script module file, which you can import in other scripts. But there are several points you need to know concerning access controls. Consider the following script file named foo.gura:

var:public = 'hello'
func() = { /* body */ }

Then, you can import it to make its properties available.

import(foo)
println(foo.var)
foo.func()

As with a module created by module() function, following rules are applied:

  • Functions defined in a module file are marked as public and are accessible from outside. If necessary, you can put :private attribute in a function assignment to encapsulate it inside the file.
  • Variables defined in a module file are marked as private and would cause an error for an access from outer scope. You have to put :public attribute in a variable assignment to make it public.

As a script module file is not different to a usual script file, it can contain any expressions as well other than assignment expressions of function and variable. These expressions are evaluated once, when import() function is called.

If a script file is imported as a module, a global variable __name__ holds its own module name. For instance, a script in foo.gura sees the variable with a value 'foo' when imported. If a script file is parsed by the interpreter firsthand, the variable is set to '__main__'. Utilizing this feature, you can write a script in a module file to test its own functions like below:

func() = { /* body */ }

if (__name__ == '__main__') {
    func()  // test func()
}

Since the body of if() function would only be evaluated when the script runs as a main one, you can write codes inside it that wouldn't be evaluated when imported as a module.

12.4Extensions by Module

Modules don't only provide functions but could enhance various capabilities.

  • Extensions of Existing Class

    Some modules would provide additional methods to classes that already exists. For example, module re would add some methods to string class like string#match().

  • Operator

    Some modules would enhance operators so that they can handle objects the modules provide. For example, a module named gmp provides operators on arbitrary precision numbers.

  • Image Format

    You can use a function image() to read a image file. Importing modules that handle image data would expand the function's capability to support additional image formats. For example, after importing jpeg module, the function can read a file in JPEG format like following:

    import(jpeg)
    img = image('foo.jpg')
    // .. any jobs
    
  • Path Name for Stream

    You can use a stream instance to access a file stored in a certain storage. While a stream is opened by specifying a path name associated with it, some modules would expand the path name handler so that it can recognize its specific name format. For example, importing a module named curl would allow access to a file stored in networks and enhance the path name handler to be able to recognize names that begin with 'http:'.

    import(curl)
    print(readlines('http://example.com/index.html'))
    

    For another example, module zip provides functions to read and write content of ZIP files. and it would make the path name accessible in a ZIP file. The example below prints a content of doc/readme.txt that is stored in foo.zip.

    import(zip)
    print(readlines('foo.zip/doc/readme.txt'))
    
  • Path Name for Directory

    Path names in functions that handle directories could also be enhanced by modules.

    A function path.walk() recursively retrieves entries in a storage with a specified path name. After importing module zip, you can seek entries in a ZIP file using that function.

    import(zip)
    println(path.walk('foo.zip/src'))
    
  • Suffix Handler

    There's a case that a module will provide additional suffix handlers. For example, module gmp can handle suffix L that creates an instance of arbitrary precision number from a number literal.

    import(gmp)
    x = 3.1415L * 2 * r
    
  • Character Codec

    Modules can provide additional handlers for character codec.

12.5List of Bundled Modules

This section describes a list of modules that are bundled with the interpreter.

Image file format:

Module Note
bmp handles BMP image file
gif handles GIF image file
jpeg handles JPEG image file
msico handles Microsoft Icon file
png handles PNG image file
ppm handles PPM image file
tiff handles TIFF image file
xpm handles XPM image file

Compression/depression/archiving/hash:

Module Note
bzip2 provides compressor/decompressor functions for bzip2 format
gzip provides compressor/decompressor functions for gzip format
tar provides function to read/write tar archive file
zip provides function to read/write ZIP archive file
hash

Image operation:

Module Note
cairo provides APIs of Cairo, a 2D graphic library
freetype provides APIs of FreeType, a library to render fonts
opengl provides APIs of OpenGL, a library to render 2D/3D graphics
glu Utility functions for OpenGL

GUI operation:

Module Note
sdl provides APIs of SDL, a library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D
tcl provides APIs of Tcl interpreter
tk provides APIs of Tk using tcl module
wx provides APIs of wxWidgets, a cross-platform GUI library
show provides image#show() method that displays image on a window

Audio operation:

Module Note
midi provides APIs to control MIDI hardware and to create MIDI files

Network operation:

Module Note
curl provides APIs to access to network using CURL library
http provides APIs for HTTP server and client functions

OS specific:

Module Note
conio controls console I/O
mswin provides APIs for OLE interface registry access
msxls provides simple classes that handle MS Excel documents
uuid generates UUID

Text file operation:

Module Note
csv Read/write CSV file
markdown parser of Markdown syntax
re Regular expression
tokenizer provides APIs that tokenize strings
xml XML parser
xhtml XHTML composer
yaml provides APIs to read/write document in YAML format

Mathematical:

Module Note
gmp provides APIs of GMP, a library for arbitrary precision arithmetic, operating on signed integers, rational numbers, and floating-point numbers.

Database:

Module Note
sqlite3 provides APIs to access to database of sqlite3

Helper to build modules:

Module Note
gurcbuild provides APIs to create a composite file
modbuild used in a script to build a binary module
modgen generates template files to build a binary module

Utilities:

Module Note
argopt provides APIs to handle argument options
calendar generates a specified year's calendar
sed replaces strings using regular expression across multiple files
testutil utilities for tester script
units definition of units
utils utilities

12.6Creating Binary Module File

Gura has a mechanism to support users who create binary modules. This document shows how to create an original binary module hoge.

At first, execute the following command.

$ gura -i modgen hoge

This would generate a builder script, build.gura, and a template source file of module, Module_hoge.cpp. Although the file Module_hoge.cpp is just a C++ source file that consists of less than 40 lines of codes, it already has an implementation for a Gura function named test.

Executing build.gura would create the module by launching a proper C++ compiler. If you try it in Windows, you need to install Visual Studio 2010 in advance. You may use Express version that is available for free of charge.

$ gura build.gura --here

If you find a binary module file hoge.gurd has successfully been built in the current directory, import it into Gura's script and test it.

$ gura
>>> import(hoge)
>>> dir(hoge)
[`__name__, `test]
>>> hoge.test(3, 5)
8

Congratulations! It's ready to edit Module_hoge.cpp for implementations as you like. If you get what you want, execute the following command to install the module into Gura's environment.

$ sudo gura build.gura install

By the way, you need to get some information about C++ functions and classes provided by Gura for actual programming. The best way for it is to see source files of other binary modules. At first, find out a module from those provided by Gura, which has a function similar to what you want to create. You can find module source files in a directory gura/src/Module_module in a source package. Each module is so simple that consists of one to two source files. I'm sure it's relatively easy to know how to realize your purpose by investigating them, because they have been developed in the same coding policy.