Skip to content

Commit 0da39c0

Browse files
gh-5: Add type THR.
Add build.ps1
1 parent a53f4bc commit 0da39c0

File tree

10 files changed

+532
-107
lines changed

10 files changed

+532
-107
lines changed

asm-lang.exe

12.1 KB
Binary file not shown.

asm-lang.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import os
77
from typing import List, Optional
8+
import threading
89

910
from extensions import ASMExtensionError, ReplContext, load_runtime_services
1011
from interpreter import ASMRuntimeError, Environment, ExitSignal, Interpreter, TracebackFormatter
@@ -62,11 +63,30 @@ def run_repl(*, verbose: bool, services) -> int:
6263
had_output = False
6364
# Tracks whether any PRINT has occurred since the last REPL input.
6465
first_print_since_input = True
66+
main_thread_id = threading.get_ident()
6567

6668
def _output_sink(text: str) -> None:
6769
nonlocal had_output, first_print_since_input
6870
# If this is not the first PRINT since the last REPL input,
6971
# emit a leading newline so outputs appear on separate lines.
72+
if threading.get_ident() != main_thread_id:
73+
# Background-thread output: ensure it appears on its own line
74+
# to avoid being glued to the prompt. Use a normal print so a
75+
# trailing newline is emitted.
76+
try:
77+
# If not first print since input, keep the separation.
78+
if not first_print_since_input:
79+
print()
80+
first_print_since_input = False
81+
had_output = True
82+
print(text)
83+
except Exception:
84+
# Best-effort: swallow to avoid crashing the REPL.
85+
pass
86+
return
87+
88+
# Main-thread REPL output: keep original inline behaviour so the
89+
# REPL can control newlines and prompt placement.
7090
if not first_print_since_input:
7191
print()
7292
first_print_since_input = False

build.ps1

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
$ErrorActionPreference = "Stop"
2+
3+
$ROOTDIR = Split-Path -Parent $MyInvocation.MyCommand.Definition
4+
Set-Location $ROOTDIR
5+
Write-Host "Building executable from $ROOTDIR..."
6+
pyinstaller --onefile --icon `
7+
"$ROOTDIR\docs\icon.png" `
8+
--add-data "$ROOTDIR\lexer.py;." `
9+
--add-data "$ROOTDIR\parser.py;." `
10+
--add-data "$ROOTDIR\interpreter.py;." `
11+
--add-data "$ROOTDIR\extensions.py;." `
12+
"$ROOTDIR\asm-lang.py"
13+
Write-Host "Build complete."
14+
Write-Host "Cleaning up build artifacts..."
15+
if (Test-Path "$ROOTDIR\asm-lang.exe") {
16+
Remove-Item "$ROOTDIR\asm-lang.exe"
17+
}
18+
Remove-Item "$ROOTDIR\build" -Recurse -Force
19+
Move-Item "$ROOTDIR\dist\asm-lang.exe" "$ROOTDIR\"
20+
Remove-Item "$ROOTDIR\dist" -Recurse -Force
21+
Remove-Item "$ROOTDIR\asm-lang.spec"
22+
Write-Host "Cleanup complete. Executable is located at $ROOTDIR\asm-lang.exe"

docs/SPECIFICATION.html

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
## 1. Overview
4747

48-
The language is a familiar statement-based, imperative language. Programs consist of variable declarations via assignment, expressions, and control-flow constructs such as `IF`, `ELSEIF`, `ELSE`, `WHILE`, and `FOR`. ASM-Lang has five runtime data types: binary integers (`INT`), binary floating-point numbers (`FLT`, IEEE754), strings (`STR`), non-scalar tensors (`TNS`), and first-class user-defined functions (`FUNC`). Identifiers, function parameters, and return values are statically typed; the type of every symbol must be declared when it is first introduced. Computation proceeds by evaluating expressions and executing statements in sequence, with explicit constructs for branching and looping. Input and output are modeled through built-in operators, in particular `INPUT` and `PRINT`.
48+
The language is a familiar statement-based, imperative language. Programs consist of variable declarations via assignment, expressions, and control-flow constructs such as `IF`, `ELSEIF`, `ELSE`, `WHILE`, and `FOR`. ASM-Lang has seven runtime data types: binary integers (`INT`), binary floating-point numbers (`FLT`, IEEE754), strings (`STR`), non-scalar tensors (`TNS`), first-class user-defined functions (`FUNC`), associative maps (`MAP`), and thread handles (`THR`). Identifiers, function parameters, and return values are statically typed; the type of every symbol must be declared when it is first introduced. Computation proceeds by evaluating expressions and executing statements in sequence, with explicit constructs for branching and looping. Input and output are modeled through built-in operators, in particular `INPUT` and `PRINT`.
4949
5050
The interpreter compiles source code into a single initial configuration (the seed state), which includes the program code, an empty variable environment, and an initial I/O history. It then advances execution by repeatedly applying a single, fixed small-step transition function that is independent of the particular program. A disassembler and log view expose all intermediate states so that every step and every control-flow decision is inspectable and replay-able.
5151
@@ -99,7 +99,7 @@
9999

100100
## 3. Data Model
101101

102-
ASM-Lang supports five runtime data types: binary integers, binary floating-point numbers, strings, non-scalar tensors, and first-class functions.
102+
ASM-Lang supports seven runtime data types: binary integers, binary floating-point numbers, strings, non-scalar tensors, first-class functions, associative maps, and thread handles.
103103

104104
Binary integer literal: an unsigned non-empty sequence of `{0,1}` (for example, `0`, `1`, `1011`), or a signed literal formed by a leading `-` (the dash is part of the literal, not an operator) followed by optional spaces, tabs, or carriage returns and then a non-empty sequence of `{0,1}`. A `-` that does not immediately introduce a literal is a syntax error.
105105

@@ -161,7 +161,13 @@
161161

162162
Blocks group one or more statements and are enclosed in curly brackets: `{ statement1 ... statementN }`. Curly braces must match (that is, `{` closes with `}`). Blocks serve as the bodies of control-flow constructs and functions.
163163

164-
ASYNC blocks: The `ASYNC` statement introduces a background task whose body is a regular block: `ASYNC{ ... }`. The statements inside the block execute synchronously with respect to each other but asynchronously relative to the rest of the program: the interpreter begins executing the block in a background task and immediately continues with the next statement in the current thread. The block shares the main program namespace (it executes with the same lexical environment), so assignments and mutations performed inside an `ASYNC` block are visible to the rest of the program immediately. Runtime errors raised inside an `ASYNC` block are reported via the interpreter's error hooks and recorded in the state log; they do not synchronously abort the main thread. Note that language invariants still apply inside the `ASYNC` block (for example, `RETURN` outside a function is a runtime error).
164+
ASYNC blocks: The `ASYNC` statement introduces a background task whose body is a regular block: `ASYNC{ ... }`. The statements inside the block execute synchronously with respect to each other but asynchronously relative to the rest of the program. The block shares the main program namespace (it executes with the same lexical environment), so assignments and mutations performed inside an `ASYNC` block are visible to the rest of the program immediately. Runtime errors raised inside an `ASYNC` block are reported via the interpreter's error hooks and recorded in the state log; they do not synchronously abort the main thread. When `ASYNC{ ... }` appears as a top-level statement the interpreter begins executing the block immediately in a background task and continues with the next statement. When `ASYNC{ ... }` is evaluated in expression position and is used as an argument to a surrounding call expression, the interpreter returns a `THR` handle but defers starting the worker thread until the immediately-enclosing call expression completes; this allows operators that receive the `THR` (for example `PAUSE` or `STOP`) to act on the thread before it begins execution. Note that language invariants still apply inside the `ASYNC` block (for example, `RETURN` outside a function is a runtime error).
165+
166+
Thread values (`THR`): a `THR` value is a handle to a background task. In Boolean contexts, a finished `THR` (including one that has been stopped) is `0`, otherwise it is `1`.
167+
168+
`THR(symbol){ block }`: the `THR` statement starts executing `block` in a background task and binds the identifier `symbol` (static type `THR`) in the current environment to the newly-created thread handle. The spawned task shares the main program namespace (it executes with the same lexical environment) similarly to `ASYNC`.
169+
170+
`ASYNC{ block }` as an expression: `ASYNC{ ... }` may also appear in expression position, where it evaluates to a `THR` handle. In statement position, `ASYNC{ ... }` remains valid and its resulting `THR` value is ignored.
165171

166172
Assignments have the syntax `TYPE : identifier = expression` on first use, where TYPE is `INT`, `FLT`, `STR`, `TNS`, or `FUNC`. Spaces around the colon and equals sign are optional. Subsequent assignments to an existing identifier may omit the type but must preserve the original type. Variables are deallocated only when `DEL(identifier)` is executed.
167173

@@ -448,7 +454,7 @@
448454
- `OR(ANY: a1, ..., ANY: aN):INT` ; Boolean OR
449455
- `XOR(ANY: a1, ..., ANY: aN):INT` ; Boolean XOR
450456
- `NOT(ANY: a):INT` ; Boolean NOT
451-
- `BOOL(ANY: item):INT` ; returns the truthiness of `item` as `INT` (`INT`: non-zero -> `1`, `STR`: non-empty -> `1`, `TNS`: true if any element is true), otherwise `0`
457+
- `BOOL(ANY: item):INT` ; returns the truthiness of `item` as `INT` (`INT`: non-zero -> `1`, `STR`: non-empty -> `1`, `TNS`: true if any element is true), otherwise `0`, `THR` is true if it is not stopped, `MAP` is true if it has any entries.
452458

453459
### 12.4 Comparisons
454460
- `EQ(ANY: a, ANY: b):INT` ; 1 if a == b else 0
@@ -504,8 +510,14 @@
504510
### 12.8 Concurrency
505511

506512
- `PARALLEL(TNS: functions):INT` Execute each element of `functions` in parallel. Each element must evaluate to a `FUNC` value; the interpreter invokes each function with no arguments (in separate worker threads) and waits for all workers to complete. Returns `INT` 0 on success. If any element is not a `FUNC` or any worker raises an error, `PARALLEL` raises a runtime error (rewrite: `PARALLEL`) and the failing worker's error is propagated.
507-
508513
- `PARALLEL(FUNC: f1, FUNC: f2, ...):INT` Variadic form: accept one or more `FUNC` values as direct arguments. Behaviour is equivalent to the tensor form: each supplied function is invoked in parallel with no arguments and the call waits for completion; returns `INT` 0 on success or raises on error.
514+
- `ASYNC{ block }:THR` — Expression form: start executing `block` asynchronously in a separate thread (while sharing the same namespace) and return a `THR` handle. In statement position, `ASYNC{ block }` is permitted and the returned handle is ignored.
515+
- `STOP(THR: thread):THR` — Cooperatively stop a running thread and mark it finished.
516+
- `AWAIT(THR: thread):THR` — Block until the thread is finished.
517+
- `PAUSE(THR: thread, FLT: seconds=-1):THR` — Pause execution while preserving the current location. If `seconds` is provided and is $\ge 0$, the thread is automatically resumed after that duration. Pausing an already-paused thread is a runtime error.
518+
- `RESUME(THR: thread):THR` — Resume a paused thread. Resuming a thread that is not paused is a runtime error.
519+
- `PAUSED(THR: thread):INT` — Returns `INT` 1 if `thread` is paused, otherwise `INT` 0.
520+
- `RESTART(THR: thread):THR` — Reinitialize and start executing the thread again.
509521

510522
### 12.9 Logarithms
511523
- `LOG(INT: a):INT` ; floor(log2(a)) for a > 0
@@ -586,7 +598,8 @@
586598
- `CONTINUE()` ; skip remaining statements in the innermost loop iteration and proceed to the next iteration; if used in the last iteration it acts like `BREAK(1)`. Using `CONTINUE()` outside of a loop is a runtime error.
587599
- `GOTOPOINT(INT|STR: n)` ; register a gotopoint with identifier `n` at this statement's location (identifier may be `INT` or `STR`) (n evaluated at runtime). Gotopoints are visible across the containing function or top-level scope rather than being restricted to a single lexical block.
588600
- `GOTO(INT|STR: n)` ; jump to a previously-registered gotopoint with identifier `n` (`INT` or `STR`) within the same function or top-level scope; runtime error if not registered in that scope
589-
- `ASYNC{ block }` ; execute `block` asynchronously in a separate thread (while sharing the same namespace); the caller continues executing immediately without waiting for the async block to complete. Any uncaught errors in the async block are logged to the interpreter's error log but do not affect the caller.
601+
- `ASYNC{ block }` ; execute `block` asynchronously in a separate thread (while sharing the same namespace); the caller continues executing immediately without waiting for the async block to complete. Any uncaught errors in the async block are logged to the interpreter's error log but do not affect the caller. When used in expression position, `ASYNC{ block }` evaluates to a `THR` handle. If the expression is used as an argument to a surrounding call expression, the worker thread's start is deferred until that enclosing call completes so that the callee may operate on the `THR` (for example, `PAUSE` or `STOP`) before the worker runs; in statement position the worker starts immediately and the returned handle is ignored.
602+
- `THR(SYMBOL: symbol){ block }` ; execute `block` asynchronously in a separate thread and bind the identifier `symbol` (static type `THR`) to the thread handle.
590603
- `TRY{ block }CATCH{ block }` ; execute `block` in the `TRY` and, if an interpreter-level runtime error occurs during the `TRY` block, stop the `TRY` and execute the directly-following `CATCH` block. If no error occurs, the `CATCH` block is skipped.
591604
- `TRY{ block }CATCH(SYMBOL: name){ block }` ; like the previous form but bind a temporary `STR` symbol `name` visible inside the `CATCH` block containing the error message. The temporary symbol shadows any existing binding for `name` and is restored or removed after the `CATCH` block completes.
592605

0 commit comments

Comments
 (0)