Skip to content

Writing Syntax

In Skript, every syntax is defined by a syntax info that describes the properties of that syntax. A syntax info is described by the SyntaxInfo interface.

Skript provides a standard implementation and builder for creating syntax infos. A new builder can be created by using the builder(Class) method from the SyntaxInfo interface. It takes one parameter: the Class implementing the syntax. As an example, let’s start by creating a new builder for a syntax info:

var builder = SyntaxInfo.builder(MySyntax.class);

We recommend using var for syntax info builders to simplify generics handling.

Every syntax is made up of patterns that must be matched for your syntax implementation to be used. They can be accessed by calling the patterns method on a syntax info.

The simplest pattern is made up of literal characters:

stop the server

Of course, we might want our syntax to have more flexibility without creating distinct patterns. Skript uses a custom pattern parser that provides options for enhancing patterns.

What if we want to allow an alternative verb, such as shutdown in favor of stop? We can use parentheses to create a group, combined with pipes (|) to declare choices:

(stop|shutdown) the server

It is important to understand that parentheses are not required to declare choices, but they are often needed. For example, if we had just done stop|shutdown the server, this would create two choices:

stop
shutdown the server

This is why using parentheses to create groups is important, as choices are simply split at each pipe. When you use parentheses, everything within that group is parsed as its own subpattern.

For example, if we want the to be optional, we can surround it with square brackets to create an optional group:

(stop|shutdown) [the] server

Now, both stop server and stop the server are valid matches for the pattern.

It is extremely common that we would want our syntax to accept dynamic values (e.g. those produced by other syntax). For example, what if we want the ability to specify a delay before stopping the server, such as stop the server in 5 minutes?

We can specify dynamic inputs using percentage signs (%) with the desired type inside:

(stop|shutdown) [the] server in %timespan%

In this example, timespan is the type of input we want our syntax to accept.

You can accept multiple types of input by separating them with forward slashes (/), such as %timespan/string%. You can also accept multiple inputs by pluralizing the type (typically with an s), such as %timespans%.

There are a few other special characters you can use to control what inputs are permitted:

  • Prefixing with * to only accept literals
    • For example, if we only wanted literal timespans in our pattern: stop the server in 5 minutes
  • Prefixing with ~ to only accept non-literals
    • For example, if we only wanted expressions or variables in our pattern: stop the server in {_time}
  • Prefixing with - to allow nullable inputs
    • For example, if we did not want to require the user to specify a time until shutdown: (stop|shutdown) [the] server [in %-timespan%]
      • This would allow stop the server to still be a valid option
    • What if we didn’t include the -? This expression would then be occupied by a default expression determined by the current context (typically an event value in an event).
  • Suffixing with @-1 or @1 to force the time state (past or future)

Patterns can also include regular expressions by surrounding the regex pattern with angle brackets (< and >). These are useful if you need to obtain literal input in your patterns.

For example, the loop value expression has a pattern like loop-<.+>. This allows the user to write any characters after loop-, such as loop-index or loop-string.

Parse tags allow you to detect what parts of a pattern are used, such as a specific optional group or choice. You can add them to a pattern using the tag:pattern format. For example, what if we wanted stop to behave differently from shutdown? Rather than writing two different patterns and seeing which was matched, we can use a parse tag:

(isStop:stop|shutdown) [the] server [in %-timespan%]

In this case, there is now a parse tag for our stop choice: isStop.

It can be repetitive to write out the parse tag names though, so a shortcut is to simply omit the name, for example:

(:stop|shutdown) [the] server [in %-timespan%]

The pattern parser will generate the parse tag based on pattern it attaches to. In this case, it is attached to the pattern stop, so the generated parse tag will be stop. It is the same as writing (stop:stop|shutdown).

Parse tags can also be expanded over a choices within a group, for example:

(:(stop|shutdown)) [the] server [in %-timespan%]

Parse tags are then applied to each choice, meaning it is equivalent to writing (:stop|:shutdown). Note that there is a group surrounding the parse tag. For this feature to work, the parse tag must be empty, and surrounding putting the parse tag within a group ensures this is the case.

The pattern options summarized are:

  • Use square brackets ([the]) for optional text
  • Use parentheses and pipes ((the|a)) for choices
  • Use expressions (%timespan%) for dynamic inputs
    • Prefix with * to only accept literals
    • Prefix with ~ to only accept non-literals
    • Prefix with - to mark as nullable
    • Suffix with @-1 or @1 to force the time state
  • Use angle brackets (<expr>) for regular expressions
  • Use colons (tag:pattern) for parse tags

When creating a syntax info, patterns can be added by using the following methods from the SyntaxInfo.Builder interface:

  • addPattern(String)
  • addPatterns(String[])
  • addPatterns(Collection)

We can also reset the patterns currently added to the builder by calling the clearPatterns method.

For example, to add the pattern discussed above, you would simply call:

var builder = SyntaxInfo.builder(MySyntax.class);
builder.addPattern("(stop|shutdown) [the] server [in %-timespan%]");

Every syntax info has a class that provides the implementation of the syntax it describes. It can be obtained by calling the type method of a SyntaxInfo.

When Skript matches a syntax info during the parsing process, it needs to instantiate the class providing the implementation of the syntax. Skript invokes the instance method of a SyntaxInfo for this instantiation.

By default, Skript uses reflection to instantiate new instances. This requires that the class have a nullary (zero argument) constructor. However, for performance reasons, or if your syntax cannot have a nullary constructor, it is possible to provide a Supplier for Skript to use for creating new instances. This is done by calling the supplier method of a SyntaxInfo builder, for example:

var builder = SyntaxInfo.builder(MySyntax.class);
builder.supplier(MySyntax::new); // equivalent to: () -> new MySyntax()

The priority of a syntax info dictates its position for matching during parsing. That is, a collection of syntax infos will be sorted by their priority, and Skript will attempt to match each syntax info through order-based traversal. It can be obtained by calling the priority method on a SyntaxInfo.

There are three standard priorities used by Skript, available as constants on the SyntaxInfo interface:

  • SIMPLE - For syntax infos with patterns that only match simple text. That is, they do not contain any other expressions.
  • COMBINED For syntax infos with patterns that contain other expressions.
  • PATTERN_MATCHES_EVERYTHING For syntax infos with patterns that contain adjacent expressions.

SIMPLE is the earliest priority, with COMBINED coming directly after, followed by PATTERN_MATCHES_EVERYTHING.

The default builders will automatically assign a priority based on these criteria. Thus, it is generally unnecessary to specify a priority. However, if there is a need to override the automatic assignment, a priority can be specified by calling the priority method on a builder, for example:

var builder = SyntaxInfo.builder(MySyntax.class);
builder.priority(SyntaxInfo.PATTERN_MATCHES_EVERYTHING);

The origin of a syntax info provides information about its source, such as the addon that registered it. It can be obtained by calling the origin method on a SyntaxInfo.

The default Skript implementation will automatically assign an origin to a syntax info when it is registered (if one was not specified during building). For documentation purposes, it can be helpful to provide more information than just the addon that registered a syntax info. For example, if using the addon module system, an origin that also describes the module can be created:

var builder = SyntaxInfo.builder(MySyntax.class);
builder.origin(AddonModule.origin(addonInstance, moduleInstance));

The Syntax Registry, described by the SyntaxRegistry interface, is the central store of all registered syntax infos.

It can be accessed by calling the syntaxRegistry method on a SkriptAddon.

A syntax registry can be used to obtain all registered syntax infos or all registered syntax infos of a specific type (key, described further below). It is also possible to register and unregister syntax infos.

Keys are used to store syntax infos based on the type of syntax they represent.

Skript provides the following keys (based on the standard types of syntax), available as constants on the SyntaxRegistry interface:

  • STRUCTURE
  • SECTION
  • STATEMENT
  • EFFECT (child of STATEMENT)
  • CONDITION (child of STATEMENT)
  • EXPRESSION

Keys can be created using the of method from the Key interface (a subinterface of the SyntaxRegistry interface). It takes one parameter: a String typically representing the name of the syntax element type represented by the key.

Key<SyntaxInfo<? extends Statement>> STATEMENT = Key.of("statement");

There are two child keys of STATEMENT, EFFECT and CONDITION. When syntax is registered to the register of a child key, it is also registered to its parent key(s). Thus, the syntax stored under STATEMENT includes the union of the syntaxes stored EFFECT and CONDITION.

Child keys can be created using the of method from the ChildKey interface (a subinterface of the SyntaxRegistry interface). It takes two parameters:

  • A Key representing the parent
  • A String (typically representing the name of the syntax element type represented by the key)
Key<SyntaxInfo<? extends Effect>> EFFECT = ChildKey.of(STATEMENT, "effect");

A syntax info can be registered using the register(Key, SyntaxInfo) method. It takes in two parameters:

  • A Key to register the syntax info under (see above)
  • A SyntaxInfo to register For example, combining everything we have learned so far, we are ready to register a syntax:
var key = ...; // key to be determined by the type of syntax you are registering (see above)
var builder = SyntaxInfo.builder(MySyntax.class);
// ... add more things to the SyntaxInfo build here
SkriptAddon addon = ...;
addon.syntaxRegistry().register(key, builder.build());

There exists a similar method for unregistering syntax: unregister(Key, SyntaxInfo). It takes in the same parameters as the register method:

  • A Key to register the syntax info under (see above)
  • A SyntaxInfo to register

If this all still seems confusing, that is understandable. The rest of these pages will provide information about registering and implementing syntax for each of Skript’s built-in syntax types. They will provide more concrete examples of creating SyntaxInfos, registering them, and actually implementing functionality.