eolang 0.34.0 → 0.35.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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "MD033": {
3
+ "allowed_elements": [
4
+ "details",
5
+ "summary"
6
+ ]
7
+ }
8
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,5 @@
1
+ # Instructions for Agents
2
+
3
+ To build the repo use 'npm install' and then 'npx grunt'.
4
+ To run all lints use 'npx eslint'.
5
+ Run eslint after every change you make.
package/Formula/eolang.rb CHANGED
@@ -7,9 +7,9 @@ require "language/node"
7
7
  class Eolang < Formula
8
8
  desc "Command-line Tool-Kit"
9
9
  homepage "https://github.com/objectionary/eoc"
10
- url "https://github.com/objectionary/eoc/archive/refs/tags/0.33.5.tar.gz"
11
- version "0.33.1"
12
- sha256 "f2a88bad46caa6a67d9058e8e7add2ff05f26959b08b3151c13f9178aab7e6a9"
10
+ url "https://github.com/objectionary/eoc/archive/refs/tags/0.34.1.tar.gz"
11
+ version "0.34.1"
12
+ sha256 "ecf7f19115086fadbe1785b789378928d81dc8f0e3aa154b268f3a747c80fd01"
13
13
  license "MIT"
14
14
 
15
15
  depends_on "node"
@@ -4,12 +4,12 @@
4
4
  #
5
5
 
6
6
  require "language/node"
7
- class Eolang < Formula
7
+ class EolangAT0320 < Formula
8
8
  desc "Command-line Tool-Kit"
9
9
  homepage "https://github.com/objectionary/eoc"
10
- url "https://github.com/objectionary/eoc/archive/refs/tags/0.33.5.tar.gz"
10
+ url "https://github.com/objectionary/eoc/archive/refs/tags/0.32.0.tar.gz"
11
11
  version "0.32.0"
12
- sha256 "f2a88bad46caa6a67d9058e8e7add2ff05f26959b08b3151c13f9178aab7e6a9"
12
+ sha256 "a24da178f48e1e64280aa467fd97986d9456a25ea023525bf36a9308ec1a4cea"
13
13
  license "MIT"
14
14
 
15
15
  depends_on "node"
@@ -4,12 +4,12 @@
4
4
  #
5
5
 
6
6
  require "language/node"
7
- class Eolang < Formula
7
+ class EolangAT0321 < Formula
8
8
  desc "Command-line Tool-Kit"
9
9
  homepage "https://github.com/objectionary/eoc"
10
- url "https://github.com/objectionary/eoc/archive/refs/tags/0.33.5.tar.gz"
10
+ url "https://github.com/objectionary/eoc/archive/refs/tags/0.32.1.tar.gz"
11
11
  version "0.32.1"
12
- sha256 "f2a88bad46caa6a67d9058e8e7add2ff05f26959b08b3151c13f9178aab7e6a9"
12
+ sha256 "6770b4eb3068b5675e2f8c1cce580693f2ad844c6f9472fd2e236f81ba356ab2"
13
13
  license "MIT"
14
14
 
15
15
  depends_on "node"
@@ -4,12 +4,12 @@
4
4
  #
5
5
 
6
6
  require "language/node"
7
- class Eolang < Formula
7
+ class EolangAT0330 < Formula
8
8
  desc "Command-line Tool-Kit"
9
9
  homepage "https://github.com/objectionary/eoc"
10
- url "https://github.com/objectionary/eoc/archive/refs/tags/0.33.5.tar.gz"
10
+ url "https://github.com/objectionary/eoc/archive/refs/tags/0.33.0.tar.gz"
11
11
  version "0.33.0"
12
- sha256 "f2a88bad46caa6a67d9058e8e7add2ff05f26959b08b3151c13f9178aab7e6a9"
12
+ sha256 "6e01b4d88ba92018c359fa0bffc3a23742610de454107a7605cd2eecb9ba9a33"
13
13
  license "MIT"
14
14
 
15
15
  depends_on "node"
package/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  [![codecov](https://codecov.io/gh/objectionary/eoc/branch/master/graph/badge.svg)](https://codecov.io/gh/objectionary/eoc)
10
10
  [![Hits-of-Code](https://hitsofcode.com/github/objectionary/eoc)](https://hitsofcode.com/view/github/objectionary/eoc)
11
11
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/objectionary/eoc/blob/master/LICENSE.txt)
12
+ [![Release](https://img.shields.io/github/v/release/objectionary/eoc.svg)](https://github.com/objectionary/eoc/releases/tag/0.34.1)
12
13
 
13
14
  This is a command-line tool-kit for [EO](https://www.eolang.org)
14
15
  programming languages, allowing you to compile EO programs, test, dataize,
@@ -19,14 +20,14 @@ First, you install [npm][npm-install] and [Java SE][java-se].
19
20
  Then, you install [eolang][npm] package, using [npm][npm-install]:
20
21
 
21
22
  ```bash
22
- npm install -g eolang@0.33.5
23
+ npm install -g eolang@0.34.1
23
24
  ```
24
25
 
25
26
  You can also use [Homebrew] (on macOS):
26
27
 
27
28
  ```bash
28
29
  brew tap objectionary/eoc https://github.com/objectionary/eoc
29
- brew install objectionary/eoc/eolang@0.33.5
30
+ brew install objectionary/eoc/eolang@0.34.1
30
31
  ```
31
32
 
32
33
  Or install it via [Nix flakes](https://nixos.wiki/wiki/Flakes):
@@ -35,7 +36,9 @@ Or install it via [Nix flakes](https://nixos.wiki/wiki/Flakes):
35
36
  nix run github:objectionary/eoc
36
37
  ```
37
38
 
38
- You can also include EOLANG in your own flake:
39
+ <details>
40
+
41
+ <summary>You can also include EOLANG in your own flake</summary>
39
42
 
40
43
  ```nix
41
44
  {
@@ -87,13 +90,15 @@ After that, select one of the methods for installing the package:
87
90
  }
88
91
  ```
89
92
 
93
+ </details>
94
+
90
95
  Then, you write a simple [EO](https://www.eolang.org) program in `hello.eo` file
91
96
  in the current directory:
92
97
 
93
98
  ```eo
94
99
  # My first object in EO!
95
100
  [args] > hello
96
- QQ.io.stdout > @
101
+ io.stdout > @
97
102
  "Hello, world!\n"
98
103
  ```
99
104
 
@@ -131,13 +136,15 @@ There are also commands that help manipulate with XMIR and EO sources
131
136
 
132
137
  * `audit` inspects all required packages and reports their status
133
138
  * `foreign` inspects all objects found in the program after the `assemble` step
134
- * `sodg` generates `.sodg` from `.xmir`, further rederable as XML or [Dot][dot]
135
139
  * `print` generates `.eo` files from `.xmir` files
136
140
  * `generate_comments` generates `.json` files with LLM-generated
137
141
  documentation for `.eo` structures
138
142
  * `docs` generates HTML documentation from `.xmir` files
139
143
  * `latex` generates `.tex` files from `.eo` sources
140
144
  * `fmt` formats `.eo` files in the source directory
145
+ * `normalize` normalizes `.eo` files via phi-calculus rewriting using
146
+ [phino](https://github.com/objectionary/phino) (must be installed separately);
147
+ original files are saved to `.eoc/before-normalize/` for debugging
141
148
  * ~~`translate` converts Java/C++/Python/etc. program to EO program~~
142
149
  * ~~`demu` removes `cage` and `memory` objects~~
143
150
  * ~~`dejump` removes `goto` objects~~
@@ -197,6 +204,5 @@ a pull request.
197
204
  [npm]: https://www.npmjs.com/package/eolang
198
205
  [java-se]: https://www.oracle.com/java/technologies/downloads/
199
206
  [npm-install]: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
200
- [dot]: https://en.wikipedia.org/wiki/DOT_%28graph_description_language%29
201
207
  [blog]: https://www.yegor256.com/2021/10/21/objectionary.html
202
208
  [Homebrew]: https://brew.sh/
package/eo-version.txt CHANGED
@@ -1 +1 @@
1
- 0.59.5
1
+ 0.59.9
package/eslint.config.js CHANGED
@@ -14,7 +14,7 @@ const localPlugin = {
14
14
 
15
15
  module.exports = [
16
16
  {
17
- ignores: ['node_modules/'],
17
+ ignores: ['node_modules/', '.aidy/'],
18
18
  },
19
19
  {
20
20
  ...configs.all,
package/home-tag.txt CHANGED
@@ -1 +1 @@
1
- 0.59.5
1
+ 0.59.7
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
2
2
  # SPDX-License-Identifier: MIT
3
3
 
4
- distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
4
+ distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.14/apache-maven-3.9.14-bin.zip
@@ -35,8 +35,6 @@
35
35
  <version>${eo.version}</version>
36
36
  <configuration>
37
37
  <excludeSources>**/.eoc/**</excludeSources>
38
- <sodgIncludes>${eo.sodgIncludes}</sodgIncludes>
39
- <sodgExcludes>${eo.sodgIncludes},**/*-test.eo</sodgExcludes>
40
38
  <foreign>${project.build.directory}/eo-foreign.json</foreign>
41
39
  <foreignFormat>json</foreignFormat>
42
40
  </configuration>
@@ -75,11 +73,11 @@
75
73
  </plugin>
76
74
  <plugin>
77
75
  <artifactId>maven-shade-plugin</artifactId>
78
- <version>3.6.1</version>
76
+ <version>3.6.2</version>
79
77
  </plugin>
80
78
  <plugin>
81
79
  <artifactId>maven-surefire-plugin</artifactId>
82
- <version>3.5.4</version>
80
+ <version>3.5.5</version>
83
81
  <configuration>
84
82
  <skipTests>true</skipTests>
85
83
  </configuration>
@@ -97,8 +95,6 @@
97
95
  <version>${eo.version}</version>
98
96
  <configuration>
99
97
  <excludeSources>**/.eoc/**</excludeSources>
100
- <sodgIncludes>${eo.sodgIncludes},**/*-test.eo</sodgIncludes>
101
- <sodgExcludes>${eo.sodgExcludes}</sodgExcludes>
102
98
  <foreign>${project.build.directory}/eo-foreign.json</foreign>
103
99
  <foreignFormat>json</foreignFormat>
104
100
  </configuration>
@@ -124,7 +120,7 @@
124
120
  </plugin>
125
121
  <plugin>
126
122
  <artifactId>maven-surefire-plugin</artifactId>
127
- <version>3.5.4</version>
123
+ <version>3.5.5</version>
128
124
  <configuration>
129
125
  <skipTests>false</skipTests>
130
126
  <failIfNoTests>true</failIfNoTests>
package/mvnw/pom.xml CHANGED
@@ -56,8 +56,6 @@
56
56
  <version>${eo.version}</version>
57
57
  <configuration>
58
58
  <excludeSources>**/.eoc/**</excludeSources>
59
- <sodgIncludes>${eo.sodgIncludes}</sodgIncludes>
60
- <sodgExcludes>${eo.sodgIncludes},**/*-test.eo</sodgExcludes>
61
59
  <foreign>${project.build.directory}/eo-foreign.json</foreign>
62
60
  <foreignFormat>json</foreignFormat>
63
61
  </configuration>
@@ -96,11 +94,11 @@
96
94
  </plugin>
97
95
  <plugin>
98
96
  <artifactId>maven-shade-plugin</artifactId>
99
- <version>3.6.1</version>
97
+ <version>3.6.2</version>
100
98
  </plugin>
101
99
  <plugin>
102
100
  <artifactId>maven-surefire-plugin</artifactId>
103
- <version>3.5.4</version>
101
+ <version>3.5.5</version>
104
102
  <configuration>
105
103
  <skipTests>true</skipTests>
106
104
  </configuration>
@@ -137,8 +135,6 @@
137
135
  <version>${eo.version}</version>
138
136
  <configuration>
139
137
  <excludeSources>**/.eoc/**</excludeSources>
140
- <sodgIncludes>${eo.sodgIncludes},**/*-test.eo</sodgIncludes>
141
- <sodgExcludes>${eo.sodgExcludes}</sodgExcludes>
142
138
  <foreign>${project.build.directory}/eo-foreign.json</foreign>
143
139
  <foreignFormat>json</foreignFormat>
144
140
  </configuration>
@@ -164,7 +160,7 @@
164
160
  </plugin>
165
161
  <plugin>
166
162
  <artifactId>maven-surefire-plugin</artifactId>
167
- <version>3.5.4</version>
163
+ <version>3.5.5</version>
168
164
  <configuration>
169
165
  <skipTests>false</skipTests>
170
166
  <failIfNoTests>true</failIfNoTests>
package/package.json CHANGED
@@ -25,10 +25,10 @@
25
25
  "grunt": "^1.6.1",
26
26
  "grunt-contrib-clean": "^2.0.1",
27
27
  "grunt-eslint": "^26.0.0",
28
- "grunt-mocha-cli": "^7.0.0",
28
+ "grunt-mocha-test": "^0.13.3",
29
29
  "istanbul": "^0.4.5",
30
30
  "mocha": "^11.7.5",
31
- "nyc": "^17.1.0",
31
+ "nyc": "^18.0.0",
32
32
  "patch-package": "^8.0.0"
33
33
  },
34
34
  "engines": {
@@ -74,5 +74,5 @@
74
74
  "postinstall": "node scripts/postinstall.js",
75
75
  "test": "mocha 'test/**/*.js' --timeout 1200000"
76
76
  },
77
- "version": "0.34.0"
77
+ "version": "0.35.0"
78
78
  }
package/renovate.json CHANGED
@@ -7,5 +7,8 @@
7
7
  "org.apache.maven.plugins:maven-compiler-plugin",
8
8
  "org.eolang:eo-maven-plugin",
9
9
  "org.eolang:eo-runtime"
10
+ ],
11
+ "ignorePaths": [
12
+ "Formula/**"
10
13
  ]
11
14
  }
@@ -16,8 +16,18 @@ const {elapsed} = require('../elapsed');
16
16
  module.exports = function(opts) {
17
17
  const target = path.resolve(opts.target);
18
18
  return elapsed(async (tracked) => {
19
- const r = await mvnw(['eo:assemble'].concat(flags(opts)), opts.target, opts.batch);
19
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
20
20
  tracked.print(`EO program assembled in ${rel(target)}`);
21
21
  return r;
22
22
  });
23
23
  };
24
+
25
+ /**
26
+ * Command to get Maven goals for assemble command.
27
+ * @return {Array.<String>} of Maven goals to run for assemble command
28
+ */
29
+ module.exports.goals = goals;
30
+
31
+ function goals() {
32
+ return ['eo:assemble'];
33
+ }
@@ -8,25 +8,8 @@ const path = require('path');
8
8
  const SaxonJS = require('saxon-js');
9
9
  const { marked } = require('marked');
10
10
  const {elapsed} = require('../elapsed');
11
+ const {findFiles} = require('../files');
11
12
 
12
- /**
13
- * Recursively reads all .xmir files from a directory.
14
- * @param {string} dir - Directory path
15
- * @return {string[]} Array of file paths
16
- */
17
- function readXmirsRecursively(dir) {
18
- const files = [];
19
- const entries = fs.readdirSync(dir, {withFileTypes: true});
20
- for (const entry of entries) {
21
- const full = path.join(dir, entry.name);
22
- if (entry.isDirectory()) {
23
- files.push(...readXmirsRecursively(full));
24
- } else if (entry.name.endsWith('.xmir')) {
25
- files.push(full);
26
- }
27
- }
28
- return files;
29
- }
30
13
 
31
14
  /**
32
15
  * Applies XSLT to XMIR
@@ -136,7 +119,7 @@ module.exports = function(opts) {
136
119
  fs.writeFileSync(css, '');
137
120
  const packages_info = {};
138
121
  const all_xmir_htmls = [];
139
- const xmirs = readXmirsRecursively(input);
122
+ const xmirs = findFiles(input, '.xmir');
140
123
  for (const xmir of xmirs) {
141
124
  const relative = path.relative(input, xmir);
142
125
  const name = path.parse(xmir).name;
@@ -2,7 +2,6 @@
2
2
  * SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
-
6
5
  const rel = require('relative');
7
6
  const {mvnw, flags} = require('../../mvnw');
8
7
  const {elapsed} = require('../../elapsed');
@@ -16,8 +15,18 @@ const path = require('path');
16
15
  module.exports = function(opts) {
17
16
  const target = path.resolve(opts.target);
18
17
  return elapsed(async (tracked) => {
19
- const r = await mvnw(['test-compile'].concat(flags(opts)), opts.target, opts.batch);
18
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
20
19
  tracked.print(`Java .class files compiled in ${rel(target)}`);
21
20
  return r;
22
21
  });
23
22
  };
23
+
24
+ /**
25
+ * Method to get Maven goals for compile command.
26
+ * @return {Array.<String>} of Maven goals to run for compile command
27
+ */
28
+ module.exports.goals = goals;
29
+
30
+ function goals() {
31
+ return ['test-compile'];
32
+ }
@@ -16,8 +16,18 @@ const path = require('path');
16
16
  module.exports = function(opts) {
17
17
  const jar = path.resolve(opts.target, 'eoc.jar');
18
18
  return elapsed(async (tracked) => {
19
- const r = await mvnw(['jar:jar', 'shade:shade'].concat(flags(opts)), opts.target, opts.batch);
19
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
20
20
  tracked.print(`Executable JAR created at ${rel(jar)}`);
21
21
  return r;
22
22
  });
23
23
  };
24
+
25
+ /**
26
+ * Command to get Maven goals for link command.
27
+ * @return {Array.<String>} of Maven goals to run for link command
28
+ */
29
+ module.exports.goals = goals;
30
+
31
+ function goals() {
32
+ return ['jar:jar', 'shade:shade'];
33
+ }
@@ -0,0 +1,35 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ const {mvnw, flags} = require('../../mvnw');
7
+ const {elapsed} = require('../../elapsed');
8
+
9
+ /**
10
+ * Runs multiple Maven goals in a single Maven invocation.
11
+ * @param {Object} coms - Command map to resolve goals and extra flags from
12
+ * @param {Array.<String>} commands - Command names to run in order
13
+ * @param {Object} opts - All options
14
+ * @return {Promise} of pipeline task
15
+ */
16
+ module.exports = function(coms, commands, opts, maven = mvnw) {
17
+ return elapsed(async (tracked) => {
18
+ const {goals, extras} = collect(coms, commands, opts);
19
+ const result = await maven(goals.concat(flags(opts)).concat(extras), opts.target, opts.batch);
20
+ tracked.print(`Pipeline [${commands.join(' \u2192 ')}] done`);
21
+ return result;
22
+ });
23
+ };
24
+
25
+ function collect(coms, commands, opts) {
26
+ const extras = [];
27
+ const goals = commands.flatMap((cmd) => {
28
+ const command = coms[cmd];
29
+ if (command.extras) {
30
+ extras.push(...command.extras(opts));
31
+ }
32
+ return command.goals(opts) || [];
33
+ });
34
+ return {goals, extras};
35
+ };
@@ -15,12 +15,19 @@ const path = require('path');
15
15
  */
16
16
  module.exports = function(opts) {
17
17
  return elapsed(async (tracked) => {
18
- await mvnw(['eo:resolve'].concat(flags(opts)), opts.target, opts.batch);
19
- const sources = path.resolve(opts.target, 'eo/6-resolve');
20
- console.info('Dependencies resolved in %s', rel(sources));
21
- const r = await mvnw(['eo:place'].concat(flags(opts)), opts.target, opts.batch);
18
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
22
19
  const classes = path.resolve(opts.target, 'classes');
23
20
  tracked.print(`Dependencies placed in ${rel(classes)}`);
24
21
  return r;
25
22
  });
26
23
  };
24
+
25
+ /**
26
+ * Command to get Maven goals for resolve command.
27
+ * @return {Array.<String>} of Maven goals to run for resolve command
28
+ */
29
+ module.exports.goals = goals;
30
+
31
+ function goals() {
32
+ return ['eo:resolve', 'eo:place'];
33
+ }
@@ -16,8 +16,18 @@ const path = require('path');
16
16
  module.exports = function(opts) {
17
17
  const sources = path.resolve(opts.target, 'generated-sources');
18
18
  return elapsed(async (tracked) => {
19
- const r = await mvnw(['eo:transpile'].concat(flags(opts)), opts.target, opts.batch);
19
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
20
20
  tracked.print(`Java sources generated in ${rel(sources)}`);
21
21
  return r;
22
22
  });
23
23
  };
24
+
25
+ /**
26
+ * Command to get Maven goals for transpile command.
27
+ * @return {Array.<String>} of Maven goals to run for transpile command
28
+ */
29
+ module.exports.goals = goals;
30
+
31
+ function goals() {
32
+ return ['eo:transpile'];
33
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ const {elapsed} = require('../../elapsed');
7
+
8
+ /**
9
+ * Runs multiple JS pipeline steps sequentially.
10
+ * @param {Object} coms - Command map to resolve step functions from
11
+ * @param {Array.<String>} commands - Command names to run in order
12
+ * @param {Object} opts - All options
13
+ * @return {Promise} of pipeline task
14
+ */
15
+ module.exports = function(coms, commands, opts) {
16
+ return elapsed(async (tracked) => {
17
+ for (const cmd of commands) {
18
+ // eslint-disable-next-line no-await-in-loop
19
+ await coms[cmd](opts);
20
+ }
21
+ tracked.print(`Pipeline [${commands.join(' \u2192 ')}] done`);
22
+ });
23
+ };
@@ -15,7 +15,7 @@ const path = require('path');
15
15
  */
16
16
  module.exports = function(opts) {
17
17
  return elapsed(async (tracked) => {
18
- const r = await eo2jsw('transpile', { ...opts, alone: true, project: 'project' });
18
+ const r = await eo2jsw('transpile', {...opts, alone: true, project: 'project'});
19
19
  tracked.print(`JS sources generated in ${rel(path.resolve(opts.target, 'project'))}`);
20
20
  return r;
21
21
  });
@@ -15,14 +15,12 @@ const semver = require('semver');
15
15
  * @return {Promise} of assemble task
16
16
  */
17
17
  module.exports = function(opts) {
18
- const extra = [
19
- `-Deo.failOnWarning=${opts.easy ? 'false' : 'true'}`,
20
- ];
18
+ const extra = extraFlags(opts);
21
19
  return elapsed(async (tracked) => {
22
- if (opts.parser.endsWith('-SNAPSHOT') || semver.gte(opts.parser, '0.45.0')) {
20
+ if (goals(opts)[0] === 'eo:lint') {
23
21
  try {
24
22
  const r = await mvnw(
25
- ['eo:lint'].concat(flags(opts)).concat(extra),
23
+ goals(opts).concat(flags(opts)).concat(extra),
26
24
  opts.target, opts.batch
27
25
  );
28
26
  tracked.print(`EO program linted in ${rel(path.resolve(opts.target))}`);
@@ -30,13 +28,13 @@ module.exports = function(opts) {
30
28
  } catch (error) {
31
29
  throw new Error(
32
30
  'There are errors and/or warnings; you may disable warnings via the --easy option',
33
- { cause: error }
31
+ {cause: error}
34
32
  );
35
33
  }
36
34
  }
37
35
  try {
38
36
  const r = await mvnw(
39
- ['eo:verify'].concat(flags(opts)).concat(extra),
37
+ goals(opts).concat(flags(opts)).concat(extra),
40
38
  opts.target, opts.batch
41
39
  );
42
40
  tracked.print(`EO program verified in ${rel(path.resolve(opts.target))}`);
@@ -44,8 +42,31 @@ module.exports = function(opts) {
44
42
  } catch (error) {
45
43
  throw new Error(
46
44
  'You may disable warnings via the --easy option',
47
- { cause: error }
45
+ {cause: error}
48
46
  );
49
47
  }
50
48
  });
51
49
  };
50
+
51
+ /**
52
+ * Command to get Maven goals for lint command.
53
+ * @return {Array.<String>} of Maven goals to run for lint command
54
+ */
55
+ module.exports.goals = goals;
56
+
57
+ /**
58
+ * Command to get extra Maven flags for lint command.
59
+ * @return {Array.<String>} of extra Maven flags to run for lint command
60
+ */
61
+ module.exports.extras = extras;
62
+
63
+ function goals(opts) {
64
+ if (opts.parser.endsWith('-SNAPSHOT') || semver.gte(opts.parser, '0.45.0')) {
65
+ return ['eo:lint'];
66
+ }
67
+ return ['eo:verify'];
68
+ }
69
+
70
+ function extras(opts) {
71
+ return [`-Deo.failOnWarning=${opts.easy ? 'false' : 'true'}`];
72
+ }
@@ -0,0 +1,64 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ const path = require('path');
7
+ const {execSync} = require('child_process');
8
+ const {mvnw, flags} = require('../mvnw');
9
+ const {elapsed} = require('../elapsed');
10
+ const {findFiles, saveFile, copyDir} = require('../files');
11
+ const relative = require('relative');
12
+
13
+ /**
14
+ * Command to normalize .EO files via phi-calculus using phino.
15
+ * Runs parse, converts XMIR → .phi → normalize → .phi → XMIR → .eo.
16
+ * Original .eo files are saved to .eoc/before-normalize/ for debugging.
17
+ * Fails if phino is not installed.
18
+ * @param {Object} opts - All options
19
+ * @return {Promise} of normalize task
20
+ */
21
+ module.exports = function(opts) {
22
+ if (opts.sources === undefined) {
23
+ throw new Error('Sources directory is not specified. Please provide it with --sources option.');
24
+ }
25
+ if (opts.target === undefined) {
26
+ throw new Error('Target directory is not specified. Please provide it with --target option.');
27
+ }
28
+ try {
29
+ execSync('phino --version', {stdio: 'pipe'});
30
+ } catch (e) {
31
+ throw new Error('phino is not installed, see https://github.com/objectionary/phino', {cause: e});
32
+ }
33
+ const sources = path.resolve(opts.sources);
34
+ const target = path.resolve(opts.target);
35
+ return elapsed(async (tracked) => {
36
+ copyDir(sources, path.join(target, 'before-normalize'), '.eo');
37
+ const parsed = path.join(target, '1-parse');
38
+ const normed = path.join(target, 'xmir-normalized');
39
+ const xmirs = findFiles(parsed, '.xmir');
40
+ console.debug('Found %d XMIR file(s) to normalize', xmirs.length);
41
+ for (const xmir of xmirs) {
42
+ const rel = path.relative(parsed, xmir);
43
+ console.debug('Normalizing %s', rel);
44
+ const ts = Date.now();
45
+ const out = execSync(
46
+ `phino rewrite --input=xmir --output=xmir --normalize ${xmir}`,
47
+ {stdio: ['pipe', 'pipe', 'pipe']}
48
+ );
49
+ console.debug('Normalized in %dms', Date.now() - ts);
50
+ saveFile(normed, rel, out);
51
+ }
52
+ const r = await mvnw(
53
+ ['eo:print']
54
+ .concat(flags(opts))
55
+ .concat([
56
+ `-Deo.printSourcesDir=${normed}`,
57
+ `-Deo.printOutputDir=${sources}`,
58
+ ]),
59
+ opts.target, opts.batch
60
+ );
61
+ tracked.print(`EO files normalized in ${relative(sources)}`);
62
+ return r;
63
+ });
64
+ };
@@ -16,8 +16,18 @@ const {elapsed} = require('../elapsed');
16
16
  module.exports = function(opts) {
17
17
  const target = path.resolve(opts.target);
18
18
  return elapsed(async (tracked) => {
19
- const r = await mvnw(['eo:parse'].concat(flags(opts)), opts.target, opts.batch);
19
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
20
20
  tracked.print(`EO sources parsed in ${rel(target)}`);
21
21
  return r;
22
22
  });
23
23
  };
24
+
25
+ /**
26
+ * Command to get Maven goals for parse command.
27
+ * @return {Array.<String>} of Maven goals to run for parse command
28
+ */
29
+ module.exports.goals = goals;
30
+
31
+ function goals() {
32
+ return ['eo:parse'];
33
+ }
@@ -16,8 +16,18 @@ const path = require('path');
16
16
  module.exports = function(opts) {
17
17
  const foreign = path.resolve(opts.target, 'eo-foreign.json');
18
18
  return elapsed(async (tracked) => {
19
- const r = await mvnw(['eo:register'].concat(flags(opts)), opts.target, opts.batch);
19
+ const r = await mvnw(goals().concat(flags(opts)), opts.target, opts.batch);
20
20
  tracked.print(`EO objects registered in ${rel(foreign)}`);
21
21
  return r;
22
22
  });
23
23
  };
24
+
25
+ /**
26
+ * Command to get Maven goals for register command.
27
+ * @return {Array.<String>} of Maven goals to run for register command
28
+ */
29
+ module.exports.goals = goals;
30
+
31
+ function goals() {
32
+ return ['eo:register'];
33
+ }
package/src/eoc.js CHANGED
@@ -26,13 +26,13 @@ const {program} = require('commander'),
26
26
  parse: require('./commands/parse'),
27
27
  print: require('./commands/print'),
28
28
  register: require('./commands/register'),
29
- sodg: require('./commands/sodg'),
30
29
  lint: require('./commands/lint'),
31
30
  docs: require('./commands/docs'),
32
31
  generate_comments: require('./commands/generate_comments'),
33
32
  jeo_disassemble: require('./commands/jeo/disassemble'),
34
33
  jeo_assemble: require('./commands/jeo/assemble'),
35
- latex: require('./commands/latex')
34
+ latex: require('./commands/latex'),
35
+ normalize: require('./commands/normalize'),
36
36
  },
37
37
  commands = {
38
38
  [language.java]: {
@@ -43,7 +43,7 @@ const {program} = require('commander'),
43
43
  link: require('./commands/java/link'),
44
44
  compile: require('./commands/java/compile'),
45
45
  dataize: require('./commands/java/dataize'),
46
- test: require('./commands/java/test')
46
+ test: require('./commands/java/test'),
47
47
  }
48
48
  },
49
49
  [language.js]: {
@@ -54,9 +54,13 @@ const {program} = require('commander'),
54
54
  link: require('./commands/js/link'),
55
55
  compile: require('./commands/js/compile'),
56
56
  dataize: require('./commands/js/dataize'),
57
- test: require('./commands/js/test')
57
+ test: require('./commands/js/test'),
58
58
  }
59
59
  }
60
+ },
61
+ pipelines = {
62
+ [language.java]: require('./commands/java/pipeline'),
63
+ [language.js]: require('./commands/js/pipeline'),
60
64
  };
61
65
 
62
66
  if (process.argv.includes('--verbose')) {
@@ -106,7 +110,8 @@ program
106
110
  .option('-c, --clean', 'Delete .eoc directory before running a command')
107
111
  .option('--debug', 'Print ALL debug messages, heavily overloading the log')
108
112
  .option('--verbose', 'Print debug messages and full output of child processes')
109
- .option('--pin <version>', 'Fail if eoc version doesn\'t match exactly', version.what);
113
+ .option('--pin <version>', 'Fail if eoc version doesn\'t match exactly', version.what)
114
+ .option('--update-snapshots', 'Update snapshots in the local repository if they are outdated');
110
115
 
111
116
  program.command('audit')
112
117
  .description('Inspect all packages and report their status')
@@ -142,8 +147,7 @@ program.command('parse')
142
147
  pin(program.opts());
143
148
  clear(str);
144
149
  if (program.opts().alone === undefined) {
145
- await coms().register(program.opts());
146
- await coms().parse(program.opts());
150
+ await pipe()(coms(), ['register', 'parse'], program.opts());
147
151
  } else {
148
152
  await coms().parse(program.opts());
149
153
  }
@@ -155,33 +159,12 @@ program.command('assemble')
155
159
  pin(program.opts());
156
160
  clear(str);
157
161
  if (program.opts().alone === undefined) {
158
- await coms().register(program.opts());
159
- await coms().assemble(program.opts());
162
+ await pipe()(coms(), ['register', 'assemble'], program.opts());
160
163
  } else {
161
164
  await coms().assemble(program.opts());
162
165
  }
163
166
  });
164
167
 
165
- program.command('sodg')
166
- .description('Generate SODG files from XMIR')
167
- .option('--xml', 'Generate .sodg.xml files')
168
- .option('--xembly', 'Generate .sodg.xe files')
169
- .option('--graph', 'Generate .sodg.graph files')
170
- .option('--dot', 'Generate .sodg.dot files')
171
- .option('--include <names>', 'Generate SODG for these object names (using mask)', '**')
172
- .option('--exclude <names>', 'Don\'t generate SODG for these objects')
173
- .action(async (str, opts) => {
174
- pin(program.opts());
175
- clear(str);
176
- if (program.opts().alone === undefined) {
177
- await coms().register(program.opts());
178
- await coms().assemble(program.opts());
179
- await coms().sodg({...program.opts(), ...str});
180
- } else {
181
- await coms().sodg({...program.opts(), ...str});
182
- }
183
- });
184
-
185
168
  program.command('print')
186
169
  .description('Generate EO files from XMIR files')
187
170
  .option(
@@ -206,9 +189,7 @@ program.command('lint')
206
189
  pin(program.opts());
207
190
  clear(str);
208
191
  if (program.opts().alone === undefined) {
209
- await coms().register(program.opts());
210
- await coms().assemble(program.opts());
211
- await coms().lint(program.opts());
192
+ await pipe()(coms(), ['register', 'assemble', 'lint'], program.opts());
212
193
  } else {
213
194
  await coms().lint(program.opts());
214
195
  }
@@ -220,10 +201,7 @@ program.command('resolve')
220
201
  pin(program.opts());
221
202
  clear(str);
222
203
  if (program.opts().alone === undefined) {
223
- await coms().register(program.opts());
224
- await coms().assemble(program.opts());
225
- await coms().lint(program.opts());
226
- await coms().resolve(program.opts());
204
+ await pipe()(coms(), ['register', 'assemble', 'lint', 'resolve'], program.opts());
227
205
  } else {
228
206
  await coms().resolve(program.opts());
229
207
  }
@@ -235,11 +213,7 @@ program.command('transpile')
235
213
  pin(program.opts());
236
214
  clear(str);
237
215
  if (program.opts().alone === undefined) {
238
- await coms().register(program.opts());
239
- await coms().assemble(program.opts());
240
- await coms().lint(program.opts());
241
- await coms().resolve(program.opts());
242
- await coms().transpile(program.opts());
216
+ await pipe()(coms(), ['register', 'assemble', 'lint', 'resolve', 'transpile'], program.opts());
243
217
  } else {
244
218
  await coms().transpile(program.opts());
245
219
  }
@@ -251,12 +225,7 @@ program.command('compile')
251
225
  pin(program.opts());
252
226
  clear(str);
253
227
  if (program.opts().alone === undefined) {
254
- await coms().register(program.opts());
255
- await coms().assemble(program.opts());
256
- await coms().lint(program.opts());
257
- await coms().resolve(program.opts());
258
- await coms().transpile(program.opts());
259
- await coms().compile(program.opts());
228
+ await pipe()(coms(), ['register', 'assemble', 'lint', 'resolve', 'transpile', 'compile'], program.opts());
260
229
  } else {
261
230
  await coms().compile(program.opts());
262
231
  }
@@ -267,13 +236,7 @@ program.command('link')
267
236
  .action(async (str, opts) => {
268
237
  clear(str);
269
238
  if (program.opts().alone === undefined) {
270
- await coms().register(program.opts());
271
- await coms().assemble(program.opts());
272
- await coms().lint(program.opts());
273
- await coms().resolve(program.opts());
274
- await coms().transpile(program.opts());
275
- await coms().compile(program.opts());
276
- await coms().link(program.opts());
239
+ await pipe()(coms(), ['register', 'assemble', 'lint', 'resolve', 'transpile', 'compile', 'link'], program.opts());
277
240
  } else {
278
241
  await coms().link(program.opts());
279
242
  }
@@ -287,13 +250,7 @@ program.command('dataize')
287
250
  pin(program.opts());
288
251
  clear(str);
289
252
  if (program.opts().alone === undefined) {
290
- await coms().register(program.opts());
291
- await coms().assemble(program.opts());
292
- await coms().lint(program.opts());
293
- await coms().resolve(program.opts());
294
- await coms().transpile(program.opts());
295
- await coms().compile(program.opts());
296
- await coms().link(program.opts());
253
+ await pipe()(coms(), ['register', 'assemble', 'lint', 'resolve', 'transpile', 'compile', 'link'], program.opts());
297
254
  await coms().dataize(
298
255
  program.args[1], program.args.slice(2), {...program.opts(), ...str}
299
256
  );
@@ -312,13 +269,7 @@ program.command('test')
312
269
  pin(program.opts());
313
270
  clear(str);
314
271
  if (program.opts().alone === undefined) {
315
- await coms().register(program.opts());
316
- await coms().assemble(program.opts());
317
- await coms().lint(program.opts());
318
- await coms().resolve(program.opts());
319
- await coms().transpile(program.opts());
320
- await coms().compile(program.opts());
321
- await coms().link(program.opts());
272
+ await pipe()(coms(), ['register', 'assemble', 'lint', 'resolve', 'transpile', 'compile', 'link'], program.opts());
322
273
  await coms().test({...program.opts(), ...str});
323
274
  } else {
324
275
  await coms().test({...program.opts(), ...str});
@@ -397,18 +348,25 @@ program.command('latex')
397
348
  .action(async (str, opts) => {
398
349
  pin(program.opts());
399
350
  clear(str);
400
- await coms().register(program.opts());
401
- await coms().parse(program.opts());
351
+ await pipe()(coms(), ['register', 'parse'], program.opts());
402
352
  await coms().latex(program.opts());
403
353
  });
404
354
 
355
+ program.command('normalize')
356
+ .description('Normalize EO files using phi-calculus normalization via phino')
357
+ .action(async (str, opts) => {
358
+ pin(program.opts());
359
+ clear(str);
360
+ await pipe()(coms(), ['register', 'parse'], program.opts());
361
+ await coms().normalize(program.opts());
362
+ });
363
+
405
364
  program.command('fmt')
406
365
  .description('Format EO files in the source directory')
407
366
  .action(async (str, opts) => {
408
367
  pin(program.opts());
409
368
  clear(str);
410
- await coms().register(program.opts());
411
- await coms().parse(program.opts());
369
+ await pipe()(coms(), ['register', 'parse'], program.opts());
412
370
  await coms().print({
413
371
  printInput: '1-parse',
414
372
  printOutput: program.opts().sources,
@@ -457,3 +415,16 @@ function coms() {
457
415
  }
458
416
  return hash;
459
417
  }
418
+
419
+ /**
420
+ * Get pipeline for the target language.
421
+ * @return {Function} - pipeline function
422
+ */
423
+ function pipe() {
424
+ const lang = program.opts().language;
425
+ const pipeline = pipelines[lang];
426
+ if (pipeline === undefined) {
427
+ throw new Error(`Unknown platform ${lang}`);
428
+ }
429
+ return pipeline;
430
+ }
package/src/files.js ADDED
@@ -0,0 +1,61 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ /**
10
+ * Recursively find all files with given extension in a directory.
11
+ * @param {string} dir - Directory to search
12
+ * @param {string} ext - File extension including dot (e.g. '.xmir')
13
+ * @return {Array.<string>} List of absolute file paths
14
+ */
15
+ function findFiles(dir, ext) {
16
+ if (!fs.existsSync(dir)) {return [];}
17
+ const result = [];
18
+ for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
19
+ const full = path.join(dir, entry.name);
20
+ if (entry.isDirectory()) {
21
+ result.push(...findFiles(full, ext));
22
+ } else if (entry.name.endsWith(ext)) {
23
+ result.push(full);
24
+ }
25
+ }
26
+ return result;
27
+ }
28
+
29
+ /**
30
+ * Write content to a file, creating parent directories as needed.
31
+ * @param {string} dir - Parent directory
32
+ * @param {string} name - File name relative to dir
33
+ * @param {Buffer} content - File content
34
+ */
35
+ function saveFile(dir, name, content) {
36
+ const file = path.join(dir, name);
37
+ fs.mkdirSync(path.dirname(file), {recursive: true});
38
+ fs.writeFileSync(file, content);
39
+ }
40
+
41
+ /**
42
+ * Recursively copy files with given extension from src to dst directory.
43
+ * @param {string} src - Source directory
44
+ * @param {string} dst - Destination directory
45
+ * @param {string} ext - File extension filter (e.g. '.eo'), or empty for all files
46
+ */
47
+ function copyDir(src, dst, ext) {
48
+ if (!fs.existsSync(src)) {return;}
49
+ fs.mkdirSync(dst, {recursive: true});
50
+ for (const entry of fs.readdirSync(src, {withFileTypes: true})) {
51
+ const source = path.join(src, entry.name);
52
+ const dest = path.join(dst, entry.name);
53
+ if (entry.isDirectory()) {
54
+ copyDir(source, dest, ext);
55
+ } else if (!ext || entry.name.endsWith(ext)) {
56
+ fs.copyFileSync(source, dest);
57
+ }
58
+ }
59
+ }
60
+
61
+ module.exports = {findFiles, saveFile, copyDir};
package/src/mvnw.js CHANGED
@@ -7,7 +7,7 @@ const path = require('path');
7
7
  const fs = require('fs');
8
8
  const rel = require('relative');
9
9
  const readline = require('readline');
10
- const { spawn } = require('child_process');
10
+ const {spawn} = require('child_process');
11
11
  const colors = require('colors');
12
12
  const parserVersion = require('./parser-version');
13
13
 
@@ -31,7 +31,13 @@ let beginning,
31
31
  * @param {Object} opts - Opts provided to the "eoc"
32
32
  * @return {Array} of Maven options
33
33
  */
34
- module.exports.flags = function (opts) {
34
+ module.exports.flags = function(opts) {
35
+ if (opts.sources === undefined) {
36
+ throw new Error('Sources directory is not specified. Please provide it with --sources option.');
37
+ }
38
+ if (opts.target === undefined) {
39
+ throw new Error('Target directory is not specified. Please provide it with --target option.');
40
+ }
35
41
  const sources = path.resolve(opts.sources);
36
42
  console.debug('Sources in %s', rel(sources));
37
43
  const target = path.resolve(opts.target);
@@ -50,6 +56,7 @@ module.exports.flags = function (opts) {
50
56
  opts.verbose ? '--errors' : '',
51
57
  opts.verbose ? '' : '--quiet',
52
58
  opts.debug ? '--debug' : '',
59
+ opts.updateSnapshots ? '--update-snapshots' : '',
53
60
  `-Deo.sourcesDir=${sources}`,
54
61
  `-Deo.targetDir=${target}`,
55
62
  `-Deo.outputDir=${path.resolve(opts.target, 'classes')}`,
@@ -58,7 +65,7 @@ module.exports.flags = function (opts) {
58
65
  `-Deo.placedFormat=csv`,
59
66
  `-Deo.skipLinting=${opts.blind ? 'true' : 'false'}`,
60
67
  opts.trackTransformationSteps ? '-Deo.trackTransformationSteps' : '',
61
- ];
68
+ ].filter(flag => flag !== '');
62
69
  };
63
70
 
64
71
  /**
@@ -68,11 +75,11 @@ module.exports.flags = function (opts) {
68
75
  * @param {Boolean} [batch] - Is it batch mode (TRUE) or interactive (FALSE)?
69
76
  * @return {Promise} of maven execution task
70
77
  */
71
- module.exports.mvnw = function (args, tgt, batch) {
78
+ module.exports.mvnw = function(args, tgt, batch) {
72
79
  return new Promise((resolve, reject) => {
73
80
  console.debug(`Running mvnw with arguments: ${args.join(' ')}`);
74
81
  target = tgt;
75
- phase = args[0];
82
+ phase = args.filter((a) => !a.startsWith('-')).join(' + ');
76
83
  const home = path.resolve(__dirname, '../mvnw');
77
84
  let bin = path.resolve(home, 'mvnw') + (process.platform === 'win32' ? '.cmd' : '');
78
85
  if (!fs.existsSync(bin)) {
@@ -82,7 +89,6 @@ module.exports.mvnw = function (args, tgt, batch) {
82
89
  const params = args.filter((t) => t !== '').concat([
83
90
  '--batch-mode',
84
91
  '--color=never',
85
- '--update-snapshots',
86
92
  '--fail-fast',
87
93
  '--strict-checksums',
88
94
  ]);
@@ -129,7 +135,7 @@ module.exports.mvnw = function (args, tgt, batch) {
129
135
  function start() {
130
136
  running = true;
131
137
  beginning = Date.now();
132
- const check = function () {
138
+ const check = function() {
133
139
  if (running) {
134
140
  print();
135
141
  setTimeout(check, 1000);
package/src/version.js CHANGED
@@ -6,6 +6,6 @@
6
6
  // The values here are replaced automatically by the .rultor.yml script,
7
7
  // at the "release" pipeline:
8
8
  module.exports = {
9
- what: '0.34.0',
10
- when: '2026-02-19'
9
+ what: '0.35.0',
10
+ when: '2026-04-20'
11
11
  };
@@ -1,45 +0,0 @@
1
- /*
2
- * SPDX-FileCopyrightText: Copyright (c) 2022-2026 Objectionary.com
3
- * SPDX-License-Identifier: MIT
4
- */
5
-
6
- const rel = require('relative');
7
- const path = require('path');
8
- const {mvnw, flags} = require('../mvnw');
9
- const {elapsed} = require('../elapsed');
10
-
11
- /**
12
- * Generate SODG files from XMIR.
13
- * @param {Hash} opts - All options
14
- * @return {Promise} of sodg task
15
- */
16
- module.exports = function(opts) {
17
- return elapsed(async (tracked) => {
18
- const argv = ['eo:sodg'].concat(flags(opts));
19
- argv.push(`-Deo.sodgIncludes=${opts.include}`);
20
- if (opts.exclude) {
21
- argv.push(`-Deo.sodgExcludes=${opts.exclude}`);
22
- }
23
- if (opts.xml) {
24
- argv.push('-Deo.generateSodgXmlFiles');
25
- }
26
- if (opts.xembly) {
27
- argv.push('-Deo.generateSodgXmlFiles');
28
- argv.push('-Deo.generateXemblyFiles');
29
- }
30
- if (opts.graph) {
31
- argv.push('-Deo.generateSodgXmlFiles');
32
- argv.push('-Deo.generateXemblyFiles');
33
- argv.push('-Deo.generateGraphFiles');
34
- }
35
- if (opts.dot) {
36
- argv.push('-Deo.generateSodgXmlFiles');
37
- argv.push('-Deo.generateXemblyFiles');
38
- argv.push('-Deo.generateGraphFiles');
39
- argv.push('-Deo.generateDotFiles');
40
- }
41
- const r = await mvnw(argv, opts.target, opts.batch);
42
- tracked.print(`SODG files generated in ${rel(path.resolve(opts.target))}`);
43
- return r;
44
- });
45
- };