• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / py-slang / 28800629579

06 Jul 2026 02:52PM UTC coverage: 77.44% (-0.9%) from 78.307%
28800629579

push

github

web-flow
Add a native Pynter/PVML pathway to the standalone repl (#216)

* Add a native Sinter/SVML pathway to the standalone repl

Extends `yarn repl` with `--engine svml`, which compiles a SICPy program to
SVML bytecode and runs it on a native Sinter `runner` binary (built
separately from the sinter repo) instead of the CSE machine. Restricted to
`-v 3`, matching what the SVML compiler currently supports.

Also adds an opt-in parity harness (generateNativeSinterTestCases, gated on
SINTER_RUNNER_PATH) that reruns the existing CSE test suite's TestCases
tables against svml/sinter, to track coverage as the compiler and Sinter
itself gain features.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Target the native Pyinter fork instead of Sinter for the SVML repl pathway

Sinter is the fallback engine for the live Source curriculum on real
hardware, so it isn't safe to grow Python-specific VM semantics into it.
source-academy/pyinter is a new fork of Sinter, kept as a separate sister
project for that purpose. Repoints the --engine svml CLI pathway added in
the previous commit at Pyinter instead: --sinter -> --pyinter,
SINTER_RUNNER_PATH -> PYINTER_RUNNER_PATH, generateNativeSinterTestCases ->
generateNativePyinterTestCases, and moves the native runner wrapper out of
engines/svml/sinter/ (which still holds the pre-existing WASM Sinter
integration used by PySvmlSinterEvaluator, untouched here) into its own
engines/svml/pyinter/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Rename the Sinter fork from Pyinter to Pynter

The sister project's name is Pynter, not Pyinter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fork the SVML wiki docs into docs/pvml/ as PVML's starting point

Copied from the js-slang wiki (SVML-Specification, SVML-Instruction-Set,
SVML-Compiler-and-Machine) so py-slang can start documenting and evolving
PVML — the bytecode format Pynter (the Sinter fork) will run — without
touching the canonical SVML docs.
... (continued)

2469 of 3473 branches covered (71.09%)

Branch coverage included in aggregate %.

166 of 230 new or added lines in 10 files covered. (72.17%)

6 existing lines in 1 file now uncovered.

5965 of 7418 relevant lines covered (80.41%)

13502.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.1
/src/utils/buffer.ts
1
declare global {
2
  interface ArrayBufferConstructor {
3
    transfer(source: ArrayBuffer, length: number): ArrayBuffer;
4
  }
5
}
6

7
if (!ArrayBuffer.transfer) {
13✔
8
  ArrayBuffer.transfer = (source, length) => {
3✔
UNCOV
9
    if (!(source instanceof ArrayBuffer))
×
10
      throw new TypeError("Source must be an instance of ArrayBuffer");
×
UNCOV
11
    if (length <= source.byteLength) return source.slice(0, length);
×
UNCOV
12
    const sourceView = new Uint8Array(source);
×
UNCOV
13
    const destView = new Uint8Array(new ArrayBuffer(length));
×
UNCOV
14
    destView.set(sourceView);
×
UNCOV
15
    return destView.buffer;
×
16
  };
17
}
18

19
/**
20
 * A little-endian byte buffer class.
21
 */
22
export default class Buffer {
13✔
23
  private _capacity: number;
24
  public cursor: number;
25
  private _written: number;
26
  private _buffer: ArrayBuffer;
27
  private _view: DataView;
28

29
  constructor() {
30
    this._capacity = 32;
48✔
31
    this.cursor = 0;
48✔
32
    this._written = 0;
48✔
33
    this._buffer = new ArrayBuffer(this._capacity);
48✔
34
    this._view = new DataView(this._buffer);
48✔
35
  }
36

37
  private maybeExpand(n: number) {
38
    if (this.cursor + n <= this._capacity) {
603✔
39
      return;
582✔
40
    }
41

42
    while (this.cursor + n > this._capacity) {
21✔
43
      this._capacity *= 2;
25✔
44
    }
45
    this._buffer = ArrayBuffer.transfer(this._buffer, this._capacity);
21✔
46
    this._view = new DataView(this._buffer);
21✔
47
  }
48

49
  private updateWritten() {
50
    this._written = Math.max(this._written, this.cursor);
603✔
51
  }
52

53
  get(signed: boolean, s: 8 | 16 | 32): number {
54
    const r = this._view[`get${signed ? "I" : "Ui"}nt${s}`](this.cursor, true);
×
55
    this.cursor += s / 8;
×
56
    return r;
×
57
  }
58

59
  getI(s: 8 | 16 | 32): number {
60
    return this.get(true, s);
×
61
  }
62

63
  getU(s: 8 | 16 | 32): number {
64
    return this.get(false, s);
×
65
  }
66

67
  getF(s: 32 | 64): number {
68
    const r = this._view[`getFloat${s}`](this.cursor, true);
×
69
    this.cursor += s / 8;
×
70
    return r;
×
71
  }
72

73
  put(n: number, signed: boolean, s: 8 | 16 | 32) {
74
    this.maybeExpand(s / 8);
570✔
75
    this._view[`set${signed ? "I" : "Ui"}nt${s}`](this.cursor, n, true);
570✔
76
    this.cursor += s / 8;
570✔
77
    this.updateWritten();
570✔
78
  }
79

80
  putI(s: 8 | 16 | 32, n: number) {
81
    this.put(n, true, s);
55✔
82
  }
83

84
  putU(s: 8 | 16 | 32, n: number) {
85
    this.put(n, false, s);
515✔
86
  }
87

88
  putF(s: 32 | 64, n: number) {
89
    this.maybeExpand(s / 8);
1✔
90
    this._view[`setFloat${s}`](this.cursor, n, true);
1✔
91
    this.cursor += s / 8;
1✔
92
    this.updateWritten();
1✔
93
  }
94

95
  putA(a: Uint8Array) {
96
    this.maybeExpand(a.byteLength);
32✔
97
    new Uint8Array(this._buffer, this.cursor, a.byteLength).set(a);
32✔
98
    this.cursor += a.byteLength;
32✔
99
    this.updateWritten();
32✔
100
  }
101

102
  align(n: number) {
103
    const rem = this.cursor % n;
64✔
104
    if (rem === 0) {
64✔
105
      return;
58✔
106
    }
107
    this.cursor += n - rem;
6✔
108
  }
109

110
  asArray(): Uint8Array {
111
    return new Uint8Array(this._buffer.slice(0, this._written));
73✔
112
  }
113

114
  get written(): number {
115
    return this._written;
×
116
  }
117
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc