This document describes the syntax, semantics and features of the Tomefile language.
Edition 1.1, 7 Jan 2026.
#!/bin/tome
# This is an example script inside of a file named `Tomefile`
:include ansi
:include log
printf "Hello World!\n"
:assert ${OS_ARCH:not:empty}
:match $OS_ARCH {
:case "x64" {
:goto build_x64
}
:case {
:goto build
}
}
:tome test "Running tests..." {
ls ./test
log_err* "not implemented"
exit 1
}
:tome build "Building the project" {
mkdir -p ./build
# ...
}
:tome build_x64 {
# ...
}- 1 Introduction
- 2 Definitions * newline * identifier * boolean * exit code * filename * local scope
- 3 Syntax
- 4 Features / Directives
- 5 Features / Modifiers
- 5.1 Not
- 5.2 To Lower
- 5.3 To Upper
- 5.4 To Snake
- 5.5 To Kebab
- 5.6 To Camel
- 5.7 To Pascal
- 5.8 To Delimited
- 5.9 Exists
- 5.10 Is Empty
- 5.11 Is File
- 5.12 Is Dir
- 5.13 Is Symlink
- 5.14 Length
- 5.15 Escape
- 5.16 Surround
- 5.17 Trim
- 5.18 Trim Prefix
- 5.19 Trim Suffix
- 5.20 Pad
- 5.21 Pad Left
- 5.22 Pad Right
- 5.23 Has Prefix
- 5.24 Has Suffix
- 5.25 Slice
- 5.26 Slice Fields
- 5.27 Reverse
- 5.28 Invert Case
- 5.29 Contains
- 6 Standard library
Tomefile is a command language created for more elegant and intuitive automation (scripting). It is not compatible with other shell languages; instead, the syntax is designed to be primitive enough to be easily understood and used.
It was inspired by Bash and Make with the addition of Tomes that allow defining multiple workflows within a single file.
This specification should be used as the definitive reference on the language behavior.
An LF character \n.
A string of characters that is made up of a-z, A-Z, 0-9, _, -.
A string of characters that represents a binary true value (true, TRUE, or most commonly 1) or a false value (false, FALSE, or most commonly 0).
The value returned by a command to its caller. Any non-zero value indicates a failure.
A string of characters used to identify a file.
A set of defined variables and macros inside of curly braces { ... } or document.
The script is made up of statements separated by a newline or a semicolon ;. A backslash \ can be used to escape those characters for multi-line statements.
statement 1; statement 2
one very long \
statement that fits \
multiple lines.
statement 4
There exist 4 types of statements: Comment, Directive, Command, and Macro.
# This is a comment
A comment causes all characters after # to be ignored during execution. It exists solely for the reader of the file or documentation tools.
#!/bin/tomeThe Unix Shebang can be placed at the very beginning of the script to indicate which interpreter to use to execute the script in Unix-like systems.
:directive arg1 arg2 {
# Body of the directive
}
:simple_directive arg1 arg2
:no_arg_directive
A directive begins with a : character followed by an identifier, optional arguments, and sometimes a body { ... }.
Note
Refer to Features / Directives for a detailed list of all existing directives.
printf* "Hello World!"
rm ./build # do not continue if `rm` fails.A command is a file path to an executable, often just the filename that can be located from the $PATH environment variable.
Commands can contain arguments, use redirections, or be a part of pipeline.
An asterisk * can be appended to the command to prevent the script from exiting preemptively if the command returns a non-zero exit code.
# example of redirecting all streams from/into files:
# read from stdin.txt -> stdin
# stdout -> write to stdout.txt
# stderr -> write to stderr.txt
command < stdin.txt > stdout.txt >> stderr.txtAny given command can redirect stdin, stdout, or stderr using <, >, and >> respectively, followed by a filename.
# Pipe command_a into command_b
# Pipe stderr of command_b into command_c
command_a | command_b >>| command_cA pipeline can be used to pipe output of one command into the input of another.
|is used to pipestdoutof the left command intostdinof the right command.>>|is used to pipestderrof the left command intostdinof the right command.
my_print! "Hello World!"
my_rm!* ./build # continue if `rm` fails.
A macro is an identifier that ends with an exclamation point !.
Calling a macro results in the interpreter going into the defined function body, creating a local scope context, executing, and then returning to the macro call.
Note
Just like commands:
Macros can contain arguments, use redirections, or be a part of pipeline.
An asterisk * can be appended to the macro (after !) to prevent the script from exiting preemptively if the command returns a non-zero exit code.
There exist ... types of arguments: ...
'This is a literal string'A string of characters contained within '. Any newline characters must be escaped with \. The string is kept as-is with no modifications.
this_is_an_interpolated_string
"And so is this, with a $variable"A string of characters optionally contained within ". The string is computed at runtime together with all variables, variable expansions, and subcommands within.
Note
Interpolated strings are always computed at runtime, even if their actual contents are static. This should not introduce any significant performance cost (depending on implementation), but it is good to keep that in mind.
$variable_nameA variable identifier that comes from either environment or local scope.
It is computed at runtime, and panics if the variable is undefined. Use [variable expansion] with a ? to prevent that behavior.
Note
Every scope has the following additional variables exposed:
$*— All input arguments in the same string, space separated.$0— The amount of input arguments.$1,$2...$n— The n-th input argument.
printf ${variable_name} ${variable:not:empty} ${optional_var?}
Behaves like a variable but with applied modifiers.
Append ? to the identifier to make it optional. An optional variable returns an empty string instead of panicking.
Modifiers can be chained to transform the variable into desired format or apply basic logical operations on the value.
printf ${...:modifier 1: modifier 2}
A function that takes the input string and converts to output string.
It is used inside of variable expansions by using the following format: :[identifier].
Some modifiers accept arguments, which should be space separated after the identifier (e.g. :[identifier] [arg1] [arg2]).
Note
Refer to Features / Modifiers for a detailed list of all existing modifiers.
command_a $(command_b arg1 arg2)
A command as part of the arguments list, placed between $( and ).
The stdout of the command will be read in-full and provided as a literal string.
This chapter describes specific directive identifiers, their meaning, and their behavior.
Syntax: :include [filepath]
Placement: anywhere
:include @log # /etc/tomefile/lib/log.tome on Unix-like systems
:include $dynamic_value # NOTE: will panic if undefined
:include ${optional_value?} # NOTE: will skip if undefinedIncludes the specified file inside of the current document.
File paths that begin with an at-sign @ are resolved as standard library files.
Syntax: :set [identifier] [value]
Placement: anywhere
:set welcome_message "Hello World!"Sets a variable to the specified value. Can also be used to export environment variables to be used by subprocesses.
Syntax: :unset [identifier]
Placement: anywhere
:unset welcome_messageUnsets a variable if it is set.
Syntax: :require [identifier]
Placement: anywhere
:require input_arg1
:require input_arg2?
:require ...the_rest
# Analogous to (assuming it's at the beginning of the document)
:set input_arg1 $0
:set input_arg2 ${1?}
:set the_rest ${*:fields_slice 2 -1}Requires that the next read positional input argument exists and sets the variable to the argument value.
A question mark ? can be appended to the identifier if the argument is optional.
Syntax: :assert [variable], :assert [variable] [==,!=] [value]
Placement: anywhere
:assert $var
:assert $var == 123Checks that the statement is true, panics if not. This is useful to ensure the correct state of the program to prevent unexpected behavior.
Syntax: :define [identifier] {...}
Placement: anywhere
:define my_macro {
# ...
}Defines a macro with its own local scope. Note that the language is interpreted, which means that definitions must come before they are referenced.
Syntax: :section [description] {...}
Placement: anywhere
:section "Building the project..." {
# ...
}Creates a section of code that helps organize tasks with its own local scope.
Every section exports the $TOME_SECTION variable that is set to its description.
Syntax: :tome [name] [description?] {...}
Placement: anywhere
:tome build {
# ...
}
:tome test "Running tests..." {
# ...
}Creates a tome with its own local scope. It acts as a separate file inside of the document.
An interpreter must have a way to run a specific tome using one of the following syntaxes:
[filename]@[tome]— inline with the filename, allows for multiple filenames to be referenced.[filename] -t [tome]— as a short CLI option[filename] --tome=[tome]— as a long CLI option
Syntax: :if [variable] {...} or :if [variable] [==,!=] [value]
Placement: anywhere
:if ${path:is_file} {
# ...
}
:if $path == "/home/tome" {
# ...
}
Run the code inside of the body only if the statement is true.
Syntax: :elif [variable] {...} or :elif [variable] [==,!=] [value]
Placement: only after an if statement
:if ${path:is_file} {}
:elif $path == "/home/tome" {
# ...
}
Run the code inside of the body only if the previous statement returned false and the current statement is true.
Syntax: :else {...}
Placement: only after an if or elif statement
:if ${path:is_file} {}
:else {
# ...
}
Run the code inside of the body only if all previous statements returned false.
Syntax: :async {...} or :async [identifier] {...}
Placement: anywhere
:async {
# ...
}
:async my_task {
# ...
}
Run the code inside of the body asynchronously. Creates a named task group if [identifier] is provided.
Syntax: :await or :await [identifiers...]
Placement: anywhere
:await
:await my_task1 my_task2
Wait for all or specified tasks to finish before continuing to the next line.
This chapter describes specific modifier identifiers, their meaning, and their behavior.
:not — inverts the result. Always gets applied last regardless of position. Any non-boolean value returns false.
:to_lower — transforms the string into all lowercase.
:to_upper — transforms the string into all uppercase.
:to_snake — transforms the string into snake_case.
:to_kebab — transforms the string into kebab-case.
:to_camel — transforms the string into camelCase.
:to_pascal — transforms the string into PascalCase.
:to_delimited [char] — transforms the string where words* are separated with [char].
- word depends on the input case i.e. PascalCase -> Pascal and Case, snake_case -> snake and case, my sentence -> my and sentence.
:exists — returns a boolean based on whether the input string is a real path that points to an entry on the filesystem.
:is_empty — returns a boolean based on whether the input string contains no characters.
:is_file — returns a boolean based on whether the input string is a path to a file that exists on the filesystem.
:is_dir — returns a boolean based on whether the input string is a path to a directory that exists on the filesystem.
:is_symlink — returns a boolean based on whether the input string is a path to a symlink that exists on the filesystem.
:length — returns a number representing the length of the input string in unicode codepoints.
:escape [chars...] — returns the input string with specified characters escaped using \.
:surround [string] — returns the input string surrounded by [string] from both ends.
:trim [string] — returns the input string with [string] being trimmed (if possible) from both sides.
:trim_prefix [string] — returns the input string with [string] being trimmed (if possible) from the beginning.
:trim_suffix [string] — returns the input string with [string] being trimmed (if possible) from the end.
:pad [number] — returns the input string padded with [number] spaces from both sides.
:pad_left [number] — returns the input string padded with [number] spaces from the left side.
:pad_right [number] — returns the input string padded with [number] spaces from the righht side.
:has_prefix [string] — returns a boolean based on whether the input string begins with [string].
:has_suffix [string] — returns a boolean based on whether the input string ends with [string].
:slice [from] [to] — returns a slice of the input string starting from [from] to [to] both inclusive. An index can be negative to count from the end -1 == last character.
:slice_fields [from] [to] — returns a slice of the input string as an array of space separated words starting from [from] to [to] both inclusive. An index can be negative to count from the end -1 == last character.
:reverse — returns the input string reversed.
:invert_case — returns the input string with the case being inverted.
:contains [strings...] — returns a boolean based on whether the input string contains all of [strings...].
The standard library is a set of files included with the tome interpreter located in:
/etc/tomefile/libon UNIX-like systems.C:\ProgramData\Tomefile\lib\on Windows systems.
The actual contents of the library is beyond the scope of this document.