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.
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.
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:
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).
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.
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
(if
…elif
…else
), loops
(while
and for
) and functions (def
and return
), there are several features it does not inherit
from its bigger cousin, such as…
yield
)
try
…except
…
finally
) and contexts (with
)
class
)
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.
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.
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.
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).
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.
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.
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.
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.
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 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 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 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.
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.
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 from
…import
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 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 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.
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.
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.
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.
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.
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
.
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 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.
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.
And of course, theif `x in ...: # ...
sys
module can be checked by specifying it
as the right operand.
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.
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.
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.
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.
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
).
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
(if
……
elifelse
), loops
(while
and for
), and function definitions
(def
), each of which are described below, along with other
types of statements.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Asp supports the writing of scripts in multiple modules, each defined in
its own .asp
file. The import
and from
…import
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.
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.
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.
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.
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.
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.
* Theint
function has an optionalcheck
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 thecheck
parameter wasTrue
(the default value in those versions), out-of-range floating-point values raised an error condition, ending the script. SpecifyingFalse
forcheck
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 forNaN
values. In version 1.2.2.0 and future versions (until the parameter is removed), the default value ofcheck
isFalse
, and the behaviour is now modified to returnNone
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 thecheck
parameter) instead of the current behaviour of returningNone
.
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.
Several math-related functions should be available, again, depending on the application in which the Asp engine is hosted.
sin
, cos
,
tan
, asin
, acos
,
atan
, and atan2
.
sinh
, cosh
,
tanh
, asinh
, acosh
, and
atanh
.
hypot
.
exp
,
log
, and log10
.
ceil
, floor
, and
round
.
abs
.
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.
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.
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.
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.
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.
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.
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