Asp Scripting Platform

Other documents


Asp Script Writer's Guide

Welcome to Asp!

The purpose of this document is to teach you how to use the Asp language to write scripts for embedded applications and then prepare them for execution. So, what is Asp?

Asp is a scripting language and a set of tools for working with scripts. The Asp language is very similar in style to Python. This is where Asp gets its name from, both being members of the snake family of languages! Python is a full-featured scripting environment, complete with an extensive set of libraries, whereas Asp is a smaller, simpler language and environment intended to address the need to provide scripting capabilities to small embedded applications. Python scripts typically run on a personal computer and may interface with files, network connections, databases, etc. On the other hand, Asp scripts typically run inside small devices and interface only with the software that controls that device. You can think of Asp as Python's little cousin. So, familiarity with Python will be extremely beneficial in learning Asp.

While Python scripts may be run from their source .py file(s), Asp source files must be compiled to prepare them for execution on the intended target. The Python interpreter uses dynamic memory allocation to meet the needs of running scripts. The Asp engine, where the executable script runs, avoids use of dynamic memory and recursion so that scripts will not negatively impact the embedded application in which they are run. While it is theoretically possible to build the Python interpreter into an embedded application, doing so presents many challenges. Asp is intended to overcome these challenges by providing a well behaved, limited environment in which to run scripts.

Where else to look

Asp script writers do not live in a vacuum. Adding Asp support to a device is the job of the embedded software developer. It is this person's job to provide the mechanisms for running Asp scripts, and to make application functions and variables available to scripts to provide the desired functionality. The Asp development environment is described in the Application Developer's Guide.

The remainder of this document describes the Asp language in detail, the environment in which scripts run, and the methods used to prepare scripts for execution.

Getting started

To learn a new language, it is helpful to have an environment in which to practice. From the foregoing discussion, it appears that if you don't have a device that supports Asp, you cannot run an Asp script. However, there is a standalone Asp application that allows you write scripts that can be run on your personal computer. This is the best way to learn the Asp language since it simplifies the script development effort and allows quick turnaround on script runs. However, the standalone application supports only generic functions; working out logic that interfaces with an application must be done with the specific device or a suitable emulator.

Whether it is run on a target device or with the standalone application, an Asp script must first be compiled using the Asp script compiler, aspc.

The compiler requires the user to specify the name of the script source (.asp) and an application specification file (.aspec). The application specification file defines the functions and variables in the application that are available to the script and is typically provided by the developer of the device on which the script will run. In the case of the standalone environment, it is typically installed along with the compiler in a well-known location (e.g., /usr/etc/asp/standalone.aspec).

For convenience, the application specification file can be specified in two other ways, eliminating the need to include it on the command line. The first method uses a file called app.aspec in the same directory as the source file. The second method utilizes the ASP_SPEC_FILE environment variable, which should be set to the full path of the desired specification file. If both of these are present, the app.aspec file takes priority over the environment variable.

aspc myscript.asp
Using /usr/etc/asp/standalone.aspec
myscript.aspe: 439 bytes

Regardless of how the application specification file is identified, the compiler writes a line of text to indicate what file it is using and, assuming a successful compile, a line indicating the size of the resulting executable.

When specifying the application specification file on the command line, the input files can be specified in either order. So, either of these two commands will work identically.

aspc myscript.asp myapp.aspec
aspc myapp.aspec myscript.asp

If you are working primarily with one application, it is likely more convenient to assign the file name to the ASP_SPEC_FILE environment variable. Writing scripts for multiple applications may be more conveniently managed by storing an app.aspec file (or link) in the same directory where the scripts reside.

To recap, the application specification file is sought in the following ways, in the order given:

  1. Specified on the command line.
  2. An app.aspec file in the same directory as the script's source file.
  3. Defined by the ASP_SPEC_FILE environment variable.

If successful, the compiler generates a binary script executable with a .aspe suffix in the current working directory. This is the file that will run in the Asp engine, or if compiled using the standalone spec, the standalone application. The output can be redirected to files of different names, or to a completely different directory, using the -o option. More information may be obtained by running aspc with no arguments.

If script execution ends abnormally, error information is normally provided, which includes an error code and the engine's program counter (the location in the script executable where the script ended). If the script was run in the standalone application, the error code will be translated to a meaningful message, and the program counter will be translated to a source file name and location (line and column).

Hello, World

As with all programming endeavors, the first step is to write a Hello, world application and then run the tools through their paces. To this end, we will use the standalone application to run the classic program, written in Asp. To start, write the following content into a file named hello.asp.

print('Hello, world')

Like in Python, the single quotes may be replaced with double quotes ("); it's a matter of style. The next step is to compile the script.

aspc hello.asp
Using /usr/etc/asp/standalone.aspec
hello.aspe: 43 bytes

Here we assume that the standalone spec has been specified via the ASP_SPEC_FILE environment variable. At this point, we should have a binary script executable. The compiler also generates a listing file (.lst) and a debug information file (.aspd), both of which we briefly cover later.

$ ls -l hello.*
-rw-rw-r-- 1 user user  22 Jul 29 14:10 hello.asp
-rw-rw-r-- 1 user user  83 Nov 23 16:36 hello.aspd
-rw-rw-r-- 1 user user  43 Nov 23 16:36 hello.aspe
-rw-rw-r-- 1 user user 932 Nov 23 16:36 hello.lst

Since this script was compiled for the standalone application, we can now run it on our computer with the following command. (Note that the file name suffix .aspe may be present or omitted.)

asps hello.asp
Hello, world

The standalone application can also display version information at the beginning of the run and memory utilization at the end by specifying the verbose option.

asps -v hello.asp
Engine version: 0.7.3.1
Code version: 0.7.1.1
Hello, world
Low free count: 1847 (max 2048)

Arguments can also be passed to the script by adding them after the script name. This is described later when external script arguments are covered.

Options are also available for modifying the amount of memory allocated for the code (-c) and data (-d). See the help information by using either the -? or -h option.

The Asp language

As mentioned above, Asp resembles Python in many ways. All the usual rules apply, for example: case sensitivity, indenting to indicate structure, etc. However, while Asp supports the basic features like conditionals (ifelifelse), loops (while and for) and functions (def and return), there are several features it does not inherit from its bigger cousin, such as…

  1. Arbitrary precision integers
  2. Complex number support
  3. Multidimensional arrays
  4. Generators (yield)
  5. Lambdas and closures
  6. List comprehensions
  7. Exception handling (tryexceptfinally) and contexts (with)
  8. Object-oriented programming (class)
  9. Threads
  10. Files

The first point (arbitrary precision integers) needs further explanation. Since Asp scripts are intended to run in small-footprint applications, variable types are kept simple. Therefore, integers are restricted to 32 bits in width and are signed.

Most of the other features are targeted at larger applications for which Python is well suited. Small embedded application will usually have rather mild demands on the scripting environment, so these features where omitted, in part, to reduce the overall footprint of the Asp engine library.

Also, the absence of explicit file support is because many embedded applications to not have a file system. However, there is nothing stopping an application from adding the necessary functions to support file operations if the application itself supports files. While Asp does not explicitly support a special file type, it does support custom application types that can serve this kind of purpose. Scripts cannot directly access the content of application type objects, but they can assign them and otherwise pass them around as opaque objects, and of course, pass them into application functions that can interpret them accordingly. For example, an application-specific object could represent an integer file handle, as is used with the POSIX open, read, write, ioctl, and close functions. The integer value itself would be accessible to application functions, but not to script code.

Syntax conventions

As mentioned above, Asp inherits Python's general approach to syntax and structure. Statements normally end at the end of the line (newline). Line continuation is possible using the backslash character (\) as the last character on a line (but see comments, below). While not recommended, multiple statements can exist on the same line by separating them with the semicolon (;).

A colon (:) at the end of a statement indicates that the following lines of code, which must be indented, are controlled by that statement. The end of a controlled set of lines is indicated by reverting to the previous indentation level. There is a short-cut to this arrangement: If the controlling statement controls only a single statement, it may follow the colon.

Comments start with the number sign (#) and end at the line termination (newline) and may follow a line of code or exist on a line by themselves. Unlike in Python, a comment may follow a line continuation character, even with intervening spaces.

Types

In Asp, objects are typed, whereas variables are not. The types supported in Asp are slightly different than those in Python. The types are as follows:

Each is described in the sections below.

Singleton types

The None and Ellipsis types are identical to those supported in Python. They both have only a single member. The value of the None type is None; the value of the Ellipsis type is ....

The None value is useful as a placeholder to indicate that a variable has no other useful value. It is also the default value returned from functions that exits without a return statement or with a return statement with no return value(s).

The value of the Ellipsis type, namely ..., is not as useful in Asp as it may be in Python. In Python, it is used as a wildcard in multidimensional array indexing. Since Asp does not support multidimensional arrays (other than implemented as lists of lists), it is less important. However, it exists and exhibits the same behaviour in other ways as in Python (e.g., conversion to Boolean value).

Booleans

The Boolean type supports two values, False and True, which can be directly assigned to a variable. Also, several contexts call for conversion to a Boolean value, such as if and while statement expressions.

Asp also supports the usual logical operators and, or, and not, which operate on Boolean values.

Integers

Integer values can be expressed in much the same way as with Python. Hexadecimal values are prefixed with 0x, and binary values with 0b. Unlike Python, Asp does not support octal literals. To aid readability, the underscore character (_) may be placed between digits. This can be useful when using lengthy binary literals.

Unlike Python, the Asp integer type is a 32-bit signed value, giving it a range of roughly ±2 million. The reason for this is pragmatic: the usual target system for script execution is an embedded platform, typically having limited resources. Other programming languages supporting much more demanding applications have survived quite well without the need for arbitrary precision integers (which is what Python supports). Besides, many embedded platforms do not support integers larger than 32 bits.

This means that if integer values in your script become large enough in magnitude, arithmetic overflow could occur, causing the script to abort. This should not be a problem in most situations, but now you have been warned.

Floating-point values

Fractional values are represented with the IEEE Standard for Floating-Point Arithmetic (IEEE 754) double precision type (64 bit format: 53 bit significand, 11 bit exponent). Floating-point constants support the usual e notation for specifying the exponent part. When expressing floating-point values, the underscore character (_) may be used to separate digits to improve readability, just as with integer literals.

Note that while Python supports complex numbers directly, Asp does not. This is not a great loss, however. If needed, complex numbers can be emulated using 2‑tuples and functions designed to interact with them.

Symbols

For the sake of efficiency, Asp translates each variable name to a unique numeric symbol value, which is used in the code generated by the compiler. In some cases, it is necessary to express the symbol of a variable name. (This will come up when we discuss the **kwargs feature of functions arguments.) To expression the symbol of a variable, say var, use this syntax:

`var

Symbols may be used as keys in sets and dictionaries, but they cannot participate in relational comparisons. When printed, symbols are displayed with their numeric value since the name is not available. The symbol value can, however, be translated back to a name using the aspinfo utility.

Ranges

Asp is different from Python in that it supports a native range type. The closest thing to this in Python is the built-in range class (xrange in Python 2). The general Asp syntax is as follows:

[start]..[end][:step]

When specified, each component must yield either an integer value or None. Each component may also be omitted. When either omitted or None, a default value is supplied: -1 or 0 for start (depending on the sign of the step component), ±∞ for end (also depending on the sign of step), and 1 for step. If step is omitted, the trailing colon must also be omitted. Here are some valid range expressions and their meanings:

..5             # From 0 to 4
1..10:2         # The odd numbers between 1 and 9, inclusive
..              # An unbounded range starting at 0 and increasing by 1
..:-1           # An unbounded range starting at -1 and decreasing by 1
i..j+k          # From i to j+k-1, inclusive
5..0:-1         # From 5 down to 1, in that order
..-5:-1         # From -1 down to -4

Ranges can be used as array indices to perform slicing operations. They are also iterable, making them very useful in for statements.

As mentioned above, Asp ranges can be specified without an end component. Such ranges are referred to as unbounded ranges and are not available in Python. Unbounded ranges can be useful, for example, when creating a loop with no end condition while still incrementing an index.

Strings

Strings are sequences of bytes, usually interpreted as ASCII characters. (In Python 3, strings are composed of Unicode characters; Asp does not support Unicode.) As in Python, strings are immutable. They are considered sequences, however, making it possible to access each character within a string as a separate string.

Strings are expressed in much the same way as in Python. The contents of a string may be enclosed in either single or double quotes (' or ") and they support the usual escape sequences via the backslash (\), except that the \nnn form uses decimal digits, not octal. The \xhh form is the same as in Python, expressing a character value with two hexadecimal digits. Raw and triple-quoted string syntax is not supported.

Tuples and lists

Tuples and lists are essentially identical to their Python counterparts. Each is specified with a comma separated list of elements, tuples being enclosed in paratheses (()) and lists in brackets ([]). To make a 1‑tuple, a trailing comma must be used to distinguish the tuple's paratheses from those used in expressions for overriding operator precedence. As in Python, the difference between the two is that tuples are immutable while lists are mutable.

Sets and dictionaries

Sets and dictionaries in Asp differ in many ways from their Python analogues. Both organize their members for random access. However, while Python uses hash tables to implement these data structures, Asp implements them as binary trees (for space efficiency). This means that members are stored in a well-defined order. When iterating over these collections, members are accessed in that order.

As in Python, the difference between sets and dictionaries is that sets contain only keys, while dictionaries contain key/value pairs.

Another difference between Python and Asp is how empty collections are expressed. In Asp, the empty set is simply {}, the same as Python's empty dictionary. To express an empty dictionary, the expression {:} must be used. Any attempt to treat a set like a dictionary (and vice versa) will result in a run-time error.

s = {}          # s is assigned an empty set
d = {:}         # d is assigned an empty dictionary
s2 = {1, 3, 5}  # a set with 3 keys
d2 = {'one': 1, 'two': 2, 'three': 3} # a dictionary with 3 kv pairs

As can be seen above, expressions of non-empty sets and dictionaries is the same as in Python.

Both types of collection support insertion and deletion operations, as well as membership tests and value access, all of which are covered in the subsequent section on operations.

Functions

Scripts can define functions using the def statement. Functions are also defined within the application in which the Asp engine is hosted. Calling an application function is no different than calling a script-defined one.

Once defined (whether via def or by the application), functions can be assigned to a variable or list element (i.e., dispatch table), or otherwise manipulated, just as in Python. For example, a script is free to override an application-defined function by reassigning its name to another function (of either type).

Functions belong to the module in which they are defined. (For a description of modules, see the next section.) For script-defined functions, this is the module in which the def statement appears, even if the function has been called through a variable in a different module. When code within a function accesses global variables, it is accessing the global namespace of the module to which it belongs.

Modules

Both Asp and Python support the notion of modules. In Asp (as in Python), a module is defined by its source file (.asp file). Modules can be imported via the import and fromimport statements, including use of the * in the second form. Asp defines a special system module (named sys) where the application functions and a few special members are defined. When looking up the value for a name, if the name cannot be found in the local or global scope, the system module is automatically searched.

The members of a module can be visited using an iterator.

Iterators

Iterators are objects that enable the traversal of objects within a collection or other iterable object. The for statement uses them to visit each member in the iterable. Unlike in Python, when iterating over a dictionary, the dereferenced value is a key/value tuple, not just the key. In the case of modules, the key in the dereferenced value tuple is a symbol. Iterators come in two types: forward and reverse.

As in Python, strings may be iterated over, but since there is no single-character type, the resulting value is a single-byte string. Also, as in Python, ranges may be iterated over as well, each iteration yielding the next value in the range.

Application objects

Application functions can create and return objects that only the application knows how to access. To scripts, these objects are opaque. Their only use is to pass them back to other application functions. Within a script, application objects may be assigned to variables, grouped into collections, compared for equality (but not compared relationally), and even used as keys for sets and dictionaries (because they are immutable). Yet, their contents cannot be examined by a script.

More information about application objects may be found in the Application Developer's Guide.

Types

Finally, there is a type that represents an object's type. A library function named type may be used to access the type of an object or expression. Types can be compared for equality and order.

Expressions

Asp uses expressions to indicate how to operate on values (operands) to yield result values. This section focuses on expressions, their syntax and meaning. The section that follows turns to statements, most of which contain expressions in their construction.

Asp supports many operations on objects using a combination of operators and built-in functions. When using operators, their relative precedence must be taken into consideration. The following table lists Asp's operators, in descending order of precedence. Most operators are binary (i.e., taking two operands on either side of the operator). Where this is not the case, italics are used to identify syntactical elements used in the context of the operators.

Operator Description Associativity
( expr ) Parenthesized expression. Left-to-right
` name Symbol. N/A
. Member selection. Left-to-right
seq [ expr ]
func ( args )
Sequence indexing/slicing and function call. Left-to-right
** Exponentiation. Right-to-left
+ expr
- expr
~ expr
Unary positive, negative, and bitwise NOT. Right-to-left
*, /, //, % Multiplication, normal division, floor division, and modulo (remainder after division). Left-to-right
+, - Addition and subtraction. Left-to-right
<<, >> Left and right bit shift. Left-to-right
& Bitwise AND. Left-to-right
^ Bitwise exclusive OR (XOR). Left-to-right
| Bitwise OR. Left-to-right
<=> Object order. Left-to-right
==, !=, <, <=, >, <=,
in, not in, is, is not
Comparisons, membership test, and identity test. Left-to-right
not expr Unary Boolean NOT. Left-to-right
and Boolean AND. Left-to-right
or Boolean OR. Left-to-right
start .. end
start .. end : step
Range with optional step. N/A
trueval if cond else falseval Conditional expression. Left-to-right

Commas (,) used to separate items within a collection constant, and colons (:) used to separate key from value in dictionary entries, are not considered operators, but are syntactically of lower precedence than the operators listed in the above table. Assignment (=, +=, =, etc.) and insertion (<-) are also special, operating at the lowest precedence within an assignment statement.

Some operations rely on an ordering between objects. For example, as mentioned previously, the entries within sets and dictionaries rely on an ordering of all objects that can be used as keys. Also, the order of any two objects can be ascertained using the <=> operator. The first thing to consider for object sort order is the type of the objects being compared. The following table lists the types in their order, from least to greatest.

  1. None
  2. Ellipsis
  3. Boolean
  4. Integer
  5. Floating-point
  6. Symbol
  7. Range
  8. String
  9. Tuple
  10. List
  11. Set
  12. Dictionary
  13. Function
  14. Module
  15. Reverse iterator
  16. Forward iterator
  17. Application object
  18. Type

Within each type, the sort order depends on the type at hand. For Booleans, False comes before True. Integers, floating-point values, and symbols are treated by their numeric order. For ranges, unbounded ranges come before bounded ones, and within each group the start value is compared first, then the end value, and finally the step value. Strings are compared lexicographically, character by character. The individual elements of sequences (tuples and lists) are compared in order from first to last. Sets and dictionaries are also compared lexicographically by key first, then for dictionaries, value. For all collections, comparison is defined recursively when the collections contain other collections. Iterators do not support relational comparison; they are not allowed as keys, and they cannot be compared with the relational operators. The sort order of functions, modules, and application objects is somewhat obscure, but is guaranteed to be well-defined. And, of course, for type objects, the order of the types listed above applies.

Each type of expression is covered in detail in the sections that follow.

Arithmetic expressions

As expected, Asp supports the same assortment of arithmetic operators that Python does. However, there are some notable differences in some result types.

The arithmetic operators typically operate on numeric operands. There are some exceptions, however, which are covered below. The numeric types include integers and floating-point values, but also include Boolean values. When treated as numeric values, Boolean values are converted in the obvious way: False becomes 0 and True becomes 1.

The unary + and operators perform the obvious operations. If the operand is either Boolean or integer, the result is an integer. For a floating-point operand, the result is floating-point.

The binary arithmetic operators are: + (addition), -(subtraction), * (multiplication), / (normal division), // (floor division), % (modulo), and ** (exponentiation). In most cases, the result is as you would expect from Python 3. For example, dividing two integers (with normal division) results in a floating-point result. There is one notable exception though. Exponentiation between two integers always results in a floating-point value.

It should be noted that because Asp integers are of fixed size (32 bits), arithmetic overflow conditions can arise. For example, dividing the most negative integer value by -1 will cause a script to end in error, as will adding, subtracting, or multiplying values that yield values that cannot be represented. In these cases, wraparound does not occur. If integer wraparound is desired, custom functions should be added to the application to provide this functionality.

In addition to operations on numeric operands, two other operations are supported which may be familiar to Python programmers: sequence concatenation and sequence repetition.

Two sequences of the same type may be concatenated using the + operator. For example, consider the following statements.

l1 = [1, 2, 3]
l2 = [4, 5]
lr = l1 + l2

The resulting list, lr, is assigned the value [1, 2, 3, 4, 5], the concatenation of the two operands. Tuples can be concatenated in the same way. Concatenation also works with strings so that adding, for example, 'abc' and 'def', results in the string 'abcdef'.

Sequence repetition uses the * operator to repeat a sequence a specified number of times. One of the operands is a sequence and the other operand must be an integer (or Boolean). Either order is acceptable, and the result is the same. Again, sequence repetition is supported for all sequence types: string, tuple, and list. Here is an example of string repetition at work.

sr = 'ha' * 3

The result, sr, is 'hahaha'.

When the % operator is used with a string as the left operand and a tuple as the right operand, string formatting is performed like old-style Python printf formatting. Unlike with Python, the right-hand operand must be a tuple, even if only one item is being formatted. Since Asp strings consist of only single-byte (ASCII) characters, the a conversion type is equivalent to the r type, both performing conversion as if the repr library function was called.

Bitwise operations

Asp directly supports bitwise operations on integer values (including Booleans converted to integer). Unary ~ (not) is supported, as well as the binary operators & (and), | (or), ^ (exclusive or), << (left shift), and >> (right shift). Shifting right performs sign extension. In all other bitwise operations, values are treated as if they are unsigned.

Note that because Asp restricts integers to be 32 bits in width, shifting left can result in loss of data and/or a change of sign. This is different than Python, which supports arbitrary precision integers.

The following example clears a bit within an integer bit mask.

bits = 0xFFFF
n = 3
result = bits & ~(1 << n)

The value in result is 65527, or in hexadecimal, 0xFFF7.

Comparisons

The standard comparison operators are as expected: == (equal), != (not equal), < (less), <= (less or equal), > (greater), and >= (greater or equal). They work nearly the same as they do in Python, yielding a Boolean result.

A couple of words of caution are appropriate here. Asp does not support Python's chained comparison expressions, e.g., a < b < c. In Python, this expression really means a < b and b < c. If the former expression is used in Asp, the left associativity of the < operator would ensure that a < b is evaluated first and would yield a Boolean value which would then be compared with c. This is likely not what was intended, so the latter expression is preferred in Asp.

Also, in Python, the relational operators (<, <=, >, and >=) are overloaded for sets to mean subset/superset testing. This is not so in Asp; like dictionaries, the relational operators are not defined for sets. If set operations like subset test, set difference, set intersection, etc. are desired, functions can be written to provide this functionality.

Unlike in Python, iterator objects may be compared for equality. (Python's equality test between iterators simply checks whether the two operands are the same object.) In Asp, two iterators are considered equal if they refer to the same iterable object and they are either positioned on the same item within the iterable or are both at the end of their travel. Note that a forward iterator is considered equal to a reverse iterator if these conditions are met, notwithstanding their difference in type. If two such iterators must be distinguished, their types or identities may be compared.

In addition to the standard comparison operators, Asp supports the object order operator, <=>. As mentioned above, Asp defines an ordering of all objects. The <=> operator can be used to determine the order of two objects of any type and yields a value of -1, 0, or 1, indicating that the left operand is ordered less, equal, or greater than the right operand, respectively. The precedence of the <=> operator is higher than the standard comparison operators, so the following code will determine the order between objects a and b, and then the resulting integer value will be compared with 0.

if a <=> b < 0:
    # a sorts less than b.

Logical operations

Logical operations include the unary not, and the binary and and or operators. As with Python, short-circuit logic is used, so for the binary operations, the first operand is interpreted as a Boolean, and the expression returns either the original value of the first operand or the value of the second operand.

Every object can be interpreted as a Boolean value. None and ... are False and True, respectively. Numeric values are interpreted as False if zero, and True otherwise. The following objects are interpreted as False: a null range, an empty string, tuple, list, set, or dictionary, and an iterator at its end. A type object is False if its value is the None type. All other values are interpreted as True. Here is an example using non-Boolean values with the logical operators.

a = 3; b = 5
r1 = not a
r2 = a and b
r3 = a or b

The values of r1, r2, and r3 are False, 5, and 3, respectively. If it is more desirable to force the results to Boolean, one way to do so is to use two not operators, as in this example.

a = 1; b = 0
r4 = not not a and not not b
r5 = not not a or not not b

Because one of the values is interpreted as False, the results for r4 and r5 are False and True, respectively.

The conditional expression also uses short-circuit logic. Consider the following example.

x = a if t else b

Here, x takes on the value of a if the expression t is interpreted as True, and the value of b otherwise.

Note that in all these cases of short-circuit logic, unused expressions are not evaluated. This becomes important when the expression has side effects, for example, a function call. Consider these examples.

True or foo()
False and foo()
foo() if False else bar()   # bar is called

In all these cases, the function foo is never called. Note also that the binary and and or operators are left associative. Expressions are evaluated only as necessary to determine the final result. As a final example, we mix things up and use other types.

a = 7.2; b = ''; c = False;
r6 = a and b and c
r7 = a or b or c

Here, the evaluation of r6 ends when b is interpreted as False, giving r6 a value of '', while the evaluation of r7 ends when a is interpreted as True, yielding a value of 7.2 for r7. Of course, and and or may be used in the same logical expression, with and having the higher precedence.

Membership tests

The in and not in operators test for membership of an object within a collection. Like Python's operators of the same names, they return Boolean results indicating whether the left operand occurs in the right operand. When strings are used, substring search is performed as in Python. Also as in Python, testing an integer for membership within a range object is computed mathematically and is therefore very fast compared with expanding the range into a list and then searching the list.

These operators can also be used to test the existence of a symbol in a module or in the local scope of a function. In this usage, the left operand must be a symbol and the right operand can be either a module or the Ellipsis (...). As described in the section on built-in functions, the module function returns the current module, so to determine whether a variable, say x, exists in the current module's global scope, the following code can be used.

if `x in module():
    # ...

To check the local scope, the Ellipsis (...), acts as a proxy for the current local scope. So, in a function, it refers to the set of variables local to the function. At the global scope of a module (i.e., outside of any function definition), it refers to the module's global scope. Here is an example of how to check whether x exists in the local scope, regardless of where it is used.

if `x in ...:
    # ...
And of course, the sys module can be checked by specifying it as the right operand.

Identity tests

As in Python, the is operator tests if two objects are the same object. This is not the same as a test for equality. Two objects, assigned to a and b, with the exact same value are not the same object, and therefore the expression a is b will yield False. The opposite test is performed with the is not operator.

Indexing

All sequences may be indexed using syntax that resembles array indexing in other languages. The index values of elements in a sequence start at zero. For tuples and lists, a valid index value yields the selected element. In the case of strings, since Asp (as with Python) does not support a single-character type, an indexing expression simply yields a single-character string. Indexing ranges is done mathematically and is therefore very fast compared with expanding the range into a list and then indexing the list. Consider these simple examples.

tup = ('abc', 'lmno', 'xyz')
lst = [10, 20, 30]
rng = 2..15:3
r1 = tup[1]
r2 = lst[2]
r3 = tup[0][1]
r4 = rng[2]

The values of r1, r2, r3, and r4 are 'lmno', 30, 'b', and 8, respectively.

For convenience, negative indices are supported, and operate from the end of a sequence, just like in Python. Therefore, an index of -1 identifies the last element, -2 the second to last, etc. Note that negative indices are not allowed when indexing unbounded ranges since the number of elements represented by an unbounded range is theoretically infinite.

Dictionaries may also be indexed, but in this case, the index value is a key, and the resulting value is the value associated with the key. Sets do not support indexing. To test whether a key exists in a set or a dictionary, the in operator (or not in) should be used.

It should be noted that the indexing syntax can be used on the left side of an assignment when the object being indexed is a list. This is discussed further in the section on assignment.

Slicing

Asp supports using a range as the index in the indexing syntax, indicating a slicing operation. This is much like Python slicing, except the syntax is slightly different. Slicing applies only to sequences (including strings and ranges), and the result of a slicing operation is a subset of the sequence being sliced. Here is an example using a list.

lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sl1 = lst[3..5]
sl2 = lst[-3..-5:-1]

The value of sl1 is [3, 4] and sl2 is [7, 6]. Notice that using slice ranges with a negative step results in elements being arranged in reverse order from the original sequence.

As mentioned above, string slicing is also supported, as in the following example.

s = 'abcdefg'
ss = s[2..:2]

The resulting string, ss, is assigned the value 'ceg'.

Because ranges represent sequences of integers, they may be sliced as well. In this case, the result is a new range. Range slicing is very efficient since it uses arithmetic to compute the new range's component values. Care should be taken when slicing unbounded ranges; the slicing range cannot index the range being sliced with a negative value (since its size cannot be determined for wraparound access).

rng = 2..30:3
rs1 = rng[5..:2]
rs2 = (..20)[..:-1]

Here, rs1 is 17..32:6, which, if expanded into a list, would yield [17, 23, 29]. The value of rs2 is 19..-1:-1. Notice that because the range being sliced is a constant, it must be enclosed in parentheses because indexing has a higher precedence than range expression syntax.

Like simple indexing, a list slicing operation may appear on the left side of an assignment. More discussion may be found in the section on assignment.

Member selection

To access a variable in another module, the . operator is used. A simple example should suffice to illustrate its usage.

print(mod.x)

Here we assume the presence of a print function (which the standalone application includes). The module must have been previously imported. A subsequence section on modules explains importing.

Member selection may also appear as the target of an assignment. See the section on assignment for more details.

Function call

Functions, whether defined in a script or in the application, may be called using function call syntax. In its simplest form, it consists of the name of a function (in fact, a variable referring to the function) followed by a list of arguments in parentheses.

foo(1, 2.4, 'abc')

Functions can return one or more values. The above example ignores any returned values. To get at the returned value(s), the function call can be used in a larger expression, or, for example, in an assignment.

Asp supports the notion of named arguments for assigning values to specific parameters listed in the function definition. For example, the following call uses named arguments.

move_to(x=10, y=20)

As in Python, a group of arguments in an iterable object may be expanded and passed as separate positional arguments to a function. This feature is related to the support for variable argument lists, which is described in the section on the function definition statement. Normally, the object being expanded is a tuple, but other types of objects can be expanded as well: range, string, list, set, and dictionary. In the case of dictionaries, key/value pairs are expanded as 2-tuples.

Here, the number of arguments passed to the print function is the number of elements in the args tuple, plus 1 for the first argument.

print(10, *args)

A group of named (keyword) arguments in a dictionary may also be expanded and passed as separate named arguments using the ** prefix. Instead of using strings for the names as Python does, Asp requires that the keys be symbols (e.g., `name).

Statements

This section discusses the various statements that Asp supports. One of the simplest statements consists of an expression on a line by itself, although this type of statement does not perform anything useful unless it is a function call.

Variables are manipulated using assignment and deletion statements and are much the same as they are in Python. Asp introduces a generic insertion statement for inserting items into collections.

Asp supports the simple control structures: conditionals (ifelifelse), loops (while and for), and function definitions (def), each of which are described below, along with other types of statements.

Assignment

A simple assignment uses the equal sign (=) with a variable on the left and an expression on the right.

x = y + z

As in Python, chained assignment is supported, so multiple variables can be assigned the same value in a single statement.

i = j = k = 0

A variable sequence expression can appear on the left side, supporting sequence assignment as in Python. The sequence is usually a tuple but may also be a list. As long as the structure of the sequences are compatible, the individual variables are assigned corresponding values from the right-hand expression. A slightly complex example is appropriate here.

a, (b, c), d = x, y, z = t

Notice that variable sequence assignment also supports chained assignment. In the above example, it is assumed that y will be a sequence (tuple or list) compatible with (b, c), where either element may be of any type (including sequences), and t must be compatible with the structure of the left-hand variable expression. Sequence assignment can also be used to swap values without the use of a temporary variable, as in the following example .

a, b = b, a

Assignment to a list element or dictionary entry value is possible using an indexing expression on the left-hand side.

a[i] = 6

If a is a list, the value 6 is assigned to the i'th element of a. If it is a dictionary, i is a key and an entry is either added or replaced in the dictionary.

An indexing expression on the left side of an assignment may also use a range to support slice assignment. Care should be taken to ensure that the number of values on the right side is the same as the number of elements in the slice.

Finally, it is possible to assign a variable in a different module by using member selection syntax on the left side.

module_b.debug = True

As in Python, Asp supports a wide variety of augmented assignments. The quintessential way to increment a variable uses the augmented += operator.

i += 1

The above statement is equivalent to i = i + 1 but is more concise.

Insertion

Asp introduces syntax for inserting items into collections. Python does not have equivalent syntax, but rather uses an insert or similar method. There are key differences between the two which are highlighted below.

Asp uses the <- operator to insert items into a collection. The collection, whether it is a list, set, or dictionary, appears on the left-hand side of the operator. (Since tuples and strings are immutable, they cannot be used in an insertion statement.)

Depending on the type of collection, the syntax of the right-hand side may be a simple expression, or a colon-separated pair of values, where the first value indicates where to insert, and the second value is the value to be inserted. Let us examine insertions into each type of collection.

The following example illustrates how to insert an item into a list.

lst = ['a', 'b', 'c', 'd', 'e']
lst <- 3 : 'abc'

The value 'abc' is inserted into the list lst as element 3. (Recalling that indices are zero-based, that would be the fourth element.) The index may be any expression that results in an integer but must be valid for the list. So, for example, inserting into a 5-element list, the index can be any integer in the range −6 to 5. A value of 5 would append the item to the end of the list and a 0 index would insert at the beginning. Negative indices access the list from the end, so an index value of −1 would also append the item to the list and −6 would insert it before the first element. Note that this is different than the treatment of negative index values passed to the Python list's insert method.

To simply append an item to the end of a list, the simpler syntax will suffice.

lst <- 'abc'

Here, 'abc' is appended to the end of lst.

Instead of using an integer index, the insertion location can be specified using an iterator, either as the key in the key/value pair, or as the target of the insertion. Elements are inserted before forward iterators and after reverse iterators. If an iterator has reached its end, the element is appended for a forward iterator or prepended for a reverse iterator.

lst = [1, 2, 3, 4, 5]
it = iter(lst)
next(it); next(it)
lst <- it : 'abc'
next(it); next(it)
it <- 'xyz'

In the above example, the final value of lst is [1, 2, 'abc', 3, 4, 'xyz', 5].

Sets hold only keys, so the insertion statement for a set should only use the simple syntax, where the value is the key to be inserted.

s <- 'abc'

This is a good point to mention that, like assignment, chained insertion is also supported (with either syntax). So multiple items can be inserted in a single statement.

s <- 1 <- 3 <- 5
lst <- 0 : 'aaa' <- 'zzz'

Notice that simple items and key/value pairs may be used interchangeably in a chained insertion. The insertions occur in order from left to right.

Finally, insertion into a dictionary requires the key/value pair syntax since every entry in a dictionary must be identified by a key.

d = {}
d <- 1 : 'aaa' <- None : 'zzz' <- 'xyzzy' : 42

For dictionaries, if the key already exists, its associated value is replaced. Again, since insertion happens from left to right, any duplicate keys in a single statement will result in the final value for a given key being stored.

Deletion

As in Python, the del statement is used to delete a variable (whether in the current module or one specified by the member selection syntax) or an item from a list, set, or dictionary. Deleting a slice of elements from a list is also supported.

Pass

The following sections discuss control structures, those statements that control the flow of execution within a script. These statements require the presence of one more target statement to be controlled (e.g., the body of an if statement). Sometimes, it is desirable to perform no action in these controlled statements (e.g., when the test expression performs all the necessary operations, or the test expression changes value via some external stimulus). In these cases, there is the pass statement. Like in Python, pass is the null operation. It performs no action other than to be a placeholder when a statement is required by the surrounding syntax, but no action is required.

Conditional statement

The conditional statement, if, is just like its counterpart in Python. After the main body, one or more elif parts may be added, and an optional else may follow at the end. At most, only one of the controlled bodies of code will be executed. If no else part exists, it is possible that none of the bodies are executed. As in Python, the test expressions are interpreted as Boolean values to decide on the path of execution.

Simple loops

Asp supports the full syntax of the Python while statement, including the optional else clause, which is executed only if no loop iterations were performed. The test expression is interpreted as a Boolean value for purposes of controlling the flow of execution.

Iterating loops

Asp provides support for the for statement, including the optional else clause as with the while statement. The for statement uses an iterator to iterate over the specified iterable, assigning each visited item to the target variable(s).

When the iterable is a dictionary, both the key and value are assigned to the control variable(s) as a tuple.

d = {1: 'a', 2: 'b', 3: 'c'}
for k, v in d:
    print(k, v)

In the above example, sequence assignment is used to assign the key and value of each dictionary entry to separate variables. If only one variable was used, it would hold a tuple of the key and value.

Loop control

Sometimes it is desirable to circumvent the default flow of control within a loop structure. The break and continue statements do just that, as they also do in Python. The break statement exits the loop in which it is specified, while the continue statement leaves the body and checks the test expression before deciding whether to execute the body again. Both are usually the target of an if statement.

Function definition

Functions may be defined in a script using the def statement. Recall that functions are also defined in the host application, but their definition is performed externally, and therefore not discussed here, other than to state that their definitions appear in the sys module, and like all other variables, may be replaced if desired.

As with Python, Asp supports default function parameter values. And, also as with Python, care should be taken with mutable parameter values (e.g., lists and dictionaries), especially when they are used as defaults. The behaviour in such cases is the same between Asp and Python. Consider the following example.

def foo(x = []):
    x <- 0
    return x
foo(); foo()
print(len(foo()))

The default value for x is an empty list, which is mutable. The function modifies the list by appending an element, so after the first call, the default value is no longer an empty list. In this example, the script will print out 3 (not 1 as may be expected). As with Python, a better approach would be to use a non-mutable default parameter instead.

def foo(x = None):
    if x is None:
        x = []
    x <- 0
    return x
foo(); foo()
print(len(foo()))

Now the script prints out 1 since a new empty list is created every time the x parameter is left to its default value.

Asp functions support both varieties of variable parameter lists: positional (*args) and named, also known as keyword (**kwargs). If specified in the function's parameter list, extra positional arguments in a function call are gathered into a tuple and passed into the function as the positional variable parameter list parameter, and likewise any extra named arguments are gathered into a dictionary and passed into the named variable parameter list parameter. Whereas Python uses strings as the keys for extra named arguments, Asp uses symbols (e.g., `name). So, to test whether a named argument exists in the named variable parameter list, something like the following code can be used.

if `extra in kwargs:
    # Do something with kwargs[`extra]

Functions can return one or more values to the caller via the return statement. If a function exits by simply executing the last statement in its body, or by executing a simple return statement with no values, the default behaviour of returning None is invoked. When multiple values are returned, the caller receives them in a tuple.

Scope control

Variable access in Asp depends on the lexical scope where the access occurs (i.e., in a function or at the global scope of the module). Each called function defines its own scope so that newly defined variables are created locally by default and destroyed upon exit from the function. Each module has its own scope where variables are maintained. Accessing the value of a variable from within a function first checks the local function call scope, and if the variable is not found there, the module's scope is checked. Finally, if the variable is not defined in either the function or its module, the sys module is checked. This is how it is possible to call application-defined functions or access application-defined variables without the need to use member selection.

Within a function, global variables (i.e., variables in the scope of the module in which the function is defined) can be accessed by using the global statement prior to the accessing statement. Unlike Python, a global statement is dynamic (not a lexical hint to the pre-compiler). In other words, a variable name can be assigned and used locally, and then overridden by using a global statement which changes the meaning of the variable name to refer to the module's global scope. To switch back to using the function's local variable, Asp has a local statement which removes the global override.

def foo():
    x = 1 # local
    global x
    x = 2 # x now refers to global scope;
    local x
    print(x) # prints 1

In this example, the assignment to x after the global statement occurs in the module's global scope. If x is undefined in the global scope, it is created (as if the assignment took place at the global scope). Otherwise, it's value is replaced with the new value.

Parameters are considered local variables but, it should be noted, are also subject to the global and local statements. Also, as with Python's global statement, multiple variables may be given in a single global or local statement, separated by commas.

Assertion

Asp supports an assert statement that aborts the script execution if the given expression evaluates as False. Python's assert statement includes an optional second expression which is used to construct the AssertionError object that is raised as an exception. Because Asp does not support exceptions, Asp does not support this second expression in its syntax.

assert i < size

The above statement will cause the script to abort if the value of i is greater than or equal to size. As with other control statements, the expression need not be Boolean. Non-Boolean expressions are converted to Boolean as covered in previous sections.

It should also be noted that while Python's assert statements can be disabled for performance reasons (using an option to the Python interpreter), Asp's assert statements are always performed.

Modules revisited

Asp supports the writing of scripts in multiple modules, each defined in its own .asp file. The import and fromimport statements (both referred to generically as import statements in what follows) operate much like their Python counterparts. The code of each module is executed upon the first execution of an import statement for the module. After the module has been executed once, subsequent imports of a module simply create the appropriate variables (referring to either the module itself or variables within the module), just as in Python.

When compiling a script, import statements trigger the compiler to process the specified module(s). The compiler uses a search path to locate the source file for each imported module. If left undefined, the default behaviour is to search only the directory in which the top-level module resides. To define a search path, assign a list of directories to the ASP_INCLUDE environment variable, separated as appropriate for the operating system (e.g., colons in Linux, semicolons for Windows). When using ASP_INCLUDE, the directory of the top-level module is not searched by default. A blank entry somewhere in the search path will restore searching of the main module's directory. For example, consider the following search path, specified for a Linux system.

/usr/local/lib/asp:/usr/lib/asp::~/asp

The compiler will search for modules in the /usr/local/lib/asp and /usr/lib/asp directories, the directory in which the top-level module resides, and then the asp directory in the user's home directory, in that order.

When working with multi-module scripts, it should be noted that the compiler generates a single executable, not multiple executable files.

The system module

As mentioned in the section on scope control, the system module, sys, is searched when accessing a variable name. This is convenient because all the application-defined functions and variables are defined there.

The developer of a system has complete control over the application functions and variables that are made available to the script writer. However, there are several extremely useful functions that are provided by the Asp system by default. Developers are encouraged to include at least the type-oriented functions, and to at least consider inclusion of the other ones mentioned in the following sections.

Note that it is possible (but not recommended) for a script to redefine and even delete application definitions that appear in the sys module. However, such actions only affect the currently running script. All the application definitions are reinstated the next time a script is run.

Built-in functions

As mentioned above, it is completely up to the application developer to make the functions listed here available to the script writer. As a script writer, you need to refer to the applicable documentation to determine which functions are available and how to use them.

System functions

Asp includes a couple of functions that can assist in checking the existence of a variable. The exists function accepts a symbol and returns True if the variable was found in any of the local, module, or system scopes. The module function is provided to return the current module, which can be used, for example, in an expression like `symbol in module() to determine whether a variable exists at the global scope of a module. Other in expressions can be used to check the local and system scopes.

The sys module also includes a variable called __main__ which is assigned to the main module. This can be use in a fashion similar to how Python's __name__ variable is used to check whether the current module is the main module. In Asp, the relevant code would be as follows.

if module() is __main__:
    # This is the main module...

Like Python, Asp provides an id function that returns an integer that is unique for every object during its lifetime. Note that the value from a destroyed object can (and most probably will) be reassigned to a new object.

Asp provides an exit function that takes an optional code value. This function causes the script to end immediately. If the value of code is either None (the default) or the integer value 0, the script ends normally. However, if a non-zero integer or other type of value is used, the script ends in error. Depending on the code value, the application may be able to discern the nature of the error, especially if it is a non-zero integer.

Type support functions

Sometimes it is useful to access the type of an object. Asp supports a type that indicates an object's type, but there is no language syntax that expresses it. Instead, a function called type is provided. The returned value is a type value which can be compared with other type values for equality and order.

The len function returns the length of a given object. Depending on the type of the object, that means different things. For a tuple or list, it's the number of elements. For a set or dictionary, it's the number of members. For a string, the number of characters in the string is returned. For a range, the number of elements that would be generated if the range was expanded into a list is computed mathematically and returned. If the type of the object is none of these, the len function returns 1.

To access the components of a range object (i.e., the start, end, and step values), the range_values function is provided. Given a range object, it returns a 3-tuple. The first and last elements in the tuple are the start and step values and will always be integers. The middle element, the end component, will be an integer for a bounded range and None if the range is unbounded.

As with Python, mutable objects cannot be used as keys in sets and dictionaries. An attempt to insert a non-key object into a set or dictionary will result in an error, ending the script. To prevent this from happening, Asp provides the key function, which converts its argument to a string if it is not a valid key. If the argument is a key, it is simply returned as is. Conversion on non-keys to string is the default behaviour, but is controlled by a stringize parameter, which is True by default. If this parameter is False, the key function returns None. In either case, the returned value is always a valid key.

Type conversion functions

Asp supports conversion of objects to various types by means of the simple type conversion functions bool, int, float, str, and repr. Collection conversion functions tuple and list are also available. Each accepts a single argument to be converted. With the exception of repr, the functions can also be called with no arguments, in which case a default value of the applicable type is returned.

The bool function returns True or False based on the Boolean interpretation of the given object. This is the same interpretation that the if and while statements use on their test expressions as well as the initial operands of the and and or operators.

The int and float functions work with numeric objects (Boolean, integer, and floating-point) and with strings. Conversion from floating-point to integer results in truncation towards zero, except if the floating-point value is out of range (including positive and negative infinities and NaN values), in which case a None value is returned. Conversion from Boolean yields 0 or 1 for conversion to integer, or in the case of conversion to floating-point, 0.0 or 1.0. When converting from a string, the content of the string is parsed and, if valid, yields an appropriate numeric value. An invalid or out-of-range number string yields a None value, which can be used to detect the invalid input*.

The int function accepts a second argument, base, which can only be used when the first argument is a string. Like the equivalent Python function, it specifies the numeric base to be used for the conversion. Valid values for base are 0 and 2 through 36. The default is 10. A base of 0 causes the base to be determined from an optional prefix in the string according to the C language interpretation (e.g., '0x10' is converted to 16).

The str function converts its argument to a string representation. This can be useful for building strings that contain object values. The repr function converts its argument to a canonical string representation, so if, for example, a string containing special characters is given, they will be converted to printable escape sequences.

The tuple and list functions convert a range, tuple, list, set, or dictionary object into the appropriate type. Unlike Python, when converting a dictionary, both keys and values are combined into tuples and then inserted into the resulting sequence. If a tuple is passed into the tuple function, the same object is returned (i.e., it is not copied). This behaviour is identical to that of Python.

* The int function has an optional check parameter which should now be considered obsolescent, as it may be removed in a future version. In versions prior to 1.2.2.0, if the check parameter was True (the default value in those versions), out-of-range floating-point values raised an error condition, ending the script. Specifying False for check disabled this behaviour, yielding a value limited to the range of a 32-bit integer for valid floating-point values outside that range, and zero for NaN values. In version 1.2.2.0 and future versions (until the parameter is removed), the default value of check is False, and the behaviour is now modified to return None for an out-of-range floating-point argument. Also in versions prior to 1.2.2.0, malformed string input raised an error condition (regardless of the the value of the check parameter) instead of the current behaviour of returning None.

Iterator functions

Use of iterators in scripts is usually done implicitly using the for statement. However, direct access to iterators is also available through three built-in functions: iter, reversed, and next.

The iter function creates an iterator. It accepts a single argument which is the iterable object to which the iterator will refer. If an existing iterator object is passed as the argument, a copy of it is returned which retains the current position. This behaviour is different than that of the Python iter function, which returns the same object (i.e., not a copy).

Like the iter function, the reversed function creates a new iterator from an iterable, but in this case, the iterator is a reverse iterator, causing iteration to occur in reverse order. Unlike with Python, an existing iterator may be passed to the reversed function. In this case, a copy is made (just like with iter), but the direction of the new iterator is the opposite of the original. So, for example, passing a reverse iterator to the reversed function returns a forward iterator. As with iter, the current position is retained in the copy.

The next function returns the next item in the iterable, advancing the iterator to refer to the next item. Successive calls to next return subsequent items in the iteration. The next function can be called with one or two arguments. The first is the iterator. An optional second parameter, called end, allows the caller to specify a value to be returned in the case that the iterator has reached its end. If left unspecified, its default value is None.

There is no function for testing whether an iterator has reached its end. Instead, conversion to a Boolean value is used. This can take the form of calling the bool function, or simply using the iterator where a Boolean expression is expected, such as the expression in an if or while statement.

Math functions

Several math-related functions should be available, again, depending on the application in which the Asp engine is hosted.

These functions are all derived from their C/C++ language equivalents. As in Python, the log function accepts an optional second parameter, base, which specifies the base of the logarithm. The default base is e, yielding the natural logarithm.

Functions in the standalone application

The standalone application, asps, supports all the functions mentioned in the previous section, plus a print function for writing object values to the standard output. The print function accepts a variable number of arguments. The output is normally terminated with a newline and when more than one value is given, a space character is normally used to separate each value on the line. The argument separator and line terminator can be overridden by specifying values for the sep and end parameters, respectively. To print a blank line, call the print function with no arguments.

External arguments

The Asp engine can make externally supplied arguments available to scripts. Whether this feature is available is up to the developer of the application. If supported, external arguments appear in the sys.args variable, which is a tuple of strings. Recall that the script can simply access the arguments with args (i.e., without specifying the module) since the sys module is searched automatically for variable references, unless of course there is a local variable called args.

The first element in the args tuple is the entire arguments string supplied by the user. Subsequent elements in the tuple contain individual arguments from the supplied string, extracted according to a simple set of rules described below.

Arguments are normally separated by white space (e.g., spaces, tabs, etc.). To specify a single argument that contains white space, the argument can be enclosed in single or double quotes (' or "). To include a quote as part of an argument, it may be escaped using the backslash (\). The backslash also works inside double quotes but is taken literally inside single quotes. And of course, to include a backslash, two backslashes must be used, the first one escaping interpretation of the second.

It should be noted that the standalone application accepts arguments on the command line and passes them to the script.

Final thoughts

All the major topics of the Asp programming language and environment that relate to the script writer have been covered. This section includes some miscellany not previously covered.

Compiler listing and debug information files

In addition to producing an executable file, the Asp compiler also generates a listing file (.lst) and a debug information file (.aspd). The listing file contains a textual representation of the binary executable file, listing each instruction on a separate line, followed by a list of symbol values for each name used in the script. Normally, the script writer does not need this file, but in case there are issues with code generation, this file can be useful in finding bugs in the compiler.

The debug information file is used for translating error information when a script ends abnormally. The standalone application performs this translation automatically, using the debug information file to determine the source location from the ending program counter. For custom embedded applications, however, the developer of the system should provide some other means of performing this translation, for example, in a separate piece of software external to the embedded target, which is controlling the application.

If no such mechanism is provided, it may be that a numeric error code and ending program counter is made available. In this case, a generic utility, aspinfo, is provided to translate the numbers into meaningful text. In addition to error code and program counter translation, the aspinfo utility can also convert symbol values to their corresponding name. In both cases, the .aspd file is required as input. Executing the utility without arguments will display help information.

Script execution in the application

When executing a script on an embedded platform, the design of the application must include some method for making binary executable files accessible to the Asp engine. A couple of examples are transferring the binary file via a communication link and storing the binary file in flash memory. If the application supports it, a method must also be made to supply arguments to the engine prior to script execution. In any case, follow the instructions provided by the supplier of the application to prepare your compiled scripts for execution.

Prior to executing a script, the Asp engine checks a couple of conditions to ensure the integrity of the script binary. First, a check is made to ensure the version of the compiler that generated the executable is compatible with the engine running the code. Versions of both compiler and engine consist of four parts: major, minor, patch, and tweak. To be considered compatible, the major and minor numbers must match. After this check, another check is made to ensure that the script was compiled for the application at hand. When compiling a script, the compiler inserts a value which is derived from the application specification file (a cyclic redundancy check word, for those who are interested). The application also includes this computed value and the two are compared. Therefore, it is impossible to run a script in an application for which it was not compiled.

What next?

Each application supporting Asp should include documentation for the functions it makes available to scripts. Before writing scripts for a specific application, this documentation must be consulted.

As mentioned previously, if you are an application developer, the Application Developer's Guide includes specific guidance for incorporating Asp into your application.

Regardless of your role, it is our sincere hope that you enjoy your adventures in Asp.


Copyright (c) 2024 Canadensys Aerospace Corporation