# Perl

The "«bold»full arithmetic + control flow + function calls«/»" tier: arrays (`@a`), hashes (`%h`), subroutines, string handling, loops and recursion all work - enough for something the size of a word-frequency counter.

`print` takes several arguments at once (printed back to back with no separator), and `$var` is interpolated inside double-quoted strings.

## How to run it on your phone

```
vml run examples/perl/app/wordcount.pl
```

Compiling takes a while (one to two minutes for C, a few seconds for scripting languages).
Once the program is running, **the gamepad is at the bottom of the screen** - the D-pad
and the four action buttons are all there. Tap the back arrow in the top-right corner to
return to the Shell.

## What to know before you write

- Declare with `my $x` / `my @a` / `my %h`; «bold»you can also use a variable without declaring it«/» (it lands in the globals, value 0)
- «bold»A newline is a statement separator«/», so the `;` at the end of a line can often be left out
- Call `ui_*` directly (the front end emits a `CALL` on the bare name); `use vml_ui;` is a «bold»declaration only«/» - remove it and the program still compiles
- Print with `print` / `say` (`say` appends a newline for you)
- `$#a` is the last index of an array and `scalar(@a)` its length

## Examples

| File | What it shows |
|---|---|
| `app/wordcount.pl` | Word frequency counting (`split` plus a hash tally plus `scalar`) |
| `hello.pl` | The smallest program (multi-argument `print` plus double-quote interpolation) |
| `test/syntax.pl` | A syntax tour: one form used for every feature |

## Pitfalls we hit on real devices

- ⚠ «bold»There are no runtime type tags«/»: types are inferred at «bold»compile time«/», and parameters read out of `@_` plus the return values of unknown library functions are of «bold»unknown type«/» (arithmetic goes integer, concatenation treats them as pointers). So `print`ing an unknown-type parameter that is really a string prints its «bold»address«/»; concatenate it once to be safe.
- ⚠ There are «bold»two«/» comparison families (numeric `== != < > <= >=` and string `eq ne lt gt le ge`), and «bold»using the wrong one is not an error«/» - it just compares by the other rule.
- ⚠ `sort` / `reverse` / `each` / `ucfirst` / `lcfirst` / `rindex` are «bold»not implemented and report a compile-time error«/» (never silently); a bad `push` argument errors too.
- «bold»`@arr` is not interpolated«/» inside a double-quoted string (array interpolation would need to join the elements, and their type cannot be decided at compile time) - use `join($sep, @arr)`.
- No references / dereferencing / anonymous arrays or hashes / regexes / packages / objects; integers are «bold»32-bit«/».
- A subroutine «bold»takes exactly one argument«/» (the address of the `@_` argument array); `my ($a, $b) = @_;` indexes into it «bold»by position«/». Scoping is «bold»flat«/»: `my` allocates a function-level stack slot, not a block-scoped one.
- Float printing goes through the shared runtime with «bold»a fixed 6 decimal places«/» (`3.4` may show as `3.399999` - that is the shared dtoa rounding, not the front end miscomputing).

---

The rest of this page is taken straight from the VML source (`third_party/vml/VMLPrepares/PerlCompiler/`):
the `README` covers what this front end supports and how to compile it, and the
language reference covers the syntax itself. When upstream changes, regenerating
this page brings it up to date.

## Language reference

### The Perl subset implemented by the PerlCompiler frontend

This document is the «bold»implementation contract«/» of the Perl frontend: what syntax
and semantics are supported, where the silent traps are, and what is deliberately
«bold»not supported«/» (and «bold»reports a compile-time error«/» instead of pretending).
The Chinese version is [`PERL_LANGUAGE_SPEC.md`](PERL_LANGUAGE_SPEC.md).

> «bold»Version«/»: v1.0 | «bold»Date«/»: 2026-10-13 | «bold»Revised by«/»: Shenzhen Tanso Intelligent Technology Co., Ltd.

#### Specification standard

| Field | Value |
|:-----|:----|
| «bold»Target standard«/» | Perl 5 subset |
| «bold»Publication year«/» | 1994 |
| «bold»Completeness«/» | Subset (not itemised) |
| «bold»Tests«/» | 24 passing / 0 failing |

#### Keywords

`my` `our` `sub` `if` `elsif` `else` `unless` `while` `until` `for` `foreach`
`do` `last` `next` `redo` `return` `and` `or` `not` `print` `say` `use`
`defined` `undef` `eq` `ne` `lt` `gt` `le` `ge` `cmp` `qw`

#### Overview
A subset of Perl 5 is supported, compiled to VML assembly. Variables carry «bold»sigils«/» (`$` scalar / `@` array / `%` hash; `$x`, `@x` and `%x` are «bold»three independent names«/» at the lexical level) and a «bold»newline is a statement separator«/» (equivalent to `;`). It «bold»deliberately does not run the C preprocessor«/» (Perl uses `#` for line comments, so treating `#` as a directive marker would turn every comment into an instruction). The frontend has «bold»no runtime type tags«/»: it picks between the integer, float and string channels using what can be decided at compile time (parameters read out of `@_` and the return values of unknown library functions are of unknown type — see §8). «bold»Unimplemented builtins produce a compile-time error«/»; there is no fallback that "quietly runs as 0".

#### Supported language features

The features this frontend accepts are organised by the sections below; each one states what is accepted and what it means:

- «bold»Lexical«/»: comments, numbers, single-quoted strings (only `\\` and `\'`), double-quoted strings with `$name` / `${name}` interpolation, identifiers, sigils, `$#a`, newline as separator — §1
- «bold»Variables and declarations«/»: `my` / `our` (synonymous in this subset), separate slots for scalars / arrays / hashes, usable without declaration, flat scoping — §2
- «bold»Expressions and operators«/»: arithmetic (`/` is float division, `«bold»` is integer power), `.` concatenation and `x` repetition, «/»two** comparison families (numeric and string), two logical families, `++` / `--` — §3
- «bold»Statements and control flow«/»: `if` / `elsif` / `else` / `unless`, `while` / `until`, `for` / `foreach`, postfix modifiers, `last` / `next` / `redo` — §4
- «bold»Strings«/»: multi-argument `print` / `say`, `length` / `substr` / `index` / `uc` / `lc` / `chomp` / `chop` / `sprintf` / `int` / `abs`, `@arr` is not interpolated — §5
- «bold»Arrays and hashes«/»: `$a[i]` (negative indices count from the end), `$#a`, `push` / `pop` / `shift` / `unshift`, `join` / `split`, `%h` / `exists` / `delete` / `keys` / `values` — §6
- «bold»Subroutines and `use`«/»: `sub` definition and calls (including the `sub f($x, $y)` form), call-before-define, `use NAME;` reading a declaration file — §7
- «bold»Compile-time type inference«/»: why it can "guess wrong" (a tradeoff accepted on purpose) — §8
- «bold»Deliberately unsupported«/»: regexes, references and dereferencing, packages and objects, package semantics for `local` / `state`, file handles, 64-bit integers — most report a compile-time error — §9

See the sections below for details.

#### 0. Principles

* «bold»Bar«/»: you can write an ordinary program — arithmetic, control flow, function
  calls, arrays, hashes, string handling.
* «bold»No silent fallback«/»: there is no builtin that "quietly runs as 0". Unimplemented
  builtins (e.g. `sort`) produce a compile-time error, and a bad `push` argument does
  too. Better to refuse than to return a wrong answer.
* «bold»AOT, no reflection, no hard-coded addresses«/»: only standard VML instructions and
  `CALL`s to named labels are emitted.

#### 1. Lexical

| Form | Notes |
|---|---|
| Comments | `#` to end of line |
| Numbers | decimal integer, `0x…` hex, fractional (`1.5`). «bold»Integers are 32-bit«/» (extra bits truncated) |
| Single-quoted | only `\\` and `\'` are escapes; everything else is literal |
| Double-quoted | `\n \t \r \0 \\ \" \$ \@`; `$name` / `${name}` «bold»interpolate«/»; `@name` does «bold»not«/» (see §5) |
| Identifiers | letters / digits / `_` / CJK |
| Sigils | `$` scalar, `@` array, `%` hash. A `%` followed by an identifier is a sigil, otherwise it is modulo |
| `$#a` | last index of array `a` (length − 1) |
| Newline | «bold»is a statement separator«/» (equivalent to `;`), but suppressed when the previous token is an operator/comma/open bracket, so multi-line expressions still work |

Keywords: `my our sub if elsif else unless while until for foreach do last next redo
return and or not print say use defined undef eq ne lt gt le ge cmp qw`.

#### 2. Variables and declarations

* `my $x` / `my @a` / `my %h`; `our` is a synonym (there is **no real package-variable
  semantics** here — `our` behaves like `my`).
* `$x` / `@x` / `%x` are «bold»three independent names«/» (the sigil decides what is fetched);
  the frontend keeps a slot per `(sigil, name)`.
* «bold»No declaration required«/» (Perl without `use strict`): an undeclared scalar lands in
  the data section as a global initialised to 0.
* «bold»Scoping is flat«/» (an intentional simplification): `my` allocates a function-level
  stack slot with no block scope; two same-named `my`s share one slot. Subroutines share
  one symbol table (the same trade-off the Lua frontend makes).

#### 3. Expressions and operators

* Arithmetic: `+ - * / % **`.
  * `/` is «bold»floating-point division«/» (Perl semantics: `7/2` → `3.5`); if either side is
    a float the whole expression goes through doubles.
  * `«bold»` is «/»integer power** (shared library `ipow`). Convert the base yourself for floats.
* Strings: `.` concatenation (numbers are converted to decimal text), `x` repetition
  (`"ab" x 3`), `.=` append.
* «bold»Two«/» comparison families (the easiest trap):
  * numeric: `== != < > <= >=`
  * string: `eq ne lt gt le ge` (via `strcmp`)
  * mixing them up does not error — you just get the other comparison (exactly as in Perl).
  * `cmp` returns `-1/0/1`.
* Logic: `&& || !` and `and or not` are «bold»both«/» accepted, both «bold»short-circuit«/» and
  return «bold»0/1«/» (not Perl's "last operand"). The ternary `?:` is supported.
* `++` / `--`: prefix and postfix; «bold»postfix returns the old value«/». Implemented as the
  equivalent `+1` then `−2` (a single step).
* `unless` is `if(not …)`; `until` is `while(not …)`.

#### 4. Statements and control flow

* `if / elsif / else`, `unless`, `while`, `until`, `for / foreach`, and C-style
  `for (init; cond; step)`.
* Postfix modifiers: `… if cond;` / `… unless cond;` / `… while cond;`.
* `last` / `next` (`redo` approximated by `next`).
* A bare `{ … }` block is expanded in place. `use NAME;` is described in §7.

#### 5. Strings

* `print` takes multiple arguments and prints them «bold»back to back with no separator«/»;
  `say` appends a newline. A bare `print` prints `$_` (approximated as 0).
* `print @a` prints the «bold»element count«/» (element type is not statically known, see §8;
  use `foreach` to print elements).
* Builtins: `length`, `substr($s,$pos[,$len])` (negative index/length supported),
  `index($s,$sub[,$start])`, `uc`, `lc`, `chomp`, `chop`, `sprintf`
  (`%d %i %x %s %c %%` plus width/left-align; precision is skipped), `int`, `abs`.
* «bold»`@arr` does not interpolate inside double quotes«/» (array interpolation would have to
  join by element and the element type is not statically known). Use `join($sep, @arr)`.

#### 6. Arrays and hashes

* Arrays: `@a`, `$a[i]` (negative index counts from the end), `$#a`, `scalar(@a)`,
  `length(@a)`; `push / pop / shift / unshift` (the first argument must be an array
  variable); `join(SEP, @a)`; `split(PAT, STR)` (PAT is a «bold»string«/», matched literally —
  equivalent to the single-separator case).
* Hashes: `%h`, `$h{k}` (a bareword `k` becomes a string), `exists($h{k})`,
  `delete($h{k})`, `keys %h`, `values %h`, `scalar(%h)`. Keys are «bold»strings«/».
* «bold»Element types«/»: array elements / hash values default to «bold»integers«/». When an
  array/hash has been assigned a list of string literals (or comes from `keys` / `split`),
  the frontend records it as a "string array/hash" and `join` / `foreach` treat it as
  pointers. Mixing integers and strings in one array is «bold»not supported«/» — there are no
  runtime type tags.
* `sort` / `reverse` / `each` are «bold»not implemented and report a compile-time error«/».

#### 7. Subroutines and `use`

```perl
sub add { my ($x, $y) = @_; return $x + $y; }   # or sub add($x, $y) { … }
print add(3, 4);
```
* Calling convention: a subroutine takes «bold»exactly one argument«/» — the address of an
  argument array; `@_` is that array.
* `my ($a, $b) = @_;` indexes into it (0-based) in the body.
* «bold»Calling before defining is allowed«/» (the frontend scans `sub` names first).
* A subroutine without `return` returns 0.
* `use NAME;` reads the declaration file `Lib/perl/NAME.pl` and registers its `sub name`
  entries (function names) and `our $name = <number>;` entries (compile-time constants).
  The file is «bold»scanned as plain text«/», never executed. For library functions `use` is
  «bold»documentation-level«/»: the frontend already resolves calls by «bold»bare name«/»
  (`ui_2d_clear(0)` emits `CALL ui_2d_clear`), so removing `use` still compiles.

#### 8. Compile-time type inference (why it sometimes "guesses wrong")

There are «bold»no runtime type tags«/». The frontend picks the integer / float / string
channel from what is statically known:

* literals, assigned variables, the result of `.` / `x`, and string-returning user
  subroutines → known.
* «bold»Parameters read from `@_`«/» and returns from unknown library functions → «bold»unknown«/»:
  arithmetic treats them as integers, and string concatenation uses them «bold»as pointers«/».
* Consequences (accepted): `"v=" . $from_arg` is fine; but `print $from_arg` (printing an
  unknown-typed parameter on its own) uses the «bold»integer«/» channel — if it is really a
  string you will see an address. For safety, concatenate explicitly or add a `use`
  declaration.

#### 9. Deliberately unsupported (most report a compile-time error)

| Feature | Status |
|---|---|
| `sort` / `reverse` / `each` / `ucfirst` / `lcfirst` / `rindex` | «bold»compile-time error«/» "not implemented" |
| References `\`, deref, anonymous array/hash | unsupported |
| Regex `=~`, `s///`, `tr///` | unsupported |
| Packages / `package` / objects / `bless` | unsupported |
| `local` / `state` / package semantics of `our` | unsupported (`our` is `my`) |
| `substr` as an lvalue, `wantarray`, context (scalar vs list) | unsupported |
| File handles / `open` / `<>` / `printf` to a handle | unsupported (use shared libs / UI tools) |
| `qw()` | supported (splits into a string list) |
| 64-bit integers | unsupported (32-bit) |
| Float printing format | uses the shared runtime's `print_double`, «bold»fixed 6 decimals«/» (`3.4` may show as `3.399999` — that is the shared dtoa's rounding, not a frontend arithmetic error) |

#### 10. Criteria

`scripts/vml-perl-probe/run.sh` runs `bugs/vml_perl_bugs/t*.pl` and compares stdout
byte for byte. It goes through the «bold»real channel«/», `vmlcli <case> --lang perl`
(registry -> assemble -> link -> run), and deliberately does «bold»not«/» bypass the
registry (that path carries its own link set and would hide a missing language
module set). «bold»Measured (with the wiring plus `GenLib -m -l perl`): 24 pass / 0 fail.«/»

Red-ability has been «bold»tested twice«/»:
* «bold»Expected-value injection«/»: changing `t02`'s expectation `22 12 85 2` to `…3` makes it
  fail immediately.
* «bold»Source injection«/»: changing the postfix `+1` in `GenerateIncDec` to `+2` turns `t15`
  from `s=10|c=1` into `s=6|c=2` (fail); reverting turns it green again.

## Compiler README

### Perl frontend compiler (PerlCompiler)

Compiles a «bold»subset of Perl«/» to VML assembly. It is the 23rd frontend under
`third_party/vml/VMLPrepares/`, built like the others: `Lexer` / `Parser` /
`ASTNode` / `CodeGenerator` / `PerlPlugin`, deriving from `CompilerBase` and
plugged into the host through `CompilerPluginExBase`.

> What the language covers, and what it deliberately does not, is in
> [`PERL_LANGUAGE_SPEC.en.md`](PERL_LANGUAGE_SPEC.en.md).
> The exact wiring steps (host registration / GenLib / packaging) are in
> [`INTEGRATION.md`](INTEGRATION.md).

#### Positioning

It sits in the «bold»"full arithmetic + control flow + function calls"«/» tier — you
can write ordinary scripts (arrays / hashes / subroutines / string handling /
loops / recursion), not just a "hello world that compiles". `Examples/perl/app/wordcount.pl`
(word frequency counting) is the scale it targets.

#### Quick start

```bash
# 1) build the frontend itself (during development it is also a CLI)
dotnet build third_party/vml/VMLPrepares/PerlCompiler -c Release

# 2) compile a .pl into a **self-contained runnable** VML assembly
dotnet third_party/vml/VMLPrepares/PerlCompiler/bin/Release/net10.0/PerlCompiler.dll \
       examples/perl/hello.pl -o /tmp/hello.vml

# 3) run it
dotnet scripts/vmlcli/bin/Release/net10.0/vmlcli.dll /tmp/hello.vml \
       --vml-home third_party/vml --timeout 5
```

Once wired into the host (see INTEGRATION.md) you can just do
`vml run examples/perl/hello.pl`.

##### Probe suite

```bash
scripts/vml-perl-probe/run.sh              # all
scripts/vml-perl-probe/run.sh t07 t10      # by prefix
```

Cases live in `bugs/vml_perl_bugs/t*.pl` and go through the «bold»real channel«/»,
`vmlcli <case> --lang perl` (registry -> assemble -> link -> run); measured
«bold»24 pass / 0 fail«/».

> «bold»Wire it up first«/» (see `INTEGRATION.md`): (1) register perl into vmlcli's
> registry per items 1/5/7; (2) run `GenLib -m -l perl` to generate
> `Lib/perl/*.vml`. Without either one the suite either «bold»exits 2 loudly«/»
> (no perl in the registry) or «bold»lets the cases go red«/» (missing language
> module set -> bare-name `.linked` resolves into another language). The suite
> deliberately does «bold»not«/» bypass the registry: that path carries its own link
> set and would hide a missing-module defect in the real channel.

#### Files

| File | Role |
|---|---|
| `TokenType.cs` / `Token.cs` | Token model |
| `Lexer.cs` | Lexer (`#` comments, three sigils, single/double quoted strings, newline as a statement separator) |
| `InterpolationParser.cs` | `$var` interpolation and escapes inside double-quoted strings |
| `ASTNode.cs` | AST nodes |
| `Parser.cs` | Parser (with paren-less list operators, postfix `if/unless`, ternary, `++`/`--`) |
| `CodeGenerator.cs` | Code generation: core + statements (slots / control flow / subs / print) |
| `CodeGenerator.Expressions.cs` | Expressions, type inference, builtin dispatch |
| `PerlCompiler.cs` | Static entry point (read file + `use` declarations + link stdlib) |
| `PerlPlugin.cs` | Plugin adapter (derives `CompilerPluginExBase`) |
| `Program.cs` | Development CLI |

Runtime and libraries:

| Path | Role |
|---|---|
| `Lib/perl/perlrt.c` | Language-specific runtime source (dynamic arrays `@a` / hashes `%h` / string helpers, `pl_` prefix) |
| `Lib/perl/perlrt.vml` | The compiled module (produced by `vmlcli --rebuild-lib`) |
| `Lib/perl/builtin.vml` | Standard library list (a `.linked` list the compiler reads automatically) |
| `Lib/perl/vml_ui.pl` | Declaration file for the UI library (constants + function names, for `use vml_ui;`) |

#### Design notes (where it differs from other frontends)

* «bold»No C preprocessor.«/» Perl uses `#` for line comments while `CompileFileStandard`
  runs the C preprocessor as soon as it sees a `#`. So `PerlCompiler` reads the file
  itself and sets `CurrentSourceFile` itself.
* «bold»A subroutine takes one "argument array".«/» `sub` compiles to a function taking a
  single argument — the address of the `@_` array; `my ($a, $b) = @_;` indexes into it
  in the body. This gives variadic subs for free and avoids one branch per arity.
  The call site is `EmitBuildArray(args)` + `CALL func_<name>`.
* «bold»Arrays/hashes have a C runtime.«/» `Lib/perl/perlrt.c` is compiled to
  `Lib/perl/perlrt.vml`. Layout is in the same family as `Lib/shared/src/array.c`
  (`[len, e0, e1, …]`); hashes carry an extra capacity header. Growing operations
  (push / unshift / set) «bold»return a new pointer«/» and the frontend writes it back.
* «bold»`%` is ambiguous«/»: `%h` is a sigil while `$a % $b` is modulo — the lexer decides
  by whether an identifier follows.

#### Known limitations

See the "not supported / intentionally simplified" section of
`PERL_LANGUAGE_SPEC.en.md`. In short: `sort` / `reverse` / `each` / `ucfirst` /
`lcfirst` / `rindex` are «bold»not implemented and report a compile-time error«/»
(no silent fallback); `@array` does not interpolate inside double quotes; no
references / packages / regex / objects; integers are 32-bit.

#### License

Same as the rest of the repository (VML has forked from upstream, see
`third_party/vml/FORK.md`).
