Kaynağa Gözat

language: EXCEPTIONS and M2EXCEPTION libraries (M3)

Add the SysExceptions runtime binding, ISO EXCEPTIONS and M2EXCEPTION modules, and t_except_lib. RAISE, CurrentNumber, GetMessage, IsCurrentSource, IsExceptionalExecution, wrong-source exException and M2Exception/SYSTEM.THROW are covered. Gates: suite 272/272, fixpoint OK 4,108,488 bytes, corpus 512/612 compile-OK, 0 crashes. The remaining M4 work is compiler-emitted range/index/CASE traps.
Eric Streit 8 saat önce
ebeveyn
işleme
5f47b76272

+ 1 - 1
README.md

@@ -47,7 +47,7 @@ docs/              STATUS.md, plan.md, plan-*.md, analysis_*.md, summary_*.md
 
 ## Status (2026-10-23)
 
-- Suite **271/271**; self-hosting **fixpoint OK** (byte-identical
+- Suite **272/272**; self-hosting **fixpoint OK** (byte-identical
   stage2/stage3, 4,108,488 bytes); corpus **512/612 compile-OK,
   0 crashes** (`tools/v3-corpus`; the corpus is gm2's testsuite, so it
   mixes conforming Modula-2 with gm2 leniencies — see `docs/gaps/`).

+ 6 - 0
compiler/run_tests.sh

@@ -322,6 +322,12 @@ expect_run_files TString 42 d_string.def d_string.mod t_string.mod
 expect_run_files FioProg 42 ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod ../runtime/syslib/FileIO.def ../runtime/syslib/FileIO.mod fio_prog.mod
 expect_run_files StorageProg 42 ../stdlib/storage.def ../stdlib/storage.mod storage_prog.mod
 expect_run_files TSysClock 42 ../stdlib/sysclock.def ../stdlib/sysclock.mod t_sysclock.mod
+expect_run_files TExceptLib 42 \
+  ../runtime/syslib/SysExceptions.def \
+  ../runtime/syslib/SysExceptions.mod \
+  ../stdlib/m2exception.def ../stdlib/m2exception.mod \
+  ../stdlib/exceptions.def ../stdlib/exceptions.mod \
+  t_except_lib.mod
 expect_run_files TAst 42 \
   ../runtime/syslib/SysShim.def ../runtime/syslib/SysShim.mod \
   ../runtime/syslib/FileIO.def ../runtime/syslib/FileIO.mod \

+ 65 - 0
compiler/tests/t_except_lib.mod

@@ -0,0 +1,65 @@
+MODULE TExceptLib;
+(* M3: EXCEPTIONS / M2EXCEPTION over user RAISE and SYSTEM.THROW. *)
+FROM EXCEPTIONS IMPORT
+  ExceptionSource, ExceptionNumber, AllocateSource, RAISE,
+  CurrentNumber, GetMessage, IsCurrentSource, IsExceptionalExecution;
+FROM M2EXCEPTION IMPORT
+  M2Exception, IsM2Exception, wholeDivException, exException;
+FROM SYSTEM IMPORT THROW;
+
+VAR ExitCode : INTEGER;
+VAR src      : ExceptionSource;
+VAR other    : ExceptionSource;
+VAR msg      : ARRAY [0..63] OF CHAR;
+VAR n        : ExceptionNumber;
+
+PROCEDURE UserRaise;
+BEGIN
+  RAISE(src, 7, "boom");
+  ExitCode := 1
+EXCEPT
+  IF IsExceptionalExecution() AND IsCurrentSource(src)
+     AND NOT IsCurrentSource(other)
+     AND NOT IsM2Exception() AND (CurrentNumber(src) = 7) THEN
+    GetMessage(msg);
+    IF msg = "boom" THEN ExitCode := 42 ELSE ExitCode := 2 END
+  ELSE
+    ExitCode := 3
+  END
+END UserRaise;
+
+PROCEDURE AskWrongSource;
+BEGIN
+  n := CurrentNumber(other);
+  ExitCode := 1
+EXCEPT
+  IF IsM2Exception() AND (M2Exception() = exException) THEN
+    ExitCode := 42
+  ELSE
+    ExitCode := 6
+  END
+END AskWrongSource;
+
+PROCEDURE LanguageRaise;
+BEGIN
+  THROW(6)
+EXCEPT
+  IF IsM2Exception() AND (M2Exception() = wholeDivException) THEN
+    ExitCode := 42
+  ELSE
+    ExitCode := 4
+  END
+END LanguageRaise;
+
+BEGIN
+  AllocateSource(src);
+  AllocateSource(other);
+  ExitCode := 0;
+  UserRaise;
+  IF ExitCode = 42 THEN
+    GetMessage(msg);
+    IF msg = "" THEN ExitCode := 0 ELSE ExitCode := 5 END
+  END;
+  IF ExitCode = 0 THEN AskWrongSource END;
+  IF ExitCode = 42 THEN LanguageRaise END
+END TExceptLib.

+ 3 - 2
docs/INDEX.md

@@ -39,8 +39,9 @@ bisecting *why* a decision was made, not for reading end-to-end).
   layout (deferred until the compiler is finished).
 - **`design-exceptions.md`** — ISO exceptions design: grammar/AST,
   handler stack, `sigsetjmp`/`siglongjmp` codegen, `EXCEPTIONS` /
-  `M2EXCEPTION`, trap mapping, tests.  M1 parser/AST and M2
-  runtime/codegen are implemented; libraries and compiler traps remain.
+  `M2EXCEPTION`, trap mapping, tests.  M1 parser/AST, M2
+  runtime/codegen and M3 libraries are implemented; compiler traps
+  remain (M4).
 - `blaise-phases-map.md`, `language-report.md`, `grammar.md` — the
   grammar/phases reference.
 - `summary_l3_selfhost_fix.md` — the self-host root cause; also where

+ 4 - 4
docs/STATUS.md

@@ -4,7 +4,7 @@ This answers "where are we now?".
 
 ## Numbers
 
-- Suite **271/271** (`compiler/run_tests.sh`).
+- Suite **272/272** (`compiler/run_tests.sh`).
 - Self-hosting **FIXPOINT OK** — stage2 and stage3 images are
   byte-identical (4,108,488 bytes).
 - Corpus **512 / 612** sources compile with 0 errors, **0 crashes**
@@ -173,8 +173,8 @@ declarations still work (`t_fwdproc`, `t_fwdok`).
    `INC`/`DEC`, leading CASE `|`, `__FUNCTION__`).  Corpus is **511/612**
    at the stop point.  The remaining `LANG` entries are mostly:
    **declaration order** cases V3 rejects by policy (`docs/gaps/`); ISO
-   exceptions (M2 runtime/codegen; M3 `EXCEPTIONS`/`M2EXCEPTION` pending
-   — see `docs/design-exceptions.md`); multi-dimensional
+   exceptions (M3 libraries/runtime; M4 compiler traps pending — see
+   `docs/design-exceptions.md`); multi-dimensional
    open arrays (representation, see `docs/handoff-2026-10-23-next.md`);
    and gm2-only extensions.  See `docs/analysis_gaps.md`.
 2. ~~**The size-sensitive V3 miscompile**~~ — **root-caused and fixed**
@@ -217,7 +217,7 @@ declarations still work (`t_fwdproc`, `t_fwdok`).
 ```sh
 cd compiler
 ./build.sh          # M2.atg -> scanner/parser/driver -> ./M2
-./run_tests.sh      # 271/271
+./run_tests.sh      # 272/272
 cd ..
 ./bootstrap/fixpoint.sh   # FIXPOINT OK (byte-identical stage2/stage3)
 ```

+ 5 - 4
docs/comparison-PIM4-gm2-V3.md

@@ -1,7 +1,7 @@
 # PIM4 vs GNU Modula-2 vs M2R10 vs TopSpeed V3 vs V3 — feature comparison
 
 **Date:** 2026-10-23 · **V3 state:** `v3-packedset` (suite
-271/271, self-hosting fixpoint OK 4,108,488 bytes, corpus 512/612,
+272/272, self-hosting fixpoint OK 4,108,488 bytes, corpus 512/612,
 0 crashes).
 
 **Scope and sources.**
@@ -103,7 +103,7 @@ change individual cells.
 | Runtime subrange range checks | ⚠️ error per language; checks implementation-defined | ✅ | ✅ (R10 safety) | 🔄 | ✅ assignment/value actual/`RETURN`/`INC`/`DEC`; `FOR` final increment exempt |
 | `SHIFT`/`ROTATE` | ⚠️ implementation SYSTEM extension | ✅ | ❌ | ✅ (`<<`/`>>` operators) | ✅ |
 | `TBITSIZE` | ❌ | ✅ | ❌ | ❌ | ❌ |
-| ISO exceptions (`EXCEPT`, `RETRY`, `EXCEPTIONS`) | ❌ | ✅ | ❌ (not in the base R10 grammar) | ❌ | 🔄 `EXCEPT`/`RETRY`/`SYSTEM.THROW`; `EXCEPTIONS`/`M2EXCEPTION` pending |
+| ISO exceptions (`EXCEPT`, `RETRY`, `EXCEPTIONS`) | ❌ | ✅ | ❌ (not in the base R10 grammar) | ❌ | 🔄 `EXCEPT`/`RETRY`/`SYSTEM.THROW`, `EXCEPTIONS`/`M2EXCEPTION`; range/index/CASE traps pending |
 | Module finalization (`FINALLY`) | ❌ | ✅ | ❌ | ❌ | ❌ |
 | `__FILE__` / `__LINE__` / `__FUNCTION__` | ❌ | ✅ | 🔄 COMPILER pseudo-module introspection | ❌ | ✅ / ✅ / ✅ |
 | `__BUILTIN__` / `__ATTRIBUTE__` pragmas | ❌ | ✅ | ✅ language pragmas `<* … *>` | ⚠️ compiler directives | ✅ (parsed, ignored) |
@@ -152,8 +152,9 @@ change individual cells.
   its chosen gm2 compatibility (`__FILE__`/`__LINE__`/`__FUNCTION__`,
   scalar→`ARRAY OF BYTE`, same-size `VAR`, bare proc values, variant
   extensions, set `CASE` labels, libc prototypes).  ISO exceptions are
-  **partial** (`EXCEPT`/`RETRY`/`SYSTEM.THROW`; `EXCEPTIONS`/
-  `M2EXCEPTION` pending).  Its hard gaps are **complex arithmetic,
+  **partial** (`EXCEPT`/`RETRY`/`SYSTEM.THROW`, `EXCEPTIONS`/
+  `M2EXCEPTION`; compiler range/index/CASE traps pending).  Its hard
+  gaps are **complex arithmetic,
   finalization/protection, multi-dimensional open arrays, ISO OO** and
   parts of the **word model** (`SHORTREAL`, `REAL` width), plus a
   **65536-bit `SET` span limit** and a **32-bit `PACKEDSET` limit**.

+ 17 - 15
docs/design-exceptions.md

@@ -1,11 +1,11 @@
 # Design — ISO exceptions for V3
 
-**Status: design + M1 parser/AST + M2 runtime/codegen (2026-10-23).**
-`EXCEPT`/`RETRY` codegen, the handler stack, `SYSTEM.THROW`, `RETURN`
-unwinding and basic signal mapping are implemented.  `EXCEPTIONS` and
-`M2EXCEPTION` (M3) are still missing.  Baseline: `v3-packedset`; suite
-**271/271**, fixpoint OK **4,108,488 bytes**, corpus **512/612**
-compile-OK, 0 crashes.
+**Status: design + M1 parser/AST + M2 runtime/codegen + M3 libraries
+(2026-10-23).**  `EXCEPT`/`RETRY`, the handler stack, `SYSTEM.THROW`,
+`EXCEPTIONS`, `M2EXCEPTION`, `RETURN` unwinding and basic signal
+mapping are implemented.  Compiler-emitted range/index/CASE traps (M4)
+remain.  Baseline: `v3-packedset`; suite **272/272**, fixpoint OK
+**4,108,488 bytes**, corpus **512/612** compile-OK, 0 crashes.
 
 The handoff calls this the largest remaining language feature.  This
 document fixes the syntax, AST, runtime model, code-generation contract,
@@ -556,7 +556,7 @@ location"`, `"divide by zero"`.
 
 ### 9.1 Unit / regression tests
 
-Implemented in M1/M2 (`compiler/run_tests.sh`):
+Implemented in M1–M3 (`compiler/run_tests.sh`):
 
 - `t_except_parse`: EXCEPT on procedure and module bodies; handlers are
   skipped when the body completes normally.
@@ -569,17 +569,17 @@ Implemented in M1/M2 (`compiler/run_tests.sh`):
 - `t_except_module`: program-module body `EXCEPT`/`RETRY`.
 - `t_except_signal`: NIL dereference maps to `invalidLocation`; handler
   retries.
+- `t_except_lib`: `EXCEPTIONS`/`M2EXCEPTION` `RAISE`, `CurrentNumber`,
+  `GetMessage`, `IsCurrentSource`, `IsExceptionalExecution`,
+  wrong-source `exException`, and `M2Exception` via `SYSTEM.THROW`.
 - `t_retry_bad`: `RETRY` outside `EXCEPT` rejected with `237`.
 
 Still planned:
 
-- `t_except_basic`: `RAISE` from a nested procedure, handler checks
-  `CurrentNumber`, `GetMessage`, `IsCurrentSource`,
-  `IsExceptionalExecution`.
+- `t_except_basic`: a dedicated nested-handler state-restoration test
+  (the current `t_except_lib` covers the public queries).
 - `t_except_trap`: integer divide-by-zero and `CASE`/range traps.
 - `t_except_unhandled`: expect_trap (rc=134).
-- `t_except_lib`: `EXCEPTIONS` source allocation and wrong-source
-  `CurrentNumber` raises `exException`.
 
 Use `expect_run_files` with:
 
@@ -611,7 +611,7 @@ cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
 tools/v3-corpus/corpus.sh
 ```
 
-Current M2 gates: suite **271/271**, fixpoint OK
+Current M3 gates: suite **272/272**, fixpoint OK
 **4,108,488 bytes**, corpus **512/612 compile-OK**, 0 crashes.
 Existing trap tests must stay rc=134.
 
@@ -629,9 +629,11 @@ Existing trap tests must stay rc=134.
    - `SYSTEM.THROW`, signal mapping for `SIGSEGV`/`SIGFPE`.
    - Positive tests for `THROW`, `RETRY`, `RETURN`, nested handlers,
      module bodies and NIL dereference.
-3. **M3 — libraries**
+3. **M3 — libraries** — **done (2026-10-23)**
    - `SysExceptions`, `EXCEPTIONS`, `M2EXCEPTION`.
-   - Library tests; `except5/7` variants; state-restoration queries.
+   - `t_except_lib`: `RAISE`, `CurrentNumber`, `GetMessage`,
+     `IsCurrentSource`, `IsExceptionalExecution`, wrong-source
+     `exException`, and `M2Exception` via `SYSTEM.THROW`.
 4. **M4 — language traps**
    - `TrapCode`; split index/range; CASE trap; range-check traps; keep
      rc=134 for unhandled; finish `t_except_trap`.

+ 4 - 4
docs/features.md

@@ -154,10 +154,10 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   composite function results (record/array/set/class, sret) and chained
   post-call method calls (`F().M()`); ⏸ per-file object emission, import
   cycles.
-- 🔄 ISO exceptions: **M2** runtime + codegen for `EXCEPT`/`RETRY`,
-  `SYSTEM.THROW`, NIL/divide-by-zero signal mapping, `RETURN`/nested
-  handlers.  Still pending: `EXCEPTIONS`, `M2EXCEPTION`, compiler-emitted
-  range/index/CASE traps.  Design in `docs/design-exceptions.md`.
+- 🔄 ISO exceptions: **M3** — `EXCEPT`/`RETRY`, handler stack,
+  `SYSTEM.THROW`, `EXCEPTIONS`, `M2EXCEPTION`, NIL/divide-by-zero signal
+  mapping, `RETURN`/nested handlers.  Still pending: compiler-emitted
+  range/index/CASE traps (M4).  Design in `docs/design-exceptions.md`.
 
 ## Backend (QBE)
 - ✅ Length-prefixed arrays (count + elements), deep record copy,

+ 5 - 5
docs/gaps/README.md

@@ -94,11 +94,11 @@ gap22_file_line.mod             invalid Fact            (__LINE__)
 
 ## Genuine gaps (worth fixing, standard Modula-2)
 
-- **ISO exceptions — M2 runtime/codegen done, libraries pending**
-  (2026-10-23): `EXCEPT`/`RETRY`, handler stack + saved state,
-  `SYSTEM.THROW`, `RETURN` unwinding, nested handlers, and basic
-  `SIGSEGV`/`SIGFPE` mapping work.  `EXCEPTIONS`/`M2EXCEPTION` and
-  compiler-emitted range/index/CASE traps remain (M3/M4).
+- **ISO exceptions — M3 done, M4 traps pending** (2026-10-23):
+  `EXCEPT`/`RETRY`, handler stack + saved state, `SYSTEM.THROW`,
+  `EXCEPTIONS`, `M2EXCEPTION`, `RETURN` unwinding, nested handlers, and
+  basic `SIGSEGV`/`SIGFPE` mapping work.  Compiler-emitted
+  range/index/CASE traps remain (M4).
   `lazyunique` now compiles (corpus 512) but its QBE hits a
   pre-existing pointer-chain (`p^.next^.value`) codegen gap, so it is
   not yet a runtime gain.  `except2/4/5/7/8/9` still fail declaration

+ 18 - 14
docs/handoff-2026-10-23-next.md

@@ -1,7 +1,7 @@
 # Next session — plan (from 2026-10-23)
 
 **Branch:** `master`.  **Feature state:** `v3-packedset`.
-**Gates:** suite **271/271**, fixpoint **OK (4,108,488 bytes)**,
+**Gates:** suite **272/272**, fixpoint **OK (4,108,488 bytes)**,
 corpus **512/612, 0 crashes**.
 
 Read first: `docs/STATUS.md`, `docs/features.md`,
@@ -26,7 +26,7 @@ inside `assert` before its declaration (declaration order, rejected by
 policy).  With `r` moved before the procedure they compile and run.
 Do not relax declaration order for this corpus case.
 
-## 2. ISO exceptions — M2 done, M3 next (`docs/design-exceptions.md`)
+## 2. ISO exceptions — M3 done, M4 next (`docs/design-exceptions.md`)
 
 A dedicated feature session, not a small fix.  The design is in
 `docs/design-exceptions.md` (grammar/AST, handler stack, `sigsetjmp`/
@@ -44,16 +44,20 @@ state, `m2_exc_push`/`leave`/`retry`/`raise`, `sigsetjmp` dispatch,
 `t_except_nested`, `t_except_module`, `t_except_signal`; negative
 `t_retry_bad` (237).
 
-Next (**M3**): `SysExceptions`, `EXCEPTIONS`, `M2EXCEPTION`; then test
-`AllocateSource`/`RAISE`/`CurrentNumber`/`GetMessage`/
-`IsCurrentSource`/`IsExceptionalExecution`, including state restoration
-after a nested handler.
+**M3 libraries — done (2026-10-23).**  `SysExceptions`,
+`EXCEPTIONS` and `M2EXCEPTION` are implemented in
+`runtime/syslib/SysExceptions.*` and `stdlib/{exceptions,m2exception}.*`.
+`t_except_lib` covers `RAISE`, `CurrentNumber`, `GetMessage`,
+`IsCurrentSource`, `IsExceptionalExecution`, wrong-source `exException`,
+and `M2Exception` after `SYSTEM.THROW`.
+
+Next (**M4**): compiler-emitted language traps
+(`indexException`/`rangeException`/`caseSelectException`), unhandled
+exception policy, and trap tests.  M5 remains docs/corpus/final tag.
 
 Still to implement:
 
-- M3 libraries (`EXCEPTIONS`, `M2EXCEPTION`, `SysExceptions`);
-- M4 compiler-emitted language traps (`indexException`/`rangeException`/
-  `caseSelectException`), unhandled-exception policy and trap tests;
+- M4 compiler-emitted language traps and trap tests;
 - M5 docs/corpus/final tag.
 
 **Corpus reality:** `lazyunique` now **compiles** (corpus **512**) but
@@ -102,7 +106,7 @@ this feature.
 ## Gates for every step
 
 ```sh
-cd compiler && ./build.sh && ./run_tests.sh          # 271/271
+cd compiler && ./build.sh && ./run_tests.sh          # 272/272
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
 tools/v3-corpus/corpus.sh                            # 512 OK, 0 crash
 ```
@@ -115,13 +119,13 @@ session summary; commit with a `v3-*` tag.  Do not push without asking.
 ```sh
 cd .../m2compiler-V3
 git checkout master
-cd compiler && ./build.sh && ./run_tests.sh          # 271/271
+cd compiler && ./build.sh && ./run_tests.sh          # 272/272
 cd .. && TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
 tools/v3-corpus/corpus.sh                            # 512 OK, 0 crash
 ```
 
-**Recommendation:** continue with **M3 of ISO exceptions** (the
-`EXCEPTIONS`/`M2EXCEPTION` libraries and `RAISE`), or take the
-multi-dimensional-open-array ABI decision if you prefer an ABI task.
+**Recommendation:** continue with **M4 of ISO exceptions**
+(compiler-emitted range/index/CASE traps and unhandled policy), or take
+the multi-dimensional-open-array ABI decision if you prefer an ABI task.
 Library folders and separate compilation remain deferred
 (`docs/plan-separate-compilation.md`).

+ 22 - 0
runtime/syslib/SysExceptions.def

@@ -0,0 +1,22 @@
+DEFINITION MODULE SysExceptions;
+(* V3 binding to the runtime exception support in shim.c.  This is an
+   implementation module, not part of ISO; EXCEPTIONS and M2EXCEPTION
+   are the public interfaces. *)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+PROCEDURE RaiseLang (code : CARDINAL);
+(* Raise language exception `code` (0..14). *)
+PROCEDURE RaiseUser (source : ADDRESS; number : CARDINAL;
+                     message : ARRAY OF CHAR);
+(* Raise a user exception; the runtime copies the message. *)
+PROCEDURE Retry;
+PROCEDURE Leave;
+PROCEDURE Active () : BOOLEAN;
+PROCEDURE IsLanguage () : BOOLEAN;
+PROCEDURE IsSource (source : ADDRESS) : BOOLEAN;
+PROCEDURE Number () : CARDINAL;
+PROCEDURE CopyMessage (VAR text : ARRAY OF CHAR);
+PROCEDURE NewSource () : ADDRESS;
+
+END SysExceptions.

+ 79 - 0
runtime/syslib/SysExceptions.mod

@@ -0,0 +1,79 @@
+IMPLEMENTATION MODULE SysExceptions;
+(* Binds the C exception runtime in runtime/syslib/shim.c. *)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+PROCEDURE m2_exc_raise_lang (code : CARDINAL);
+  EXTERNAL;
+PROCEDURE m2_exc_raise_user (source : ADDRESS; number : CARDINAL;
+                             message : ARRAY OF CHAR);
+  EXTERNAL;
+PROCEDURE m2_exc_retry;
+  EXTERNAL;
+PROCEDURE m2_exc_leave;
+  EXTERNAL;
+PROCEDURE m2_exc_active () : BOOLEAN;
+  EXTERNAL;
+PROCEDURE m2_exc_language () : BOOLEAN;
+  EXTERNAL;
+PROCEDURE m2_exc_is_source (source : ADDRESS) : BOOLEAN;
+  EXTERNAL;
+PROCEDURE m2_exc_number () : CARDINAL;
+  EXTERNAL;
+PROCEDURE m2_exc_copy_message (VAR text : ARRAY OF CHAR);
+  EXTERNAL;
+PROCEDURE m2_exc_new_source () : ADDRESS;
+  EXTERNAL;
+
+PROCEDURE RaiseLang (code : CARDINAL);
+BEGIN
+  m2_exc_raise_lang(code)
+END RaiseLang;
+
+PROCEDURE RaiseUser (source : ADDRESS; number : CARDINAL;
+                     message : ARRAY OF CHAR);
+BEGIN
+  m2_exc_raise_user(source, number, message)
+END RaiseUser;
+
+PROCEDURE Retry;
+BEGIN
+  m2_exc_retry
+END Retry;
+
+PROCEDURE Leave;
+BEGIN
+  m2_exc_leave
+END Leave;
+
+PROCEDURE Active () : BOOLEAN;
+BEGIN
+  RETURN m2_exc_active()
+END Active;
+
+PROCEDURE IsLanguage () : BOOLEAN;
+BEGIN
+  RETURN m2_exc_language()
+END IsLanguage;
+
+PROCEDURE IsSource (source : ADDRESS) : BOOLEAN;
+BEGIN
+  RETURN m2_exc_is_source(source)
+END IsSource;
+
+PROCEDURE Number () : CARDINAL;
+BEGIN
+  RETURN m2_exc_number()
+END Number;
+
+PROCEDURE CopyMessage (VAR text : ARRAY OF CHAR);
+BEGIN
+  m2_exc_copy_message(text)
+END CopyMessage;
+
+PROCEDURE NewSource () : ADDRESS;
+BEGIN
+  RETURN m2_exc_new_source()
+END NewSource;
+
+END SysExceptions.

+ 16 - 0
stdlib/exceptions.def

@@ -0,0 +1,16 @@
+DEFINITION MODULE EXCEPTIONS;
+(* ISO 10514-1 exception source and enquiry interface. *)
+
+TYPE
+  ExceptionSource;   (* opaque *)
+  ExceptionNumber = CARDINAL;
+
+PROCEDURE AllocateSource (VAR newSource : ExceptionSource);
+PROCEDURE RAISE (source : ExceptionSource; number : ExceptionNumber;
+                 message : ARRAY OF CHAR);
+PROCEDURE CurrentNumber (source : ExceptionSource) : ExceptionNumber;
+PROCEDURE GetMessage (VAR text : ARRAY OF CHAR);
+PROCEDURE IsCurrentSource (source : ExceptionSource) : BOOLEAN;
+PROCEDURE IsExceptionalExecution () : BOOLEAN;
+
+END EXCEPTIONS.

+ 48 - 0
stdlib/exceptions.mod

@@ -0,0 +1,48 @@
+IMPLEMENTATION MODULE EXCEPTIONS;
+(* ISO EXCEPTIONS implemented over the V3 runtime binding.  The opaque
+   ExceptionSource is an identity token; it is never dereferenced. *)
+
+FROM SYSTEM IMPORT ADDRESS;
+IMPORT SysExceptions;
+IMPORT M2EXCEPTION;
+
+TYPE
+  ExceptionSource = ADDRESS;
+
+PROCEDURE AllocateSource (VAR newSource : ExceptionSource);
+BEGIN
+  newSource := SysExceptions.NewSource()
+END AllocateSource;
+
+PROCEDURE RAISE (source : ExceptionSource; number : ExceptionNumber;
+                 message : ARRAY OF CHAR);
+BEGIN
+  SysExceptions.RaiseUser(source, number, message)
+END RAISE;
+
+PROCEDURE CurrentNumber (source : ExceptionSource) : ExceptionNumber;
+BEGIN
+  IF SysExceptions.IsSource(source) THEN
+    RETURN SysExceptions.Number()
+  ELSE
+    SysExceptions.RaiseLang(ORD(M2EXCEPTION.exException));
+    RETURN 0
+  END
+END CurrentNumber;
+
+PROCEDURE GetMessage (VAR text : ARRAY OF CHAR);
+BEGIN
+  SysExceptions.CopyMessage(text)
+END GetMessage;
+
+PROCEDURE IsCurrentSource (source : ExceptionSource) : BOOLEAN;
+BEGIN
+  RETURN SysExceptions.IsSource(source)
+END IsCurrentSource;
+
+PROCEDURE IsExceptionalExecution () : BOOLEAN;
+BEGIN
+  RETURN SysExceptions.Active()
+END IsExceptionalExecution;
+
+END EXCEPTIONS.

+ 15 - 0
stdlib/m2exception.def

@@ -0,0 +1,15 @@
+DEFINITION MODULE M2EXCEPTION;
+(* ISO 10514-1 language-exception identification. *)
+
+TYPE
+  M2Exceptions =
+    (indexException, rangeException, caseSelectException, invalidLocation,
+     functionException, wholeValueException, wholeDivException,
+     realValueException, realDivException, complexValueException,
+     complexDivException, protException, sysException, coException,
+     exException);
+
+PROCEDURE M2Exception () : M2Exceptions;
+PROCEDURE IsM2Exception () : BOOLEAN;
+
+END M2EXCEPTION.

+ 21 - 0
stdlib/m2exception.mod

@@ -0,0 +1,21 @@
+IMPLEMENTATION MODULE M2EXCEPTION;
+(* Enquiries over the runtime language-exception state. *)
+
+IMPORT SysExceptions;
+
+PROCEDURE M2Exception () : M2Exceptions;
+BEGIN
+  IF SysExceptions.IsLanguage() THEN
+    RETURN VAL(M2Exceptions, SysExceptions.Number())
+  ELSE
+    SysExceptions.RaiseLang(ORD(exException));
+    RETURN exException
+  END
+END M2Exception;
+
+PROCEDURE IsM2Exception () : BOOLEAN;
+BEGIN
+  RETURN SysExceptions.IsLanguage()
+END IsM2Exception;
+
+END M2EXCEPTION.