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]