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 to 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
(ending with .asp
) and an application
specification file (ending with .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 one of the standalone specs, 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 402 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 1186 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.aspe
Hello, world
The standalone application can also display version and other information at the beginning of the run and memory utilization at the end by specifying the verbose option (-v).
$ asps -v hello
Asp standalone application version: 1.3.1.0
Engine version: 1.3.1.0
App spec: standalone (standard)
Code version: 1.3.1.0
Hello, world
Low free count: 1746 (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)
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 were omitted, in part, to reduce the overall footprint of the Asp engine library.
Object-oriented programming (i.e., support for classes, etc.) is an
optional feature in Asp and its inclusion is decided by the application
developer. The presence of this feature is indicated in the application
specification file that you use to compile your scripts. If a script
contains class definitions and is compiled with a
specification file that does not include class support, the compilation
will end in error. It is best to consult with the developer of the
application at hand before deciding whether to use classes in your scripts.
To compile scripts with class definitions for the standalone
application, the standalone-oo.aspec application
specification should be used instead of the standard one,
standalone.aspec.
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 line 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 simple statements, they may appear
on the same line, after the colon, and separated by semicolons if there are
more than one. Again, this single-line arrangement is not recommended.
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 use differently
in Asp than it is in Python. In Python, it is used as a wildcard in
multidimensional array indexing. Asp does not support multidimensional
arrays (other than implemented as lists of lists), so its use has been
reimagined. Its main purpose in Asp is to represent the local scope in
applicable contexts. Otherwise, it 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. Internally, objects and modules use symbols for member lookup. (In fact, access to object and module members can be done using symbols, although this is rarely needed.) 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. Strings may
be juxtaposed (e.g., over multiple lines with line continuation), in which
case they are concatenated.
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. Dictionaries support value access via indexing. All of these uses are covered in the subsequent section on operations.
Asp supports a type not available in Python: simple objects. These objects
are distinguished from class instances (described in the next section).
Simple objects store values by name, so they are similar to dictionaries,
except that the key must be a name (symbol). Initialization resembles that
of dictionaries, except that the equal sign (=) is used
instead of the colon (:).
eo = {=} # eo is assigned an empty simple object
o1 = {x = -1, y = 5, loc = 'Here'} # o1 has members x, y, and loc
eo.pos = o1.loc # eo now has member pos with value 'Here'
Like sets and dictionaries, simple objects support all the operations that work with the collection types. Simple objects are available regardless of whether class support is enabled.
Asp optionally supports classes and related types. Whether these types are supported by your specific application is determined by the developer. So before getting too far with classes, first check to see if they are supported in your application. Notwithstanding that caveat, the standalone application can run scripts compiled with and without class support. To compile a script for class support, use the standalone-oo.aspec application specification file. Scripts compiled with the standard standalone.aspec file do not support classes.
Classes are essentially the same as they are in Python, except that only single inheritance is supported. Calling a class creates an instance of the class. In this documentation, we use the word instance when referring to a class object, and simple object when referring to a classless object.
Classes support the following dunder
methods: __init__,
__get__, and __call__, all with the same meaning
as their Python counterparts. Classes may also be decorated with the
@staticmethod or @classmethod class decorators,
with the same functionality provided in Python.
Other class-related types visible to the script writer are bound methods
and super
objects, both of which parallel types in Python and act in
the same manner.
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.
As briefly mentioned in the section on
class-related types, Asp supports
decorators. Like in Python, decorators can be applied to both classes and
functions. The primary reason for including decorators in Asp is in order
to provide the @staticmethod and @classmethod
decorators, which will normally be predefined if your application supports
classes. However, script writers can also author their own decorators by
writing functions or classes designed for the purpose.
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 may also be predefined
for a given application. 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
top-level 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.
When importing an application-defined module, if a module source file with the same base name exists, the application module takes precedence and a warning is issued indicating that the module source file will be ignored.
Asp does not support packages (i.e., importing submodules), so statements like the following are not supported.
import math.trig.hyperbolic # Invalid; submodules not supported
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 simple objects, instances, classes, and 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 .. endstart .. end : step
|
Range with optional step. | N/A |
trueval if cond else falseval
|
Conditional expression. | Left-to-right |
:= |
Assignment expression. | N/A |
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 assignment and insertion statements, respectively.
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.
Superobject
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 simple objects, objects of all the
class-related types, 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, when using the < operator with a negated expression
as the right operand, the operator and right operand must be separated with
white space to distinguish the syntax from Asp's insertion operator
(<-). Python's style guide (PEP 8) recommends surrounding
comparison operators with spaces, and this is a good practice to carry over
into Asp.
if i<-3: print('less') # Syntax error
if i < -3: print('less') # Correct
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 for the existence of a symbol in a
simple object, module, or in the local scope of a function. If the
application supports classes, then classes and their instances are included
as well. (This is similar to looking up a variable name as a string in the
__dict__ member of a class or instance in Python.) In this
usage, the left operand must be a symbol and the right operand can be
either a simple object, instance, class, 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():
# ...
Of course, any module can be checked by specifying it as the right operand
(e.g., `x in sys). The same is true of the other types of
objects.
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 ...:
# ...
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 can be used to update the value of a list element. The same is not true for tuples and strings, which are immutable. 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]
lst[1] = 42
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.
Both simple objects and modules may also be indexed, but now the index
value must be a symbol. If classes are supported by the application,
classes and their instances can be indexed as well in the same manner. For
example, for a simple object obj, the following two statements
are equivalent. Although the first form is preferred, the second form
allows the symbol to be variable.
obj.a = 42 obj[`a] = 42
As mentioned above, the indexing syntax can be used on the left side of an assignment when the object being indexed is appropriate (i.e., for lists, dictionaries, simple objects, instances, classes, and modules). This is discussed further in the section on the assignment statement.
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 a simple object, instance, class, or module, the
. operator is used. A couple simple examples should suffice to
illustrate its usage.
obj = {a = 5, b = 10}
print(obj.a)
import mod
print(mod.x)
Here we assume the presence of a print function (which the
standalone application includes), and that the imported module
mod contains a definition for the member x. A
subsequent section on modules explains importing.
Member selection may also appear as the target of an assignment. See the section on assignment for more details.
Just like in Python, when a member selection is performed on a class and the member is not found in the class itself, the search continues up the class hierarchy (i.e., base classes). When a member is sought in a class instance and the member is not found in the instance itself, the search continues with the instance's class hierarchy. When a member search in a class instance finds a function in the instance's class hierarchy, the function is not returned as is. Instead, the member selection yields a bound method. See the next section on call expressions for details on what happens when a bound method is called.
Functions, whether defined in a script or in the application, may be called using call expression 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. In addition to functions, if classes are supported by the application, a class can be called to create an instance of the class. Also, as mentioned above, bound methods generated by a member selection operation can also be the object of a call expression.
foo(1, 2.4, 'abc')
Functions and other callables can return one or more values. The above example ignores any returned values. To get at the returned value(s), the call expression 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 callable. 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).
When the object of a call expression is a class or a bound method
(available only when the application supports classes), the instance
involved (i.e., the instance being created, or the instance used in the
member selection operation) is inserted at the front of the argument list
automatically (usually referred to as self in the function
definition within the class). Also, when calling a class, if the class or
one of its base classes contains a function definition by the name of
__init__, that function is automatically called to assist in
the initialization of the new instance.
Assignments to variables can be made within an expression using the
:= operator, informally known as the "walrus" operator. This
is useful when it is desirable to retain the value of a subexpression
within a larger expression or statement without having to break the code up
into multiple statements. A variable assigned in one subexpression can also
be reused later (i.e., to the right) in the enveloping expression.
Assignment expressions in Asp are based on the same concept introduced into
Python via PEP 572, and officially added in Python version 3.8. Asp's
syntax rules for assignment expressions are somewhat relaxed relative to
those of Python. For example, parentheses are not required when using
assignment expressions in assert and return
statements (the later case only being useful if the variable being assigned
has been made global). Parentheses are also not required when an assignment
expression occurs as an element of an unparenthesized tuple, or the key or
value in a dictionary entry.
Here is a typical use of an assignment expression that allows much more compact code than would have been required without it.
s = 'abcde'
it = iter(s)
while c := next(it):
print(c)
And here is an example that reuses the value of an expensive operation later in the same expression.
t = t1 := log(x), -t1
In Asp, the above code does not require parentheses. Two equivalent lines of code in Python would be as follows. Both require parentheses.
t = (t1 := math.log(x)), -t1 # Parenthesized assignment in Python
t = (t1 := math.log(x), -t1) # Parenthesized tuple in Python
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…elif…else), 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 target
expression on the left and a value expression on the right. The target can
be a simple variable, an index or slice expression, or a member selection
expression.
x = y + z
As in Python, chained assignment is supported, so multiple targets can be assigned the same value in a single statement.
i = j = k = 0
A target 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 targets 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 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
As mentioned above, 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 also possible to assign a variable in an object or in a different module by using member selection syntax on the left side.
obj.a = value 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, and has the
advantage that the target expression is evaluated only
once*.
* Unfortunately, all 1.2 versions of Asp evaluate the target of an augmented assignement twice, once to obtain the value, and the second time to determine the target address. This is fine as long as the target expression (the part to the left of the augmented assignment operator) does not have any side effects. If it does, the side effect will occur twice and may cause unexpected results. The fix to this bug required ABI-breaking changes to the way Asp performs this operation. Therefore, it is fixed in version 1.3.
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.
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.
The key/value form of the insertion statement can also be used to add or
update members in simple objects, instances, classes, and modules. In this
case, the key must be a symbol, but can be variable if needed. Assuming a
simple object obj exists, the following statement adds a
member a to it if it does not already exist, or updates its
value if it does.
obj <- `a : 42
As in Python, the del statement is used to delete a variable,
a member of a collection-like object or module using member selection or
indexing syntax. Deleting a slice of elements from a list is also
supported. Unlike in Python, deleting a member of a set can be accomplished
using indexing, even though indexing a set is not allowed in other
contexts.
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. The same applies to simple objects, instances, classes, and modules, except that the key is a symbol.
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
or other application-defined 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.
When supported by the application, class definitions are nearly identical to their counterpart in Python. The only significant difference is that multiple inheritance is not supported. Single inheritance is supported so reasonable class hierarchies are still possible.
class Shape:
def area(self):
return 0
class Square(Shape):
def __init__(self, side):
super().__init__()
self.side = side
def area(self):
return self.side * self.side
sq = Square(3)
print(sq.area())
The output from the above script would be 9.
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.
As mentioned previously, the application can define its own modules which take precedence over any source files found via the search path.
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 normally a type value which can be compared with
other type values for equality and order. In the case of a class instance,
the value returned is the class from which the instance was created.
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.
* Theintfunction has an optionalcheckparameter which should now be considered obsolescent, as it may be removed in a future version. In versions prior to 1.2.2.0, if thecheckparameter wasTrue(the default value in those versions), out-of-range floating-point values raised an error condition, ending the script. SpecifyingFalseforcheckdisabled this behaviour, yielding a value limited to the range of a 32-bit integer for valid floating-point values outside that range, and zero forNaNvalues. In version 1.2.2.0 and future versions (until the parameter is removed), the default value ofcheckisFalse, and the behaviour is now modified to returnNonefor 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 thecheckparameter) 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 several built-in functions: iter,
reversed, at, next, and
del_at.
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).
In addition to the regular types of iterable objects (i.e., range, string,
tuple, list, set, and dictionary), a module or the ellipsis
(...) may be given as the argument to iter to
visit the symbols and associated values of the applicable scope. When using
local scope iterators (ones created from ...), care must be
taken to use them only within the scope in which they were created. If the
engine detects a use of such an iterator outside this context, the script
will end in error.
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 at and next functions both return the current
item in the iterable, the difference being that next advances
the iterator to refer to the next item (after fetching the current item),
while at leaves the iterator unchanged. Successive calls to
next return subsequent items in the iteration. Both
at and next 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.
The del_at function is used to remove items from mutable
collections (i.e., lists, sets, and dictionaries) using an iterator. It
complements the language's del statement used with an indexing
expression. In the case of the del_at function, however, only
the iterator is required as an argument, not the collection it refers to.
Sometimes it is necessary to obtain the iterable for a given iterator. This
can be done using the iterable function, passing the iterator
in question as the only parameter. Note that if the returned iterable
happens to be the ellipsis (...), care must be taken because
this value represents a local scope which could be different from the scope
in which the iterator was originally created.
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.
For applications that support classes, the isinstance,
issubclass, and super functions are supported,
and provide the same functionality as their Python counterparts.
For the isinstance and issubclass functions, the
second argument can be a (possibly nested) tuple or list. Python only
allows tuples (for performance reasons), but Asp is not restricted in this
way.
When called from a class method, the super function should be
called with no arguments. In other contexts, it can be called with two
arguments (a class and instance, which must be compatible with each other),
but this is rare. The returned value is a super
object which can be
used in a member selection operation and is usually used to call a method
defined in a base class.
Scripts compiled with either the standard application specification file, standalone.aspec, or the class-supporting version, standalone-oo.aspec can be run with the standalone application, asps. The compiled code is checked for compatibility with both specs. If no match is found, the standalone application gives up and reports an error. Note also that the class support functions mentioned above are not defined in the standard spec.
The standalone application supports all the applicable 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 standalone application also supports a sleep function. The
single parameter represents the number of seconds to sleep for and can be a
floating-point value to specify a fractional number of seconds.
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 into the binary that 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) 2026 Canadensys Aerospace Corporation