The Scaly Programming Language

Chapter 3

Lexical Structure

Overview

This chapter describes the lexical structure of Scaly: how source text is divided into tokens. The lexer transforms a sequence of characters into a sequence of tokens that the parser then processes.

Whitespace and Line Breaks

Whitespace characters (space, tab) separate tokens but are otherwise insignificant. Line breaks, however, are syntactically meaningful in Scaly — they can terminate statements and declarations.

A colon (:) can be used in place of a line break when multiple statements should appear on a single line.

Comments

Scaly supports two forms of comments:

  • Single-line comments start with a semicolon (;) and extend to the end of the line.

  • Multi-line comments start with ;* and end with *;.


; This is a single-line comment

;* This is a
   multi-line comment *;
    

Literals

Integer Literals

Integer literals represent whole numbers. They can be written in decimal or hexadecimal notation.


42        ; decimal
0x2A      ; hexadecimal (same value)
      

Floating-Point Literals

Floating-point literals represent decimal numbers with a fractional part or an exponent.


3.14159       ; pi
2.5e10        ; scientific notation
1.0e-5        ; small number
      

String Literals

String literals are enclosed in double quotes. The supported escape sequences are \", \\, \', \n, \r, \t, \0, and \uXXXX (four hex digits, encoded as UTF-8).


"Hello, World!"
"Line 1\nLine 2"      ; with newline escape
"Tab:\tValue"         ; with tab
"Quote: \"nested\""   ; escaped quotes
"\u0041"            ; Unicode escape - the letter A
      

Strings can span multiple lines without any special syntax — newlines are simply included as part of the string content:


"This is a string
that spans multiple
lines."
      

A backtick inside a string embeds an expression: the string is built from its parts, and each embedded value is written in the text form of its type — a String, a char, the bytes of a Slice[u8], an integer, a bool, or a floating-point number as the shortest text that reads back to it. A value of another type needs a conversion inside the backticks, and \` writes a backtick itself.


let n 42
let x 0.1
"The answer is `n`, not `n + 1`, and x is `x`"
                      ; The answer is 42, not 43, and x is 0.1
      

Character Literals

A character is written as a string literal of one character wherever a char is expected — a char parameter, a typed binding, or the cast as char. Escapes count as one character.


let a: char "a"        ; lowercase a
let nl "\n" as char    ; newline character
      

Boolean Literals

Boolean literals are true and false.


true
false
      

Operators and Special Symbols

The Equals Sign

Unlike many programming languages, Scaly reserves the equals sign (=) exclusively for equality comparison. It is not used for variable declarations or assignments.


; Variable declaration (NO equals sign)
var x 42                  ; declares x with value 42
let y: i32 100            ; with type annotation

; Assignment (its own sign)
x := 43                   ; assigns 43 to x

; Equality comparison
if x = 43                 ; tests if x equals 43
    print "x is 43"
      

This design eliminates the classic confusion between assignment and comparison that leads to bugs in C-style languages (if (x = 42) vs if (x == 42)).

Identifiers

Identifiers name variables, functions, types, and other entities. Scaly supports three forms of identifiers:

  • Alphanumeric identifiers start with a letter and continue with letters, digits, or underscores.

  • Operator identifiers consist of operator characters such as +, -, *, /.

  • String identifiers are enclosed in single quotes and can contain any characters.


myVariable        ; alphanumeric
calculate_total   ; with underscore
++                ; operator identifier
'my identifier'   ; string identifier (with spaces)
    

The form of an identifier does not by itself decide what it names. A variable, a function, or an operator may carry any of the three forms; an operator-character name such as ++ can name an ordinary function, and a string identifier such as 'divided by' can name an operator. What a name denotes is fixed by its declaration, not by how it is spelled (see the chapter on definitions).