diff options
| author | Anghelo Carvajal <angheloalf95@gmail.com> | 2024-02-01 09:50:32 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-01 09:50:32 -0300 |
| commit | 5db52cfd395479fb5aa7a0e236478781904e0c52 (patch) | |
| tree | ad1833e5968feca4d4a14c65d72f141709b435db /docs | |
| parent | 2c600ddd3d06d91fb8208d0502fbfd4dfd8f0512 (diff) | |
A higher level language for schedule scripts (#1462)
* schedule script
* start something
* tokenize
* make a tree
* start to emit macros
* start to handle args
* handle branch targets
* cleanup a bit
* script to convert the current schedule scripts to the new language
* Add schedule compilation to Makefile
* Fix bug on compiler if the script doesn't have an extra empty line
* Allow to have `else`s without braces
* convert a lot of schedules
* door
* en_tk
* fix some checks being the wrong way around
* borkens
* door borken schedules
* kafei schedule
* postman schedule
* Implement `if_since_time_*` command
* Allow `if`s to not have an `else`
* Implement some debugging options
* fix another one from door
* D_808675D0
* door D_8086775C
* don't print else if there isn't one
* start supporting labels
* rewrite comment handling
* add branch support
* emit debugging information
* postman and one door
* the remaining scripts
* add tokens for the non _s/_l versions
* meh
* Add built schedules
* Remove _s/_l suffixes from almost every command
* TokenProperties
* Move macros and length into TokenProperties
* move short and long to TokenType
* rename member
* some cleanups
* fatalError
* Write various notes on the compiler
* more errors and comments
* debug flags
* Move the built schedules to the build folder
* manually cleanup control flow of many schedule scripts
* cleanup postman schedule
* start write up
* write more stuff
* write a little more
* Start writing about the commands
* 2 more commands
* more
* almost everything, except returns
* hopefully finish documenting the commands
* cleanup
* formal grammar
* Table of contents
* typo
* Rename schc.py
* remove todo
* Warn if a if_before_time/if_since_time is negated
* Add colors to warnings and errors
* delete rebuild schedules script
* version
* Apply suggestions from code review
Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
* suggestions on md file
* compiler review
* invented is invented
* ScheduleScript typedef
* fix
* Convert Anju's schedule to the new format
* simplify anju's schedule
* forgot to remove `_s`/`_l`
* bss
* Rework schc to allow multiple scripts per file
* Migrate all the schedules to the new format
* update language specification
* Update docs/schedule_scripting_language.md
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
* Update docs/schedule_scripting_language.md
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
---------
Co-authored-by: Dragorn421 <Dragorn421@users.noreply.github.com>
Co-authored-by: engineer124 <47598039+engineer124@users.noreply.github.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/schedule_scripting_language.md | 925 |
1 files changed, 925 insertions, 0 deletions
diff --git a/docs/schedule_scripting_language.md b/docs/schedule_scripting_language.md new file mode 100644 index 000000000..872eac06a --- /dev/null +++ b/docs/schedule_scripting_language.md @@ -0,0 +1,925 @@ +# 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_FLAG_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_FLAG_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 "build/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_51_08) { + if_since_time (22, 0) { + return_s (12) + } else { + return_none + } +} else { + return_s (12) +} +``` + +```c +not if_week_event_reg (WEEKEVENTREG_51_08) { + 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 [^\(\)]+ +``` |
