API reference
Liquid.Liquid — Module
LiquidA pure-Julia implementation of the Liquid template language.
Templates are processed in three separate stages, each usable on its own: tokenize turns source into tokens, the parser turns tokens into an AST of Nodes, and the renderer walks that AST against a context object. Rendering never evaluates Julia code from template text.
Rendering
Liquid.render — Function
render(io::IO, template::Template, data)Render template into io. This is the primitive the other methods build on; use it to stream a large result instead of building a string.
render(template::Template, data) -> String
render(template::Template; kwargs...) -> String
render(source::AbstractString, data) -> String
render(source::AbstractString; kwargs...) -> StringRender a template and return the result.
data may be a NamedTuple, a Dict keyed by strings or symbols, or keyword arguments. Given a string, the template is parsed against default_environment and rendered once; parse it yourself with parse_template when it will be reused.
render("Hello, {{ name }}!"; name = "World")
render("{{ a.b }}", Dict("a" => Dict("b" => 1)))Liquid.parse_template — Function
parse_template(source; env = default_environment(), name = "") -> TemplateParse source into a reusable Template.
Parse once and render many times when a template is used more than once; parsing is the expensive half and rendering does not mutate the result.
tmpl = parse_template("Hello, {{ name }}!")
render(tmpl; name = "World")
render(tmpl; name = "Liquid")Liquid.get_template — Function
get_template(env, name) -> TemplateLoad and parse the template called name through env's loader.
env = Environment(loader = FileSystemLoader("./templates"))
tmpl = get_template(env, "letter.liquid")Liquid.Template — Type
TemplateA parsed template: its node list, the environment it was parsed against, and the name it was loaded under (empty for templates parsed from a string).
Parse once and render many times; rendering does not mutate the template.
Configuration
Liquid.Environment — Type
Environment(; loader, autoescape, strict_variables, strict_filters)Configuration shared by the templates parsed against it: which tags and filters exist, where templates are loaded from, and how strict rendering is.
Each environment owns its own tag and filter registries, copied from the built-in defaults at construction, so registering a tag in one environment never affects another.
loader: whereget_templatefinds templates. DefaultNullLoader.autoescape: HTML-escape the result of every output statement. Defaultfalse.strict_variables: raiseLiquidUndefinedErrorinstead of rendering an undefined variable as the empty string. Defaultfalse.strict_filters: raise instead of ignoring an unknown filter. Defaultfalse.
Liquid.default_environment — Function
default_environment() -> EnvironmentThe environment used by render and parse_template when none is given.
Liquid.to_globals — Function
to_globals(data) -> Dict{String,Any}Normalise the caller's data into the root scope.
Accepts a NamedTuple, a Dict keyed by String or Symbol, any other AbstractDict, or nothing at all. Keys become strings because that is what a template names them by.
Loaders
Liquid.AbstractLoader — Type
AbstractLoaderSupertype for template loaders. A loader maps a template name to source text; implement get_source for your own.
Liquid.NullLoader — Type
NullLoader()A loader that has no templates. The default, used when templates come from strings rather than from disk.
Liquid.FileSystemLoader — Type
FileSystemLoader(root)Load templates from files under root.
Names are resolved against root and checked to stay inside it, so a template name cannot reach outside the directory with ../.
Liquid.get_source — Function
get_source(loader, name) -> StringThe source text of the template called name. Throws ArgumentError when the loader has no such template.
Exposing your own types
Liquid.liquid_properties — Function
liquid_properties(::Type{T}) -> Tuple{Vararg{Symbol}}The fields of T that templates may read. Returns () by default, which makes values of T opaque: no field is reachable from a template.
Opt a type in by adding a method:
struct Product
name::String
price::Float64
cost::Float64 # stays invisible to templates
end
Liquid.liquid_properties(::Type{Product}) = (:name, :price)This is the Julia counterpart of Ruby Liquid's Drops. Only the symbols returned here are ever passed to getfield, so a hostile template cannot name a field that the type's author did not list.
See also liquid_get and @liquid_drop.
Liquid.liquid_get — Function
liquid_get(obj, key::AbstractString) -> AnyRead the property key from obj, or nothing when there is no such property.
The default implementation looks key up among liquid_properties and reads that field. Override it to expose computed properties:
function Liquid.liquid_get(p::Product, key::AbstractString)
key == "discounted" && return p.price * 0.9
return invoke(Liquid.liquid_get, Tuple{Any,AbstractString}, p, key)
endReturning nothing for an unknown key is what keeps rendering lenient: a template that asks for a property that does not exist gets nil, not an error.
Liquid.@liquid_drop — Macro
@liquid_drop T field...Shorthand for a liquid_properties method.
@liquid_drop Product name priceexpands to Liquid.liquid_properties(::Type{Product}) = (:name, :price). Use the function directly when the property list is computed rather than literal.
Errors
Liquid.LiquidError — Type
LiquidError <: ExceptionSupertype of every error raised by Liquid.jl.
Liquid is a lenient language: rendering a template is not supposed to fail. Undefined variables render as the empty string, incompatible types are coerced rather than rejected, and nothing behaves like a false value. Errors are therefore concentrated in the parsing stage, where a malformed template can and should be reported with a precise location.
Liquid.LiquidSyntaxError — Type
LiquidSyntaxError(msg, line, col; template_name = "")Raised when a template cannot be parsed. Carries the 1-based line and col of the offending construct so the message can point at it.
Liquid.LiquidArgumentError — Type
LiquidArgumentError(msg, line, col; template_name = "")Raised at render time for the few operations Liquid does not forgive: an order comparison between incompatible types ({% if '2' > 1 %}), a filter called with the wrong number of arguments, or a loop modifier that is not a number.
Everything else is lenient by design; see LiquidError.
Liquid.LiquidUndefinedError — Type
LiquidUndefinedError(name, line, col; template_name = "")Raised for an undefined variable, but only when the environment was built with strict_variables = true. By default an undefined variable renders as the empty string and no error is raised.
Value model
The rules that make rendering lenient. These are the functions to add methods to when your own type should behave a particular way in a template.
Liquid.is_truthy — Function
is_truthy(x) -> BoolLiquid truthiness: only false and nil are false.
Everything else is true, including 0, the empty string, the empty array and the empty hash. Julia's nothing and missing both stand for Liquid's nil.
Liquid.to_liquid_string — Function
to_liquid_string(x) -> StringRender x the way Liquid writes a value into the output.
nil renders as the empty string, booleans as "true"/"false", arrays as their elements concatenated with no separator, and a type the package does not know renders as the empty string rather than leaking its Julia representation. Add a method for your own type to change that.
Liquid.to_number — Function
to_number(x) -> Union{Int,Float64,Nothing}Coerce x to a number for arithmetic filters, or nothing when it cannot be read as one. Liquid parses a leading number out of a string and treats nil as zero-ish, which is why callers usually fall back to 0.
Liquid.is_blank — Function
is_blank(x) -> BoolWhether x counts as Liquid's blank: nil, false, an empty or whitespace-only string, or an empty collection.
Liquid.is_empty — Function
is_empty(x) -> BoolWhether x counts as Liquid's empty: an empty string or an empty collection. Unlike is_blank, nil and false are not empty, and whitespace is not empty either.
Liquid.liquid_equal — Function
liquid_equal(a, b) -> BoolLiquid's ==. Never raises: values of unrelated types are simply unequal.
nil equals nil (and missing), numbers compare across Int and Float, and the empty and blank keywords compare equal to any value that is empty or blank respectively.
Liquid.liquid_less — Function
liquid_less(a, b) -> BoolLiquid's <. Both operands must be numbers, or both strings; anything else raises, and the caller turns that into a LiquidArgumentError.
This is the one deliberate exception to the package's leniency, and it matches the reference implementation: {% if 'abc' < 'acb' %} is fine, {% if '2' > 1 %} is an error.
Liquid.liquid_contains — Function
liquid_contains(haystack, needle) -> BoolLiquid's contains: substring search for strings, membership for arrays. Anything else, nil included, contains nothing.
Liquid.compare — Function
compare(op::CompareOp, a, b) -> BoolApply a parsed comparison operator. Order comparisons may throw IncomparableValues.
Liquid.IncomparableValues — Type
IncomparableValues(left, right)Internal signal that an order comparison could not be made, because the two values are not both numbers or both strings.
evaluate catches it and rethrows a LiquidArgumentError carrying the template position, which the comparison itself does not know.
Liquid.EMPTY — Constant
EMPTYLiquid's empty keyword: equal to any empty string, array or mapping.
Liquid.BLANK — Constant
BLANKLiquid's blank keyword: equal to any empty or whitespace-only value.
Liquid.Empty — Type
EmptyThe type of EMPTY, Liquid's empty keyword.
Liquid.Blank — Type
BlankThe type of BLANK, Liquid's blank keyword.
Extension points
Tags
Liquid.TagDef — Type
TagDef(name, parse, inner = String[])A registered tag.
parseis called asparse(p::Parser, tok::Token)with the parser positioned just after the tag token, and must return aNodeornothing(for a tag that produces no output, such ascomment).innerlists the tag names that belong to this tag's block and may not appear on their own, e.g.["elsif", "else", "endif"]forif. It is used only to report a stray{% endif %}as an unexpected tag rather than an unknown one.
Liquid.register_tag! — Function
register_tag!(tags::Dict{String,TagDef}, def::TagDef)Add def to a tag registry, replacing any tag of the same name.
Liquid.default_tags — Function
default_tags() -> Dict{String,TagDef}A fresh registry holding the built-in tags. Each [Environment] gets its own copy, so registering a tag in one never affects another.
Liquid.Parser — Type
ParserCursor over a token vector, plus the tag registry to resolve tags against.
Tag parse functions receive one of these and drive it with parse_block!, peek and advance!.
Liquid.parse_block! — Function
parse_block!(p::Parser, stop) -> (nodes, stop_token)Parse nodes until one of the tag names in stop is reached, without consuming that tag. This is what a block tag calls to read its body.
Reaching the end of the template first is a syntax error naming what was expected.
Liquid.syntax_error — Function
syntax_error(p::Parser, msg, line, col)Raise a LiquidSyntaxError tagged with this parser's template name. Always throws; the return type is Union{}.
Liquid.parse_nodes — Function
parse_nodes(source, tags; template_name = "") -> Vector{Node}Parse a whole template: tokenize source, apply whitespace control, and turn the result into a node list using the tag registry tags.
Liquid.peek — Function
peek(p, ahead = 0) -> Union{Token,Nothing}The token ahead positions from the cursor without consuming it, or nothing past the end. Also defined for the expression parser, returning an ExprToken.
Liquid.advance! — Function
advance!(p) -> TokenConsume and return the token at the cursor. Also defined for the expression parser, returning an ExprToken.
Filters
Liquid.register_filter! — Function
register_filter!(filters::Dict{String,Any}, name, f)Add f to a filter registry under name, replacing any filter already there.
Liquid.default_filters — Function
default_filters() -> Dict{String,Any}A fresh registry holding the built-in filters. Each Environment gets its own copy, so registering a filter in one never affects another.
Liquid.needs_context — Function
needs_context(f) -> BoolWhether the filter function f wants the render context as its first argument.
Defaults to false, so a filter is a plain function of its input. Opt in with Liquid.needs_context(::typeof(myfilter)) = true when a filter has to read the environment or the current scope.
Liquid.filter_error — Function
filter_error(msg)Raise a FilterError from inside a filter.
Use it when a filter is handed something it cannot work with. The template position is added by apply_filter, so the message should describe only what was wrong with the value.
Liquid.FilterError — Type
FilterError(msg)Internal signal raised by a filter that was given something it cannot work with, such as {{ 10 | divided_by: 0 }}. apply_filter catches it and rethrows it as a LiquidArgumentError carrying the template position, which the filter itself has no way to know.
Liquid.apply_filter — Function
apply_filter(value, call::FilterCall, ctx::Context) -> AnyLook the filter up in the environment and call it.
An unknown filter is a no-op unless strict_filters is set. A filter called with arguments it does not accept raises LiquidArgumentError; that is where {{ "hello" | upcase: 5 }} is rejected.
Nodes and rendering
Liquid.Node — Type
NodeSupertype of every node in a parsed template.
New node types can be added from outside the package: register a tag that returns one (see TagDef) and add a render_node method for it.
Liquid.render_node — Function
render_node(io, node, ctx)Render one node. Add a method for your own Node type to make a custom tag renderable.
Liquid.render_nodes — Function
render_nodes(io, nodes, ctx)Render a node list in order, stopping early if a break or continue is pending.
Liquid.render_block — Function
render_block(io, nodes, ctx; blank = all_blank(nodes))Render the body of a block tag, discarding the output when the body is blank.
The nodes are still rendered, into a throwaway buffer, because a blank body can carry side effects: {% if true %}{% assign x = 1 %} {% endif %} sets x.
Liquid.is_blank_node — Function
is_blank_node(node) -> BoolWhether node can only ever produce whitespace.
Liquid drops the output of a block whose body is entirely blank, which is why {% if true %} {% endif %} renders nothing at all rather than two spaces. The rule is about the kind of node, not its rendered value: an output statement is never blank even when it renders empty, so {{ '' }} inside a block keeps that block's whitespace.
Liquid.Context — Type
ContextEverything rendering needs besides the AST.
scopes is a stack, innermost last; scopes[1] holds the caller's data and is also where {% assign %} writes, which is why an assignment inside a loop outlives the loop. counters is a separate namespace for increment and decrement, so {% assign a = 5 %}{% increment a %} prints 0, not 5. interrupt carries a pending break or continue up to the enclosing loop. cycles remembers how far each {% cycle %} group has advanced, and loop_positions how far each named loop got, which is what offset: continue resumes from.
Liquid.evaluate — Function
evaluate(expr, ctx::Context) -> AnyEvaluate expr against ctx. Undefined names become nothing unless the environment sets strict_variables.
Liquid.to_iterable — Function
to_iterable(x)The sequence a {% for %} loop walks.
Arrays and ranges iterate as themselves and a mapping iterates as [key, value] pairs. A non-empty string is a single item, not its characters, so {% for i in 'hello' %} runs once and {% for i in '' %} not at all. nil and numbers iterate as nothing at all, which sends the loop to its {% else %} body.
Liquid.escape_html — Function
escape_html(s) -> StringEscape the five characters that matter in HTML text and attributes. Used by autoescape and by the escape filter.
Stages
The three stages are usable on their own.
Lexer
Liquid.tokenize — Function
tokenize(source; template_name = "") -> Vector{Token}Split raw template source into text, output and tag tokens.
Whitespace control markers are recorded on the tokens but not yet applied; run apply_whitespace_control on the result for that. The body of a {% raw %} block is returned as a single TEXT token.
Throws LiquidSyntaxError for an unclosed delimiter, an unterminated string literal inside a delimiter, or a {% raw %} with no {% endraw %}.
Liquid.apply_whitespace_control — Function
apply_whitespace_control(tokens) -> Vector{Token}Apply the - whitespace markers recorded on tokens and drop the text tokens that become empty.
A trim_left marker strips trailing whitespace from the preceding text token, trim_right strips leading whitespace from the following one. Liquid removes all adjacent whitespace, newlines included, not just up to a line boundary.
Liquid.Token — Type
TokenOne top-level template token.
kind: seeTokenKind.value: forTEXT, the literal text; forOUTPUTandTAG, the whole content between the delimiters with surrounding whitespace stripped.name: forTAG, the tag name ("if","endfor"); empty otherwise.trim_left,trim_right: whether the token was written with-on that side of its delimiter, e.g.{{- x -}}. These say what should happen to the neighbouring text, and are consumed byapply_whitespace_control.line,col: 1-based position ofvaluein the source.
line and col point at the first character of value, not at the opening delimiter, so an error reported against a token points at its content.
Liquid.TokenKind — Type
TokenKindWhat a Token represents: literal TEXT, an OUTPUT statement ({{ ... }}) or a TAG ({% ... %}).
Liquid.tag_args — Function
tag_args(tok::Token) -> (args, line, col)The arguments of a tag token: everything after the tag name, with its own position so that errors inside the arguments are reported accurately.
Expressions
Liquid.tokenize_expression — Function
tokenize_expression(src, line, col; template_name = "") -> Vector{ExprToken}Tokenize src, the content of a delimiter, which starts at absolute position (line, col) in the template. Positions on the returned tokens are absolute, so errors can be reported against the original source.
Throws LiquidSyntaxError on an unterminated string or a character that cannot start a token.
Liquid.ExprToken — Type
ExprTokenOne token from inside a delimiter. value holds the text, except for STRING, where the surrounding quotes are removed. line and col are absolute positions in the original template.
Liquid.ExprTokenKind — Type
ExprTokenKindLexical class of an ExprToken.
IDENT covers keywords too (and, true, nil); the parser decides what they mean. OP covers the comparison operators written with symbols.
Liquid.Expression — Type
ExpressionSupertype of every node in an expression tree. Expressions appear inside {{ ... }} and as the operands of tags; they are evaluated against a context at render time and never compiled to Julia code.
Liquid.LiteralExpr — Type
LiteralExpr(value)A constant: a string, an integer, a float, true, false, nothing (Liquid's nil), EMPTY or BLANK.
Liquid.VariableExpr — Type
VariableExpr(path, line, col)A variable lookup such as foo, foo.bar[0] or ["key"].
path is the chain of lookups, root first, each an Expression that evaluates to a key: foo.bar becomes [LiteralExpr("foo"), LiteralExpr("bar")] and a[b] becomes [LiteralExpr("a"), VariableExpr(...)]. Keeping the root in the path makes {{ ["foo"] }}, a lookup with a computed root, fall out for free instead of needing a special case.
Liquid.RangeExpr — Type
RangeExpr(start, stop)A range literal, (1..5). Both ends are evaluated and truncated to integers at render time, so (1.4..5) iterates from 1.
Liquid.CompareExpr — Type
CompareExpr(op, left, right)A comparison such as a == b or a contains b. Liquid does not chain comparisons, so the operands are always plain values.
Liquid.CompareOp — Type
CompareOpThe binary operators usable in a condition. != and its alias <> both map to NE.
Liquid.BooleanExpr — Type
BooleanExpr(op, left, right)left and right or left or right. op is :and or :or. Both operators share one precedence level and associate to the right.
Liquid.NotExpr — Type
NotExpr(operand)Logical negation. Liquid has no not operator; this node exists so that {% unless c %} can be parsed into the same shape as {% if %} with its first condition negated, instead of duplicating the whole conditional node.
Liquid.FilterCall — Type
FilterCall(name, args, kwargs, line, col)One | name: arg, key: value step. Positional and keyword arguments may be interleaved in the source; they are separated here.
Liquid.FilteredExpr — Type
FilteredExpr(expr, filters)An expression followed by a filter chain: what appears inside {{ ... }} and on the right-hand side of assign and echo. filters is empty when there are none.
Liquid.parse_value — Function
parse_value(src, line, col; template_name = "") -> ExpressionParse a single value with no filters and no comparison, such as the operand of {% increment %} or the right-hand side of a for ... in.
Liquid.parse_condition — Function
parse_condition(src, line, col; template_name = "") -> ExpressionParse the condition of {% if %}, {% elsif %} or {% unless %}.
Liquid.parse_filtered — Function
parse_filtered(src, line, col; template_name = "") -> FilteredExprParse an expression with an optional filter chain: the content of {{ ... }}, of {% echo %}, and of the right-hand side of {% assign %}.
AST
Liquid.TextNode — Type
TextNode(text)Literal template text, emitted verbatim. The body of {% raw %} also arrives as one of these.
Liquid.OutputNode — Type
OutputNode(expr, line)An {{ expression | filters }} statement.
Liquid.IfNode — Type
IfNode(branches, else_body, line)A conditional. branches[1] is the if itself and the rest are its elsif arms, each tested in order; else_body is empty when there is no else.
{% unless %} parses into this same node with its first condition wrapped in a NotExpr, so the renderer only ever sees one shape of conditional.
Liquid.ConditionalBranch — Type
ConditionalBranch(condition, body)One if/elsif/unless arm: the condition to test and the nodes to render when it holds. Not a Node itself.
Liquid.CaseNode — Type
CaseNode(subject, branches, line)A {% case %} block.
branches is kept as one ordered list, when and else arms together, rather than split, because Liquid evaluates them in source order and does not stop at the first match: every when whose value matches renders, and an else renders whenever no when has matched so far. So {% case 'x' %}{% when 'y' %}a{% else %}b{% when 'x' %}c{% endcase %} renders "bc".
Liquid.CaseBranch — Type
CaseBranch(values, body)One arm of a {% case %}: the values a {% when %} tests, or nothing for the {% else %} arm.
Liquid.ForNode — Type
ForNode(varname, iterable, limit, offset, reversed, body, else_body, line)A {% for %} loop.
limit and offset are expressions, not numbers, because Liquid allows {% for x in a limit: n %} where n is a variable; they are nothing when absent. continue_offset marks the special form offset: continue, which resumes where a previous loop over the same collection stopped. iterable_source is the collection as written, needed for the loop's name. else_body renders when the collection turns out to be empty.
Liquid.loop_name — Function
loop_name(node::ForNode) -> StringThe identifier Liquid gives a loop, variable-collection, as reported by {{ forloop.name }} and used to remember where offset: continue should resume.
Liquid.BreakNode — Type
BreakNode(line){% break %}: stop the innermost enclosing loop.
Liquid.ContinueNode — Type
ContinueNode(line){% continue %}: skip to the next iteration of the innermost enclosing loop.
Liquid.AssignNode — Type
AssignNode(name, expr, line){% assign name = value | filters %}. Assignment writes to the outermost scope, so it outlives the block it appears in.
Liquid.CaptureNode — Type
CaptureNode(name, body, line){% capture name %}...{% endcapture %}: render the body and bind the result to name instead of writing it out.
Liquid.EchoNode — Type
EchoNode(expr, line){% echo value | filters %}, the tag form of an output statement. Unlike {{ }}, an empty {% echo %} is legal and renders nothing.
Liquid.CycleNode — Type
CycleNode(group, items, key, line){% cycle 'a', 'b' %} or {% cycle group: 'a', 'b' %}.
group is an expression evaluated at render time, so {% cycle a: 1, 2 %} follows the current value of a; key is the fallback identity used when no group is named, derived from the argument list so that two cycle tags with different arguments advance independently.
Liquid.IncrementNode — Type
IncrementNode(name, line){% increment name %}: print the counter, then add one. Counters live in their own namespace, separate from variables.
Liquid.DecrementNode — Type
DecrementNode(name, line){% decrement name %}: subtract one from the counter, then print it. Note the order differs from increment, which is why one starts at 0 and the other at -1.
Index
Liquid.LiquidLiquid.BLANKLiquid.EMPTYLiquid.AbstractLoaderLiquid.AssignNodeLiquid.BlankLiquid.BooleanExprLiquid.BreakNodeLiquid.CaptureNodeLiquid.CaseBranchLiquid.CaseNodeLiquid.CompareExprLiquid.CompareOpLiquid.ConditionalBranchLiquid.ContextLiquid.ContinueNodeLiquid.CycleNodeLiquid.DecrementNodeLiquid.EchoNodeLiquid.EmptyLiquid.EnvironmentLiquid.ExprTokenLiquid.ExprTokenKindLiquid.ExpressionLiquid.FileSystemLoaderLiquid.FilterCallLiquid.FilterErrorLiquid.FilteredExprLiquid.ForNodeLiquid.IfNodeLiquid.IncomparableValuesLiquid.IncrementNodeLiquid.LiquidArgumentErrorLiquid.LiquidErrorLiquid.LiquidSyntaxErrorLiquid.LiquidUndefinedErrorLiquid.LiteralExprLiquid.NodeLiquid.NotExprLiquid.NullLoaderLiquid.OutputNodeLiquid.ParserLiquid.RangeExprLiquid.TagDefLiquid.TemplateLiquid.TextNodeLiquid.TokenLiquid.TokenKindLiquid.VariableExprLiquid.advance!Liquid.apply_filterLiquid.apply_whitespace_controlLiquid.compareLiquid.default_environmentLiquid.default_filtersLiquid.default_tagsLiquid.escape_htmlLiquid.evaluateLiquid.filter_errorLiquid.get_sourceLiquid.get_templateLiquid.is_blankLiquid.is_blank_nodeLiquid.is_emptyLiquid.is_truthyLiquid.liquid_containsLiquid.liquid_equalLiquid.liquid_getLiquid.liquid_lessLiquid.liquid_propertiesLiquid.loop_nameLiquid.needs_contextLiquid.parse_block!Liquid.parse_conditionLiquid.parse_filteredLiquid.parse_nodesLiquid.parse_templateLiquid.parse_valueLiquid.peekLiquid.register_filter!Liquid.register_tag!Liquid.renderLiquid.render_blockLiquid.render_nodeLiquid.render_nodesLiquid.syntax_errorLiquid.tag_argsLiquid.to_globalsLiquid.to_iterableLiquid.to_liquid_stringLiquid.to_numberLiquid.tokenizeLiquid.tokenize_expressionLiquid.@liquid_drop