#Script aims to be a familiar and expressive dynamic language for scripting .NET Apps, that is optimal at generating text, especially HTML where it's pre-configured with the HTML Page Format and HTML Scripts but unlike C#'s Razor can support multiple pluggable page formats which can be used for generating any kind of text.

For maximum familiarity #Script uses JavaScript Expressions that for increased readability and expressiveness supports being used within Template Expressions which like Vue.js filters, and Angular's Template Expressions lets you use the | pipe operator to chain the return values of methods into subsequent methods from left-to-right. For statements #Script adopts the familiar Handlebars-like Script Blocks that is also popular among HTML templating engines.

Effectively #Script lets you use the same familiar JS language for server HTML rendering as you would do in client-side rendering of Single Page Apps despite it binding natively to C# objects and calling C# methods behind-the-scenes.

Mustache expressions

Like Vue/Angular Templates, only expressions inside mustaches are evaluated, whilst everything outside are emitted as-is:

Which calls the upper default script method where the argument on the left-side of the "pipe" symbol is passed as the first argument to the upper method which is implemented as:

public string upper(string text) => text?.ToUpper();

This can be rewritten without the "pipe forward" operator by calling it as a method or an extension method instead:

Methods can be chained

Methods are chained from left-to-right where the value on the left side of the "pipe" symbol is passed as the first argument in the method on the right and the output of that is passed as the input of the next method in the chain and so on:

Methods can also accept additional arguments which are passed starting from the 2nd argument since the first argument is the value the method is called with. E.g. here are the implementations for the substring and padRight default scripts:

public string substring(string text, int startIndex) => text.SafeSubstring(startIndex);
public string padRight(string text, int totalWidth, char padChar) => text?.PadRight(totalWidth, padChar);

JavaScript literal notation

You can use the same literal syntax used to define numbers, strings, booleans, null, Objects and Arrays in JavaScript within templates and it will get converted into the most appropriate .NET Type, e.g:

ES6 Shorthand notation is also supported where you can use the argument name as its property name in a Dictionary:

Local Variables

Like JavaScript you can declare a locally scoped variable with var:

var sliders = dirFiles('img/sliders')

Like JS you can use either var, let or const but they all behave like let and assign a locally scoped variable at the time the expression is executed. Also like JS the semicolon is optional and you can assign multiple variables in a single expression:

let a = 1 + 2, b = 3 * 4, c, d = 'D';

Global Variables

Global Variables in #Script are maintained in the PageResult.Args dictionary which you could previously assign to using the toGlobal script method where it’s accessible to all scripts rendered within that PageResult.

#Script now mimics JS's behavior to allow assignment expressions to assign global variables where **Assignment Expressions** on undeclared variables (i.e. where no locally scoped variable exists) will assign a global variable:

a = 1

A more descriptive syntax available to declare a global variable is to assign it to the global object (inspired by node’s global) which is an alias to the PageResult.Args dictionary:

global.a = 1

Assignment Expressions

In addition to declaring and assigning variables, there’s also support for using assignment expressions to assign and mutate Collections and Type Properties using either Member Expression or Index expression syntax, e.g:

intList[1] = 10
stringArray[1] = "foo"
stringMap["foo"] = "bar"
person.Age = 27
objectMap.Person.Name = "kurt"
objectMap['Per' + 'son'].Name = "kurt"
intList[1.isOdd() ? 2 : 3] = 30

Dynamically resolve args

The resolve* APIs let you resolve a variable named with the result of an expression, e.g:

var tableNames = resolveGlobal(`${db}_tables`)

Equivalent to:

var tableNames = global[`${db}_tables`]

Whereas resolveArg lets you resolve a variable using locally scope resolution hierarchy:

var tableNames = resolveArg(`${db}_tables`)


Strings can be defined using single quotes, double quotes, prime quotes or backticks:

Strings can also span multiple lines.

Template Literals

Backticks strings implement JavaScript's Template literals which can be be used to embed expressions:
The example above also shows the difference in escaping where Template literals evaluate escaped characters whilst normal strings leave \ backspaces unescaped.

Shorthand arrow expression syntax

#Script Expressions have full support for JavaScript expressions but doesn't support statements or function declarations although it does support JavaScript's arrow function expressions which can be used in functional methods to enable LINQ-like queries. You can use fat arrows => immediately after methods to define lambda's with an implicit (it => ...) binding, e.g:

This is a shorthand for declaring lambda expressions with normal arrow expression syntax:

Using normal lambda expression syntax lets you rename lambda parameters as seen in the map(x => ...) example.

Special string argument syntax

As string expressions are a prevalent in #Script, we've also given them special wrist-friendly syntax where you can add a colon at the end of the method name which says to treat the following characters up until the end of the line or mustache expression as a string, trim it and convert '{' and '}' chars into mustaches. With this syntax you can write:

and it will be rewritten into its equivalent and more verbose form of:

SQL-like Boolean Expressions

To maximize readability and intuitiveness for non-programmers, boolean expressions can also adopt an SQL-like syntax where instead of using && or || operator syntax to define boolean expressions you can also use the more human-friendly and and or alternatives:

Include Raw Content Verbatim

Use #raw blocks to ignore evaluating expressions and emit content verbatim. This is useful when using a client Handlebars-like templating solution like Vue or Angular templates where expressions need to be evaluated with JavaScript in the browser instead of on the Server with Templates:

Multi-line Comments

Any text within {{#noop}} ... {{/noop}} block statements are ignored and can be used for temporarily removing sections from pages without needing to delete it.

Everything within multi-line comments {{‌* and *‌}} is ignored and removed from the page.

An alternative way to temporarily disable an expression is to prefix the expression with the end method to immediately short-circuit evaluation, e.g: {{ end |> now |> dateFormat }}

See Ignoring Pages for different options for ignoring entire pages and layout templates.

Script Methods can be used as Extension Methods

A core feature of #Script is that it runs in a sandbox and only has access to functionality that's configured in its ScriptContext that it runs in, so by design #Script is prohibited from calling instance methods so they only have a read-only view of your objects unless you explicitly register ScriptMethods that allows them to change them.

This frees up the instance.method() syntax to be put to other use which can now be used to call every script method as an extension method. This can greatly improve the readability and execution flow of code, e,g. we can rewrite our previous JS Utils Eval example:

itemsOf(3, padRight(reverse(arg), 8, '_'))

into the more readable form using the same methods as extension methods off the first argument, e.g:

3.itemsOf(arg.reverse().padRight(8, '_'))

As an example, C#'s 101 LINQ examples most complicated LINQ expression can now be rewritten from its original source:

{{ customers 
   |> map => { 
        CompanyName: it.CompanyName, 
        YearGroups: map (
            groupBy(it.Orders, it => it.OrderDate.Year),
            yg => { 
                Year: yg.Key,
                MonthGroups: map (
                    groupBy(yg, o => o.OrderDate.Month),
                    mg => { Month: mg.Key, Orders: mg }
     |> htmlDump }}

to use extension methods which greatly improves its readability as its execution flow is now able to read from left-to-right:

{{ customers |> map => { 
    CompanyName: it.CompanyName, 
    YearGroups: it.Orders.groupBy(it => it.OrderDate.Year).map(yg =>
            Year: yg.Key,
            MonthGroups: yg.groupBy(o => o.OrderDate.Month).map(mg => 
                { Month: mg.Key, Orders: mg }
   |> htmlDump }}

JavaScript Array Support

We can use extension methods to define instance methods that can be called on any object to implement JS Array methods support to further improve #Script source compatibility with JavaScript.

Here are Mozilla's Array examples in #Script utilizing its Code Blocks feature to reduce the amount of boilerplate required:

`Create an Array`
['Apple', 'Banana'] |> to => fruits

`\nAccess (index into) an Array Item`
fruits[0] |> to => first

`\nLoop over an Array`
#each item in fruits
    `${item}, ${index}`

[] |> to => sb
fruits.forEach((item,index,array) => sb.push(`${item}, ${index}`))

`\nAdd to the end of an Array`

fruits.push('Orange') |> to => newLength

`\nRemove from the end of an Array`
fruits.pop() |> to => last

`\nRemove from the front of an Array`
fruits.shift() |> to => first

`\nAdd to the front of an Array`
fruits.unshift('Strawberry') |> to => newLength

`\nFind the index of an item in the Array`
fruits.push('Mango') |> end

fruits.indexOf('Banana') |> to => pos

`\nRemove an item by index position`
fruits.splice(pos, 1) |> to => removedItem
removedItem |> join
fruits |> join

`\nRemove items from an index position`
['Cabbage', 'Turnip', 'Radish', 'Carrot'] |> to => vegetables 
vegetables |> join

1 |> to => pos
2 |> to => n

vegetables.splice(pos, n) |> to => removedItems 
vegetables |> join
removedItems |> join

`\nCopy an Array`
fruits.slice() |> join

Which can be run with x run {script}.ss to view its expected output:

Create an Array

Access (index into) an Array Item

Loop over an Array
Apple, 0
Banana, 1

Apple, 0
Banana, 1

Add to the end of an Array

Remove from the end of an Array

Remove from the front of an Array

Add to the front of an Array

Find the index of an item in the Array

Remove an item by index position

Remove items from an index position

Copy an Array

Most JS Array methods are supported, including the latest additions from ES2019:

Language Blocks and Expressions

We've caught a glimpse using language blocks with the code JavaScript Array Example above which allows us to invert #Script from "Template Mode" where all text is emitted as-is with only code within Template Expressions {{ ... }} are evaluated and changed it to "Code Mode" where all code is evaluated a code expression.

This is akin to Razor's statement blocks which inverts Razor's mode of emitting text to treating text inside statement blocks as code, e.g:

    var a = 1;
    var b = 2;
Basic Calc
a + b = @(a + b)

The equivalent in #Script using code language blocks:

var a = 1
var b = 2
Basic Calc
a + b = {{a + b}}

Which is useful in reducing boilerplate when you need to evaluate code blocks with 2+ or more lines without the distracting boilerplate of wrapping each expression within a {{ ... }} Template Expression.


Refer to the languages page to learn about alternative languages you can use within Language Blocks and Expressions:

Language Blocks

code fragments are executed using #Script language blocks in the format:



Where #Script will parse the statement body with the language registered in its ScriptContext's ScriptLanguages collection that's pre-registered with ScriptCode.Language which is used to process code block statements, e.g:

{{ 1 |> to => a}}

(a + 2) |> to => result

Code result: {{ result }}

Code Statement Blocks are evaluated within the same scope so any arguments that are assigned are also accessible within the containing page as seen above.

Evaluate Lisp

You can use language blocks to embed and evaluate any of the languages registered in your ScriptContext, e,g. to evaluate #Script Lisp register it in your ScriptContext or SharpPagesFeature:

var context = new ScriptContext {
    ScriptLanguages = { ScriptLisp.Language },

Where it will let you use Language Blocks to evaluate LISP code within #Script:

{{ 1 |> to => a}}

(def local-arg (+ a 2))
(export result local-arg)

Lisp result: {{ result }}

Unlike code blocks, Lisp evaluates its code using its own Symbols table, it's able to reference arguments not in its Global symbols by resolving them from the containing scope, but in order for the outer #Script to access its local bindings they need to be exported as seen above which registers the value of its local-arg value into the result argument.

Language Block Modifiers

You can provide any additional modifiers to language blocks by specifying them after them after the | operator, languages can use these modifiers to change how it evaluates the script. By default the only modifiers the built-in languages support are |quiet and its shorter |mute and |q aliases which you can use to discard any output from being rendered within the page.

If you use Lisp's setq special form to assign a variable, that value is also returned which would be rendered in the page, you can ignore this output by using one of the above modifiers, e.g:

{{ 1 |> to => a}}

(setq local-arg (+ a 2))
(export result local-arg)

Lisp result: {{ result }}

Language Expressions

If you wanted to instead embed an expression in a different language instead of executing an entire statement block, you can embed them within Language Expressions {|<lang> ... |}, e.g:

{{ 1 |> to => a }}

Lisp result: {|lisp (+ a 2) |}

You could also use them to evaluate code expressions, e.g. {|code 1 + 2 |}, but that's also how #Script more concise Template Expressions are evaluated {{ 1 + 2 }}.

Multi-language support

Despite being implemented in different languages a #Script page containing multiple languages, e.g:

Still only produces a single page AST, where when first loaded #Script parses the page contents as a contiguous ReadOnlyMemory<char> where page slices of any Language Blocks and Expressions on the page are delegated to the ScriptContext registered ScriptLanguages for parsing which returns a fragment which is added to the pages AST:

When executing the page, each language is responsible for rendering its own fragments which all write directly to the pages OutputStream to generate the pages output.

The multi-languages support in #Script is designed to be extensible where everything about the language is encapsulated within its ScriptLanguage implementation so that if you omit its registration:

var context = new ScriptContext {
//    ScriptLanguages = { ScriptLisp.Language }

Any language expressions and language blocks referencing it become inert and its source code emitted as plain-text.

Language Comparisons

Whilst all languages have access to the same Script Methods and .NET Scripting interop, they all have unique characteristics that make them suitable for different use-cases. The default template syntax is ideal for generating text output where you need to embed logic when generating text output, #Script Code is ideal when logic is more important then the text output like in Sharp Scripts, whilst #Script Lisp is ideal for defining algorithms or for developers who prefer Lisp syntax or for devs who prefer development in a functional style.

A good comparison showing the differences, strengths and weaknesses of each language can be seen with the implementation of FizzBuzz in each language:


#Script is a "template-first" language where only expressions within {{ ... }} literals are evaluated, everything outside it is emitted verbatim, as consequence it's white-space sensitive where the indention of logic blocks affect its output as seen above where all lines are indented with the same 2 spaces as its free-text contents.


By contrast #Script Code is an inverted Templates where it's a "code-first" language where all statements are treated as code blocks so statements don't require the {{ ... }} boilerplate and are not white-space sensitive. Like Templates each content block still generates output (unless its suppressed with the quiet modifier) but needs to be a valid expression like a string literal, argument or expression as seen in the code example above.


#Script Lisp lets you use the venerable and highly dynamic and functional Lisp language that's particularly strong at capturing algorithms and composing functions, although FizzBuzz isn't a particularly good showcase of either of its dynamism of functional strengths, the last line shows a glimpse of its natural functional composition.

Combine strengths of all Languages

As all languages compile down into the same AST block we can freely use all languages within the same Script block to take advantage of the strengths of each, e.g. Template is great at generating text and Lisp excels at algorithms which make a great combination if you ever need to combine the 2, so you could easily for example use the defn Script Block to define a function in Lisp, compiled to a .NET delegate so its invocable as a regular function inside a Template Block that generates markdown captured by the capture Script Block and converted to HTML using the markdown filter transformer:

made with by ServiceStack