Semantic Values
If successful, a parser produces a semantic value, which describes the input in some way useful to the application invoking the parser. In addition, semantic values may be used to control how other parts of the input are to be parsed. DaeDaLus has a number of built-in semantic values types, and allows for user-defined record and union types.
Booleans
The type bool
classifies the usual boolean values true
and false
.
The operator !
may be used to negate a boolean value.
The operators ||
and &&
are for (short-circuiting) “or” and “and”
respectively.
Boolean values may be compared for equality using ==
and are ordered
with false < true
.
Decisions on a boolean may be made either using If-then-else, by using Guards, or by using Case.
Numeric Types
DaeDaLus supports a variety of numeric types: int
, uint N
, and
sint N
, the latter two being families of types indexed by a number.
The type int
classifies integers of arbitrary size.
The uint N
classify unsigned numbers that can be represented using N
bits and sint N
is for signed numbers that can be represented
in N
bits.
Numeric Literals
Literals of the numeric types may written either using decimal or hexadecimal
notation (e.g., 10
or 0xA
). The type of a literal can be inferred
from the context (e.g., 10
can be used as both int
a uint 8
).
Comparisons
Numeric types can also be compared for equality, using ==
and ordering
using <
, <=
, >
, and >=
.
Basic Arithmetic
Numeric types support basic arithmetic: addition, subtraction,
multiplication, division, and modulus using the usual operators
+
, -
, *
, /
, and %
.
Bitwise Operations
DaeDaLus also supports shift operations <<
and >>
.
These operations are overloaded and can be used on all numeric types,
with the restriction that the inputs and the outputs must be of the
same type. The shift amount is a value of type uint 64
.
Unsigned integers may also be treated as bit-vectors, and support various bitwise operations:
complement:
~
bitwise exclusive-or
.^.
bitwise-or
.|.
bitwise-and
.&.
.
Unsigned numbers can also be appended to other numbers via the
#
and <#
operator. To see the difference between the two,
consider two bitvectors (x : uint A)
and (y : uint B)
.
The result of x # y
is a bitvector of type A + B
with
x
in the more significant bits, and y
in the less significant bits.
The result of x <# y
is a bitvector of type A
that contains
x # y
but truncated to the A
less significant bits.
maybe
type
DaeDaLus supports the special polymorphic type maybe A
, which has possible
values nothing
and just x
, for some value, x
of type A
.
The parser Optional P
will try to parse the input using and produce
a maybe
value. If P
succeeds with result x
then
Optional P
will succeed with just x
, and if P
fails, then
Optional P
will succeed with nothing
.
def MaybeLetter = Optional $[ 'A'..'Z' ]
To examine values of the maybe
type you may use
Guards or Case.
Arrays
The type of arrays containg elements of type T
is [T]
.
-- Array literals
[] -- empty array
[1,2,3] -- array with 3 elements
"Hello" -- array with 5 elements
-- Get the element at the given array index
-- This is a parser, which fails if the index is out of bounds
Index (a : [?a]) (i : uint 64) : ?a
-- Length of an array
length (a : [?a]) : uint 64
To visit all elements in array you may use a for
loop for loops.
Array Builders
A builder
is a datastructure that helps build arrays.
To build an array, start with the empty builder builder
and use
emit
to add elements to the builder. Once all elements have been
added, you may use build
to convert the builder
to an array.
-- empty builder
builder : builder ?a
emit (front : builder ?a) (back : ?a) : builder ?a
-- Add an array of element to the end of the builder
emitArray (front : builder ?a) (back : [?a]) : builder ?a
-- Add a builder at the end of another builder
emitBuilder (front : builder ?a) (back : builder ?a) : builder ?a
-- Turn a builder into an array
build (b : builder ?a) : [?a]
Association Maps
The type of association maps with keys of type K
and elements of type
T
is [ K -> T ]
.
-- An empty map
empty : [ ?k -> ?v ]
-- Insert an element in a map.
-- The element is replaced, if it was already present.
insert (key : ?k) (value : ?v) (m : [ ?k -> ?v ]) : [ ?k -> ?v ]
-- Insert an element in a map.
-- This is a parser which fails if the element was already in the map.
Insert (key : ?k) (value : ?v) (m : [ ?k -> ?v ]) : [ ?k -> ?v ]
-- Lookup an element in the map.
lookup (key : ?k) (m : [ ?k -> ?v ]) : maybe ?v
-- Lookup an element in the map.
-- This is a parse which fails if the element is not in the map.
Lookup (key : ?k) (m : [ ?k -> ?v ]) : ?v
To visit all elements of an association map you may use a
for
loop for loops.
Streams
The type stream
is for values representing streams of data that
can be parserd by a parser. See Stream Manipulation for more examples
of how to manipualte the parser’s stream.
-- Get the current stream for the parser
GetStream : stream
-- Restrict a stream to the fist `n` bytes.
-- Will fail if the stream does not have enough bytes
Take (n : uint 64) (s : stream) : stream
-- Restrict a stream to at most `n` bytes.
-- The resulting stream might be shorter if there are not
-- enough bytes
take (n : uint 64) (s : stream) : stream
-- Advance a stream by `n` bytes.
-- Will fail if the stream does not have enough bytes
Drop (n : uint 64) (s : stream) : stream
-- Make a stream with the given name and bytes to parser
arrayStream (name : [uint 8]) (data : [uint 8]) : stream
-- Get the bytes associates with a stream as an array
bytesOfStream (s : stream) : [uint 8]