Text::...::Kolon(3pm)

okay so bestie, this is the tea on Text::Xslate::Syntax::Kolon (3pm) β€” the default template syntax for Xslate. It's giving main character energy and it literally has all the features. Let's go. ✨


πŸ’– NAME πŸ’–

Text::Xslate::Syntax::Kolon β€” The default template syntax. Short, iconic, periodt. πŸ’…

✨ SYNOPSIS ✨

use Text::Xslate;
my $tx = Text::Xslate->new(
    syntax => 'Kolon', # optional
);

print $tx->render_string(
    'Hello, <: $dialect :> world!',
    { dialect => 'Kolon' }
);

🌸 DESCRIPTION 🌸

Kolon is the default syntax, using <: ... :> tags and : ... line code. In this syntax all the features in Xslate are available. No gatekeeping, just vibes. πŸ¦‹

πŸ¦‹ SYNTAX πŸ¦‹

🌈 Variable access 🌈

Variable access:

<: $var :>

Field access:

<: $var.0 :>
<: $var.field :>
<: $var.accessor :>

<: $var["field"] :>
<: $var[0] :>

Variables may be HASH references, ARRAY references, or objects. Because $var.field and $var["field"] are the same semantics, $obj["accessor"] syntax may be call object methods. iykyk. πŸ’…

πŸ“ Literals πŸ“

Special:

: nil   # as undef, indicating "nothing"
: true  # as the integer 1
: false # as the integer 0

String:

: "foo\n" # the same as perl
: 'foo\n' # the same as perl

Number:

: 42
: 3.14
: 0xFF   # hex
: 0777   # octal
: 0b1010 # binary

Array:

: for [1, 2, 3] -> $i { ... }

Hash:

: foo({ foo => "bar" })

Note that { ... } is always parsed as hash literals, so you don't need to use the <+> prefix as Perl sometimes requires:

:  {}.kv(); # ok
: +{}.kv(); # also ok

🧚 Expressions 🧚

Conditional operator (?:):

: $var.value == nil ? "nil" : $var.value

Relational operators (== != < <= > >=):

: $var == 10 ? "10"     : "not 10"
: $var != 10 ? "not 10" : "10"

Note that == and != are similar to Perl's eq and ne except that $var == nil is true iff $var is uninitialized, while other relational operators are numerical.

Arithmetic operators (+ - * / % min max):

: $var * 10_000
: ($var % 10) == 0
: 10 min 20 min 30 # 10
: 10 max 20 max 30 # 30

Bitwise operators (prefix:<+^> +& +| +^)

: 0x1010 +| 0x3200 # bitwise or:  0x3210
: 0x1010 +& 0x3200 # bitwise and: 0x1000
: 0x1010 +^ 0x3200 # bitwise xor: 0x0210
: +^0x1010         # bitwise neg: 0xFFFFEFEF (on 32 bit system)

Logical operators (! && || // not and or)

: $var >= 0 && $var <= 10 ? "ok" : "too smaller or too larger"
: $var // "foo" # as a default value

String operators (~)

: "[" ~ $var ~ "]" # concatenation

The operator precedence is very like Perl's:

. () []
prefix:<!> prefix:<+> prefix:<-> prefix:<+^>
* / % x +&
+ - ~ +| +^
prefix:<defined>
< <= > >=
== !=
|
&&
|| // min max
?:
not
and
or

πŸ’œ Constants (or binding) πŸ’œ

You can define lexical constants with constant, which requires a bare word, and my, which requires a variable name.

: constant FOO = 42;
: my      $foo = 42;

These two statements has the same semantics, so you cannot modify $foo.

: my $foo = 42; $foo = 3.14; # compile error!

πŸ”₯ Loops πŸ”₯

There are for loops that are like Perl's foreach.

: # iterate over an ARRAY reference
: for $data -> $item {
    [<: $item.field :>]
: }

: # iterate over a HASH reference
: # You must specify how to iterate it (.keys(), .values() or .kv())
: for $data.keys() -> $key {
    <: $key :>=<: $data[$key] :>
: }

And the for statement can take an else block:

: for $data -> $item {
    [<: $item.field :>]
: }
: else {
    Nothing in data
: }

The else block is executed if $data is an empty array or nil.

You can get the iterator index in for statements as $~ITERATOR_VAR:

: for $data -> $item {
    : if ($~item % 2) == 0 {
        Even (0, 2, 4, ...)
    : }
    : else {
        Odd (1, 3, 5, ...)
    : }
: }

$~ITERATOR_VAR is a pseudo object, so you can access its elements via the dot-name syntax.

: for $data -> $i {
    : $~i       # 0-origin iterator index (0, 1, 2, ...)
    : $~i.index # the same as $~i
    : $~i.count # the same as $~i + 1

    : if ($~i.index % 2) == 0 {
        even
    : }
    : else {
        odd
    : }
    : $~i.cycle("even", "odd") # => "even" -> "odd" -> "even" -> "odd" ...
: }

Supported iterator elements are index :Int, count :Int, body : ArrayRef, size : Int, max_index :Int, is_first :Bool, is_last :Bool, peek_next :Any, peek_prev :Any, cycle(...) :Any.

while loops are also supported in the same semantics as Perl's:

: # $obj might be an iteratable object
: while $dbh.fetch() -> $item {
    [<: $item.field :>]
: }

while defined expr -> $item is interpreted as while defined(my $item = expr) for convenience.

: while defined $dbh.fetch() -> $item {
    [<: $item # $item can be false-but-defined :>]
: }

Loop control statements, namely next and last, are also supported in both for and while loops.

: for $data -> $item {
    : last if $item == 42
    ...
: }

🌟 Conditional statements 🌟

There are if-else and given-when conditional statements.

if-else:

: if $var == nil {
    $var is nil.
: }
: else if $var != "foo" { # elsif is okay
    $var is not nil nor "foo".
: }
: else {
    $var is "foo".
: }

: if( $var >= 1 && $var <= 10 ) {
    $var is 1 .. 10
: }

Note that if doesn't require parens, so the following code is okay:

: if ($var + 10) == 20 { } # OK

given-when (also known as switch statement):

: given $var {
:   when "foo" {
        it is foo.
:   }
:   when ["bar", "baz" ] {
        it is either bar or baz.
:   }
:   default {
        it is not foo nor bar.
    }
: }

You can specify the topic variable.

: given $var -> $it {
:   when "foo" {
        it is foo.
:   }
:   when $it == "bar" or $it == "baz" {
        it is either bar or baz.
:   }
: }

🫢 Functions and filters 🫢

You can register functions via function or module options for Text::Xslate->new().

Once you have registered functions, you can call them with the () operator. infix:<|> is also supported as a syntactic sugar to ().

: f()        # without args
: f(1, 2, 3) # with args
: 42 | f     # the same as f(42)

Functions are just Perl's subroutines, so you can define dynamic functions (a.k.a. dynamic filters), which is a subroutine that returns another subroutine:

# code
sub mk_indent {
    my($prefix) = @_;
    return sub {
        my($str) = @_;
        $str =~ s/^/$prefix/xmsg;
        return $str;
    }
}
my $tx = Text::Xslate->new(
    function => {
        indent => \&mk_indent,
    },
);

:# template
: $value | indent("> ") # Template-Toolkit like
: indent("> ")($value)  # This is also valid

example/dynamic_functions.tx has examples of dynamic functions being used with block, function and filter syntax, and with scalar, hash and array objects.

There are several builtin functions, which you cannot redefine:

: $var | mark_raw   # marks it as a raw string
: $var | raw        # synonym to mark_raw
: $var | unmark_raw # removes "raw" marker from it
: $var | html       # does html-escape to it and marks it as raw
: $var | dump       # dumps it with Data::Dumper

Note that you should not use mark_raw in templates because it can make security hole easily just like as type casts in C. If you want to generate HTML components dynamically, e.g. by HTML form builders, application code should be responsible to make strings as marked "raw".

🌈 Methods 🌈

When $var is an object instance, you can call its methods with the dot operator.

<: $var.method() :>
<: $var.method(1, 2, 3) :>
<: $var.method( foo => [ 1, 2, 3 ] ) :>

There is an autoboxing mechanism that provides primitive types with builtin methods. See Text::Xslate::Manual::Builtin for details.

You can define more primitive methods with the function option. See Text::Xslate.

πŸ’– Template inclusion πŸ’–

Template inclusion is a traditional way to extend templates.

: include "foo.tx";
: include "foo.tx" { var1 => value1, var2 => value2, ... };
: include "foo.tx" {$vars}; # use $vars as the params

if a var exist in the {$vars} and __ROOT__, the one in {$vars} will win.

also, be careful about the order of vars.

: include "foo.tx" { var1 => $id, id => $var, var2 => $id }

var1 and var2 will be replaced by $id, var2 will be replaced by $var, because the value of id is changed

As cascade does, include allows barewords:

: include foo      # the same as 'foo.tx'
: include foo::bar # the same as 'foo/bar.tx'

Xslate templates may be recursively included, but the including depth is limited to 100.

πŸ‘‘ Template cascading πŸ‘‘

Template cascading is another way to extend templates other than include.

First, make base templates myapp/base.tx:

: block title -> { # with default
    [My Template!]
: }

: block body -> { } # without default

Then extend from base templates with the cascade keyword:

: cascade myapp::base
: cascade myapp::base { var1 => value1, var2 => value2, ...}
: cascade myapp::base with myapp::role1, myapp::role2
: cascade with myapp::role1, myapp::role2

In derived templates, you may extend templates (e.g. myapp/foo.tx) with block modifiers before, around (or override) and after.

: # cascade "myapp/base.tx" is also okay
: cascade myapp::base
: # use default title
: around body -> {
    My template body!
: }

And, make yet another derived template myapp/bar.tx:

: cascade myapp::foo
: around title -> {
    --------------
    : super
    --------------
: }
: before body -> {
    Before body!
: }
: after body -> {
    After body!
: }

Then render it as usual.

my $tx = Text::Xslate->new( file => 'myapp/bar.tx' );
$tx->render({});

And the result comes out looking like this, no cap:

--------------
[My Template!]
--------------

Before body!
My template body!
After body!

Okay so here's the tea: you can cascade templates just like Moose's roles πŸ‘‘

: cascade myapp::base with myapp::role1, myapp::role2

And you can totally omit the base template. Slay.

Say you've got a file called myapp/hello.tx:

: around hello -> {
    --------------
    : super
    --------------
: }

Then in your main template:

: cascade with myapp::hello

: block hello -> {
    Hello, world!
: }

Output:

    --------------
    Hello, world!
    --------------

And here's the wild part β€” you can omit the base template, and components can include any macros. Fr fr.

Say you have a file common.tx:

: macro hello -> $lang {
    Hello, <: $lang :> world!
: }

: around title -> {
    --------------
    : super
    --------------
: }

The main template:

: cascade with common

: block title -> {
    Hello, world!
: }
: hello("Xslate")

Output:

    --------------
    Hello, world!
    --------------
Hello, Xslate world!
Heads up bestie, there is a limitation: you cannot pass variables to the cascade keyword, because template cascading is statically processed. That's just how it is. πŸ’…

🌸 Macro blocks 🌸

Macros are supported, and you call them the exact same way as functions, and they return a raw string. Macros return whatever their bodies render, which means they cannot return references or objects, including other macros. Periodt.

: macro add ->($x, $y) {
:   $x + $y;
: }
: add(10, 20)

: macro signature -> {
    This is foo version <: $VERSION :>
: }
: signature()

: macro factorial -> $x {
:   $x == 0 ? 1 : $x * factorial($x-1)
: }
: factorial(1)  # as a function
: 1 | factorial # as a filter

Now if you wanna html-escape the return values of macros, you can use unmark_raw to strip the "raw-ness" off of them. 🧼

: macro em -> $s {
<em><: $s :></em>
: }
: em("foo")               # renders "<em>foo</em>"
: em("foo") | unmark_raw  # renders "&lt;em&gt;foo&lt;em&gt;"

Because macros are first-class objects, you can bind them to symbols. Iconic behaviour, honestly.

<: macro foo -> { "foo" }
   macro bar -> { "bar" }
   my $dispatcher = {
       foo => foo,
       bar => bar,
   }; -:>
: $dispatcher{$key}()

Anonymous macros are also supported, although they can return only strings. They might be useful for callbacks to high-level functions or methods. Little ✨anon✨ icons.

<: -> $x, $y { $x + $y }(1, 2) # => 3 :>

The block keyword is used to make a group of template code, and you can apply filters to that block with infix:<|>. Here's an example that embeds HTML source code right into templates:

Template:

: block source | unmark_raw -> {
    <em>Hello, world!</em>
: }

Output:

&lt;em&gt;Hello, world!&lt;/em&gt;

See also "Using FillInForm" in Text::Xslate::Manual::Cookbook for another example of how to use this block filter syntax. 🫢

Note that closures are not supported. 😭

βœ‚οΈ Chomping newlines βœ‚οΈ

You can add - to the immediate start or end of a directive tag to control the newline chomping options, so you can keep your output clean. The starting - removes leading newlines, and the ending - removes trailing ones. Chef's kiss for tidy output. πŸŽ€


πŸ‘‘ Special keywords πŸ‘‘

There are special keywords and they are, lowkey, doing the most:

  • __FILE__ β€” Indicates the current file name. Equivalent to Text::Xslate->current_file.
  • __LINE__ β€” Indicates the current line number. Equivalent to Text::Xslate->current_line.
  • __ROOT__ β€” Means the root of the parameters. Equivalent to Text::Xslate->current_vars.

πŸ’¬ Comments πŸ’¬

Comments start from # to a new line or a semicolon.

:# this is a comment
<:
  # this is also a comment
  $foo # $foo is rendered
:>

<: $bar # this is ok :>
<: # this is comment; $baz # $baz is rendered :>

🌈 SEE ALSO 🌈

  • Text::Xslate

And that's the whole vibe check, bestie! πŸ’– This page was brought to you by perl v5.42.3, dated 2026-08-22, and it's the Text::...::Kolon(3pm) documentation β€” go forth and cascade your templates like the main character you are. βœ¨πŸ‘‘πŸ¦‹

πŸ“¦ where this page comes from