** xref:type/index.adoc[Data Types]
*** xref:type/index.adoc[List of Data Types]
+*** xref:type/cast.adoc[Casts]
*** xref:type/ip.adoc[IP Addresses]
*** xref:type/numb.adoc[Numbers]
*** xref:type/string/single.adoc[Single Quoted Strings]
--- /dev/null
+= Casts
+
+Values can be *cast* from one data type to another. However, this
+cast only changes the _type_ of the data, it does not change the datas
+_value_. That is, a cast allows you to convert an `octets` data type
+to a `string`, but the resulting `string` may still contain
+non-printable characters.
+
+Casting is used when the server is unable to automatically figure out
+the correct data type to use. For example, in the following
+expansion, it is not immediately obvious that the right side of the
+expression is an IP address. It could instead be interpreted as a string
+`"192.0.2.1"`.
+
+[source,unlang]
+----
+if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == 192.0.2.1) }
+ ....
+}
+----
+
+Since there is no attribute reference on either side of the `==`
+operator, the interpreter has no way of knowing that the string
+`192.0.2.1` is an IP address. There is unfortunately no way of
+_always_ parsing strings in order to automatically determine which
+data type to use. Any such automatic parsing would work most of the
+time, but it would have error cases where the parsing was incorrect.
+
+The solution is to resolve these ambiguities by allowing the values to
+be cast to a particular type. Casting a value to a type tells the
+interpreter how that value should be parsed. Casting is done by
+prefixing a value with the type name, surrounded by brackets;
+`(...)`.
+
+.Syntax
+----
+(...)value
+----
+
+We can add a cast to the above example, as follows:
+
+[source,unlang]
+----
+if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == (ipaddr)192.0.2.1) }
+ ....
+}
+----
+
+In this example, we prefix the IP address with the string `(ipaddr)`.
+The interpreter then knows that the value `192.0.2.` should be
+interpreted as the data type `ipaddr`, and not as the literal string
+`"192.0.2."`.
+
+For a full list of data types which can be used in a cast, please see
+the xref:unlang/type/all_types.adoc[list of data types] page, and the
+"Basic Type Types" section.
+
+In most cases, the server can automatically determine what data type
+to use. The cast syntax is used when either the data type is
+ambiguous, or when data should be normalized prior to comparison, or
+when a specific data type is required.
+
+=== Compatibility
+
+For compatibility with version 3, the `<cast>` syntax is also
+supported. We recommend, however, that people use the new syntax.
+
+// Copyright (C) 2021 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Development of this documentation was sponsored by Network RADIUS SAS.
a number, or an IP address, depending on its context.
Note that the interpretation of text _strongly_ depends on the
-context. The text `"0000"` can be interpreted as a data type
+context. The text `0000` can be interpreted as a data type
"integer", having value zero, or a data type "string", having value
`"0000"`. In general when a particular piece of text is used, it is
used with the context of a known attribute. That attribute has a
`"a string"` +
`"this has embedded\ncharacters"`
+== Methods of Creating Strings
+
+There are a few different ways in which double-quoted strings can be
+created. The simplest is just an in-line string, as in `"string"`.
+However, strings can also be created via
+xref:unlang/expression.adoc[expressions]. and
+xref:xlat/index.adoc[dynamic expansions].
+
+In general, creating strings via xref:xlat/index.adoc[dynamic
+expansions] will result in the _printed_ version of the expansion
+being used.
+
+.Example
+[source,unlang]
+----
+"User-Name is %{User-Name}"
+"IP Address is %{reply.Framed-IP-Address}
+----
+
+Both of the above expansions will return the _printed_ version of the
+expansion. For `User-Name`, it will be the string version of the
+users name, as would be expected. However, for the
+`Framed-IP-Address` example, the printed version will be an ASCII
+string such as `192.0.2.1`, even though the actual IP address is a
+32-bit number.
+
+When a string is created via an
+xref:unlang/expression.adoc[expression] using the `+` operator, the
+resulting string can be quite different, depending on the inputs.
+
+.Example
+[source,unlang]
+----
+"User-Name is " + &User-Name
+"IP Address is " + (string) &reply.Framed-IP-Address
+----
+
+The first expansion here returns the same string as the previous
+example `"User-Name is %{User-Name}"`. This behavior is because the
+`User-Name` attribute is defined as type `string`. So appending a
+`string` to a `string` yields a `string`.
+
+However, the second example returns the string `"IP Address is "`
+followed by four (4) bytes of the actual IP address, with embedded
+zeros, and other non-ASCII characters
+
+This behavior is because the xref:type/cast.doc[cast] operator
+`(string)` will convert the data type from `ipv4addr` to `string`, but
+it will not change or modify the _contents_ of the data. The result
+of the `+` concatenation is therefore the original `string`, followed
+by the 4 bytes of the `ipv4addr` data.
+
+If the printed version of the data is needed, the expansion should be
+enclosed in double-quotes, as in the first example.
+
+=== To Remember
+
+`"foo" + (string) &Bar` produces `"foo"` plus `&Bar` _cast_ to a string.
+
+`"foo%{Bar}"` produces `"foo"` plus `&Bar` _printed_ to a string.
+
// Copyright (C) 2021 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
// Development of this documentation was sponsored by Network RADIUS SAS.
=== Casting Data Types
-In some cases, it is necessary to parse values which do not refer to
-attributes. This situation usually occurs when two values need to be
-compared, as in the following example:
-
-[source,unlang]
-----
-if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == 192.0.2.1) }
- ....
-}
-----
-
-Since there is no attribute on either side of the `==` operator, the
-interpreter has no way of knowing that the string `192.0.2.1` is an IP
-address. There is unfortunately no way of automatically parsing
-strings in order to determine the data type to use. Any such
-automatic parsing would work most of the time, but it would have
-error cases where the parsing was incorrect.
-
-The solution is to resolve these ambiguities by allowing the values to
-be cast to a particular type. Casting a value to a type tells the
-interpreter how that value should be parsed. Casting is done by
-prefixing a value with the type name, surrounded by brackets;
-`(...)`.
-
-.Syntax
-----
-(...)value
-----
-
-We can add a cast to the above example, as follows:
-
-[source,unlang]
-----
-if ("%{sql:SELECT ipaddress FROM table WHERE user=%{User-Name}}" == (ipaddr)192.0.2.1) }
- ....
-}
-----
-
-In this example, we prefix the IP address with the string `(ipaddr)`.
-The interpreter then knows that the value `192.0.2.` should be
-interpreted as the data type `ipaddr`, and not as the literal string
-`"192.0.2."`.
-
-For a full list of data types which can be used in a cast, please see
-the xref:unlang/type/all_types.adoc[list of data types] page, and the
-"Basic Type Types" section.
-
-In most cases, the server can automatically determine what data type
-to use. The cast syntax is used when either the data type is
-ambiguous, or when data should be normalized prior to comparison, or
-when a specific data type is required.
-
-=== Compatibility
-
-For compatibility with version 3, the `<cast>` syntax is also
-supported. We recommend, however, that people use the new syntax.
+Values can be xref:type/cast.adoc[cast] from one data type to another.
+However, this cast only changes the _type_ of the data, it does not
+change the datas _value_. That is, a cast allows you to convert an
+`octets` data type to a `string`, but the resulting `string` may still
+contain non-printable characters.
// Copyright (C) 2021 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
// Development of this documentation was sponsored by Network RADIUS SAS.
Will result in `&Reply-Message == "foo"` !.
+Note that the `-=` operator behaves differently from earlier versions
+of the server! Since the server no longer needs `update` sections, we
+could re-purpose the operator.
+
Other data types will generally yield results which make sense. For
example:
Mathematical operations which cause overflow, underflow, or division
by zero will return a `null` result. This result will propagate
through any calculations, so that an expression which relies on `null`
-will also return `null`.
+will generally also return `null`.
+
+A `null` result can be removed via the `||` operator. For example:
+
+[source,unlang]
+----
+&NAS-Port-Id = (5 - "foo") || 6
+----
+Will return `6`, as the left side expression of the `||` operator evaluates to `null`.
expanded at run time. For historical reasons, these string expansions
are called "xlats".
-Dynamioc expansion is performed via the following syntax:
+Dynamic expansion is performed via the following syntax:
`%{...}`
Where the `%{` signals the start of a dynamic expansion, and `}`
-signals the end of the dynamic expansion. The contents of the
-expansion can be many things:
+signals the end of the dynamic expansion. Dynamic expansions are most
+commonly used in xref:type/string/double.adoc[double-quoted strings],
+and xref:unlang/expression.adoc[expressions / conditions].
+
+The contents of an expansion can be many things, such as attribute
+references, function calls, etc. The table below gives more examples of expansions.
.Types of Expansions
[options="header"]
| xref:xlat/character.adoc[single character] | Single character expansions.
| xref:xlat/module.adoc[modules] | Pass a string to a module such as `sql`.
| xref:xlat/alternation.adoc[condition] | Conditionally expand a string.
-| xref:xlat/builtin.adoc[built-in expansions] | Such as string length, tolower, etc...
+| xref:xlat/builtin.adoc[built-in expansions] | Functions as string length, tolower, etc...
|=====
-This feature is used to create policies which refer to concepts rather
-than to specific values. For example, a policy can be created that
-refers to the User-Name in a request, via:
-
-`%{User-Name}`
-
-This expansion is done only for double-quoted strings and for
-the back-tick operator.
+Expansions are used inside of
+xref:type/string/double.adoc[double-quoted strings] and for the
+xref:string/backticks.adoc[back-tick quoted strings]
== Caveats
Unlike other languages, there is no way to define new variables. All
-of the expansions must refer to attributes that already exist,
-or to modules that will return a string value.
+of the expansions must refer to attributes that already exist, or to
+pre-defined functions which take arguments, and return values.
+
+New variables are defined in the xref:raddb:dictionary.adoc[site-local dictionaries].
== Character Escaping
string `%{...}`. The `%` character is used for variable expansion, so a
literal `%` character can be created by using `%%`.
-Other than within a dynamically expanded string, very little
-character escaping is needed. The rules of the enclosing string context
-determine whether or not a space or " character needs to be escaped.
+Other than within a dynamically expanded string, very little character
+escaping is needed. The rules of the enclosing string context
+determine whether or not a space or `"` character needs to be escaped.
+See the ref:type/string/double.adoc[double-quoted strings] and
+xref:string/backticks.adoc[back-tick quoted strings] pages for more
+information.
.Example