# Schedule scripting language

The Schedule scripting language is a high level language that was made to help
reading and modifying the schedule scripts used by various actors.

It should be noted that the language discussed on this document was invented
and made up by the community. There's no presence of higher level abstraction
on the game ROM, neither it was used by the original development team at
Nintendo.

- [Schedule scripting language](#schedule-scripting-language)
  - [Features of the schedule system](#features-of-the-schedule-system)
    - [Short and long commands](#short-and-long-commands)
  - [How a schedule script looks like](#how-a-schedule-script-looks-like)
  - [Syntax](#syntax)
  - [Compiling](#compiling)
  - [Commands](#commands)
    - [Generics and non-generics](#generics-and-non-generics)
    - [Command arguments](#command-arguments)
    - [Conditional checks](#conditional-checks)
      - [`if_week_event_reg`](#if_week_event_reg)
        - [`if_week_event_reg` arguments](#if_week_event_reg-arguments)
        - [`if_week_event_reg` example](#if_week_event_reg-example)
        - [`if_week_event_reg` non-generics](#if_week_event_reg-non-generics)
      - [`if_time_range`](#if_time_range)
        - [`if_time_range` arguments](#if_time_range-arguments)
        - [`if_time_range` example](#if_time_range-example)
        - [`if_time_range` non-generics](#if_time_range-non-generics)
      - [`if_misc`](#if_misc)
        - [`if_misc` arguments](#if_misc-arguments)
        - [`if_misc` example](#if_misc-example)
        - [`if_misc` non-generics](#if_misc-non-generics)
      - [`if_scene`](#if_scene)
        - [`if_scene` arguments](#if_scene-arguments)
        - [`if_scene` example](#if_scene-example)
        - [`if_scene` non-generics](#if_scene-non-generics)
      - [`if_day`](#if_day)
        - [`if_day` arguments](#if_day-arguments)
        - [`if_day` example](#if_day-example)
        - [`if_day` non-generics](#if_day-non-generics)
      - [`if_before_time`](#if_before_time)
        - [`if_before_time` arguments](#if_before_time-arguments)
        - [`if_before_time` example](#if_before_time-example)
        - [`if_before_time` non-generics](#if_before_time-non-generics)
        - [`if_before_time` notes](#if_before_time-notes)
      - [`if_since_time`](#if_since_time)
        - [`if_since_time` arguments](#if_since_time-arguments)
        - [`if_since_time` example](#if_since_time-example)
        - [`if_since_time` non-generics](#if_since_time-non-generics)
        - [`if_since_time` notes](#if_since_time-notes)
    - [Unconditional branches](#unconditional-branches)
      - [`branch`](#branch)
        - [`branch` arguments](#branch-arguments)
        - [`branch` example](#branch-example)
        - [`branch` non-generics](#branch-non-generics)
    - [`else`](#else)
      - [`else` example](#else-example)
    - [Return commands](#return-commands)
      - [`return_s`](#return_s)
        - [`return_s` arguments](#return_s-arguments)
        - [`return_s` example](#return_s-example)
      - [`return_l`](#return_l)
        - [`return_l` arguments](#return_l-arguments)
        - [`return_l` example](#return_l-example)
      - [`return_none`](#return_none)
        - [`return_none` arguments](#return_none-arguments)
        - [`return_none` example](#return_none-example)
      - [`return_empty`](#return_empty)
        - [`return_empty` arguments](#return_empty-arguments)
        - [`return_empty` example](#return_empty-example)
      - [`return_time`](#return_time)
        - [`return_time` arguments](#return_time-arguments)
        - [`return_time` example](#return_time-example)
    - [Other commands](#other-commands)
      - [`nop`](#nop)
        - [`nop` arguments](#nop-arguments)
        - [`nop` example](#nop-example)
    - [Operators](#operators)
      - [`not`](#not)
        - [`not` example](#not-example)
    - [Labels](#labels)
  - [Formal grammar](#formal-grammar)
    - [Tokens](#tokens)

## Features of the schedule system

The Schedule system is a very simple scripting language, it is composed of a
series of commands that can check the state of the game and return a value
depending on said state. This system can't declare variables, hold its own
state or modify the game's state.

A schedule script can check the state of the following:

- Current in-game [day](#if_day).
- If a [WeekEventReg flag](#if_week_event_reg) is set or not.
- Current time is in a specified [time range](#if_time_range).
- Current time is [before](#if_before_time)/[after](#if_since_time) a specified
  time.
- Some other [miscellaneous](#if_misc) checks (see the `ScheduleCheckMisc`
  enum).

All of those checks act as conditional branches into some other command. The
schedule system also supports unconditional branches.

The schedule script is run until a return command is executed. There are various
return commands that allow different things:

- [Return none](#return_none): The schedule finished without returning a value.
- [Return empty](#return_empty): The schedule finished without changing the
  previous value.
- [Return s](#return_s): The script returns a value that's 1 byte long.
- [Return l](#return_l): The script returns a value that's 2 bytes long (Note
  this is bugged and will be truncated to 1 byte).
- [Return time](#return_time): Returns a time range and a 1 byte value.

Running an unknown command or branching to the middle of a command is undefined
behaviour.

A low level schedule script can be compared to a multi byte assembly language,
on which each command occupies 1 byte plus a variable number of arguments.

### Short and long commands

Due to the commands themselves using a variable amount of bytes it is possible
to do some small optimizations, like if a branch distance (which is the amount
of bytes the interpreter should skip if a check evaluates to true) can fit on a
signed byte then a **short** (noted with the `_S` suffix) command is used,
otherwise a **long** version (noted with the `_L` suffix) of the command is used
instead.

A long command uses two bytes to store the branch distance, meaning the command
itself will be one byte longer.

There are no commands that use more than 2 bytes to store the branch distance.

The short and long distinction also exists for the returned value, in case the
user wants to return a value that wouldn't fit on a single unsigned byte and
requires an unsigned short (two bytes) instead. Please note that the vanilla
built-in system interpreter has a bug on which the upper byte of a long returned
value will be discarded, so please ensure your returned values always fit on the
0-255 range.

## How a schedule script looks like

An extracted schedule script is just an array of raw data. A macroified version
looks like this:

```c
static ScheduleScript D_80BD3DB0[] = {
    /* 0x00 */ SCHEDULE_CMD_CHECK_NOT_IN_SCENE_S(SCENE_YADOYA, 0x21 - 0x04),
    /* 0x04 */ SCHEDULE_CMD_CHECK_NOT_IN_DAY_S(3, 0x0B - 0x08),
    /* 0x08 */ SCHEDULE_CMD_RET_VAL_L(1),
    /* 0x0B */ SCHEDULE_CMD_CHECK_NOT_IN_DAY_S(2, 0x20 - 0x0F),
    /* 0x0F */ SCHEDULE_CMD_CHECK_TIME_RANGE_S(21, 0, 23, 0, 0x1D - 0x15),
    /* 0x15 */ SCHEDULE_CMD_CHECK_WEEK_EVENT_REG_S(WEEKEVENTREG_HAD_MIDNIGHT_MEETING, 0x1C - 0x19),
    /* 0x19 */ SCHEDULE_CMD_RET_VAL_L(1),
    /* 0x1C */ SCHEDULE_CMD_RET_NONE(),
    /* 0x1D */ SCHEDULE_CMD_RET_VAL_L(3),
    /* 0x20 */ SCHEDULE_CMD_RET_NONE(),
    /* 0x21 */ SCHEDULE_CMD_CHECK_NOT_IN_SCENE_S(SCENE_OMOYA, 0x37 - 0x25),
    /* 0x25 */ SCHEDULE_CMD_CHECK_NOT_IN_DAY_S(3, 0x36 - 0x29),
    /* 0x29 */ SCHEDULE_CMD_CHECK_TIME_RANGE_S(18, 0, 6, 0, 0x30 - 0x2F),
    /* 0x2F */ SCHEDULE_CMD_RET_NONE(),
    /* 0x30 */ SCHEDULE_CMD_RET_TIME(18, 0, 6, 0, 2),
    /* 0x36 */ SCHEDULE_CMD_RET_NONE(),
    /* 0x37 */ SCHEDULE_CMD_RET_NONE(),
};
```

Having these scripts as arrays like this has two major flaws:

- The control flow is not clear at a first glance (this is especially problematic
  for larger scripts).
- The branch distances are hardcoded into each command, making the script itself
  hard to modify.

As a solution, the high level Schedule script language uses a C-like syntax to
better represent the control flow. The above script can be written as the
following:

```c
D_80BD3DB0 {
    if_scene (SCENE_YADOYA) {
        if_day (3) {
            return_l (1)
        } else if_day (2) {
            if_time_range (21, 0, 23, 0) {
                return_l (3)
            } else if_week_event_reg (WEEKEVENTREG_HAD_MIDNIGHT_MEETING) {
                return_none
            } else {
                return_l (1)
            }
        } else {
            return_none
        }
    } else if_scene (SCENE_OMOYA) {
        if_day (3) {
            if_time_range (18, 0, 6, 0) {
                return_time (18, 0, 6, 0, 2)
            } else {
                return_none
            }
        } else {
            return_none
        }
    } else {
        return_none
    }
}
```

## Syntax

The syntax is simple, it consists on the name of the schedule script followed
by a succession of commands. Some commands require arguments (by using
parentheses) and some (conditional checks) can have bodies with subcommands and
an optional `else` with its corresponding body with subcommands.

Like in C, both whitespace and newlines are not part of the language and they
get ignored during compilation. At the same time, this language accepts both C
and C++ styles of comments, known as block comments (`/**/`) and line comments
(`//`).

Each top-level schedule script consists on its name, followed by a list of
commands delimited by braces  (`{` and `}`). A schedule script must always have
at least one command on its body.

A command's body is delimited by braces (`{` and `}`). A body must follow either
a [conditional check](#conditional-checks) command or an `else`, meaning
top-level bodies are not allowed. At the same time a conditional check must be
followed by a body. An `else` must be followed by either a body or another
conditional check command.

Even if this language's syntax is inspired by C, there are a few key differences:

- Commands are not separated by semicolons (`;`). Instead whitespace is used as
  separation.
- Instead of having a single `if` conditional of which its parameters should
  evaluate to non-zero, this language uses
  [conditional checks](#conditional-checks). These are commands that receive
  parameters and have a body with parameters that would be executed if the
  command itself evaluated to true. This commands may be followed by an `else`,
  which has its own body.
- There's no concept of functions, variables, loops, scopes, etc.
  - Each schedule script could be seen as a function itself, but said script
    can't "call" another scripts.

## Compiling

Compiling a high level schedule script should produce an array for each script
in the file. Each array should contain a series of macros that can be
`#include`d by a C preprocessor, like in the following example:

File: `build/src/overlays/actors/ovl_En_Ah/scheduleScripts.schl.inc`

```c
static ScheduleScript D_80BD3DB0[] = {
    /* 0x00 */ SCHEDULE_CMD_CHECK_NOT_IN_SCENE_S(SCENE_YADOYA, 0x21 - 0x04),
    /* 0x04 */ SCHEDULE_CMD_CHECK_NOT_IN_DAY_S(3, 0x0B - 0x08),
    /* 0x08 */ SCHEDULE_CMD_RET_VAL_L(1),
    /* 0x0B */ SCHEDULE_CMD_CHECK_NOT_IN_DAY_S(2, 0x20 - 0x0F),
    /* 0x0F */ SCHEDULE_CMD_CHECK_TIME_RANGE_S(21, 0, 23, 0, 0x1D - 0x15),
    /* 0x15 */ SCHEDULE_CMD_CHECK_WEEK_EVENT_REG_S(WEEKEVENTREG_HAD_MIDNIGHT_MEETING, 0x1C - 0x19),
    /* 0x19 */ SCHEDULE_CMD_RET_VAL_L(1),
    /* 0x1C */ SCHEDULE_CMD_RET_NONE(),
    /* 0x1D */ SCHEDULE_CMD_RET_VAL_L(3),
    /* 0x20 */ SCHEDULE_CMD_RET_NONE(),
    /* 0x21 */ SCHEDULE_CMD_CHECK_NOT_IN_SCENE_S(SCENE_OMOYA, 0x37 - 0x25),
    /* 0x25 */ SCHEDULE_CMD_CHECK_NOT_IN_DAY_S(3, 0x36 - 0x29),
    /* 0x29 */ SCHEDULE_CMD_CHECK_TIME_RANGE_S(18, 0, 6, 0, 0x30 - 0x2F),
    /* 0x2F */ SCHEDULE_CMD_RET_NONE(),
    /* 0x30 */ SCHEDULE_CMD_RET_TIME(18, 0, 6, 0, 2),
    /* 0x36 */ SCHEDULE_CMD_RET_NONE(),
    /* 0x37 */ SCHEDULE_CMD_RET_NONE(),
};
```

In the actor's C code:

```c
#include "src/overlays/actors/ovl_En_Ah/scheduleScripts.schl.inc"
```

## Commands

Commands are the fundamental (and almost only) building block of this language.
A schedule script file must always contain at least one script, and a schedule
script must always have at least one command. It's undefined behaviour if the
script's control flow doesn't always lead to a return command.

To see how the command arguments work, please see the
[corresponding section](#command-arguments).

Commands can be categorized in 4 major types:
[Conditional checks](#conditional-checks),
[unconditional branches](#unconditional-branches),
[return commands](#return-commands) and
[other commands](#other-commands).

### Generics and non-generics

Schedule scripts use both [short and long commands](#short-and-long-commands)
for most of the [conditional checks](#conditional-checks) and
[unconditional branches](#unconditional-branches) commands, making it optimal
space-wise, but terrible to worry about when actually writing a schedule script
from the user's point of view.

Due to this, this high level language allows for generic versions of those
commands (a.k.a. suffix-less versions) that allow not having to worry about how
many commands the body of a check has (and the byte length of them).

The rest of this section will mostly refer to the generic versions of the
commands. The non-generic versions of each command are available to be used too,
but their use is not recommended.

### Command arguments

Some commands require arguments. Arguments are used by checking commands to
check something specific of the state of the game (what's the current day? what's
the current scene? etc.) and take a decision based on it (branch to another
command).

Arguments are enclosed in parentheses (`(` and `)`) and passed verbatim to the
generated output, allowing a compiler for this language to not need to recognize
any identifier used in arguments, allowing to use any custom identifier
(specially useful for schedule result enums). Note this behaviour may
change in a future version of the language.

### Conditional checks

Conditional checks commands are commands that take a decision based on
the state of the game, allowing it to decide which set of subcommands to
execute.

All conditional checks require an argument and a body of subcommands. A
conditional check can optionally be followed by an `else`.

If the condition of a command is not satisfied and that command has
an `else`, then control jumps to the body of that `else`. If
there's no `else` and the condition isn't satisfied then control just
falls through into the next command.

#### `if_week_event_reg`

Checks if the passed WeekEventReg flag is set, and execute the body of the
command if it is set.

##### `if_week_event_reg` arguments

- Argument 0: A WeekEventReg flag. `WEEKEVENTREG_` macros are preferred.

##### `if_week_event_reg` example

```c
if_week_event_reg (WEEKEVENTREG_61_02) {
    return_s (31)
}
return_s (30)
```

##### `if_week_event_reg` non-generics

`if_week_event_reg_s` and `if_week_event_reg_l`

#### `if_time_range`

Checks if the current time is between the passed time range.

##### `if_time_range` arguments

- Argument 0: Hour component of the start time
- Argument 1: Minute component of the start time
- Argument 2: Hour component of the end time
- Argument 3: Minute component of the end time

##### `if_time_range` example

```c
// Checks if the current time is between 18:00 ~ 6:00
if_time_range (18, 0, 6, 0) {
    return_time (18, 0, 6, 0, 2)
} else {
    return_none
}
```

##### `if_time_range` non-generics

`if_time_range_s` and `if_time_range_l`

#### `if_misc`

Checks if the passed miscellaneous argument is true.

##### `if_misc` arguments

- Argument 0: A value of the `ScheduleCheckMisc` enum.

##### `if_misc` example

```c
if_misc (SCHEDULE_CHECK_MISC_MASK_ROMANI) {
    return_s (33)
} else {
    return_s (34)
}
```

##### `if_misc` non-generics

`if_misc_s`. Note there's no long version of this command. It is advised to
minimize the amount of commands used on a `ìf_misc` body and `else`'s body.

#### `if_scene`

Checks if the current scene matches the one passed as argument.

##### `if_scene` arguments

- Argument 0: A value of the `SceneId` enum.

##### `if_scene` example

```c
if_scene (SCENE_SECOM) {
    return_s (7)
}
```

##### `if_scene` non-generics

`if_scene_s` and `if_scene_l`

#### `if_day`

Checks if the current day matches the passed argument.

##### `if_day` arguments

- Argument 0: The day to check. For example, passing `1` will check for day 1.

##### `if_day` example

```c
if_day (3) {
    return_l (1)
}
```

##### `if_day` non-generics

`if_day_s` and `if_day_l`

#### `if_before_time`

Checks if the current time is before the time passed as argument.

##### `if_before_time` arguments

- Argument 0: The hour component of the time.
- Argument 1: The minute component of the time.

##### `if_before_time` example

```c
if_before_time (8, 0) {
    return_s (19)
} else {
    return_none
}
```

##### `if_before_time` non-generics

`if_before_time_s` and `if_before_time_l`

##### `if_before_time` notes

This command performs the opposite check of `if_since_time`.

#### `if_since_time`

Checks if the current time is after or equal to the time passed as argument.

##### `if_since_time` arguments

- Argument 0: The hour component of the time.
- Argument 1: The minute component of the time.

##### `if_since_time` example

```c
if_since_time (13, 0) {
    return_s (9)
} else {
    return_none
}
```

##### `if_since_time` non-generics

`if_since_time_s` and `if_since_time_l`

##### `if_since_time` notes

This command performs the contrary check to `if_before_time`.

### Unconditional branches

Unconditional branches are basically the equivalent of C's `goto`s. They require
a [label](#labels) to know where to branch to.

#### `branch`

Unconditionally transfer control to the passed label.

##### `branch` arguments

- Argument 0: The label to branch to.

##### `branch` example

```c
if_day (3) {
    if_since_time (10, 0) {
        label_0x8:
        return_none
    } else {
        branch (label_0xF)
    }
} else {
    if_time_range (10, 0, 20, 0) {
        branch (label_0x8)
    } else {
        label_0xF:
        return_s (21)
    }
}
```

##### `branch` non-generics

`branch_s` and `branch_l`

### `else`

An `else` signals which commands should be executed in case a
[conditional check](#conditional-checks) does not evaluates to true. The `else`
signals this by either having those instructions inside its own body (marked by
braces) or by not having those braces but being immediately followed by another
conditional check command, similar to C's `else if`.

#### `else` example

The following two scripts are equivalent:

```c
if_time_range (9, 50, 18, 1) {
    return_time (9, 50, 18, 1, 1)
} else {
    if_time_range (18, 0, 6, 0) {
        return_time (18, 0, 6, 0, 2)
    } else {
        return_l (0)
    }
}
```

```c
if_time_range (9, 50, 18, 1) {
    return_time (9, 50, 18, 1, 1)
} else if_time_range (18, 0, 6, 0) {
    return_time (18, 0, 6, 0, 2)
} else {
    return_l (0)
}
```

### Return commands

A return command signals the end of the script and halts its execution.

A return command allows to optionally return a value and/or a time range to the
calling actor so it can change behavior accordingly.

A schedule script with a control flow that leads to no return command is
undefined behaviour.

#### `return_s`

Allows to return a short (one byte) value.

##### `return_s` arguments

- Argument 0: The value to return. It must fit in a `u8`.

##### `return_s` example

```c
if_time_range (8, 0, 12, 0) {
    return_s (EN_NB_SCH_1)
}
```

#### `return_l`

Allows to return a long (two bytes) value.

Please note that the vanilla interpreter for the schedule scripts has a bug on
which the upper byte of a long returned value will be discarded (will be
truncated to a `u8`), so please ensure your returned values always fit in the
0-255 range.

##### `return_l` arguments

- Argument 0: The value to return. It must fit in a `u16`.

##### `return_l` example

```c
if_scene (SCENE_TOWN) {
    return_l (1)
} else {
    return_none
}
```

#### `return_none`

The schedule finished without returning a value. The internal `hasResult`
member of the `ScheduleOutput` struct will be set to `false`.

##### `return_none` arguments

No arguments.

##### `return_none` example

```c
if_week_event_reg (WEEKEVENTREG_HAD_MIDNIGHT_MEETING) {
    return_none
}
```

#### `return_empty`

The schedule finished without changing the previous value. The internal `hasResult`
member of the `ScheduleOutput` struct will be set to `true`.

##### `return_empty` arguments

No arguments.

##### `return_empty` example

```c
if_time_range (15, 50, 16, 15) {
    return_empty
}
```

#### `return_time`

Returns a time range and a 1 byte value.

##### `return_time` arguments

- Argument 0: Hour component of the start time
- Argument 1: Minute component of the start time
- Argument 2: Hour component of the end time
- Argument 3: Minute component of the end time
- Argument 4: A value to return. It must fit in a `u8`

##### `return_time` example

```c
if_time_range (0, 0, 6, 0) {
    return_time (0, 0, 6, 0, TOILET_HAND_SCH_AVAILABLE)
} else {
    return_none
}
```

### Other commands

#### `nop`

No operation. Doesn't perform an action or a check.

##### `nop` arguments

- Argument 0: Unknown meaning.
- Argument 1: Unknown meaning.
- Argument 2: Unknown meaning.

##### `nop` example

```c
nop (0, 1, 2)
```

### Operators

Operators can be applied to some commands.

Currently only one operator is allowed on the language, the [`not`](#not) operator.

#### `not`

A `not` is a special kind of command that doesn't get compiled into the actual
low level script, instead it changes the meaning of the check that's right next
to it by inverting the logic of the check. In other words, the subcommands of a
conditional check command will be executed if the check of said command
evaluates to false instead of true.

A `not` must always be followed by a [conditional check command](#conditional-checks).

##### `not` example

The following two scripts are equivalent:

```c
if_week_event_reg (WEEKEVENTREG_KAFEI_ENTRUSTED_LINK) {
    if_since_time (22, 0) {
        return_s (12)
    } else {
        return_none
    }
} else {
    return_s (12)
}
```

```c
not if_week_event_reg (WEEKEVENTREG_KAFEI_ENTRUSTED_LINK) {
    return_s (12)
} else {
    if_since_time (22, 0) {
        return_s (12)
    } else {
        return_none
    }
}
```

### Labels

A label is a special kind of command that doesn't get compiled into the actual
low level script, instead it is used as a marker to be used for
[`branch`es](#branch).

A label is defined as an alphanumeric identifier (a to z, digits and
underscores) followed by a colon (`:`). The colon is not considered part of the
label's name.

A label must always be followed by another command that isn't another label.

## Formal grammar

This section presents the formal grammar for the Schedule scripting language.

```yac
<scriptFile>            : <functionList>
                        ;

<functionList>          : <functionScript>
                        | <functionList> <functionScript>
                        ;

<functionScript>        : IDENTIFIER '{' <cmdList> '}'
                        ;

<cmdList>               : <labeledCmd>
                        | <cmdList> <labeledCmd>
                        ;

<labeledCmd>            : <command>
                        | IDENTIFIER ':' <command>
                        ;

<command>               : <conditionalCmd>
                        | <unconditionalCmd>
                        | <returnCmd>
                        | <miscCmd>
                        ;

<conditionalCmd>        : <conditionalExprBody> <else>
                        | <conditionalExprBody>
                        ;

<unconditionalCmd>      : <tokenBranch> <args>
                        ;

<returnCmd>             : RETURN_S <args>
                        | RETURN_L <args>
                        | RETURN_NONE
                        | RETURN_EMPTY
                        | RETURN_TIME <args>
                        ;

<miscCmd>               : NOP <args>
                        ;

<conditionalExprBody>   : NOT <conditionalExpr> <body>
                        | <conditionalExpr> <body>
                        ;

<conditionalExpr>       : <tokenIfWeekEventReg> <args>
                        | <tokenIfTimeRange> <args>
                        | <tokenIfMisc> <args>
                        | <tokenIfScene> <args>
                        | <tokenIfDay> <args>
                        | <tokenIfBeforeTime> <args>
                        | <tokenIfSinceTime> <args>
                        ;

<else>                  : ELSE <body>
                        | ELSE <conditionalCmd>
                        ;

<tokenIfWeekEventReg>   : IF_WEEK_EVENT_REG
                        | IF_WEEK_EVENT_REG_S
                        | IF_WEEK_EVENT_REG_L
                        ;

<tokenIfTimeRange>      : IF_TIME_RANGE
                        | IF_TIME_RANGE_S
                        | IF_TIME_RANGE_L
                        ;

<tokenIfMisc>           : IF_MISC
                        | IF_MISC_S
                        ;

<tokenIfScene>          : IF_SCENE
                        | IF_SCENE_S
                        | IF_SCENE_L
                        ;

<tokenIfDay>            : IF_DAY
                        | IF_DAY_S
                        | IF_DAY_L
                        ;

<tokenIfBeforeTime>     : IF_BEFORE_TIME
                        | IF_BEFORE_TIME_S
                        | IF_BEFORE_TIME_L
                        ;

<tokenIfSinceTime>      : IF_SINCE_TIME
                        | IF_SINCE_TIME_S
                        | IF_SINCE_TIME_L
                        ;

<tokenBranch>           : BRANCH
                        | BRANCH_S
                        | BRANCH_L
                        ;

<body>                  : '{' '}'
                        | '{' <cmdList> '}'
                        ;

<args>                  : '(' ')'
                        | '(' <args_list> ')'
                        ;

<args_list>             : <arg_elem>
                        | <args_list> ',' <arg_elem>
                        ;

<arg_elem>              : NO_PARENTHESIS
                        | <args>
                        ;
```

### Tokens

The presented grammar expects a few tokens.

First column is the corresponding token and right is a regular expression to
match said token.

```regex
IDENTIFIER              [a-zA-Z0-9_]+

IF_WEEK_EVENT_REG       if_week_event_reg
IF_WEEK_EVENT_REG_S     if_week_event_reg_s
IF_WEEK_EVENT_REG_L     if_week_event_reg_l
IF_TIME_RANGE           if_time_range
IF_TIME_RANGE_S         if_time_range_s
IF_TIME_RANGE_L         if_time_range_l
IF_MISC                 if_misc
IF_MISC_S               if_misc_s
IF_SCENE                if_scene
IF_SCENE_S              if_scene_s
IF_SCENE_L              if_scene_l
IF_DAY                  if_day
IF_DAY_S                if_day_s
IF_DAY_L                if_day_l
IF_BEFORE_TIME          if_before_time
IF_BEFORE_TIME_S        if_before_time_s
IF_BEFORE_TIME_L        if_before_time_l
IF_SINCE_TIME           if_since_time
IF_SINCE_TIME_S         if_since_time_s
IF_SINCE_TIME_L         if_since_time_l

BRANCH                  branch
BRANCH_S                branch_s
BRANCH_L                branch_l

RETURN_S                return_s
RETURN_L                return_l
RETURN_NONE             return_none
RETURN_EMPTY            return_empty
RETURN_TIME             return_time

NOP                     nop

ELSE                    else
NOT                     not

NO_PARENTHESIS          [^\(\)]+
```
