Syntax
Ghost's syntax is designed to be simple and predictable: curly braces for blocks, parentheses around conditions, and as few special cases as the language can get away with.
Scripts are stored in plain text files with a .gs file extension. Ghost does not compile ahead of time: programs are run directly from source, from top to bottom like any other scripting language.
Comments
Single line comments start with // and continue to the end of the line.
// This is a single line comment
Block comments are enclosed within /* ... */ and can span as many lines as necessary.
/*
This is a block comment.
*/
Comments behave like whitespace and are discarded during execution. While single line comments are the norm, block comments are useful within an expression or to disable large swaths of code.
Statements
A statement simply ends where its grammar says it ends. Ghost has no significant-newline rule and no automatic semicolon insertion to reason about; a trailing ; is optional, and accepted after every kind of statement.
name = "Ghost"
console.log(name)
name = "Ghost"; // also fine
; is required between the three clauses of a for loop header.
When a line break is not a separator
Because a newline means nothing to the parser, two statements on separate lines can still be read as one when the second line opens with a token the first can continue into — [, (, ., ++, --, or a binary operator:
x = 1
[10, 20, 30]
That is not an assignment followed by a list. The [ after 1 reads as an index, so Ghost tries to parse x = 1[10, 20, 30] as a single statement and reports a syntax error on the first comma. When the continuation happens to be valid, you get no error at all — just a statement that quietly did something other than what the layout suggests.
Ending the first line with ; settles it:
x = 1;
[10, 20, 30]
This comes up most with destructuring, which begins with [ or {. It is worth the semicolon whenever the next line starts with one of those tokens. Ghost keeps the rule rather than making newlines significant because a statement that can span lines is what lets a method chain break across them:
result = values
.filter(isReady)
.map(toLabel)
Strings
Strings are written in single or double quotes, and may span several lines literally.
name = "Ghost"
other = 'Ghost'
Backtick strings are template literals, and interpolate any expression written inside ${}:
count = 3
console.log(`there are ${count} item${count == 1 ? "" : "s"}`)
// >> there are 3 items
Each interpolated value is converted using its own string form, so nothing needs an explicit toString() to appear cleanly. This is the fluent way to build a string out of mixed types — + still requires both sides to be the same type. See Strings.
Reserved Words
Ghost has a small subset of reserved words used as predefined identifiers. None of the identifiers listed here should be used as identifiers in any of your scripts.
and as break case class continue
default else extends false for from
function if import in new null
or return super switch this trait
true use while
There are 27 of them, and they are all lowercase and case-sensitive.
console and type — are not reserved words either, and neither is the name of a module you have imported. A variable that shares a name with one shadows it for the rest of the scope, which matters most for a name like font or image inside a file that imported the module of that name.Identifiers
Naming rules are similar to other programming languages. Identifiers must start with a letter or underscore and may then contain letters, digits, and underscores. Case is sensitive.
hello
camelCase
PascalCase
_under_score
abc123
ALL_CAPS
Blocks
Ghost uses curly braces to define blocks. A block is the body of a control flow statement, a function, a class, or a trait — it is always attached to one of those, never written on its own.
pressure = 3.6
if (pressure > 3.4) {
console.log("Pressure is above critical levels.")
}
Braces standing alone are a map literal, not a block, so there is no bare-block form for grouping statements or introducing a scope.
Blocks group statements; they do not introduce a new scope. A variable first assigned inside an if, while, or for body belongs to the scope around it and outlives the block. See Variables for what does introduce a scope.