Add proper docs

This commit is contained in:
Stone_Red
2026-03-04 23:33:08 +01:00
parent 163bf677ae
commit 882c4f3a51
7 changed files with 1198 additions and 8 deletions
+10 -6
View File
@@ -1,12 +1,17 @@
# YesNt # YesNt
> YesNt is a imperative and interpreted language inspired by the Assembly language. > YesNt is a imperative and interpreted language inspired by the Assembly language.
## Syntax ## Documentation
Current language syntax is documented in [SYNTAX_V2.md](SYNTAX_V2.md). | Document | Description |
| -------------------------------------------------------- | ----------------------------------------- |
| [docs/README.md](docs/README.md) | Getting started |
| [docs/language-reference.md](docs/language-reference.md) | Full language reference |
| [docs/library-api.md](docs/library-api.md) | Embedding the interpreter as a C# library |
| [docs/editor.md](docs/editor.md) | Using the terminal code editor |
Example: ## Quick example
```ynt ```ynt
var name = world var name = world
@@ -16,6 +21,5 @@ print_line Hello ${name}
## Run ## Run
```bash ```bash
dotnet run --project YesNt.Interpreter -- path/to/script.ynt dotnet run --project YesNt.Interpreter.App -- path/to/script.ynt
``` ```
+11 -1
View File
@@ -48,6 +48,14 @@ internal class InputHandler(TextEditor textEditor)
textEditor.CursorPosition.X = textEditor.Lines.Count > textEditor.CursorPosition.Y ? textEditor.Lines[textEditor.CursorPosition.Y].TrimEnd().Length : 0; textEditor.CursorPosition.X = textEditor.Lines.Count > textEditor.CursorPosition.Y ? textEditor.Lines[textEditor.CursorPosition.Y].TrimEnd().Length : 0;
return true; return true;
case ConsoleKey.R:
ExecuteWithDebugScreen("run", false, false);
return true;
case ConsoleKey.D:
ExecuteWithDebugScreen("debug", true, false);
return true;
case ConsoleKey.F: case ConsoleKey.F:
textEditor.FormatLines(); textEditor.FormatLines();
textEditor.Display(true); textEditor.Display(true);
@@ -352,6 +360,8 @@ internal class InputHandler(TextEditor textEditor)
return; return;
} }
Mode previousMode = textEditor.EditMode;
textEditor.EditMode = Mode.Debug; textEditor.EditMode = Mode.Debug;
textEditor.IsStepDebugMode = stepMode; textEditor.IsStepDebugMode = stepMode;
Console.Clear(); Console.Clear();
@@ -373,6 +383,6 @@ internal class InputHandler(TextEditor textEditor)
_ = Console.ReadKey(); _ = Console.ReadKey();
WriteStatus(string.Empty); WriteStatus(string.Empty);
textEditor.IsStepDebugMode = false; textEditor.IsStepDebugMode = false;
textEditor.EditMode = Mode.Command; textEditor.EditMode = previousMode;
} }
} }
+2 -1
View File
@@ -81,7 +81,8 @@ internal static partial class Evaluator
/// <summary> /// <summary>
/// Evaluates a numeric arithmetic expression string and returns the result as a string. /// Evaluates a numeric arithmetic expression string and returns the result as a string.
/// Supports <c>+</c>, <c>-</c>, <c>*</c>, and <c>/</c> operators. /// Supports <c>+</c>, <c>-</c>, <c>*</c>, <c>/</c>, <c>%</c> (modulo), and <c>^</c> (power) operators
/// with standard precedence (<c>^</c> highest, <c>+</c>/<c>-</c> lowest) and parentheses.
/// Adjacent sign characters (<c>++</c>, <c>--</c>, <c>-+</c>, <c>+-</c>) are normalised before evaluation. /// Adjacent sign characters (<c>++</c>, <c>--</c>, <c>-+</c>, <c>+-</c>) are normalised before evaluation.
/// </summary> /// </summary>
/// <param name="input">The arithmetic expression to evaluate.</param> /// <param name="input">The arithmetic expression to evaluate.</param>
+47
View File
@@ -0,0 +1,47 @@
# YesNt Documentation
YesNt is a line-based, interpreted scripting language inspired by assembly.
Each line is one statement. There are no multi-line expressions.
## Guides
| Document | Description |
| ------------------------------------------- | ---------------------------------------------------- |
| [Language Reference](language-reference.md) | Every statement, token, and operator in the language |
| [Library API](library-api.md) | How to embed the interpreter in a C# project |
| [Editor](editor.md) | How to use the terminal code editor |
## Quick start
### Running a script from the command line
```bash
dotnet run --project YesNt.Interpreter.App -- path/to/script.ynt
```
### Hello world
```ynt
print_line Hello, world!
```
### Variables and output
```ynt
var name = Alice
print_line Hello, ${name}!
```
### Functions
```ynt
func greet:
var msg = Hello, ${name}!
print_line ${msg}
return
var name = Bob
call greet
```
Script files use the `.ynt` extension by convention.
+52
View File
@@ -0,0 +1,52 @@
# YesNt Code Editor
`YesNt.CodeEditor` is a terminal editor for writing, formatting, running, and debugging YesNt scripts.
## Start the editor
```bash
dotnet run --project YesNt.CodeEditor -- [optional-path-to-file.ynt]
```
If you pass a file path, it is loaded on startup.
## Modes
- **Command mode:** enter editor commands in the `>>>` prompt.
- **Edit mode:** direct text editing with keyboard navigation.
- **Debug mode:** script output/debug information while running.
## Command mode commands
| Command | Description |
| --------------------- | --------------------------------------------------------- |
| `edit` | Switch to edit mode |
| `line <n>` | Jump to line `n` and switch to edit mode |
| `save [path]` | Save to current path or a new path |
| `load <path>` | Load a file |
| `new` | Create new file |
| `format` | Auto-format indentation |
| `run [path]` | Run script |
| `debug [path] [step]` | Run in debug mode (`step` enables step-by-step execution) |
| `exit` | Close the editor |
## Edit mode controls
- Arrow keys: move cursor
- Enter: split line
- Backspace/Delete: remove characters/merge lines
- **Alt+C:** return to command mode
- **Alt+T:** jump to top
- **Alt+B:** jump to bottom
- **Alt+S:** jump to start of line
- **Alt+E:** jump to end of line
- **Alt+R:** run script
- **Alt+D:** run debug mode
- **Alt+F:** format current file
## Formatter behavior (quick summary)
- Indents `func`, `if`, `else`, and `while` blocks.
- Dedents on `return`, `end_if`, and `end_while`.
- `exit` / `throw` / `error` close active non-function blocks for following lines.
- Comment lines (`# ...`) are kept unindented.
+811
View File
@@ -0,0 +1,811 @@
# YesNt Language Reference
YesNt is a line-based scripting language. Every non-empty, non-comment line is one statement.
Execution proceeds top-to-bottom unless a control-flow statement changes the line counter.
---
## Table of contents
1. [Basic rules](#basic-rules)
2. [Comments](#comments)
3. [String literals](#string-literals)
4. [Variables](#variables)
5. [Console I/O](#console-io)
6. [Arithmetic](#arithmetic)
7. [Conditions](#conditions)
8. [Control flow](#control-flow)
9. [Functions](#functions)
10. [Lists](#lists)
11. [Processing](#processing)
12. [System](#system)
13. [Predefined tokens](#predefined-tokens)
14. [Termination](#termination)
---
## Basic rules
- Lines are trimmed of leading and trailing whitespace before execution.
- Blank lines are silently skipped.
- Lines starting with `#` are comments and are silently skipped.
- Variable interpolation uses `${name}` and is evaluated before the statement runs.
- Special characters inside string literals are encoded internally and decoded on output — this is transparent to scripts.
---
## Comments
```ynt
# This is a comment.
print_line Hello # inline comments are NOT supported — everything after print_line is the argument
```
Only whole-line comments (lines whose first non-whitespace character is `#`) are supported.
---
## String literals
Double-quoted strings protect their content from keyword matching and allow escape sequences.
```ynt
print_line "Hello, world!"
print_line "Line one\nLine two"
print_line "She said \"hi\""
```
| Escape | Meaning |
| ------ | -------------------- |
| `\n` | Newline |
| `\r` | Carriage return |
| `\t` | Horizontal tab |
| `\"` | Literal double-quote |
| `\\` | Literal backslash |
Variable interpolation (`${name}`) is **not** evaluated inside string literals — the braces
and content are passed through verbatim.
```ynt
var x = world
print_line "${x}" # prints the literal text: ${x}
print_line "hello " ${x} # prints: hello world (interpolation outside the literal)
```
---
## Variables
### Local variables — `var`
```
var <name> = <value>
```
Defines or updates a variable scoped to the current function (or the top level if called outside a function).
The value is everything after `=`, trimmed.
```ynt
var count = 0
var greeting = Hello, world!
```
Variable names may only contain letters and digits (`[a-zA-Z0-9]`).
### Global variables — `global`
```
global <name> = <value>
```
Defines or updates a variable that is visible across all function scopes and background tasks.
```ynt
global total = 100
```
### Reading a variable — `${name}`
`${name}` is an inline token that is replaced with the variable's value before the statement executes.
It can appear anywhere in a line and multiple occurrences are replaced left to right.
Local variables are checked first; if not found, the global table is checked.
```ynt
var a = 5
var b = 10
print_line ${a} plus ${b}
```
### Deleting a variable — `delete`
```
delete <name>
```
Removes the variable. Local scope is checked first; if not found, the global table is used.
Raises an error if the variable does not exist in either scope.
```ynt
var temp = scratch
delete temp
```
---
## Console I/O
### Print with newline — `print_line`
```
print_line <text>
print_line
```
Writes `<text>` followed by a newline. With no argument, writes a blank line.
```ynt
print_line Hello!
print_line
print_line Done.
```
### Print without newline — `print`
```
print <text>
```
Writes `<text>` without a trailing newline.
```ynt
print Enter your name:
var name = %read_line
print_line Hello, ${name}!
```
### Read a line of input — `%read_line`
`%read_line` is an inline token that is replaced with one line of text read from standard input.
```ynt
var answer = %read_line
print_line You typed: ${answer}
```
### Read a single key — `%read_key`
`%read_key` is an inline token that is replaced with the single character pressed by the user (no Enter required).
```ynt
print Press any key...
var key = %read_key
print_line You pressed: ${key}
```
### Clear the console — `clear`
```
clear
```
Clears the console window.
---
## Arithmetic
Arithmetic is a **postfix** modifier applied at the end of a line with the `calc` keyword.
```
<expression> calc
```
Any numeric sub-expression matching the pattern `number op number [op number …]` is evaluated
and replaced with the result. Supported operators (highest to lowest precedence):
| Operator | Operation |
| -------- | ----------------------------- |
| `(…)` | Parentheses (evaluated first) |
| `^` | Exponentiation |
| `%` | Modulo |
| `/` | Division |
| `*` | Multiplication |
| `-` | Subtraction |
| `+` | Addition (lowest precedence) |
Adjacent sign characters (`++`, `--`, `-+`, `+-`) are normalised before evaluation.
```ynt
var x = 3
var y = 4
var sum = ${x} + ${y} calc # 7
var expr = 2 + 3 * 4 calc # 14 (* before +)
var parens = (2 + 3) * 4 calc # 20
var power = 2 ^ 10 calc # 1024
var remainder = 17 % 5 calc # 2
```
---
## Conditions
Conditions are used in `if` and `while` statements. A condition is a string of the form:
```
<left> <operator> <right>
```
| Operator | Meaning |
| -------- | --------------------------------------------- |
| `==` | Equal (string comparison, case-sensitive) |
| `!=` | Not equal (string comparison, case-sensitive) |
| `<` | Less than (numeric) |
| `>` | Greater than (numeric) |
| `<=` | Less than or equal (numeric) |
| `>=` | Greater than or equal (numeric) |
Numeric comparisons (`<`, `>`, `<=`, `>=`) parse both sides with
culture-invariant decimal rules (`.` or `,` as decimal separator).
A bare value of `True` or `False` (case-insensitive) is also a valid condition.
```ynt
var x = 10
if ${x} > 5:
print_line x is greater than 5
end_if
```
---
## Control flow
### If / else / end_if
```
if <condition>:
<body>
else:
<alternative>
end_if
```
`else:` is optional. `if` / `else:` / `end_if` blocks can be nested.
```ynt
var score = 75
if ${score} >= 60:
print_line Pass
else:
print_line Fail
end_if
```
### While loop
```
while <condition>:
<body>
end_while
```
The condition is checked before each iteration. `while` / `end_while` blocks can be nested.
```ynt
var i = 1
while ${i} <= 5:
print_line ${i}
var i = ${i} + 1 calc
end_while
```
### Labels and goto
```
label <name>:
goto <name>
```
`label` marks a target. `goto` performs an unconditional jump to that label.
Labels are scoped to the current function; you cannot jump to a label outside the calling function.
```ynt
label loop:
print_line tick
goto loop
```
### Conditional goto
```
if <condition> goto <label>
```
Jumps to `<label>` only when the condition is true.
```ynt
var n = 0
label start:
var n = ${n} + 1 calc
if ${n} < 10 goto start
print_line done
```
### Conditional function call
```
if <condition> call <function>
```
Calls `<function>` only when the condition is true.
```ynt
var debug = True
if ${debug} == True call dump_state
```
---
## Functions
### Declaring a function — `func`
```
func <name>:
<body>
return
```
A function declaration registers the function name and the line it starts on.
The body runs until `return` is reached.
**Nested function declarations are not allowed.**
```ynt
func add:
var result = %in + %in calc
push_out ${result}
return
```
### Calling a function — `call`
```
call <name>
call <name> with <arg1>, <arg2>, …
```
`call` without `with` uses any values previously pushed with `push_in`.
`call … with …` pushes the comma-separated arguments and then calls the function.
```ynt
call greet
call add with 3, 7
```
### Input arguments
#### Push an input argument — `push_in`
```
push_in <value>
```
Pushes a value onto the input argument stack. Values are consumed in the order they were pushed —
the first `push_in` call is the first value consumed by `%in` inside the function.
```ynt
push_in Alice
call greet
```
#### Pop the next input argument — `%in`
`%in` is an inline token (only valid inside a function) that is replaced with the next value
popped from the argument stack.
```ynt
func greet:
var name = %in
print_line Hello, ${name}!
return
```
#### Check if input argument exists — `%has_in`
`%has_in` is replaced with `True` or `False` depending on whether the input stack is non-empty.
Only valid inside a function.
```ynt
func greet:
if %has_in == True call do_greet
return
```
### Output values
#### Push an output value — `push_out`
```
push_out <value>
```
Only valid inside a function. Pushes a return value onto the output stack.
#### Consume an output value — `%out`
`%out` is an inline token that pops and inserts the top value from the output stack.
Valid anywhere after a function call or list/processing statement that pushes to the output stack.
```ynt
call add with 3, 7
var total = %out
print_line ${total}
```
#### Check if output value exists — `%has_out`
`%has_out` is replaced with `True` or `False` depending on whether the output stack is non-empty.
```ynt
call maybe_produce
if %has_out == True call consume_result
```
### Clear the call stack — `clear_call_stack`
```
clear_call_stack
```
Discards all frames on the function call stack. Useful for error recovery.
---
## Lists
Lists are ordered, mutable sequences of strings. All list operations start with the keyword `list`.
### Create or reset — `list … new`
```
list <name> new
```
Creates an empty list. If the list already exists it is cleared.
### Add an item — `list … add`
```
list <name> add <value>
```
Appends `<value>` to the end of the list.
### Get an item — `list … get`
```
list <name> get <index>
```
Pushes the item at zero-based `<index>` onto the output stack. Access it with `%out`.
```ynt
list fruits new
list fruits add apple
list fruits add banana
list fruits get 0
var first = %out
print_line ${first}
```
### Set an item — `list … set`
```
list <name> set <index> <value>
```
Replaces the item at `<index>` with `<value>`.
### Remove an item — `list … remove`
```
list <name> remove <index>
```
Removes the item at `<index>`. Subsequent items shift down.
### Insert an item — `list … insert`
```
list <name> insert <index> <value>
```
Inserts `<value>` before position `<index>`.
### Get the length — `list … length`
```
list <name> length
```
Pushes the number of items onto the output stack.
```ynt
list fruits length
var n = %out
print_line ${n} items
```
### Clear all items — `list … clear`
```
list <name> clear
```
Removes all items but keeps the list alive.
### Delete a list — `list … delete`
```
list <name> delete
```
Removes the list entirely.
---
## Processing
### Arithmetic — `calc`
See [Arithmetic](#arithmetic).
### Decode a safe string — `eval`
```
<expression> eval
```
Decodes any internally-encoded characters in the current line back to their plain-text form.
Useful after producing text from string literal operations that you want to re-use as plain text.
### Run from the current line in a background task — `task`
```
<line> task
```
Starts a background interpreter from the current line. In that background run, the current line is
executed without the trailing `task`, and execution then continues through the remaining lines.
The main script continues immediately. The task shares the global variable table with the main script.
```ynt
print_line Starting background work... task
print_line Main thread continues.
```
#### Important behavior
- `task` does **not** run just one statement; it starts a second execution flow from that point onward.
- Without careful control flow, lines after the `task` statement may run twice:
once on the main thread and once in the background task.
- Global variables are shared between both flows.
#### Recommended pattern
Use a direct function call with `task`, and terminate inside that function with `exit`.
This keeps the worker logic isolated and avoids `goto` scaffolding.
```ynt
call background_job task
print_line Main thread keeps going
func background_job:
print_line Work in background
exit
```
This works well for task-only worker flows. In non-task/shared flows, prefer `return` if you want
to return to the caller instead of terminating that execution flow with `exit`.
### Sleep — `sleep`
```
sleep <milliseconds>
```
Pauses execution for the given number of milliseconds. Respects cancellation: if the script is
stopped (e.g. via `abort_all` from another task), `sleep` returns early.
```ynt
sleep 1000
print_line One second later.
```
### Get string length — `length`
```
length <text>
```
Pushes the character count of `<text>` onto the output stack.
```ynt
length Hello, world!
var len = %out
print_line ${len}
```
### Import another script — `import`
```
import <path>
```
Inlines the contents of another `.ynt` file at the current position.
The extension `.ynt` is appended automatically if omitted.
The path is resolved relative to the directory of the importing script.
```ynt
import utils
import lib/math.ynt
```
---
## System
### Execute a program — `exec`
```
exec <program>
exec <program> with <arg1>, <arg2>, …
```
Runs an external program and waits for it to finish.
Standard output and standard error are forwarded to the console in real time.
The exit code and each line of output are pushed onto the output stack (exit code on top).
```ynt
exec notepad
exec git with status, --short
var exit_code = %out
print_line Exited with ${exit_code}
```
You can also stage arguments with `push_in` before a bare `exec`:
```ynt
push_in --version
exec python
```
---
## Predefined tokens
These inline tokens are replaced with their value before the statement executes.
| Token | Replaced with |
| ------- | --------------------------------------------------- |
| `%time` | Current Unix timestamp (seconds) |
| `%os` | Operating system platform name (e.g. `Win32NT`) |
| `%cpu` | Processor architecture (e.g. `X64`) |
| `%is64` | `True` if the OS is 64-bit, otherwise `False` |
| `%pi` | The value of π |
| `%rand` | A random integer in the range `[32767, 2147483647)` |
```ynt
print_line Time: %time
print_line OS: %os
print_line PI: %pi
var roll = %rand
```
---
## Termination
### Normal exit — `exit`
```
exit
```
Terminates the script gracefully. Background tasks that are still running are not cancelled.
### Exit and cancel all tasks — `abort_all`
```
abort_all
```
Terminates the script and signals all background tasks to stop.
### Throw an error — `throw`
```
throw <message>
```
Terminates the script and all tasks with `<message>` as the error description.
A stack trace is printed showing the line and file where the error occurred.
```ynt
var x = -1
if ${x} < 0 call validate_fail
func validate_fail:
throw x must not be negative
return
```
### Non-fatal error — `error`
```
error <message>
```
Like `throw` but does **not** cancel background tasks.
---
## Full statement reference
| Statement | Form | Description |
| ------------------ | ------------------------------ | ------------------------------------------------------------ |
| `var` | `var name = value` | Define/update local variable |
| `global` | `global name = value` | Define/update global variable |
| `delete` | `delete name` | Delete variable |
| `print_line` | `print_line [text]` | Print line (optional text) |
| `print` | `print text` | Print without newline |
| `clear` | `clear` | Clear console |
| `if … :` | `if cond:` | Start conditional block |
| `else:` | `else:` | Else branch |
| `end_if` | `end_if` | End conditional block |
| `while … :` | `while cond:` | Start while loop |
| `end_while` | `end_while` | End while loop |
| `label` | `label name:` | Declare a jump target |
| `goto` | `goto name` | Unconditional jump |
| `if … goto` | `if cond goto name` | Conditional jump |
| `func` | `func name:` | Declare a function |
| `call` | `call name` | Call a function |
| `call … with` | `call name with a, b` | Call a function with arguments |
| `if … call` | `if cond call name` | Conditional function call |
| `push_in` | `push_in value` | Push input argument |
| `push_out` | `push_out value` | Push output value (inside function) |
| `return` | `return` | Return from function |
| `clear_call_stack` | `clear_call_stack` | Clear call stack |
| `list … new` | `list name new` | Create/reset list |
| `list … add` | `list name add value` | Append to list |
| `list … get` | `list name get index` | Get item → `%out` |
| `list … set` | `list name set index value` | Replace item |
| `list … remove` | `list name remove index` | Remove item |
| `list … insert` | `list name insert index value` | Insert item |
| `list … length` | `list name length` | Get count → `%out` |
| `list … clear` | `list name clear` | Clear all items |
| `list … delete` | `list name delete` | Delete list |
| `calc` | `expr calc` | Evaluate arithmetic |
| `eval` | `expr eval` | Decode string encoding |
| `task` | `line task` | Run current line (without `task`) and continue in background |
| `sleep` | `sleep ms` | Sleep N milliseconds |
| `length` | `length text` | Get char count → `%out` |
| `import` | `import path` | Inline-include a `.ynt` file |
| `exec` | `exec prog` | Run external program |
| `exec … with` | `exec prog with a, b` | Run external program with args |
| `exit` | `exit` | Graceful exit |
| `abort_all` | `abort_all` | Exit and cancel all tasks |
| `throw` | `throw message` | Fatal error (cancels all tasks) |
| `error` | `error message` | Non-fatal error |
| `%read_line` | inline | Insert one line of user input |
| `%read_key` | inline | Insert one keypress |
| `%in` | inline | Pop next function input argument |
| `%has_in` | inline | `True`/`False` if input stack non-empty |
| `%out` | inline | Pop top output value |
| `%has_out` | inline | `True`/`False` if output stack non-empty |
| `%time` | inline | Unix timestamp |
| `%os` | inline | OS platform |
| `%cpu` | inline | Processor architecture |
| `%is64` | inline | `True` if 64-bit OS |
| `%pi` | inline | Value of π |
| `%rand` | inline | Random integer |
+265
View File
@@ -0,0 +1,265 @@
# YesNt Library API
The `YesNt.Interpreter` project is a .NET 8 class library. You can reference it from any C# project
to embed the YesNt interpreter and run scripts programmatically.
---
## Table of contents
1. [Adding the reference](#adding-the-reference)
2. [Running a script file](#running-a-script-file)
3. [Running an in-memory script](#running-an-in-memory-script)
4. [Capturing output (debug mode)](#capturing-output-debug-mode)
5. [Adding custom statements](#adding-custom-statements)
6. [Stopping a script](#stopping-a-script)
7. [Reading registered statements](#reading-registered-statements)
8. [API reference](#api-reference)
---
## Adding the reference
Add a project reference to `YesNt.Interpreter` in your `.csproj`:
```xml
<ItemGroup>
<ProjectReference Include="..\YesNt.Interpreter\YesNt.Interpreter.csproj" />
</ItemGroup>
```
Then add the using directive:
```csharp
using YesNt.Interpreter.Runtime;
```
---
## Running a script file
```csharp
var interpreter = new YesNtInterpreter();
interpreter.Execute("path/to/script.ynt");
```
`Execute` is synchronous - it returns when the script finishes (or terminates with an error).
---
## Running an in-memory script
Supply a `List<string>` instead of a file path:
```csharp
var lines = new List<string>
{
"var x = 42",
"print_line The answer is ${x}",
};
var interpreter = new YesNtInterpreter();
interpreter.Execute(lines);
```
---
## Capturing output (debug mode)
Pass `isDebugMode: true` to suppress direct console writes. Output is delivered through the
`OnDebugOutput` event instead, and `OnLineExecuted` fires after executed lines.
```csharp
var interpreter = new YesNtInterpreter();
var output = new System.Text.StringBuilder();
interpreter.OnDebugOutput += text => output.Append(text);
interpreter.OnLineExecuted += args =>
{
if (args is null)
{
// null means execution reached end-of-file (EOF)
Console.WriteLine("Script reached EOF.");
return;
}
Console.WriteLine($"Line {args.LineNumber}: {args.CurrentLine}");
};
interpreter.Execute(new List<string> { "print_line hello" }, isDebugMode: true);
Console.Write(output);
```
`OnLineExecuted` receives a `DebugEventArgs` with:
| Property | Type | Description |
| -------------- | -------- | ------------------------------------------- |
| `LineNumber` | `int` | 1-based line number |
| `OriginalLine` | `string` | Raw source text |
| `CurrentLine` | `string` | Text after all substitutions |
| `IsTask` | `bool` | `true` if executed inside a background task |
| `TaskId` | `int` | Task identifier (0 for the main thread) |
`OnLineExecuted` is invoked with `null` only when execution reaches end-of-file (EOF).
If execution stops via `exit`, `throw`, `error`, or `Stop()`, no terminal `null` event is emitted.
---
## Adding custom statements
Use `AddStatement` to register keywords before calling `Execute`.
### Simple keyword at the start of a line
```csharp
using YesNt.Interpreter.Enums;
var interpreter = new YesNtInterpreter();
interpreter.AddStatement("log", SearchMode.StartOfLine, SpaceAround.End, args =>
{
Console.WriteLine($"[LOG] {args}");
});
interpreter.Execute(new List<string> { "log Hello from custom statement" });
```
### With a syntax-highlight colour
```csharp
interpreter.AddStatement("log", SearchMode.StartOfLine, SpaceAround.End,
ConsoleColor.Cyan,
args => Console.WriteLine($"[LOG] {args}"));
```
### Using a pre-built `StatementAttribute`
```csharp
using YesNt.Interpreter.Attributes;
var attr = new StatementAttribute("log", SearchMode.StartOfLine, SpaceAround.End)
{
Priority = Priority.VeryLow,
};
interpreter.AddStatement(attr, args => Console.WriteLine($"[LOG] {args}"));
```
### `SearchMode` values
| Value | The keyword matches when… |
| ------------- | -------------------------------------------- |
| `StartOfLine` | the line **starts with** the keyword |
| `EndOfLine` | the line **ends with** the keyword |
| `Contains` | the keyword appears **anywhere** in the line |
| `Exact` | the line is **exactly** the keyword |
### `SpaceAround` values
| Value | Space requirement |
| ---------- | -------------------------------- |
| `None` | No surrounding spaces required |
| `Start` | A space must precede the keyword |
| `End` | A space must follow the keyword |
| `StartEnd` | Spaces required on both sides |
Custom statements run at `Priority.Normal` by default. Lower priority values run first.
Use `StatementAttribute.Priority` to control ordering relative to built-in statements.
---
## Stopping a script
```csharp
var interpreter = new YesNtInterpreter();
// Start the script on a background thread so we can stop it from this thread.
var thread = new System.Threading.Thread(() =>
interpreter.Execute(new List<string> { "while True:", "sleep 100", "end_while" }));
thread.Start();
System.Threading.Thread.Sleep(500);
interpreter.Stop(); // signals the script to terminate at the next line boundary
thread.Join();
```
---
## Reading registered statements
`StatementInformation` returns a read-only snapshot of every registered statement.
This is useful for building syntax highlighters or tooling.
```csharp
var interpreter = new YesNtInterpreter();
foreach (var info in interpreter.StatementInformation)
{
Console.WriteLine($"{info.Name,-20} {info.SearchMode,-12} color={info.Color}");
}
```
Each `StatementInformation` object exposes:
| Property | Type | Description |
| -------------------------- | -------------- | ---------------------------- |
| `Name` | `string` | The keyword |
| `SearchMode` | `SearchMode` | Where the keyword is matched |
| `SpaceAround` | `SpaceAround` | Required surrounding spaces |
| `Color` | `ConsoleColor` | Syntax-highlight colour |
| `IgnoreSyntaxHighlighting` | `bool` | Whether to skip highlighting |
| `Separator` | `string?` | Optional required sub-string |
---
## API reference
### `YesNtInterpreter`
```csharp
public class YesNtInterpreter
```
#### Constructor
```csharp
public YesNtInterpreter()
```
Creates a new interpreter instance and registers all built-in statements.
#### Events
```csharp
public event Action<string> OnDebugOutput;
public event Action<DebugEventArgs> OnLineExecuted;
```
Only raised in debug mode (`isDebugMode: true`).
`OnLineExecuted` receives `null` only on EOF completion.
#### Methods
```csharp
// Execute a .ynt file
public void Execute(string path, bool isDebugMode = false);
// Execute in-memory lines
public void Execute(List<string> lines, bool isDebugMode = false);
// Register a custom statement (full control)
public void AddStatement(StatementAttribute attribute, Action<string> handler);
// Register a custom statement (convenience overloads)
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, Action<string> handler);
public void AddStatement(string name, SearchMode searchMode, SpaceAround spaceAround, ConsoleColor color, Action<string> handler);
// Request graceful stop
public void Stop();
```
#### Properties
```csharp
// Read-only snapshot of all registered statements
public ReadOnlyCollection<StatementInformation> StatementInformation { get; }
```