@plylang/plylang 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Algosail
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,391 @@
1
- # Temporary Holding Version
1
+ # Ply in 5 minutes
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Ply is a concatenative language. A program is a sequence of words. A word takes values off the single stack and puts new ones on. The type of a word is that transformation. In stack notation the top is always on the right.
4
+
5
+ The compiler translates a module into JavaScript. There is no separate virtual machine. The language version is 1. There are no subtypes: two types are compatible when they unify.
6
+
7
+ This text walks through the language once, with a short example of each feature.
8
+
9
+ The repository contains the `plylang` compiler and the `plylang-ls` server. Words that work on ready-made values are not built into the compiler: they are imported. The neighboring package `@algosail/ply-prelude` is one such place, and it has its own documentation. The imports in the examples below illustrate the notation.
10
+
11
+ ## Stack
12
+
13
+ A signature says what a word takes and what it puts. The tail under that prefix stays in place.
14
+
15
+ ```ply
16
+ ~ "Num" (add)
17
+
18
+ : double ( Num -- Num )
19
+ dup add
20
+ ```
21
+
22
+ The body runs from left to right. A number is on the stack. `dup` copies the top. `add` takes both numbers and puts the sum.
23
+
24
+ A word starts with `:`. Then `foreign` and `private` may appear, in either order and each at most once, followed by the name and the scheme in parentheses. Spaces inside the parentheses do not matter. The body may continue on the same line: `: double ( Num -- Num ) dup add`.
25
+
26
+ A name in a scheme is one slot. `Num Num` is two slots. `Maybe a` is also two: the constructor needs an argument, and that argument is written in parentheses, `(Maybe a)`. `(Num)` is the same type as `Num`.
27
+
28
+ A lowercase name is a scheme variable, in order of appearance. An explicit list before the dot is also accepted: `a b. a b -- b`. The kind is inferred. A stack slot has kind `Type`. In `(f a)` the kind of `f` is `Type -> Type`.
29
+
30
+ An omitted stack bottom is the same tail on the left and on the right. An explicit empty bottom is written with a dot: `. a -- . a` requires a stack of exactly one value. The tail `..s` takes the rest as a whole. `..s` and `..s a` are different schemes.
31
+
32
+ ## Values
33
+
34
+ Five built-in types and two constructors of arity 1 are visible in every module. They cannot be redefined.
35
+
36
+ | Type | Literal | In JavaScript |
37
+ | --- | --- | --- |
38
+ | `Num` | `1`, `42`, `1.5`, `1e-3` | number |
39
+ | `Int` | `0n`, `42n`, `-42n` | `bigint` |
40
+ | `Str` | `"hi"`, `"a\nb"` | string |
41
+ | `Bool` | `true`, `false` | boolean |
42
+ | `Void` | the word `Void` | `undefined` |
43
+
44
+ `(Array a)` is an array. `(StrMap a)` is a dictionary with a string key. Bare digits are `Num`, an ordinary number in JavaScript. `Int` is written with the suffix `n` and becomes a `bigint`. In the examples below a number is `Num`. `1.0` is a number. `1.` is the number `1` and a separate dot. A string accepts `\\`, `\"`, `\n`, `\t`, and `\r`.
45
+
46
+ `Void` is both a type and a word. The word puts the single value of that type.
47
+
48
+ Operations on these values are imported. This text does not list them.
49
+
50
+ ## Stack words
51
+
52
+ These words are built in. They need no import. On a stack `x y` the top is `y`:
53
+
54
+ ```ply
55
+ : copy ( a -- a a )
56
+ dup
57
+
58
+ : gone ( a b -- a )
59
+ drop
60
+
61
+ : flipped ( a b -- b a )
62
+ swap
63
+
64
+ : top ( a b -- b )
65
+ nip
66
+
67
+ : again ( a b -- a b a )
68
+ over
69
+ ```
70
+
71
+ The same permutations as in Factor follow, with names made of letters: `dupd`, `swapd`, `tuck`, `pick`, `rot`, `unrot`, `dup2`, `drop2`, `nip2`, `swap2`, `over2`. A name does not start with a digit, so the pair is written `dup2`. On `x y z` with `z` on top, `rot` yields `y z x` and `unrot` yields `z x y`.
72
+
73
+ ## Quotation
74
+
75
+ `[ ... ]` is a value. It does not capture the stack outside, and it does not run by itself. Square brackets are a quotation. An array is written in angle brackets, a record in braces.
76
+
77
+ ```text
78
+ : call ( ..s (..s -- ..t) -- ..t )
79
+ : dip ( ..s a (..s -- ..t) -- ..t a )
80
+ : keep ( ..s a (..s a -- ..t) -- ..t a )
81
+ : bi ( ..s a (a -- b) (a -- c) -- ..s b c )
82
+ : if ( ..s Bool (..s -- ..t) (..s -- ..t) -- ..t )
83
+ : quote ( ..s a -- ..s (-- a) )
84
+ ```
85
+
86
+ `call` takes a quotation and runs it on the rest. `dip` hides the value under the quotation, runs the quotation, and returns what it hid to the top. `dip2` and `dip3` hide two and three values. `keep` runs the quotation and returns the value it took. `keep2` returns two.
87
+
88
+ `bi` applies two quotations to one value. Each returns exactly one. `tri` does the same with three quotations. `bis` gives the deeper value to the first quotation and the top to the second. `bia` applies one quotation to each of two values of the same type.
89
+
90
+ `if` takes a `Bool` and two quotations. The `true` branch is placed first and the `false` branch second, so `false` ends up on top. The branches agree on the stack.
91
+
92
+ `quote` wraps the top. `42 quote` is a quotation of type `(-- Num)`.
93
+
94
+ ```ply
95
+ ~ "Num" (eq, sub, mul)
96
+
97
+ : fact ( Num -- Num )
98
+ dup 0 eq [ drop 1 ] [ dup 1 sub fact mul ] if
99
+ ```
100
+
101
+ Zero is replaced by one. Otherwise `n` and `n - 1` remain on the stack, the recursive `fact` computes the second, and `mul` multiplies.
102
+
103
+ Names before `->` name stack values. The left name is the deeper value, the right name is the top. A quotation sees these names, and they are not part of the input.
104
+
105
+ ```ply
106
+ : curry ( a (a b -- c) -- (b -- c) )
107
+ x q ->
108
+ [ x swap q call ]
109
+ ```
110
+
111
+ This is an ordinary word of a module. It is not built into the compiler.
112
+
113
+ `call` requires a known finite length for the quotation's input.
114
+
115
+ ## Pattern matching
116
+
117
+ When the first line of a body is patterns and `->`, the whole body is equations. The right-hand pattern is the top. A name binds a slot. A literal is compared with the value. `_` discards a value. The first match wins. A value cannot be matched in the middle of a body: write a separate word for that, and make its whole body equations.
118
+
119
+ ```ply
120
+ : toString ( Bool -- Str )
121
+ true -> "true"
122
+ false -> "false"
123
+ ```
124
+
125
+ `true` together with `false` covers `Bool`. `Num`, `Int`, and `Str` need a final branch with a name or `_`.
126
+
127
+ A bare constructor puts its fields on the stack from left to right, the last field on top. Parentheses hold one slot: `(Just x)` names the field and does not put it until the name is mentioned.
128
+
129
+ An array in a pattern has a fixed length. `<>` matches only the empty array. One length does not cover every `Array`.
130
+
131
+ ```ply
132
+ : isEmpty ( (Array a) -- Bool )
133
+ <> -> true
134
+ _ -> false
135
+ ```
136
+
137
+ A record in a pattern reads the named fields. The tail `| r` is written in the type of the slot, not in the pattern. `{}` fits any record of that slot.
138
+
139
+ ```ply
140
+ : nameOf ( { name : Str | r } -- Str )
141
+ { name : n } -> n
142
+ ```
143
+
144
+ ## Data
145
+
146
+ `;` in column 0 names an algebraic type. A constructor is a word. A type and a word with one name live in different spaces.
147
+
148
+ ```ply
149
+ ; Maybe a = Nothing | Just a
150
+
151
+ : fromMaybe ( a (Maybe a) -- a )
152
+ Nothing ->
153
+ Just -> nip
154
+
155
+ : mapMaybe ( (Maybe a) (a -- b) -- (Maybe b) )
156
+ Nothing q -> q drop Nothing
157
+ (Just x) q -> x q call Just
158
+ ```
159
+
160
+ The empty body of the `Nothing` branch leaves the fallback value. For `Just` the field lands above it, and `nip` drops what is underneath. In `mapMaybe` the quotation is the top: `q` names it, and `(Just x)` names the field.
161
+
162
+ A list with its own structure is a separate type. The head sits in the constructor, and the tail is another value of that type.
163
+
164
+ ```ply
165
+ ; List a = Nil | Cons a (List a)
166
+ ```
167
+
168
+ In JavaScript a constructor with no fields is `{ tag: "Nothing" }`. One field is the function `(value) => ({ tag: "Just", value })`. Several fields are `value0`, `value1`, from left to right.
169
+
170
+ ## Array
171
+
172
+ `< 1 2 3 >` is an array of three numbers, of type `(Array Num)`. Index 0 is the left element. The empty array is `<>`. A word is not written inside the angle brackets: an element is a literal, a quotation, a nested array, or a record.
173
+
174
+ A word that walks an array is imported. In the example the quotation sits on top. The name comes from one module, and the instance for the array comes from another.
175
+
176
+ ```ply
177
+ ~ "Map" (map)
178
+ ~ "Num" (add)
179
+ ~ "Arr" ()
180
+
181
+ : shifted ( -- (Array Num) )
182
+ < 1 2 3 > [ 1 add ] map
183
+ ```
184
+
185
+ Empty parentheses bring the instances of a module and bring no names. The result is `2`, `3`, `4`.
186
+
187
+ ## Record
188
+
189
+ A record is an unnamed set of fields. Field order does not matter: `{ name : Str, age : Num }` and `{ age : Num, name : Str }` are one type.
190
+
191
+ ```ply
192
+ : person ( -- { name : Str, age : Num } )
193
+ { name: "Ada", age: 36 }
194
+
195
+ : nameOf ( { name : Str | r } -- Str )
196
+ /name
197
+
198
+ : older ( { name : Str, age : Num } -- { name : Str, age : Num } )
199
+ 37 =age
200
+ ```
201
+
202
+ `/name` reads a field. An open tail `| r` accepts extra labels, so `nameOf` also takes a record with an `age` field. A closed record `{ name : Str }` accepts no extra field.
203
+
204
+ `=age` replaces a field that is already there and returns a new record. The old JavaScript object stays as it was. `+city` adds a label that is not there yet. The record under `+name` must be closed.
205
+
206
+ ```ply
207
+ - Person { name : Str, age : Num }
208
+ - PersonWithCity { city : Str | Person }
209
+
210
+ : inParis ( Person -- PersonWithCity )
211
+ "Paris" +city
212
+ ```
213
+
214
+ `| Person` substitutes the fields of a closed record. `| (Person a)` does the same when the synonym has an argument: the parentheses are required. The empty record is `{}`.
215
+
216
+ When the record is still needed after a read, copy it: `dup /name`.
217
+
218
+ A named type and a record do different jobs. A constructor may carry a record whole.
219
+
220
+ ```ply
221
+ ; Person = Mk { name : Str, age : Num }
222
+
223
+ : personName ( Person -- Str )
224
+ Mk -> /name
225
+ ```
226
+
227
+ ## A name for a type
228
+
229
+ `-` names a type that already exists. The right-hand side follows immediately. It is the same type, and the name has no constructor.
230
+
231
+ ```ply
232
+ - Person { name : Str, age : Num }
233
+
234
+ : older ( Person -- Person )
235
+ 37 =age
236
+ ```
237
+
238
+ Parameters are written as they are on a type: `- Pair a b { left : a, right : b }`. In a slot a synonym is saturated, `(Pair Num Str)`.
239
+
240
+ ## Wrapper
241
+
242
+ `;` with `==` is a distinct type with one constructor and one field. It does not unify with the field. In JavaScript the constructor returns its argument as it is.
243
+
244
+ ```ply
245
+ ~ "Num" (add)
246
+
247
+ ; Age == Age Num
248
+
249
+ : older ( Age -- Age )
250
+ Age -> 1 add Age
251
+ ```
252
+
253
+ Zero fields, two fields, or two constructors do not fit `==`. A type with `=` is for those.
254
+
255
+ ## Dictionary
256
+
257
+ `(StrMap a)` is a dictionary with key `Str`. The key becomes known at run time and is not named in the type ahead of time. There is no literal. An empty dictionary in JavaScript is `Object.create(null)`.
258
+
259
+ ```ply
260
+ ~ "Dict" (empty, put)
261
+
262
+ : ages ( -- (StrMap Num) )
263
+ empty "Ada" 36 put
264
+ ```
265
+
266
+ The order in the example is the dictionary, the key, and the value on top.
267
+
268
+ ## Classes
269
+
270
+ `&` declares a class. A method is written without a colon. A constraint is not written in the class text: the compiler adds it. `#` declares an instance. The head is the type after the class name. Superclasses stand to the left of `=>`. A subclass constraint brings a superclass method when that method's name is imported. Several constraints in a scheme are separated by a comma.
271
+
272
+ ```ply
273
+ & Equal a
274
+ eq ( a a -- Bool )
275
+
276
+ & Equal a => Ordered a
277
+ before ( a a -- Bool )
278
+ ```
279
+
280
+ ```ply
281
+ ~ "Equal" (Equal, eq)
282
+ ~ "Num" ()
283
+
284
+ : same ( Equal a => a a -- Bool )
285
+ eq
286
+ ```
287
+
288
+ The instance for numbers is declared in the `Num` module. `()` brings the instances of that module and brings no names. The empty import of `Num` gives `same` that instance.
289
+
290
+ A hand-written instance is a word for each method. A module may write one when it defines the class, the type, or the wrapper in the head, or when the head contains a built-in constructor `Int`, `Num`, `Bool`, `Str`, `Void`, `Array`, or `StrMap`.
291
+
292
+ ```ply
293
+ & Equal a
294
+ eq ( a a -- Bool )
295
+
296
+ ; Box a = Mk a
297
+
298
+ : eqBox ( Equal a => (Box a) (Box a) -- Bool )
299
+ (Mk x) (Mk y) -> x y eq
300
+
301
+ # Equal (Box a)
302
+ eq = eqBox
303
+ ```
304
+
305
+ A parameter of kind `Type -> Type` writes the constructor in the head.
306
+
307
+ ```ply
308
+ & Mapped f
309
+ map ( (f a) (a -- b) -- (f b) )
310
+ ```
311
+
312
+ An instance of such a class begins with `# Mapped Box`.
313
+
314
+ Two classes with one head are written with a comma. An instance arrives only through a direct import: it does not pass through a second module.
315
+
316
+ When an exported word has a constraint in its scheme, the method dictionary becomes the first argument in JavaScript.
317
+
318
+ ## Module
319
+
320
+ One `*.ply` file is one module. The file itself has no name. The path from the source directory gives it: `src/Data/Maybe.ply` is called `Data.Maybe`. Each segment starts with a capital Latin letter.
321
+
322
+ Imports stand at the beginning of the file. A path inside this package is written from the source directory. A path into another package is a package specifier. `..` in the parentheses means every public name.
323
+
324
+ ```ply
325
+ ~ "Data/Maybe" (Just, Nothing, fromMaybe)
326
+ ~ "@algosail/example/Num" (add)
327
+ ```
328
+
329
+ `private` is visible in this file, is written into `.d.ply`, and is not exported.
330
+
331
+ A comment runs from `.` to the end of the line, when the dot is not inside `()`. `..` in column 0, directly above `:`, `;`, `-`, or `&`, is the text of that declaration. One space after `..` is not part of the text. A type has one text for all of its constructors. A class has one text for its name and for all of its methods. A `..` line above `#` does not attach to the declaration.
332
+
333
+ A declaration stands in column 0 and starts with `:`, `;`, `-`, `&`, `#`, or `~`.
334
+
335
+ A package is described by `package.json`.
336
+
337
+ ```json
338
+ {
339
+ "name": "@algosail/example",
340
+ "type": "module",
341
+ "ply": { "source": "src", "foreign": "foreign", "out": "dist", "language": 1 },
342
+ "exports": {
343
+ "./Data/Maybe": {
344
+ "ply": "./dist/Data/Maybe.d.ply",
345
+ "import": "./dist/Data/Maybe.js"
346
+ }
347
+ }
348
+ }
349
+ ```
350
+
351
+ A `foreign` word has no body. Its implementation lives in the `ply.foreign` directory, and the number of JavaScript parameters matches the input length. It combines with `private`.
352
+
353
+ ```ply
354
+ : foreign private length ( (Array a) -- Num )
355
+ ```
356
+
357
+ `ply.foreign` is required when the package has such a word. The directory lies inside the package and is distinct from `source` and `out`. The generated `.js` imports the implementation from there. Those files are not copied into `dist`.
358
+
359
+ ```text
360
+ plylang check [dir]
361
+ plylang build [dir] [--exports]
362
+ plylang docs [dir] [--out <file>]
363
+ plylang check --parse <file>
364
+ plylang check --js <file>
365
+ ```
366
+
367
+ `plylang check` checks a package the same way `build` does: every file under `ply.source`, the imports, and `foreign`. It writes no files. The default directory is the current one. After the same check, `plylang build` writes a `.js` file and a `.d.ply` interface into `ply.out`. `plylang build --exports` replaces the `exports` field of `package.json` with entries for the built modules. A key is `./` followed by the module path. `ply` points at the `.d.ply`, and `import` points at the `.js`. The other fields of the file stay as they were. With this flag an import of the package's own module succeeds even when that entry was not there yet. Without the flag the entry must already be present in `exports`.
368
+
369
+ `plylang docs` writes `DOCS.md` at the package root. `--out` names another file. A module heading is the string used to import it: the package name and the path, or only the path when there is no name. The prose comes from `..`. Inside a module the sections are `Types`, `Classes`, `Instances`, and `Words`. An empty section is left out. The name of a type, a class, or a word is a heading under its section. `Instances` is a list of `#` heads, with no comment and no method bindings.
370
+
371
+ `plylang check --parse` prints how one file was parsed, and it does not check types. `plylang check --js` prints the JavaScript that `build` would write for that file. Outside a package that is the JavaScript of the one file, and imports are not resolved.
372
+
373
+ The exit code is 0 for success, 1 for a diagnostic, and 2 for bad arguments. `--help` prints the same lines. Messages are in English: `file:line:column: error: PLxxxx: message`, and the next line is a link to the matching section of `docs/Errors.md`.
374
+
375
+ What enters a word's function is its own prefix, from the bottom upward. One result is returned by itself. Several results come back as a JavaScript array: that is the calling convention, and it is not an `(Array a)` value. The words from `dup` through `over2` do not become calls. `call`, `dip`, `keep`, `bi`, `if`, and their neighbors inline the body of the quotation.
376
+
377
+ ## Building
378
+
379
+ From this repository:
380
+
381
+ ```text
382
+ cargo build --release
383
+ ```
384
+
385
+ `target/release/plylang` and `target/release/plylang-ls` are the compiler and the server. The server speaks LSP over stdio. In a package with a `ply` field it resolves imports the same way `plylang build` does.
386
+
387
+ The npm package is named `@plylang/plylang` and installs the same two commands. Version `1.x` of that package is language `1`. A minor or a patch of the compiler does not change the language number. `PLYLANG_BIN` and `PLYLANG_LS_BIN` supply a binary of their own.
388
+
389
+ Highlighting is the [tree-sitter-ply](https://github.com/algosail/tree-sitter-ply) grammar. The Zed extension is the neighboring repository `zed-ply`: install it as a dev extension after `cargo build -p plylang-ls`.
390
+
391
+ The license is MIT.
package/bin/native.js ADDED
@@ -0,0 +1,128 @@
1
+ // The npm commands `plylang` and `plylang-ls` start here and run the Rust
2
+ // binary. An installed optional package `@plylang/plylang-<os>-<arch>` wins.
3
+ // A checkout of this repository uses `target/release`, then `target/debug`.
4
+
5
+ import { spawnSync } from "node:child_process";
6
+ import { accessSync, constants, statSync } from "node:fs";
7
+ import { createRequire } from "node:module";
8
+ import { dirname, join } from "node:path";
9
+
10
+ const root = join(import.meta.dirname, "..");
11
+ const require = createRequire(import.meta.url);
12
+
13
+ export const PLATFORMS = new Set([
14
+ "darwin-arm64",
15
+ "darwin-x64",
16
+ "linux-arm64",
17
+ "linux-x64",
18
+ "win32-arm64",
19
+ "win32-x64",
20
+ ]);
21
+
22
+ export function platformId(platform = process.platform, arch = process.arch) {
23
+ const id = `${platform}-${arch}`;
24
+ return PLATFORMS.has(id) ? id : null;
25
+ }
26
+
27
+ export function resolveBinary(command, options = {}) {
28
+ if (command !== "plylang" && command !== "plylang-ls") {
29
+ throw new Error(`unknown command ${command}`);
30
+ }
31
+ const env = options.env ?? process.env;
32
+ const platform = options.platform ?? process.platform;
33
+ const arch = options.arch ?? process.arch;
34
+ const packageRoot = options.packageRoot ?? root;
35
+ const resolvePackage = options.resolvePackage ?? defaultResolvePackage;
36
+ const overrideName = command === "plylang" ? "PLYLANG_BIN" : "PLYLANG_LS_BIN";
37
+ const override = env[overrideName];
38
+ if (override) {
39
+ if (!isExecutable(override)) {
40
+ throw new Error(`${overrideName} is not an executable file: ${override}`);
41
+ }
42
+ return override;
43
+ }
44
+
45
+ const id = platformId(platform, arch);
46
+ if (id) {
47
+ const pkgName = `@plylang/plylang-${id}`;
48
+ const pkgJson = resolvePackage(pkgName);
49
+ if (pkgJson) {
50
+ const name = fileName(command, platform);
51
+ const file = join(dirname(pkgJson), "bin", name);
52
+ if (!isExecutable(file)) {
53
+ throw new Error(`${pkgName} does not contain an executable bin/${name}`);
54
+ }
55
+ return file;
56
+ }
57
+ }
58
+
59
+ const built = checkoutBinary(command, packageRoot, platform);
60
+ if (built) {
61
+ return built;
62
+ }
63
+ if (!id) {
64
+ throw new Error(`${command} does not ship a binary for ${platform}-${arch}.`);
65
+ }
66
+ return missing(command, id);
67
+ }
68
+
69
+ function missing(command, id) {
70
+ throw new Error(
71
+ `${command} was not found for ${id}.\n` +
72
+ "From a checkout, run `cargo build --release -p plylang -p plylang-ls`.\n" +
73
+ `From npm, install the optional package @plylang/plylang-${id}.`,
74
+ );
75
+ }
76
+
77
+ function checkoutBinary(command, packageRoot, platform) {
78
+ const name = fileName(command, platform);
79
+ for (const profile of ["release", "debug"]) {
80
+ const file = join(packageRoot, "target", profile, name);
81
+ if (isExecutable(file)) {
82
+ return file;
83
+ }
84
+ }
85
+ return null;
86
+ }
87
+
88
+ function fileName(command, platform) {
89
+ return platform === "win32" ? `${command}.exe` : command;
90
+ }
91
+
92
+ function defaultResolvePackage(name) {
93
+ try {
94
+ return require.resolve(`${name}/package.json`);
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ export function run(command) {
101
+ let bin;
102
+ try {
103
+ bin = resolveBinary(command);
104
+ } catch (error) {
105
+ console.error(error.message);
106
+ process.exit(1);
107
+ }
108
+ const child = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
109
+ if (child.error) {
110
+ console.error(`${command}: ${child.error.message}`);
111
+ process.exit(1);
112
+ }
113
+ process.exit(child.status ?? 1);
114
+ }
115
+
116
+ function isExecutable(file) {
117
+ try {
118
+ if (!statSync(file).isFile()) {
119
+ return false;
120
+ }
121
+ if (process.platform !== "win32") {
122
+ accessSync(file, constants.X_OK);
123
+ }
124
+ return true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "./native.js";
3
+
4
+ run("plylang-ls");
package/bin/plylang.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "./native.js";
3
+
4
+ run("plylang");
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@plylang/plylang",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Ply compiler and language server",
5
+ "license": "MIT",
6
+ "author": "Algosail",
7
+ "type": "module",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/algosail/plylang.git"
11
+ },
12
+ "homepage": "https://github.com/algosail/plylang#readme",
13
+ "bugs": "https://github.com/algosail/plylang/issues",
14
+ "keywords": [
15
+ "ply",
16
+ "plylang",
17
+ "compiler",
18
+ "language-server"
19
+ ],
20
+ "engines": {
21
+ "node": ">=24"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "bin": {
27
+ "plylang": "bin/plylang.js",
28
+ "plylang-ls": "bin/plylang-ls.js"
29
+ },
30
+ "files": [
31
+ "bin/plylang.js",
32
+ "bin/plylang-ls.js",
33
+ "bin/native.js"
34
+ ],
35
+ "optionalDependencies": {
36
+ "@plylang/plylang-darwin-arm64": "1.0.0",
37
+ "@plylang/plylang-darwin-x64": "1.0.0",
38
+ "@plylang/plylang-linux-arm64": "1.0.0",
39
+ "@plylang/plylang-linux-x64": "1.0.0",
40
+ "@plylang/plylang-win32-arm64": "1.0.0",
41
+ "@plylang/plylang-win32-x64": "1.0.0"
42
+ },
43
+ "scripts": {
44
+ "build": "cargo build --release",
45
+ "test": "cargo test",
46
+ "test:launcher": "node --test bin/native.test.js script/stage-native.test.js"
47
+ }
48
+ }