CSP (C Server Pages) - server-side view rendering technology written on C. It is designed to be familiar for users of technologies such as JSP, GSP, Freemarker etc. and to be usable in embedded applications
- Pure C implementation
- Groovy like scripting language
- Supports variables, loops, conditions, includes, collections, ternary and elvis operators...
- Simple and user-friendly design
How to add CPM to the project, check the link
CPMAddPackage(
NAME CSP
GITHUB_REPOSITORY ximtech/CSP
GIT_TAG origin/main)
target_link_libraries(${PROJECT_NAME} CSP)
add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})
# For Clion STM32 plugin generated Cmake use
target_link_libraries(${PROJECT_NAME}.elf CSP)
#include "CSPTemplate.h"
#include "CSPRenderer.h"
CSP supports the usage of ${}
for parameter binding and evaluation:
<!--index.csp-->
<html>
<body>
Hello ${param}
</body>
</html>
CspTemplate *cspTemplate = newCspTemplate("index.csp"); // compile template file
CspObjectMap *params = newCspParamObjMap(8); // create map to store parameters
cspAddStrToMap(params, "param", "World!!!"); // add parameter name and value
CspRenderer *renderer = NEW_CSP_RENDERER(cspTemplate, params); // create renderer from template and add parameters
CspTableString *result = renderCspTemplate(renderer);
if (!isCspTemplateOk(cspTemplate)) { // check that no errors
printf("%s\n", cspTemplateErrorMessage(cspTemplate));
return 1;
}
printf("%s", result->value);
// Release resources
deleteCspRenderer(renderer);
deleteCspTemplate(cspTemplate);
<!--index.csp-->
<html>
<body>
Hello World!!!
</body>
</html>
CspObjectMap *params = newCspParamObjMap(8); // create map to store parameters
CspObjectArray *array = newCspParamObjArray(4); // create array object
cspAddStrToArray(array, "Action");
cspAddIntToArray(array, 123);
cspAddFloatToArray(array, 1.1f);
cspAddVecToMap(array, params, "array"); // add array to root params and assign name
CspObjectMap *params = newCspParamObjMap(8); // create map to store parameters
CspObjectMap *innerObj = newCspParamObjMap(8); // create map object
cspAddStrToMap(innerObj, "str", "test"); // add key/value pairs
cspAddIntToMap(innerObj, "int", 1);
cspAddFloatToMap(innerObj, "decimal", 3.14f);
cspAddMapToMap(innerObj, paramMap, "obj"); // add map to root params and assign name
Then you can access map values as follows:
${obj.str} // "test"
${obj.int} // 1
${obj.decimal} // 3.14
Note: Array and map can support nested array and map parameters.
- Use functions
cspAddMapToMap()
,cspAddVecToMap()
,cspAddMapToArray()
andcspAddArrayToArray()
- Check
CSPInterpreter.h
for other param functions
The syntax of the template derives from the high level programming languages grammar like Java, but enhances it with specific constructs.
The placeholder expressions are surrounded by ${}
:
<html>
<body>
Hello ${params.name}
</body>
</html>
Single-quoted strings are a series of characters surrounded by single quotes:
${'a single-quoted string'}
Double-quoted strings are a series of characters surrounded by double quotes:
${"a double-quoted string"}
Note: To escape a double quote, you can use the backslash character: "A double quote: "".
Strings can be concatenated with the +
operator:
${'a' + 'b'} // "ab"
${'one' * 3} // repeat string 3 times: "oneoneone"
By default, CSP supports int32_t
and float
numeric types. To change size of types define macros:
#define CSP_INT_TYPE int32_t
#define CSP_FLOAT_TYPE float
${1} // unsigned `int` literal
${-1} // signed `int` literal
${1.0} // unsigned `float/double` literal
${-1.0} // signed `float/double` literal
- Lower or upper case
NULL
literal:
${NULL}
${null}
- Lower or upper case Booleans:
${true}
${false}
${TRUE}
${FALSE}
CSP uses a comma-separated array of values, surrounded by square brackets, to denote arrays:
${[]} // empty array
${[1, 2, 3]}
${['one', 'two']}
${[1.0] + 2.0} // add value to array -> [1.0, 2.0]
${[0...5]} // fill array in range -> [0, 1, 2, 3, 4, 5]
Note: Range values must be int
type and compile known constants
You can also create arrays containing values of heterogeneous types
${[1, 2...5, 'a', true, param.name]}
Array here contains a number
, range
, string
, boolean
and parameter
value
Sometimes called dictionaries or associative arrays in other languages. Maps associate keys to values, separating keys and values with colons, and each key/value pairs with commas, and the whole keys and values surrounded by square brackets.
${[:]} // empty map
${['key1': 1, 'key2': 'two', 'key3': 3.0, 'key4': param.name]}
${['red': '#FF0000', 'green': '#00FF00', 'blue': '#0000FF']}
The last one is Map of string
color names, associated with their hexadecimal-coded html colors
Note: Key names must be a string
literals
CSP template supports the usual familiar arithmetic operators like Groovy
programming language
Operator | Purpose |
---|---|
+ |
addition |
- |
subtraction |
* |
multiplication |
/ |
division |
% |
remainder |
** |
power |
Here are a few examples of usage of those operators:
${1 + 2}
${4 - 3}
${3 * 5}
${3 / 2}
${10 % 3}
${2 ** 3}
The +
and -
operators are also available as unary operators:
${+3 == 3}
${-4 == 0 - 4}
${--1 == 1}
Note: the ++
and --
is used only as unary operators and can be stacked ----1
Operator | Purpose |
---|---|
== |
equal |
!= |
not equal |
< |
less than |
<= |
less than or equal |
> |
greater than |
>= |
greater than or equal |
Here are some examples of simple number and string comparisons using these operators:
${1 + 2 == 3}
${3 != 4}
${-2 < 3}
${2 <= 2}
${3 <= 4}
${5 > 1}
${5 >= -2}
${2 == '2'}
${3.2 == '3.2'}
${'foo' == 'baz'}
${'foo' != 'baz'}
CSP offers three logical operators for boolean expressions:
&&
: logical "and"||
: logical "or"!
: logical "not"
${!false} // "not" false is true
${true && true} // true "and" true is true
${true || false} // true "or" false is true
The logical "not" has a higher priority than the logical "and".
${!false && false}
The result is false
, because not
has a higher precedence than and
The logical "and" has a higher priority than the logical "or".
${true || true && false }
Here result is true
, because and
has a higher precedence than or
, therefore the or
is executed last and returns true
The logical ||
operator supports short-circuiting: if the left operand is true
, it knows that the result will be true
in any case, so it won’t evaluate the right operand. The right operand will be evaluated only if the left operand is false
.
Likewise for the logical &&
operator: if the left operand is false
, it knows that the result will be false
in any case, so it won’t evaluate the right operand. The right operand will be evaluated only if the left operand is true
.
${!true} // the negation of `true` is `false`
${!false} // the negation of `false` is `true`
${!'foo'} // 'foo' is a non-empty string, evaluating to `true`, so negation returns `false`
${!''} // empty string, evaluating to `false`, so negation returns `true`
- The ternary operator is a shortcut expression that is equivalent to an
if/else
branch assigning some value to a variable.
${param != null ? 'Found' : 'Not found'}
- The "Elvis operator" is a shortening of the ternary operator. One instance of where this is handy is for returning a 'default' value if an expression resolves to
false
${user.name ? user.name : 'Anonymous'} // with the ternary operator, you have to repeat the value you want to view
${user.name ?: 'Anonymous'} // with the Elvis operator the value, which is tested, is used if it is not `false`
Sometimes there is need to skip parameters ${}
. For example JS have string interpolation
that contains same parameter enclosing symbols that in CSP and can cause conflict situations.
Thus, to ignore parameter enclose it by backtick (`) characters:
`some text with ${expression} param`
- The
++
(increment) and--
(decrement) operators are not available - Assignment arithmetic operators (
+=
,-=
,*=
,/=
,%=
,**=
) are not available - Bitwise and bit shift operators (
&
,|
,^
,-
,<<
,>>
) not supported
Variables can be defined within a CSP template using the set
tag:
<csp:set var="number" value="${1 + 2}"/>
<csp:set var="fromParam" value="${param.name}"/>
<csp:set var="list" value="${['one', name, 123, 1.23, 'some']}"/>
<csp:set var="map" value="${['key1': 1, 'key2': 'two', 'key3': 3.0, 'key4': param.name]}"/>
var
- The name of the variablevalue
- The initial value
The logical if tag to switch on an expression
<csp:if test="${name == 'fred'}">
<p>Hello ${name}!</p>
</csp:if>
test
- The expression to test
The logical elseif tag
<csp:if test="${name == 'Fred'}">
<p>Hello Fred!</p>
</csp:if>
<csp:elseif test="${name == 'Bob'}">
<p>Hello Bob!</p>
</csp:elseif>
test
- The expression to test
The logical else tag
<csp:if test="${name == 'Fred'}">
<p>Hello Fred!</p>
</csp:if>
<csp:else>
Hello ${name}! Do I know you?
</csp:else>
Uses a "for(var in collection)" like loop to iterate over each element of the specified object
<csp:loop var="book" in="${books}">
<p>Title: ${book.title}</p>
<p>Author: ${book.author}</p>
</csp:loop>
- Using the
status
parameter to alternate the coloring of a table’s rows:
<tbody>
<csp:loop status="i" var="item" in="${itemList}">
<!-- Alternate CSS classes for the rows. -->
<tr class="${ (i % 2) == 0 ? 'a' : 'b' }">
<td>${item.id}</td>
<td>${item.parentId}</td>
<td>${item.type}</td>
<td>${item.status}</td>
</tr>
</csp:loop>
</tbody>
- Using
set
tag variable as a counter
<ul class="list-group container">
<csp:set var="cnt" value="${1}"/>
<csp:loop var="val" in="${list}">
<li class="list-group-item">${cnt}</li>
<csp:set var="cnt" value="${cnt + 1}"/>
</csp:loop>
</ul>
in
- The object to iterate overvar
- The name of the itemstatus
(optional) - The name of a variable to store the iteration index in. Starts with 0 and increments for each iteration
Applies an inbuilt or user-defined CSP template so that templates can be shared and reused
- Example template
include.csp
:
<p>${title}</p>
<p>${author}</p>
- This template can now be reused whether you have parameters as
title
andauthor
:
<csp:render template="../templates/include.csp"/>
template
- The name of the template to apply