> For the complete documentation index, see [llms.txt](https://osl.mistium.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://osl.mistium.com/language-guide/programs-and-variables.md).

# Programs and variables

An OSL file runs from top to bottom. You do not need a `main` function.

```osl
log "first"
log "second"
```

Commands use whitespace to separate the command name from its arguments. `log (10 + 10)` prints `20`. Function calls keep the opening parenthesis next to the name, as in `abs(-20)`; `abs (-20)` is invalid.

Function, class, struct, and enum declarations are available before their source position. Other statements run where they appear.

## Declarations

Put the type before the name:

```osl
string username = "Ada"
int attempts = 0
number ratio = 0.75
boolean ready = false
string[] tags = ["admin", "active"]
```

Use `auto` when the compiler should infer a concrete type:

```osl
auto count = tags.len
auto upper = username.toUpper()
```

Use `any` for a value that is intentionally dynamic:

```osl
any payload = json.parse(raw)
payload = "invalid"
```

An untyped assignment also creates dynamic storage:

```osl
payload = {ok: true}
```

The first assignment fixes the variable's type. Assigning a value of an incompatible type later, such as `x = 1` followed by `x = "s"`, is a compile error; declare the variable as `any` when it must hold different types.

Production OSL tends to declare types at function boundaries and for long-lived state. Short local values often use `auto` when their type is obvious from the right side.

`auto` is limited to declarations with an initializer. Function parameters cannot use it because there is no declaration-site value to infer from. Use `any` for a dynamically typed parameter or write its explicit type.

Use `const<T>` when a binding should remain fixed:

```osl
const<int> limit = 10
const<string[]> names = ["Ada", "Grace"]
```

The compiler rejects reassignment and mutation through const bindings. Ordinary aliases can still change shared data; const provides a read-only view. See [const types](/language-guide/types.md#const-types) for collection reads, deep copies, parameters, and return types.

## Assignment and mutation

```osl
attempts += 1
attempts++
username ++= " Lovelace"
```

OSL supports `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `++=`, `??=`, and `=??`. Postfix `++` and `--` are statements. In an expression, `++` means concatenation.

```osl
string label = "attempts: " ++ attempts
```

## Scope

Variables declared inside a function are local to that call. A local declaration may shadow a global.

```osl
string status = "global"

def readStatus() string (
  string status = "local"
  return status
)
```

Blocks do not create a separate variable lifetime in the same way a function does. A variable declared in an `if`, loop, or other block remains readable after that block ends. Keep declarations close to their use and avoid reusing one name for unrelated values.

Sibling blocks may each declare the same name with a different type. The later declaration applies until its block ends. After that block, the name refers to the earlier declaration again.

```osl
def describe(boolean compact) (
  if compact (
    int size = 1
    log size
  ) else (
    string size = "large"
    log size
  )
)
```

A typed redeclaration with another type still reports `'size' is already declared as int` when it appears in the same block, in a block nested inside the original declaration's block, or at function level after that block. Assign without a type to update the existing variable, or choose a new name.

Statement keywords cannot name a variable or parameter: `return`, `if`, `else`, `for`, `while`, `until`, `loop`, `switch`, `case`, `default`, `break`, `continue`, `def`, `class`, `struct`, `enum`, `import`, `export`, `defer`, and `throw`. The compiler reports the declaration, for example `'until' is a reserved keyword and cannot be used as a variable name; choose another name such as 'untilValue'`.

## Explicit `main`

You can define a zero-argument `main` function. The runtime calls it after top-level statements finish.

```osl
log "setup"

def main() (
  log "main"
)
```

Most applications use top-level code as the composition root. The OriginChats server follows this pattern in `src/main.osl`: it declares shared state, imports feature directories, mounts routes, then starts the server.

## Comments and statement separators

Use `//` for line comments. Newlines separate statements. Semicolons are unnecessary.

```osl
// Requests expire after five minutes.
int timeout = 300
```
