== Defining Constants The simplest FPP model consists of one or more *constant definitions*. A constant definition associates a name with a value, so that elsewhere you can use the name instead of re-computing or restating the value. Using named constants makes the model easier to understand (the name says what the value means) and to maintain (changing a constant definition is easy; changing all and only the relevant uses of a repeated value is not). This section covers the following topics: * Writing an FPP constant definition. * Writing an *expression*, which is the source text that defines the value associated with the constant definition. * Writing multiple constant definitions. * Writing a constant definition that spans two or more lines of source text. === Writing a Constant Definition To write a constant definition, you write the keyword `constant`, an equals sign, and an expression. A <> describes all the expressions you can write. Here is an example that uses an integer literal expression representing the value 42: [source,fpp] ---- constant ultimateAnswer = 42 ---- This definition associates the name `ultimateAnswer` with the value 42. Elsewhere in the FPP model you can use the name `ultimateAnswer` to represent the value. You can also generate a {cpp} header file that defines the {cpp} constant `ultimateAnswer` and gives it the value 42. As an example, do the following: * On the command line, run `fpp-check`. * When the prompt appears, type the text shown above, type return, type control-D, and type return. You should see something like the following on your console: ---- % fpp-check constant ultimateAnswer = 42 ^D % ---- As an example of an incorrect model that produces an error message, repeat the exercise, but omit the value 42. You should see something like this: ---- % fpp-check constant ultimateAnswer = ^D fpp-check stdin: end of input error: expression expected ---- Here the `fpp-check` tool is telling you that it could not parse the input: the input ended where it expected an expression. === Names Names in FPP follow the usual rules for identifiers in a programming language: * A name must contain at least one character. * A name must start with a letter or underscore character. * The characters after the first may be letters, numbers, or underscores. For example: * `name`, `Name`, `_name`, and `name1` are valid names. * `1invalid` is not a valid name, because names may not start with digits. ==== Reserved Words Certain sequences of letters such as `constant` are called out as *reserved words* (also called keywords) in FPP. Each reserved word has a special meaning, such as introducing a constant declaration. _The FPP Language Specification_ has a complete list of reserved words. In this document, we will introduce reserved words as needed to explain the language features. Using a reserved word as a name in the ordinary way causes a parsing error. For example, this code is incorrect: [source,fpp] -------- constant constant = 0 -------- To use a reserved word as a name, you must put the character `$` in front of it with no space. For example, this code is legal: [source,fpp] ---- constant $constant = 0 ---- The character sequence `$constant` represents the name `constant`, as opposed to the keyword `constant`. You can put the character `$` in front of any identifier, not just a keyword. If the identifier is not a keyword, then the `$` has no effect. For example, `$name` has the same meaning as `name`. ==== Name Clashes FPP will not let you define two different symbols of the same kind with the same name. For example, this code will produce an error: [source,fpp] -------- constant c = 0 constant c = 1 -------- Two symbols can have the same unqualified name if they reside in different <> or <>; these concepts are explained below. Two symbols can also have the same name if the analyzer can distinguish them based on their kinds. For example, an <> (described below) and a constant can have the same name, but an array type and a <> may not. _The FPP Language Specification_ has all the details. === Expressions This section describes the expressions that you can write as part of a constant definition. Expressions appear in other FPP elements as well, so we will refer back to this section in later sections of the manual. ==== Primitive Values A *primitive value expression* represents a primitive machine value, such as an integer. It is one of the following: * A decimal integer literal value such as `1234`. * A hexadecimal integer literal value such as `0xABCD` or `0xabcd`. * A floating-point literal value such as `12.34` or `1234e-2`. * A Boolean literal expression `true` or `false`. As an exercise, construct some constant definitions with primitive values as their expressions, and feed the results to `fpp-check`. For example: [source,fpp] ---- constant a = 1234 constant b = 0xABCD ---- If you get an error, make sure you understand why. ==== String Values A *string value* represents a string of characters. There are two kinds of string values: single-line strings and multiline strings. *Single-line strings:* A single-line string represents a string of characters that does not contain a newline character. It is written as a string of characters enclosed in double quotation marks `"`. For example: [source,fpp] ---- constant s = "This is a string." ---- To put the double-quote character in a string, write the double quote character as `\"`, like this: [source,fpp] ---- constant s = "\"This is a quotation within a string,\" he said." ---- To encode the character `\` followed by the character `"`, write the backslash character as `\\`, like this: [source,fpp] ---- constant s = "\\\"" ---- This string represents the literal character sequence `\`, `"`. In general, the sequence `\` followed by a character _c_ is translated to _c_. This sequence is called an *escape sequence*. *Multiline strings:* A multiline string represents a string of characters that may contain a newline character. It is enclosed in a pair of sequences of three double quotation marks `"""`. For example: [source,fpp] ---- constant s = """ This is a multiline string. It has three lines. """ ---- When interpreting a multiline string, FPP ignores any newline characters at the start and end of the string. FPP also ignores any blanks to the left of the column where the first `"""` appears. For example, the string shown above consists of three lines and starts with `This`. Literal quotation marks are allowed inside a multiline string: [source,fpp] ---- constant s = """ "This is a quotation within a string," he said. """ ---- Escape sequences work as for single-line strings. For example: [source,fpp] ---- constant s = """ Here are three double-quote characters in a row: \"\"\" """ ---- ==== Array Values An *array value expression* represents a fixed-size array of values. To write an array value expression, you write a comma-separated list of one or more values (the array elements) enclosed in square brackets. Here is an example: [source,fpp] ---- constant a = [ 1, 2, 3 ] ---- This code associates the name `a` with the array of integers `[ 1, 2, 3 ]`. As mentioned in the introduction, an FPP model describes the structure of a FSW application; the computations are specified in a target language such as {cpp}. As a result, FPP does not provide an array indexing operation. In particular, it does not specify the index of the leftmost array element; that is up to the target language. For example, if the target language is {cpp}, then array indices start at zero. Here are some rules for writing array values: . An array value must have at least one element. That is, `[]` is not a valid array value. . The types of the elements must match. For example, the following code is illegal, because the value `1` (which has type `Integer`) and the value `"abcd"` (which has type `string`) are incompatible: + [source,fpp] -------- constant mismatch = [ 1, "abcd" ] -------- Try entering this example into `fpp-check` and see what happens. What does it mean for types to match? _The FPP Specification_ has all the details, and we won't attempt to repeat them here. In general, things work as you would expect: for example, we can convert an integer value to a floating-point value, so the following code is allowed: [source,fpp] ---- constant a = [ 1, 2.0 ] ---- It evaluates to an array of two floating-point values. If you are not sure whether a type conversion is allowed, you can ask `fpp-check`. For example: can we convert a Boolean value to an integer value? In older languages like C and {cpp} we can, but in many newer languages we can't. Here is the answer in FPP: ---- % fpp-check constant a = [ 1, true ] ^D fpp-check stdin: 1.16 constant a = [ 1, true ] ^ error: cannot compute common type of Integer and bool ---- So no, we can't. Here are two more points about array values: . Any legal value can be an element of an array value, so in particular arrays of arrays are allowed. For example, this code is allowed: + [source,fpp] ---- constant a = [ [ 1, 2 ], [ 3, 4 ] ] ---- + It represents an array with two elements: the array `[ 1, 2 ]` and the array `[ 3, 4 ]`. . To avoid repeating values, a numeric, string, or Boolean value is automatically promoted to an array of appropriate size whenever necessary to make the types work. For example, this code is allowed: + [source,fpp] ---- constant a = [ [ 1, 2, 3 ], 0 ] ---- + It is equivalent to this: + [source,fpp] ---- constant a = [ [ 1, 2, 3 ], [ 0, 0, 0 ] ] ---- ==== Array Elements An *array element expression* represents an element of an array value. To write an array element expression, you write an array value followed by an index expression enclosed in square brackets. The index expression must resolve to a number. The number specifies the index of the array element, where the array indices start at zero. Here is an example: [source,fpp] ---- constant a = [ 1, 2, 3 ] constant b = a[1] ---- In this example, the constant `b` has the value 2. The index expression must resolve to a number that is in range for the array. For example, this code is incorrect, because `"hello"` is not a number: [source,fpp] -------- constant a = [ 1, 2, 3 ] constant b = a["hello"] -------- This code is incorrect because 3 is not in the range [0, 2]: [source,fpp] -------- constant a = [ 1, 2, 3 ] constant b = a[3] -------- ==== Struct Values A *struct value expression* represents a C- or {cpp}-style structure, i.e., a mapping of names to values. To write a struct value expression, you write a comma-separated list of zero or more *struct members* enclosed in curly braces. A struct member consists of a name, an equals sign, and a value. Here is an example: [source,fpp] ---- constant s = { x = 1, y = "abc" } ---- This code associates the name `s` with a struct value. The struct value has two members `x` and `y`. Member `x` has the integer value 1, and member `y` has the string value `"abc"`. *The order of members:* When writing a struct value, the order in which the members appear does not matter. For example, in the following code, constants `s1` and `s2` denote the same value: [source,fpp] ---- constant s1 = { x = 1, y = "abc" } constant s2 = { y = "abc", x = 1 } ---- *The empty struct:* The empty struct is allowed: [source,fpp] ---- constant s = {} ---- *Arrays in structs:* You can write an array value as a member of a struct value. For example, this code is allowed: [source,fpp] ---- constant s = { x = 1, y = [ 2, 3 ] } ---- *Structs in arrays:* You can write a struct value as a member of an array value. For example, this code is allowed: [source,fpp] ---- constant a = [ { x = 1, y = 2 }, { x = 3, y = 4 } ] ---- This code is not allowed, because the element types don't match -- an array is not compatible with a struct. [source,fpp] -------- constant a = [ { x = 1, y = 2 }, [ 3, 4 ] ] -------- However, this code is allowed: [source,fpp] ---- constant a = [ { x = 1, y = 2 }, { x = 3 } ] ---- Notice that the first member of `a` is a struct with two members `x` and `y`. The second member of `a` is also a struct, but it has only one member `x`. When the FPP analyzer detects that a struct type is missing a member, it automatically adds the member, giving it a default value. The default values are the ones you would expect: zero for numeric members, the empty string for string members, and `false` for Boolean members. So the code above is equivalent to the following: [source,fpp] ---- constant a = [ { x = 1, y = 2 }, { x = 3, y = 0 } ] ---- ==== Struct Members A *struct member expression* represents a member of a struct value. To write a struct member expression, you write a struct value followed by a dot and the member name. For example: [source,fpp] ---- constant a = { x = 1, y = [ 2, 3 ] } constant b = a.y ---- Here `a` is a struct value with members `x` and `y` The constant `b` has the array value `[ 2, 3 ]`, which is the value stored in member `y` of `a`. This code is incorrect because `a` is not a struct value: [source,fpp] -------- constant a = 0 constant b = a.y -------- This code is incorrect because `z` is not a member of `a`: [source,fpp] -------- constant a = { x = 1, y = [ 2, 3 ] } constant b = a.z -------- ==== Name Expressions A *name expression* is a use of a name appearing in a constant definition. It stands for the associated constant value. For example: [source,fpp] ---- constant a = 1 constant b = a ---- In this code, constant `b` has the value 1. The order of definitions does not matter, so this code is equivalent: [source,fpp] ---- constant b = a constant a = 1 ---- The only requirement is that there may not be any cycles in the graph consisting of constant definitions and their uses. For example, this code is illegal, because there is a cycle from `a` to `b` to `c` and back to `a`: [source,fpp] -------- constant a = c constant b = a constant c = b -------- Try submitting this code to `fpp-check`, to see what happens. Names like `a`, `b`, and `c` are simple or unqualified names. Names can also be qualified: for example `A.a` is allowed. We will discuss qualified names further when we introduce module definitions and enum definitions below. ==== Value Arithmetic Expressions A *value arithmetic expression* performs arithmetic on values. It is one of the following: * A negation expression, for example: + [source,fpp] ---- constant a = -1 ---- * A binary operation expression, where the binary operation is one of `+` (addition), `-` (subtraction), `*` (multiplication), and `/` (division). For example: + [source,fpp] ---- constant a = 1 + 2 ---- * A parenthesis expression, for example: + [source,fpp] ---- constant a = (1) ---- The following rules apply to arithmetic expressions: * The subexpressions must be integer or floating-point values. * If there are any floating-point subexpressions, then the entire expression is evaluated using 64-bit floating-point arithmetic. * Otherwise the expression is evaluated using arbitrary-precision integer arithmetic. * In a division operation, the second operand may not be zero or (for floating-point values) very close to zero. ==== Compound Expressions Wherever you can write a value inside an expression, you can write a more complex expression there, so long as the types work out. For example, these expressions are valid: [source,fpp] ---- constant a = (1 + 2) * 3 constant b = [ 1 + 2, 3 ] ---- The first example is a binary expression whose first operand is a parentheses expression; that parentheses expression in turn has a binary expression as its subexpression. The second example is an array expression whose first element is a binary expression. This expression is invalid, because `1 + 2.0` evaluates to a floating-point value, which is incompatible with type `string`: [source,fpp] -------- constant a = [ 1 + 2.0, "abc" ] -------- Compound expressions are evaluated in the obvious way. For example, the constant definitions above are equivalent to the following: [source,fpp] ---- constant a = 9 constant b = [ 3, 3 ] ---- For compound arithmetic expressions, the precedence and associativity rules are the usual ones (evaluate parentheses first, then multiplication, and so forth). === Multiple Definitions and Element Sequences Typically you want to specify several definitions in a model source file, not just one. There are two ways to do this: . You can separate the definitions by one or more newlines, as shown in the examples above. . You can put the definitions on the same line, separated by a semicolon. For example, the following two code excerpts are equivalent: [source,fpp] ---- constant a = 1 constant b = 2 ---- [source,fpp] ---- constant a = 1; constant b = 2 ---- More generally, a collection of several constant definitions is an example of an *element sequence*, i.e., a sequence of similar syntactic elements. Here are the rules for writing an element sequence: . Every kind of element sequence has optional *terminating punctuation*. The terminating punctuation is either a semicolon or a comma, depending on the kind of element sequence. For constant definitions, it is a semicolon. . When writing elements on separate lines, the terminating punctuation is optional. . When writing two or more elements on the same line, the terminating punctuation is required between the elements and optional after the last element. === Multiline Definitions Sometimes, especially for long definitions, it is useful to split a definition across two or more lines. In FPP there are several ways to do this. First, FPP ignores newlines that follow opening symbols like `[` and precede closing symbols like `]`. For example, this code is allowed: [source,fpp] ---- constant a = [ 1, 2, 3 ] ---- Second, the elements of an array or struct form an element sequence (see the previous section), so you can write each element on its own line, omitting the commas if you wish: [source,fpp] ---- constant s = { x = 1 y = 2 z = 3 } ---- This is a clean way to write arrays and structs. The assignment of each element to its own line and the lack of terminating punctuation make it easy to rearrange the elements. In particular, one can do a line-by-line sort on the elements (for example, to sort struct members alphabetically by name) without concern for messing up the arrangement of commas. If we assume that the example represents the first five lines of a source file, then in vi this is easily done as `:2-4!sort`. Third, FPP ignores newlines that follow connecting symbols such as `=` and `+` For example, this code is allowed; [source,fpp] ---- constant a = 1 constant b = 1 + 2 ---- Finally, you can always create an explicit line continuation by escaping one or more newline characters with `\`: [source,fpp] ---- constant \ a = 1 ---- Note that in this example you need the explicit continuation, i.e., this code is not legal: [source,fpp] -------- constant a = 1 -------- === Framework Constants Certain constants defined in FPP have a special meaning in the F Prime framework. These constants are called *framework constants*. For example, the constant `Fw.DpCfg.CONTAINER_USER_DATA_SIZE` defines the size of the user data field in a data product container. (Data products are an F Prime feature that we describe <>.) You typically set these constants by overriding configuration files provided in the directory `default/config` in the F Prime repository. For example, the file `default/config/DpCfg.fpp` provides a default value for `Fw.DpCfg.CONTAINER_USER_DATA_SIZE`. You can override this default value by providing your own version of the file `DpCfg.fpp`. The https://fprime.jpl.nasa.gov/devel/docs/user-manual/framework/configuring-fprime/[F Prime User Manual] explains how to do this configuration. The FPP analyzer does not require that framework constants be defined unless they are used. For example, the following model is valid, because it neither defines nor users `Fw.DpCfg.CONTAINER_USER_DATA_SIZE`: [source,fpp] ---- constant a = 0 ---- The following model is valid because it defines and uses `Fw.DpCfg.CONTAINER_USER_DATA_SIZE`: [source,fpp] ---- module Fw { module DpCfg { constant CONTAINER_USER_DATA_SIZE = 10 } } constant a = Fw.DpCfg.CONTAINER_USER_DATA_SIZE ---- The following model is invalid, because it uses `Fw.DpCfg.CONTAINER_USER_DATA_SIZE` without defining it: [source,fpp] -------- constant a = Fw.DpCfg.CONTAINER_USER_DATA_SIZE -------- If framework constants are defined in the FPP model, then then they must conform to certain rules. These rules are spelled out in detail in the https://nasa.github.io/fpp/fpp-spec.html#Definitions_Framework-Definitions[_The FPP Language Specification_]. For example, `Fw.DpCfg.CONTAINER_USER_DATA_SIZE` must have an integer type. So this model is invalid: [source,fpp] -------- module Fw { module DpCfg { constant CONTAINER_USER_DATA_SIZE = "abc" } } -------- Here is what happens when you run this model through `fpp-check`: ---- % fpp-check module Fw { module DpCfg { constant CONTAINER_USER_DATA_SIZE = "abc" } } ^D fpp-check stdin:5.5 constant CONTAINER_USER_DATA_SIZE = "abc" ^ error: the F Prime framework constant Fw.DpCfg.CONTAINER_USER_DATA_SIZE must have an integer type ----