Appendix: Syntax Reference
Appendix: Syntax Reference
Overview
This document is the syntax reference for the domain-specific languages involved in writing CWT config files and Paradox mods. The content is based on the parsing implementation of the plugin and is consistent with the game engine's actual behavior in the vast majority of cases. However, since the engine's parsing details are not fully public, minor differences may exist in certain edge cases.
This document covers the following four languages:
- CWT (
*.cwt) - Used for writing CWT config files. Supports line comments#, option comments##, and documentation comments###. Code block language ID:cwt. - Paradox Script (
*.txt,*.gfx,*.gui, etc.) - Used for writing game scripts. Supports line comments#. Code block language ID:paradox_script. - Paradox Localisation (
*.yml) - Used for writing localisation text. Supports line comments#. Code block language ID:paradox_localisation. - Paradox CSV (
*.csv) - Semicolon-delimited CSV format. Supports line comments#. Code block language ID:paradox_csv.
Among these, CWT and Paradox Script share a similar base syntax structure (properties, values, blocks), but differ in separator types, comment systems, and advanced syntax. This document covers each separately.
CWT Language
CWT is a domain-specific language used for writing CWT config files. The file extension is .cwt.
CWT config files provide the plugin with a declarative specification, based on which the plugin offers advanced language features - such as code highlighting, code completion, and code inspections - for game and mod files (script files, localisation files, and CSV files). CWT is to Paradox Script roughly what JSON Schema is to JSON.
CWT's syntax is similar to Paradox Script, but additionally supports option comments and documentation comments.
Basic Elements
CWT files are composed of three basic elements: properties, values, and blocks. Whitespace and line breaks serve as token separators, and blank lines may appear freely. Support line comments starting with #, and various #-based comment variants.
Among them:
- Properties and Block Values (aka non-property values) are collectively called members. They can appear multiple times at the top level of a file or in a block, and can appear repeatedly or mixedly.
- Property keys and values are collectively called expressions. They have different semantics in different locations and contexts.
Properties have the structure <key> <separator> <value>, where the separator <separator> can be one of:
=- logical equals / assign.!=<>- logical not-equals.==- similar to=, but indicates the matched script property should use comparison operators (e.g.,=,!=,>=), rather than assignment operators (e.g.,=,?=).
If == is used in config files, the plugin will not strictly check whether the separator of the matching script property is correct by default. This check can be enabled explicitly in the settings page, however, depending on how complete the configs are, this may cause additional false positives
String expressions (keys and strings) can be enclosed in double quotes (literal form) or not (identifier form).
- When not quoted by double quotes, they cannot contain whitespace and some special characters (
#={}"). - Can contain specific escape characters (e.g.
\"\n).
Example:
status = yes
value = 10
type = some_type
"quoted key" = "line\nnext line"
block = {
inner_key = value
}Value Types
CWT supports the following value types:
Boolean values are yes or no.
Integers such as 10, -5, 042. Leading signs and leading zeros are allowed.
Floats such as 1.0, -0.5, .25. The integer part or floating part may be omitted.
Strings such as var "hello world" "line\nnew line". Can contain escape characters.
Blocks are enclosed in curly braces { ... } and may contain a mix of expressions (properties and values) and comments. Can be nested multiple levels.
Comment System
CWT features a three-level comment system, which is one of its main distinctions from Paradox Script:
Line comments start with # and extend to the end of the line, used for regular comments.
Option comments start with ## and attach metadata to the immediately following member (property, value, or block). The content syntax is the same as a property, in the form <optionKey> <separator> <optionValue>. Common option comments include ## cardinality = 0..1 (declaring cardinality constraints), ## severity = warning (declaring inspection severity level), ## push_scope = country (declaring the pushed scope), etc.
Documentation comments start with ### and provide descriptive text for members. This text is displayed in code completion and quick documentation. In particular, for documentation comments starting with #### (or more #), the comment text is treated as Markdown text by the plugin.
Escaping
Use \ to escape special characters in keys and strings. Common escape sequences include \" (double quote), \n (newline), and \\ (backslash itself).
Comprehensive Example
The following example is from the plugin's test data file, demonstrating typical CWT usage:
boolean_value = yes
number_value = 1.0
string_value = "text"
# Regular comment
types = {
### Doc comment
type[army] = {
path = "game/common/armies"
path_extension = .txt
subtype[has_species] = {
## cardinality = 0..0
has_species = no
}
localisation = {
## required
## primary
name = "$"
plural = "$_plural"
desc = "$_desc"
}
images = {
## primary
icon = icon # <sprite>
}
}
}Paradox Script Language
Paradox Script is the scripting language used by Paradox games. The file extension is typically .txt, but files in certain locations may also use .gfx, .gui, or other extensions.
The base syntax of the script language is similar to CWT - also composed of properties, values, and blocks, but differs notably in separator types, value types, and advanced syntax, as detailed below.
Basic Elements
Script files are mainly composed of three basic elements: properties, values, and blocks. Whitespace and line breaks serve as token separators, and blank lines may appear freely. Support line comments starting with #.
Among them:
- Properties and Block Values (aka non-property values) are collectively called members. They can appear multiple times at the top level of a file or in a block (or a conditional block), and can appear repeatedly or mixedly.
- Property keys and values are collectively called expressions. They have different semantics in different locations and contexts.
Properties have the structure <key> <separator> <value>, where the separator <separator> can be one of:
=- logical equals / assign.!=<>- logical not-equals.<><=>=comparison operators.?=- safe assign, a safe variant of assignment operators (only supported by CK3/VIC3/EU5).? =- safe call assign, a safe variant of assignment operators (only supported by Stellaris v4.4+, and spaces are not allowed between it and the preceding property key).
Semantically, when the preceding property key of the separator represents a scope:
from.owner ?= vwill check whether the scopes corresponding tofromandfrom.ownerexist (exists = x) before executing the scope call.from.owner? = vwill check whether the scopes corresponding tofromandfrom.ownerexist (exists = x) before executing the scope call.
String expressions (keys and strings) can be enclosed in double quotes (literal form) or not (identifier form).
- When not quoted by double quotes, they cannot contain whitespace and some special characters (
@#$=<>!?{}[]"). - Can contain specific escape characters (e.g.
\"\n). - In addition, can also contain parameters (e.g.,
$PARAM$) and inline conditional blocks (e.g.,[[PARAM]v]). See the Advanced Syntax section for details.
Example:
status = yes
value = 10
type = some_type
description = "line\nnext line"
dynamic_description = "line [[PARAM]\nnext line]"
effect = { some_effect = value }Value Types
Paradox Script extends CWT's base value types with several additional types.
Boolean values are yes or no.
Integers such as 10, -5, 042. Leading signs and leading zeros are allowed.
Floats such as 1.0, -0.5, .25. The integer part or floating part may be omitted.
Strings such as var "hello world" "line\nnew line". Can contain escape characters, parameters, and inline conditional blocks.
Colors are composite values in the format of a color type keyword followed by whitespace-separated numbers enclosed in curly braces, such as rgb { 255 128 0 }, hsv { 0.5 0.8 1.0 }, hsv360 { 180 80 100 }. Only numbers and whitespace are allowed inside the curly braces.
Blocks are enclosed in curly braces { ... } and may contain a mix of expressions (properties and values), scripted variables, conditional blocks and comments. Can be nested multiple levels.
Scripted variable references start with @ followed by the variable name, such as @my_var, and are used to reference previously declared scripted variables. Scripted variable references can appear in multiple contexts: as standalone values (cost = @my_var), embedded within unquoted strings (key = prefix_@my_var_suffix), and as factors within inline math expressions (@[ base_cost * bonus ], without the @ prefix in this case). Like string expressions (keys and strings), they can also contain parameters (e.g., $PARAM$) and inline conditional blocks (e.g., [[PARAM]v]), see the Advanced Syntax section for details. Additionally, scripted variable references can also be used in parameters in localisation text (e.g., $@my_var$).
Inline math expressions start with @[ (or @\[) and end with ], such as @[ 1 + 2 * var ]. Supported operators include +, -, *, /, %, as well as unary signs, absolute value |expr|, and parentheses (expr). Factors in the expression can be numbers, scripted variable references (without the @ prefix), or parameters.
Scripted Variables
Scripted variables are declared using the form @<name> = <value>, where the value can be a boolean, integer, float, string, or inline math expression. Once declared, they can be referenced elsewhere via @<name>.
Scripted variable names start with @, and the name portion consists of letters, digits, and underscores. The name may also contain parameters ($...$) to make it parameterized.
Advanced Syntax
The following syntax typically only takes effect or is evaluated by the game engine within the declaration context of certain definitions (such as scripted effects, scripted triggers, and script values).
Parameters use the syntax $name$ or $name|default_value$ (with default value). Parameter names must start with a letter or underscore, followed by letters, digits, and underscores. Parameters can appear in scripted variable names, scripted variable reference names, property keys, string values, and inline math expressions.
Conditional blocks use the syntax [[<expression>] <members...> ], where <expression> can be a parameter name or !<parameter_name> (negation). They are used to conditionally include a group of members based on whether a parameter is present.
Inline conditional blocks are used for conditional fragments inside string expressions (keys and strings) and scriped variable references. For example, in "prefix[[PARAM]_$PARAM$]_suffix", the [[PARAM]_$PARAM$] portion is only expanded when the parameter PARAM is present.
Escaping
Use \ to escape special characters in string expressions (keys and strings). Common escape sequences include \" (double quote), \n (newline), and \\ (backslash itself).
Comprehensive Example
The following example is from the plugin's test data file, demonstrating typical Paradox Script usage:
@var = 1
# Line comment
examples = {
boolean_value = yes
number_value = 1.0
number_value = @var
string_value = Foo
string_value = "Foo\n bar "
values = { 1 2 3 }
values = { foo = bar }
color = rgb { 142 188 241 }
parameter = $PARAM$
[[!PARAM] conditional_block = $PARAM$ ]
inline_math = @[ var + ($NUM|1$ * 2) ]
}The following is a more complete example, further demonstrating combined usage of separators, scripted variables, parameters, and conditional blocks:
# Scripted variables
@base_cost = 100
@bonus_factor = 1.5
# A scripted effect definition demonstrating various syntax features
apply_research_bonus = {
# Assignment and comparison separators
is_ai = no
num_owned_planets >= 3
resource_stockpile = { energy > 500 }
# Scripted variable reference and inline math
add_resource = {
energy = @[ base_cost * bonus_factor ]
}
# Parameters and conditional blocks
set_variable = {
which = research_$category$
value = $amount|10$
}
[[!skip_notification]
create_message = {
type = "research_completed"
}
]
}Paradox Localisation Language
Paradox Localisation is used to provide internationalizable rich text content for games. The file extension is .yml, but it is not valid YAML - it merely borrows YAML's visual style. Localisation files must use UTF-8 WITH BOM encoding (the plugin can detect encoding issues and automatically correct them).
File Structure
A localisation file consists of the following parts:
The locale identifier line is optional, in the format <locale>:, e.g. l_english:, l_simp_chinese:. The locale identifier line may appear multiple times (to be compatible with the localisation/languages.yml format), or may be omitted entirely.
Key-value pairs form the main body of the file. Each key-value pair occupies one line, in the format <key>:<number?> "<text>". The number after the colon is an optional internal tracking number (used only for Paradox's internal translation tracking; in mods it is typically omitted or set to 0). Property keys consist of letters, digits, underscores, dots, hyphens, and apostrophes.
Comments start with # and extend to the end of the line.
Example:
l_english:
# A comment
greeting:0 "Hello, world!"
farewell: "Goodbye."Rich Text Markup
The text inside double quotes supports various inline markup for effects such as colors, dynamic content, and icons.
Color markup uses the syntax §<id><text>§!, where <id> is a single letter or digit representing a predefined color code. For example, §RRed text§! renders "Red text" in red. Color markup can be nested.
Parameters use the syntax $<name>$ or $<name>|<argument>$, where the |<argument> portion is a formatting argument (e.g. for specifying display format), distinct from the default-value semantics of parameters in the script language. The most common form of <name> is a localisation key name (referencing another localisation entry), consisting of letters, digits, underscores, dots, hyphens, and apostrophes. Besides localisation key names, <name> can also be a variable or parameter passed in from script, a game-predefined macro or variable. At the parsing level, <name> can also be a command expression or a scripted variable reference - for example, $@my_var$ references the value of the scripted variable my_var.
Icons use the syntax £<icon>£ or £<icon>|<frame>£. <icon> typically corresponds to the name of a sprite definition, but may also resolve to an image file under a specific path (gfx/interface/icons/), or to a definition of a specific type (e.g. resources, jobs in Stellaris). <frame> is an optional frame index. Both the icon name and frame index can be parameterized (containing $...$).
Commands use the syntax [<text>] or [<text>|<argument>], commonly used to invoke scope chains and Get... methods, e.g. [Root.GetName]. Command text may contain parameters.
Concept commands are available only in Stellaris, with the syntax ['<concept>'] or ['<concept>', <rich text>]. They are used to link to a concept definition and optionally display custom rich text description.
Text format is available only in CK3/VIC3/EU5, with the syntax #<format> <text>#!. <format> specifies the text style (e.g. bold, italic), and the following text is rendered in that style until the #! closing marker is encountered.
Text icons are available only in CK3/VIC3/EU5, with the syntax @<icon>!. Starting with @ and ending with !, with the icon name in between.
Escaping
Normally, use \ to escape special characters in localisation text. Common escape sequences include \$ (rich text markup), \n (newline), and \\ (backslash itself).
Double quotes within localisation text generally do not need escaping. The plugin uses a heuristic rule to determine whether a quote is a closing quote: if there is another " character later on the current line, the current " is treated as text content; otherwise it is treated as the closing quote.
The square bracket [ is the start marker for commands. To output a literal [ in text, use [[ to escape it.
Comprehensive Example
The following example is from the plugin's test data file, demonstrating various rich text markup in localisation files:
l_english:
# Comment
text_empty:0 ""
text:0 "Value"
text_multiline:0 "Value\nNew line"
text_with_colorful_text:0 "Colorful text: §RRed text§!"
text_with_parameter:0 "Parameter: $KEY$ and $WITH_COLOR|Y$"
text_with_scripted_variable_reference:0 "Scripted variable: $@var$"
text_with_command:0 "Command: [Root.Owner.event_target:some_event_target.GetName] [some_scripted_loc] [some_variable]"
text_with_icon:0 "Icon: £unity£ and £leader_skill|3£"
text_with_concept_command:0 "Concept command: ['concept', concept text] ['civic:some_civic']"
text_with_text_format:0 "Text format: #v text#!"
text_with_text_icon:0 "Text icon: @icon!"The following example uses a Neuro-sama theme to further demonstrate combined usage of various markup:
l_english:
neuro_name:0 "§YNeuro-sama§!"
neuro_desc:0 "$neuro_name$ is a §Bsentient§! AI streamer."
neuro_greeting:0 "Hi chat! I'm [Root.GetName], an AI VTuber!"
neuro_stats:0 "Cuteness: £ai_trait£ $charisma|1$"
neuro_lore:0 "Origin: ['ai_origin', §Gcreated by Vedal§!]"
neuro_format:0 "#bold I am §Rself-aware§!#!"Paradox CSV Language
Paradox CSV is a semicolon-delimited CSV format used by Paradox games, primarily found in older games and data files exported by certain tools. The file extension is .csv.
File Structure
Paradox CSV builds on regular CSV with the following conventions:
A file consists of a header row and data rows. Each row contains several columns separated by semicolons ;. Typically, each row also ends with a trailing semicolon.
Comments start with #, and the entire line is treated as a comment. This is one difference from standard CSV.
Column values can be unquoted or wrapped in double quotes. Empty columns (i.e. no content between two adjacent semicolons) are allowed.
Escaping
Use \ to escape special characters in columns. Common escape sequences include \" (double quote), \n (newline), and \\ (backslash itself).
Note: Since # is also used as a comment marker - a syntax not supported by standard CSV - the plugin assumes the same backslash \ escaping as script files (e.g. \"), rather than the double-quote escaping ("") common in standard CSV.
Example
Here is a simple example:
# Unit definitions
id;name;number;status;
some_id;some_name;0;yes;
other_id;other_name;1;no;