diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b3f7d01..9b3fdd5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: build: strategy: matrix: - os: [windows-latest, ubuntu-latest, macos-latest] + os: [ubuntu-24.04, windows-2022, macos-13] node: [16] runs-on: ${{ matrix.os }} steps: @@ -37,6 +37,10 @@ jobs: - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 21 - run: npm install - run: npm install --force -g grunt-cli - run: npm test diff --git a/eo2js-runtime/package-lock.json b/eo2js-runtime/package-lock.json index 9a56232..d745371 100644 --- a/eo2js-runtime/package-lock.json +++ b/eo2js-runtime/package-lock.json @@ -8,6 +8,9 @@ "name": "eo2js-runtime", "version": "0.0.0", "license": "MIT", + "dependencies": { + "colors": "1.4.0" + }, "devDependencies": { "eslint": "9.6.0", "eslint-config-google": "0.14.0", @@ -501,10 +504,10 @@ "dev": true }, "node_modules/colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", - "dev": true, + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -1613,6 +1616,16 @@ "node": ">=10" } }, + "node_modules/grunt-legacy-log/node_modules/colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/grunt-legacy-util": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", diff --git a/eo2js-runtime/src/objects/org/eolang/cage$encaged$encage.js b/eo2js-runtime/src/objects/org/eolang/cage$encaged$encage.js deleted file mode 100644 index eee0150..0000000 --- a/eo2js-runtime/src/objects/org/eolang/cage$encaged$encage.js +++ /dev/null @@ -1,26 +0,0 @@ -const object = require('../../../runtime/object') -const {LAMBDA, RHO} = require('../../../runtime/attribute/specials'); -const at_void = require('../../../runtime/attribute/at-void'); -const cages = require('../../../runtime/cages'); -const dataized = require('../../../runtime/dataized'); -const {NUMBER} = require('../../../runtime/types'); -const data = require('../../../runtime/data') - -/** - * Cage.encaged.encage. - * @return {Object} - Cage.encaged.encage object - */ -const cage$encaged$encage = function() { - const obj = object('cage$encaged$encage') - obj.attrs['object'] = at_void('object') - obj.assets[LAMBDA] = function(self) { - cages.encage( - dataized(self.take(RHO).take('locator'), NUMBER), - self.take('object') - ); - return data.toObject(true) - } - return obj -} - -module.exports = cage$encaged$encage diff --git "a/eo2js-runtime/src/objects/org/eolang/cage$encaged$\317\206.js" "b/eo2js-runtime/src/objects/org/eolang/cage$encaged$\317\206.js" deleted file mode 100644 index f1d9667..0000000 --- "a/eo2js-runtime/src/objects/org/eolang/cage$encaged$\317\206.js" +++ /dev/null @@ -1,21 +0,0 @@ -const object = require('../../../runtime/object') -const {LAMBDA, RHO} = require('../../../runtime/attribute/specials'); -const dataized = require('../../../runtime/dataized'); -const {NUMBER} = require('../../../runtime/types'); -const {traced} = require('../../../runtime/traced'); -const cages = require('../../../runtime/cages'); - -/** - * Cage.encaged.φ. - * @return {Object} - Cage.encaged.φ object - */ -const cage$encaged$φ = function() { - const obj = object('cage$encaged$φ') - obj.assets[LAMBDA] = function(self) { - const locator = dataized(self.take(RHO).take('locator'), NUMBER) - return traced(cages.get(locator), locator) - } - return obj -} - -module.exports = cage$encaged$φ diff --git "a/eo2js-runtime/src/objects/org/eolang/cage$\317\206.js" "b/eo2js-runtime/src/objects/org/eolang/cage$\317\206.js" deleted file mode 100644 index ea02f9f..0000000 --- "a/eo2js-runtime/src/objects/org/eolang/cage$\317\206.js" +++ /dev/null @@ -1,23 +0,0 @@ -const object = require('../../../runtime/object') -const {LAMBDA, RHO} = require('../../../runtime/attribute/specials'); -const data = require('../../../runtime/data'); -const cages = require('../../../runtime/cages'); - -/** - * Cage.φ. - * @return {Object} - Cage.φ object - */ -const cage$φ = function() { - const obj = object('cage$φ') - obj.assets[LAMBDA] = function(self) { - const rho = self.take(RHO) - return rho.take('encaged').with({ - locator: data.toObject( - cages.init(rho.take('object')) - ) - }) - } - return obj -} - -module.exports = cage$φ diff --git a/eo2js-runtime/src/objects/org/eolang/malloc$of$allocated$resize.js b/eo2js-runtime/src/objects/org/eolang/malloc$of$allocated$resized.js similarity index 79% rename from eo2js-runtime/src/objects/org/eolang/malloc$of$allocated$resize.js rename to eo2js-runtime/src/objects/org/eolang/malloc$of$allocated$resized.js index c3ef063..ac841c1 100644 --- a/eo2js-runtime/src/objects/org/eolang/malloc$of$allocated$resize.js +++ b/eo2js-runtime/src/objects/org/eolang/malloc$of$allocated$resized.js @@ -7,10 +7,10 @@ const dataized = require('../../../runtime/dataized'); const data = require('../../../runtime/data') /** - * Malloc.of.allocated.resize. - * @return {Object} - Malloc.of.allocated.resize object + * Malloc.of.allocated.resized. + * @return {Object} - Malloc.of.allocated.resized object */ -const malloc$of$allocated$resize = function() { +const malloc$of$allocated$resized = function() { const obj = object('malloc$of$allocated$resize') obj.attrs['size'] = at_void('size') obj.assets[LAMBDA] = function(self) { @@ -23,4 +23,4 @@ const malloc$of$allocated$resize = function() { return obj } -module.exports = malloc$of$allocated$resize +module.exports = malloc$of$allocated$resized diff --git "a/eo2js-runtime/src/objects/org/eolang/malloc$of$\317\206.js" "b/eo2js-runtime/src/objects/org/eolang/malloc$of$\317\206.js" index 0d30555..398bee2 100644 --- "a/eo2js-runtime/src/objects/org/eolang/malloc$of$\317\206.js" +++ "b/eo2js-runtime/src/objects/org/eolang/malloc$of$\317\206.js" @@ -18,14 +18,15 @@ const malloc$of$φ = function() { ) let res; try { - dataized( - rho.take('scope').with({ - 0: rho.take('allocated').with({ - id: data.toObject(identifier) + res = data.toObject( + dataized( + rho.take('scope').with({ + 0: rho.take('allocated').with({ + id: data.toObject(identifier) + }) }) - }) + ) ) - res = data.toObject(heaps.read(identifier, 0, heaps.size(identifier))) } finally { heaps.free(identifier) } diff --git a/eo2js-runtime/src/objects/org/eolang/string$length.js b/eo2js-runtime/src/objects/org/eolang/string$length.js deleted file mode 100644 index fa373e7..0000000 --- a/eo2js-runtime/src/objects/org/eolang/string$length.js +++ /dev/null @@ -1,21 +0,0 @@ -const object = require('../../../runtime/object') -const {LAMBDA, RHO} = require('../../../runtime/attribute/specials'); -const {STRING} = require('../../../runtime/types'); -const dataized = require('../../../runtime/dataized'); -const data = require('../../../runtime/data') - -/** - * String.length. - * @return {Object} - String.length object - */ -const string$length = function() { - const obj = object('string$length') - obj.assets[LAMBDA] = function(self) { - return data.toObject( - dataized(self.take(RHO), STRING).length - ) - } - return obj -} - -module.exports = string$length diff --git a/eo2js-runtime/src/objects/org/eolang/string$slice.js b/eo2js-runtime/src/objects/org/eolang/string$slice.js deleted file mode 100644 index b8ed9fd..0000000 --- a/eo2js-runtime/src/objects/org/eolang/string$slice.js +++ /dev/null @@ -1,44 +0,0 @@ -const object = require('../../../runtime/object') -const {LAMBDA, RHO} = require('../../../runtime/attribute/specials'); -const at_void = require('../../../runtime/attribute/at-void'); -const dataized = require('../../../runtime/dataized'); -const {NUMBER, STRING} = require('../../../runtime/types'); -const data = require('../../../runtime/data') -const ErFailure = require('../../../runtime/error/ErFailure'); - -/** - * String.slice. - * @return {Object} - String.slice object - */ -const string$slice = function() { - const obj = object('string$slice') - obj.attrs['start'] = at_void('start') - obj.attrs['len'] = at_void('len') - obj.assets[LAMBDA] = function(self) { - const str = dataized(self.take(RHO), STRING) - const start = dataized(self.take('start'), NUMBER) - const length = dataized(self.take('len'), NUMBER) - const end = length + start; - if (start < 0) { - throw new ErFailure( - `Start index must be greater than 0 but was ${start}`, - ); - } - if (start > end) { - throw new ErFailure( - `End index must be greater or equal to start but was ${end} < ${start}`, - ); - } - if (end > str.length) { - throw new ErFailure( - `Start index + length must not exceed string length but was ${end} > ${str.length}`, - ); - } - return data.toObject( - str.slice(Number(start), Number(end)) - ) - } - return obj -} - -module.exports = string$slice diff --git a/eo2js-runtime/src/runtime/cages.js b/eo2js-runtime/src/runtime/cages.js deleted file mode 100644 index 2473f26..0000000 --- a/eo2js-runtime/src/runtime/cages.js +++ /dev/null @@ -1,72 +0,0 @@ -const ErFailure = require('./error/ErFailure'); - -/** - * Encaged objects. - * @type {{}} - */ -const OBJECTS = {} - -/** - * Locators generator. - * @type {number} - */ -let locator = 0 - -/** - * Cages for objects - * @type {{ - * init: function(Object): number, - * encage: function(Number, Object), - * get: function(Number): Object - * }} - */ -const cages = { - /** - * Encage object for the first time. - * New locator will be generated. - * @param {Number} object - * @return {number} - */ - init: function(object) { - const loc = ++locator - if (!OBJECTS.hasOwnProperty(loc)) { - OBJECTS[loc] = object - } - return loc - }, - /** - * Encage object to the storage by locator. - * @param {Number} loc - Locator - * @param {Object} object - Object - */ - encage: function(loc, object) { - if (!OBJECTS.hasOwnProperty(loc)) { - throw new ErFailure( - `Encaged object with locator ${loc} was not initialized, can't reencage, can't encage` - ) - } - const current = OBJECTS[loc].forma() - const forma = object.forma() - if (current !== forma) { - throw new ErFailure( - `Can't encage an object formed by ${forma} because object formed by ${current} was encaged before` - ) - } - OBJECTS[loc] = object - }, - /** - * Retrieve object from storage by locator - * @param {Number} loc - Locator - * @return {Object} - */ - get: function(loc) { - if (!OBJECTS.hasOwnProperty(loc)) { - throw new ErFailure( - `Object with locator ${loc} is absent in cage, can't get` - ) - } - return OBJECTS[loc] - } -} - -module.exports = cages diff --git a/eo2js-runtime/test/objects/org/eolang/cage.test.js b/eo2js-runtime/test/objects/org/eolang/cage.test.js deleted file mode 100644 index 99b8030..0000000 --- a/eo2js-runtime/test/objects/org/eolang/cage.test.js +++ /dev/null @@ -1,123 +0,0 @@ -const phi = require('../../../../temp/runtime/phi'); -const data = require('../../../../temp/runtime/data'); -const dataized = require('../../../../temp/runtime/dataized'); -const attr = require('../../../../temp/runtime/attribute/attr'); -const object = require('../../../../temp/runtime/object'); -const assert = require('assert'); -const {NUMBER} = require('../../../../src/runtime/types'); -const {LAMBDA} = require('../../../../src/runtime/attribute/specials'); -const {RECURSION_THRESHOLD} = require('../../../../src/runtime/traced'); - -/** - * Encaged object. - * @param {Object} object - Object to encage - * @return {Object} - Cage - */ -const encaged = function(object) { - return phi.take('org.eolang.cage').with({ - 0: object - }).take('new') -} - -/** - * Encage given object to given cage. - * @param {Object} cage - Cage - * @param {Object} obj - Object to encage - */ -const encageTo = function(cage, obj) { - dataized(cage.take('encage').with({ - 0: obj - })) -} - -/** - * Dummy object. - * @param {Number} num - * @return {Object} - Dummy object - */ -const dummy = function(num) { - const obj = object('dummy') - obj.attrs['x'] = attr.simple(data.toObject(num)) - return obj -} - -/** - * Recursive dummy. - * @param {Object} cage - Cage - * @param {Number} depth - Depth - * @return {Object} - Recursive dummy - */ -const recursiveDummy = function(cage, depth) { - let counter = 0 - const obj = object('dummy') - obj.assets[LAMBDA] = function(_) { - if (++counter >= depth) { - return data.toObject(1) - } else { - return cage - } - } - return obj -} - -describe('cage', function() { - it('should encage via application', function() { - const cage = encaged(data.toObject(1)) - assert.equal(dataized(cage, NUMBER), 1) - }) - it('should encage and reencage', function() { - const cage = encaged(data.toObject(1)) - encageTo(cage, data.toObject(2)) - assert.equal(dataized(cage, NUMBER), 2) - }) - it('should overwrite caged object', function() { - const cage = encaged(dummy(1)) - assert.equal(dataized(cage.take('x'), NUMBER), 1) - encageTo(cage, dummy(2)) - assert.equal(dataized(cage.take('x'), NUMBER), 2) - }) - it('should encage object on copying', function() { - const first = encaged(data.toObject(1)) - const second = first.copy() - encageTo(second, data.toObject(2)) - assert.equal(dataized(first, NUMBER), 2) - }) - it('should write and rewrite primitive', function() { - const cage = encaged(data.toObject(1)) - assert.equal(dataized(cage, NUMBER), 1) - encageTo(cage, data.toObject(5)) - assert.equal(dataized(cage, NUMBER), 5) - }) - it('should no write primitive formed differently', function() { - const cage = encaged(data.toObject(1)) - assert.throws(() => encageTo(cage, data.toObject('Hello, world!'))) - }) - it('should not write bounded method', function() { - const five = data.toObject(5) - const ten = five.take('plus').with({ - 0: data.toObject(5) - }) - const cage = encaged(five) - assert.throws(() => encageTo(cage, ten)) - }) - it('should throw error on recursion', function() { - const cage = encaged(phi.take('org.eolang.cage').take('encaged')) - encageTo(cage, cage) - assert.throws(() => dataized(cage)) - }) - it('should not throw an error on small depth', function() { - const cage = encaged(recursiveDummy(object(), 1)) - encageTo(cage, recursiveDummy(cage, RECURSION_THRESHOLD / 2)) - assert.doesNotThrow(() => dataized(cage)) - }) - it('should not throw on max depth', function() { - const cage = encaged(recursiveDummy(object(), 1)) - encageTo(cage, recursiveDummy(cage, RECURSION_THRESHOLD)) - assert.doesNotThrow(() => dataized(cage)) - }) - it('should throw an error on big depth', function() { - const cage = encaged(recursiveDummy(object(), 1)) - encageTo(cage, recursiveDummy(cage, RECURSION_THRESHOLD + 1)) - assert.throws(() => dataized(cage)) - }) -}) diff --git a/eo2js-runtime/test/runtime/cages.test.js b/eo2js-runtime/test/runtime/cages.test.js deleted file mode 100644 index 9cecacb..0000000 --- a/eo2js-runtime/test/runtime/cages.test.js +++ /dev/null @@ -1,31 +0,0 @@ -const cages = require('../../temp/runtime/cages'); -const object = require('../../temp/runtime/object'); -const assert = require('assert'); - -describe('cages', function() { - it('should initialize object for the first time', function() { - assert.doesNotThrow(() => cages.init(object())) - }) - it('should encage object with locator', function() { - const first = object() - const second = object() - const locator = cages.init(first) - cages.encage(locator, second) - assert.equal(cages.get(locator).toString(), second.toString()) - }) - it('should fail to encage object if it was not initialized', function() { - assert.throws(() => cages.encage(0, object())) - }) - it('should fail to encage object of different forma', function() { - const locator = cages.init(object('first')) - assert.throws(() => cages.encage(locator, object('second'))) - }) - it('should fail to get object if it was not initialized', function() { - assert.throws(() => cages.get(0)) - }) - it('should get object by right locator', function() { - const first = cages.init(object('first')) - cages.init(object('second')) - assert.ok(cages.get(first).toString().includes('first')) - }) -}) diff --git a/eo2js/package-lock.json b/eo2js/package-lock.json index 01bab80..94f29af 100644 --- a/eo2js/package-lock.json +++ b/eo2js/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "license": "MIT", "dependencies": { + "colors": "1.4.0", "commander": "^12.0.0", "fast-xml-parser": "^4.3.5", "saxon-js": "^2.6.0" @@ -545,6 +546,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", diff --git a/eo2js/package.json b/eo2js/package.json index 9f1e3e3..824342d 100644 --- a/eo2js/package.json +++ b/eo2js/package.json @@ -20,7 +20,8 @@ "dependencies": { "commander": "^12.0.0", "fast-xml-parser": "^4.3.5", - "saxon-js": "^2.6.0" + "saxon-js": "^2.6.0", + "colors": "1.4.0" }, "devDependencies": { "grunt": "^1.6.1", diff --git a/eo2js/src/commands/transpile.js b/eo2js/src/commands/transpile.js index 83b790b..bb66261 100644 --- a/eo2js/src/commands/transpile.js +++ b/eo2js/src/commands/transpile.js @@ -46,12 +46,15 @@ const makeDirIfNotExist = function(dir) { * @return {boolean} - If given XMIR has tests meta or not */ const hasMeta = function(xmir, name) { - const metas = xmir.program.metas.meta + const metas = xmir.program.metas let res = false - if (Array.isArray(metas)) { - res = metas.findIndex((meta) => meta.head === name) !== -1 - } else if (typeof metas === 'object' && metas.hasOwnProperty('head')) { - res = metas.head === name + if (metas != null) { + const metas = xmir.program.metas.meta + if (Array.isArray(metas)) { + res = metas.findIndex((meta) => meta.head === name) !== -1 + } else if (typeof metas === 'object' && metas.hasOwnProperty('head')) { + res = metas.head === name + } } return res } diff --git a/eo2js/src/compile-stylesheets.js b/eo2js/src/compile-stylesheets.js index 692eb0a..5a6142d 100644 --- a/eo2js/src/compile-stylesheets.js +++ b/eo2js/src/compile-stylesheets.js @@ -4,7 +4,7 @@ const {execSync} = require('child_process'); const xsls = path.resolve('src/resources/xsl') const jsons = path.resolve('src/resources/json') -const all = ['attrs', 'data', 'objects', 'package', 'to-js', 'tests'] +const all = ['_funcs', 'attrs', 'data', 'objects', 'package', 'to-js', 'tests'] const ext = '.sef.json' /** diff --git a/eo2js/src/resources/xsl/_funcs.xsl b/eo2js/src/resources/xsl/_funcs.xsl new file mode 100644 index 0000000..026ac67 --- /dev/null +++ b/eo2js/src/resources/xsl/_funcs.xsl @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/eo2js/src/resources/xsl/data.xsl b/eo2js/src/resources/xsl/data.xsl index 763ace7..c201d73 100644 --- a/eo2js/src/resources/xsl/data.xsl +++ b/eo2js/src/resources/xsl/data.xsl @@ -22,28 +22,27 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - + + - + - - + + - - - [ - - - , - - '0x - - ' - - ] - - + [ + + + + , + + '0x + + ' + + + ] diff --git a/eo2js/src/resources/xsl/to-js.xsl b/eo2js/src/resources/xsl/to-js.xsl index b34a5b5..e8c2d38 100644 --- a/eo2js/src/resources/xsl/to-js.xsl +++ b/eo2js/src/resources/xsl/to-js.xsl @@ -395,6 +395,7 @@ SOFTWARE. + .copy() rho @@ -577,14 +578,18 @@ SOFTWARE. - = + = applied( - .with({ + , { + 0: object().with({ + ' ': + + }) }) diff --git a/eo2js/test/commands/transpile.test.js b/eo2js/test/commands/transpile.test.js index 5dc1ce2..f3acfdd 100644 --- a/eo2js/test/commands/transpile.test.js +++ b/eo2js/test/commands/transpile.test.js @@ -12,14 +12,14 @@ const compileStylesheets = require('../../src/compile-stylesheets'); * Don't forget to clear the array before push! * @type {string[]} */ -const only = ['prepares-data'] +const only = [] const home = path.resolve('temp/test-transpile') describe('transpile', function() { before('compile stylesheets', function() { compileStylesheets() fs.rmSync(home, {recursive: true, force: true}) - fs.mkdirSync(home) + fs.mkdirSync(home, {recursive: true}) }) describe('command', function() { const target = path.resolve(home, 'target') @@ -27,53 +27,39 @@ describe('transpile', function() { fs.rmSync(target, {recursive: true, force: true}) fs.mkdirSync(target) }) - it('should fail if eo-foreign is not found', function() { - assert.throws(() => runSync(['transpile', '-t', target], false)) - }) - it('should fail if eo-foreign file is not .json', function() { - const foreign = 'eo-foreign.csv' - fs.writeFileSync(path.resolve(target, foreign), 'some data') - assert.throws(() => runSync(['transpile', '-t', target, '-f', foreign], false)) - }) - /** - * Prepare files for transpilation. - * Creates eo-foreign.json file and copies XMIR file from test resources. - * @param {String} name - Name of the object to transpile, defaults to 'simple' - */ - const prepare = function(name = 'simple') { - const verified = path.resolve(target, `6-verify/com/eo2js/${name}.xmir`) - const foreign = [{ - id: `com.eo2js.${name}`, - verified: verified - }] - fs.writeFileSync(path.resolve(target, 'eo-foreign.json'), JSON.stringify(foreign)) - fs.mkdirSync(path.resolve(target, '6-verify/com/eo2js'), {recursive: true}) - fs.copyFileSync(path.resolve(`test/resources/transpile/${name}.xmir`), verified) - } + /* eslint-disable valid-jsdoc */ /** * Run transpile command with verbose output. - * @param {String} name - Name of the object to transpile, defaults to 'simple' - * @return {String} - Command stdout from transpilation - */ - const transpile = function(name = 'simple') { - prepare(name) - return runSync([ - 'transpile', - '--verbose', - '-t', target, - ]) - } - /** - * Run transpile command again with verbose output, without preparing files. - * @return {String} - Command stdout from retranspilation + * @param {{[name]: String, [prepare]: Boolean}} opts - Options + * @return {String} - Output */ - const retranspile = function() { + const transpile = function(opts= {}) { + opts.name = opts.name || 'simple' + opts.prepare = opts.prepare !== undefined ? opts.prepare : true + if (opts.prepare) { + const verified = path.resolve(target, `6-verify/com/eo2js/${opts.name}.xmir`) + const foreign = [{ + id: `com.eo2js.${opts.name}`, + verified: verified + }] + fs.writeFileSync(path.resolve(target, 'eo-foreign.json'), JSON.stringify(foreign)) + fs.mkdirSync(path.resolve(target, '6-verify/com/eo2js'), {recursive: true}) + fs.copyFileSync(path.resolve(`test/resources/transpile/${opts.name}.xmir`), verified) + } return runSync([ 'transpile', '--verbose', '-t', target, ]) } + it('should fail if eo-foreign is not found', function() { + assert.throws(() => runSync(['transpile', '-t', target], false)) + }) + it('should fail if eo-foreign file is not .json', function() { + const foreign = 'eo-foreign.csv' + fs.writeFileSync(path.resolve(target, foreign), 'some data') + assert.throws(() => runSync(['transpile', '-t', target, '-f', foreign], false)) + }) it('should create transpiled XMIRs', function() { const traspiled = '8-transpile/com/eo2js/simple.xmir' assertFilesExist(transpile(), target, [traspiled]) @@ -84,14 +70,14 @@ describe('transpile', function() { }); ['simple-test', 'alone-test'].forEach((name) => { it(`should generate test JS file for ${name}`, function() { - assertFilesExist(transpile(name), target, [`project/com/eo2js/${name}.test.js`]) + assertFilesExist(transpile({name}), target, [`project/com/eo2js/${name}.test.js`]) }) }) it('should skip transpilation if source was not modified', function() { transpile() const transpiled = path.resolve(target, '8-transpile/com/eo2js/simple.xmir') const first = fs.statSync(transpiled).mtime - retranspile() + transpile({prepare: false}) const second = fs.statSync(transpiled).mtime assert.equal(first.getTime(), second.getTime()) }) @@ -102,7 +88,7 @@ describe('transpile', function() { const first = fs.statSync(transpiled).mtime await new Promise((resolve) => setTimeout(resolve, 1000)) fs.writeFileSync(source, fs.readFileSync(source)) - retranspile() + transpile({prepare: false}) const second = fs.statSync(transpiled).mtime assert.notEqual(first.getTime(), second.getTime()) }) @@ -119,17 +105,18 @@ describe('transpile', function() { fs.rmSync(folder, {recursive: true}) } const json = JSON.parse(fs.readFileSync(path.resolve(packs, test)).toString()) - const res = pack({home: folder, sources: 'src', target: 'target', json}) - if (res.skip) { - this.skip() - } else { - assert.equal( - res.failures.length, - 0, - `Result XMIR:\n ${res.xmir}\nJSON: ${JSON.stringify(res.json, null, 2)}\nFailed tests: ${res.failures.join(';\n')}` - ) - done() - } + pack({home: folder, sources: 'src', target: 'target', json}).then((res) => { + if (res.skip) { + this.skip() + } else { + assert.equal( + res.failures.length, + 0, + `Result XMIR:\n ${res.xmir}\nJSON: ${JSON.stringify(res.json, null, 2)}\nFailed tests: ${res.failures.join(';\n')}` + ) + done() + } + }) }) }) }) diff --git a/eo2js/test/helpers.js b/eo2js/test/helpers.js index e78da5c..95e1d5d 100644 --- a/eo2js/test/helpers.js +++ b/eo2js/test/helpers.js @@ -68,9 +68,9 @@ const parser = new XMLParser({ignoreAttributes: false}) /** * Transformations test pack. * @param {{home: String, sources: String, target: String, json: Object}} params - Pack params - * @return {{skip: boolean, failures: array., xmir: String, json: Object}} - Output + * @return {Promise<{skip: boolean, failures: array., xmir: String, json: Object}>} */ -const pack = function(params) { +const pack = async function(params) { const res = { skip: false, failures: [], @@ -78,60 +78,64 @@ const pack = function(params) { json: '' } if (params.json['skip'] || !params.json['xsls'] || params.json['xsls'].length === 0) { - res.skip = true + return new Promise((resolve) => { + res.skip = true + resolve(res) + }) } else { const sources = path.resolve(params.home, params.sources) const target = path.resolve(params.home, params.target) fs.mkdirSync(sources, {recursive: true}) fs.mkdirSync(target, {recursive: true}) fs.writeFileSync(path.resolve(sources, `test.eo`), `${params.json['eo'].join('\n')}\n`) - mvnw(['register', 'parse', 'optimize', 'shake'], params) - const shaken = JSON.parse( - fs.readFileSync(path.resolve(target, 'eo-foreign.json')).toString() - )[0]['shaken'] - let xml = fs.readFileSync(shaken).toString() - const transformations = path.resolve(__dirname, '../src/resources/json') - params.json['xsls'] - .map((name) => path.resolve(transformations, `${name}.sef.json`)) - .forEach((transformation) => { - xml = saxon.transform({ - stylesheetFileName: transformation, - sourceText: xml, - destination: 'serialized' - }).principalResult - }) - res.xmir = xml - const saved = path.resolve(target, '3-transpiled') - fs.mkdirSync(saved, {recursive: true}) - fs.writeFileSync(path.resolve(saved, 'test.xmir'), res.xmir) - xml = parser.parse(xml, {}) - res.json = xml - params.json['tests'].forEach((test) => { - if (typeof test === 'object') { - const node = test.node - const method = test.method - const args = test.args - const applied = jp.apply(node, xml) - if (applied.length === 0) { - res.failures.push(node) + return mvnw(['register', 'parse', 'optimize', 'shake'], params).then((r) => { + const shaken = JSON.parse( + fs.readFileSync(path.resolve(target, 'eo-foreign.json')).toString() + )[0]['shaken'] + let xml = fs.readFileSync(shaken).toString() + const transformations = path.resolve(__dirname, '../src/resources/json') + params.json['xsls'] + .map((name) => path.resolve(transformations, `${name}.sef.json`)) + .forEach((transformation) => { + xml = saxon.transform({ + stylesheetFileName: transformation, + sourceText: xml, + destination: 'serialized' + }).principalResult + }) + res.xmir = xml + const saved = path.resolve(target, '3-transpiled') + fs.mkdirSync(saved, {recursive: true}) + fs.writeFileSync(path.resolve(saved, 'test.xmir'), res.xmir) + xml = parser.parse(xml, {}) + res.json = xml + params.json['tests'].forEach((test) => { + if (typeof test === 'object') { + const node = test.node + const method = test.method + const args = test.args + const applied = jp.apply(node, xml) + if (applied.length === 0) { + res.failures.push(node) + } else { + args.forEach((arg) => { + if (Array.isArray(arg)) { + arg = arg.join('\n') + } + if (!applied[0][method](arg)) { + res.failures.push(`NODE: ${node}, METHOD: ${method}, ARG: ${arg}`) + } + }) + } } else { - args.forEach((arg) => { - if (Array.isArray(arg)) { - arg = arg.join('\n') - } - if (!applied[0][method](arg)) { - res.failures.push(`NODE: ${node}, METHOD: ${method}, ARG: ${arg}`) - } - }) - } - } else { - if (jp.apply(test, xml).length === 0) { - res.failures.push(test) + if (jp.apply(test, xml).length === 0) { + res.failures.push(test) + } } - } + }) + return res }) } - return res } /** diff --git a/eo2js/test/it/runtime-tests.test.js b/eo2js/test/it/runtime-tests.test.js index c2b57ee..58f4d8d 100644 --- a/eo2js/test/it/runtime-tests.test.js +++ b/eo2js/test/it/runtime-tests.test.js @@ -1,9 +1,9 @@ -const path = require('path'); -const fs = require('fs'); -const mvnw = require('../mvnw/mvnw.js'); +const path = require('path') +const fs = require('fs') +const mvnw = require('../mvnw/mvnw.js') const {execSync} = require('child_process') -const {runSync} = require('../helpers'); -const assert = require('assert'); +const {runSync} = require('../helpers') +const assert = require('assert') /** * Excluded tests. @@ -38,7 +38,7 @@ const exclude = [ * @return {Array.} - Files from the directory */ const allFilesFrom = function(dir) { - const files = fs.readdirSync(dir, {withFileTypes: true}); + const files = fs.readdirSync(dir, {withFileTypes: true}) const res = [] for (const file of files) { if (file.isDirectory()) { @@ -63,19 +63,15 @@ const COMPILE = true * eo-maven-plugin, transpiles and executes using eo2js. */ describe('runtime tests', function() { + this.timeout(0) const home = path.resolve('temp/runtime-tests') const target = path.resolve(home, 'target') const project = path.resolve(target, 'project') const runtime = path.resolve('../eo2js-runtime') - before('prepare environment', function() { + before('prepare environment', async function() { if (COMPILE) { fs.rmSync(home, {recursive: true, force: true}) fs.mkdirSync(project, {recursive: true}) - } - }) - it('should execute all eo-runtime tests', function(done) { - this.timeout(0) - if (COMPILE) { execSync([ 'git init', 'git remote add origin https://github.com/objectionary/home.git', @@ -89,11 +85,13 @@ describe('runtime tests', function() { .join('\n') }`) console.debug(`\nExcluded:\n${exclude.map((pth) => `- ${pth}`).join('\n')}`) - mvnw( - ['register', 'assemble', 'verify'], - {home, sources: 'tests', target: 'target'} - ) + const opts = {home, sources: 'tests', target: 'target'} + await mvnw('register', opts) + await mvnw('assemble', opts) + await mvnw('verify', opts) } + }) + it('should execute all eo-runtime tests', function(done) { if (!COMPILE) { runSync(['link -t', target, '-p project --tests --alone -d', runtime]) } diff --git a/eo2js/test/it/simple.test.js b/eo2js/test/it/simple.test.js index 39a2fe3..dadc6bb 100644 --- a/eo2js/test/it/simple.test.js +++ b/eo2js/test/it/simple.test.js @@ -5,13 +5,25 @@ const {runSync} = require('../helpers'); const compileStylesheets = require('../../src/compile-stylesheets'); const assert = require('assert'); +/** + * Prepare sources. + * @param {String} home - Home directory + * @return {Promise|String>} - Maven promise + */ +const prepare = function(home) { + return mvnw( + ['register', 'assemble', 'verify'], + {home, sources: 'src/eo', target: 'target'} + ) +} + describe('integration test', function() { const home = path.resolve('temp/it-test') const target = path.resolve(home, 'target') const project = path.resolve(target, 'project') const runtime = path.resolve('../eo2js-runtime') this.timeout(1000000) - before('recompile stylesheets', function() { + before('recompile stylesheets', async function() { compileStylesheets() fs.rmSync(home, {recursive: true, force: true}) fs.mkdirSync(project, {recursive: true}) @@ -20,10 +32,7 @@ describe('integration test', function() { path.resolve(home, 'src/eo'), {recursive: true} ) - mvnw( - ['register', 'assemble', 'verify'], - {home, sources: 'src/eo', target: 'target'} - ) + await prepare(home) }) it('should execute simple unit test', function(done) { const log = runSync(['test', '-t', target, '-p project -d', runtime]) diff --git a/eo2js/test/mvnw/eo-version.txt b/eo2js/test/mvnw/eo-version.txt index d0a1915..a8ab6c9 100644 --- a/eo2js/test/mvnw/eo-version.txt +++ b/eo2js/test/mvnw/eo-version.txt @@ -1 +1 @@ -0.40.1 +0.44.0 diff --git a/eo2js/test/mvnw/mvnw.js b/eo2js/test/mvnw/mvnw.js index 2bf38a0..c2c237e 100644 --- a/eo2js/test/mvnw/mvnw.js +++ b/eo2js/test/mvnw/mvnw.js @@ -1,12 +1,33 @@ -const path = require('path'); -const {execSync} = require('child_process'); -const fs = require('fs'); +const path = require('path') +const {spawn} = require('child_process') +const fs = require('fs') +const colors = require('colors') +const readline = require('readline') /** * Version. * @type {null|String} */ let ver = null +/** + * Is execution running. + * @type {boolean} + */ +let running = false +/** + * Start datetime. + * @type {Number} + */ +let beginning +/** + * Target directory. + * @type {String} + */ +let target +/** + * Current phase + */ +let phase = null /** * Get EO version. @@ -25,7 +46,7 @@ const version = function() { */ function shell() { if (process.platform === 'win32') { - return 'C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe'; + return 'C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe' } } @@ -42,33 +63,118 @@ const flags = function(opts) { ] } +/* eslint-disable valid-jsdoc */ /** * Execute given goals of eo-maven-plugin. - * @param {Array.} goals - Goals to execute in right order - * @param {{sources: String, target: String, home: String}} opts - Options + * @param {Array.|String} goals - Goals to execute in right order + * @param {{sources: String, target: String, home: String, [skip]: Boolean}} opts - Options + * @return {Promise|String>} */ -const mvnw = function(goals, opts) { - const bin = path.resolve(__dirname, 'mvnw') + (process.platform === 'win32' ? '.cmd' : ''); - const params = [ - ...flags(opts), - '--batch-mode', - '--color=never', - '--update-snapshots', - '--fail-fast', - '--strict-checksums', - ] - execSync( - [ - bin, +const mvnw = function(goals, opts = {}) { + return new Promise((resolve, reject) => { + if (opts.skip) { + resolve(goals) + } + phase = Array.isArray(goals) ? null : goals + goals = Array.isArray(goals) ? goals : [goals] + const bin = path.resolve(__dirname, 'mvnw') + (process.platform === 'win32' ? '.cmd' : '') + target = path.resolve(opts.home, opts.target) + const params = [ + ...flags(opts), + '--batch-mode', + '--color=never', + '--update-snapshots', + '--fail-fast', + '--strict-checksums', + ] + const args = [ ...goals.map((goal) => `org.eolang:eo-maven-plugin:${goal}`), ...process.platform === 'win32' ? params.map((p) => `"${p}"`) : params, - ].join(' '), - { - cwd: __dirname, - // stdio: 'inherit', - shell: shell(), + ] + const command = [bin, ...args].join(' '); + console.debug(`Running: ${command}`) + const result = spawn( + bin, + args, + { + cwd: __dirname, + stdio: 'inherit', + shell: shell(), + } + ) + start() + result.on('close', (code) => { + if (code !== 0) { + console.error(`The command "${command}" exited with #${code} code`) + process.exit(1) + } + stop() + resolve(goals) + }) + }) +} + +/** + * Starts mvnw execution status detection. + */ +function start() { + running = true + beginning = Date.now() + const check = function() { + if (running) { + print() + setTimeout(check, 1000) } - ) + } + check() +} + + +/** + * Stops mvnw execution status detection. + */ +function stop() { + running = false + readline.clearLine(process.stdout, 0) +} + +/** + * Prints mvnw execution status. + */ +function print() { + const duration = Date.now() - beginning; + /** + * Recursively calculates number of files under a directory. + * @param {String} dir - Directory where to count. + * @param {Integer} curr - Current counter. + * @return {Integer} Total number files. + */ + function count(dir, curr) { + if (fs.existsSync(dir)) { + for (const f of fs.readdirSync(dir)) { + const next = path.join(dir, f); + if (fs.statSync(next).isDirectory()) { + curr = count(next, curr); + } else { + curr++; + } + } + } + return curr; + } + let elapsed; + if (duration < 1000) { + elapsed = `${duration}ms`; + } else if (duration < 60 * 1000) { + elapsed = `${Math.ceil(duration / 1000)}s`; + } else { + elapsed = `${Math.ceil(duration / 3600000)}min`; + } + process.stdout.write( + colors.yellow(`${phase ? `[${phase}]: `: ''} ${elapsed}; ${count(target, 0)} files generated so far...`) + ); + readline.clearLine(process.stdout, 1); + readline.cursorTo(process.stdout, 0); } module.exports = mvnw diff --git a/eo2js/test/resources/transpile/packs/data-as-bytes.json b/eo2js/test/resources/transpile/packs/data-as-bytes.json index 76a4c43..af43027 100644 --- a/eo2js/test/resources/transpile/packs/data-as-bytes.json +++ b/eo2js/test/resources/transpile/packs/data-as-bytes.json @@ -23,9 +23,11 @@ "args": [ [ " let ret = phi.take('org').take('eolang').take('number')", - " let ret_1 = phi.take('org').take('eolang').take('bytes')", - " ret_1 = ret_1.with({", - " 'Δ': ['0x40', '0x45', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00']", + " let ret_1 = phi.take('org').take('eolang').take('bytes').copy()", + " ret_1 = applied(ret_1, {", + " 0: object().with({", + " 'Δ': ['0x40', '0x45', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00']", + " })", " })", " ret = applied(ret, {", " 0: ret_1", @@ -41,9 +43,11 @@ [ " let ret = phi.take('org').take('eolang').take('float')", " let ret_1 = phi.take('org').take('eolang').take('number')", - " let ret_1_1 = phi.take('org').take('eolang').take('bytes')", - " ret_1_1 = ret_1_1.with({", - " 'Δ': ['0x40', '0x0C', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00']", + " let ret_1_1 = phi.take('org').take('eolang').take('bytes').copy()", + " ret_1_1 = applied(ret_1_1, {", + " 0: object().with({", + " 'Δ': ['0x40', '0x0C', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00']", + " })", " })", " ret_1 = applied(ret_1, {", " 0: ret_1_1", @@ -60,9 +64,41 @@ "method": "includes", "args": [ [ - " let ret = phi.take('org').take('eolang').take('bytes')", - " ret = ret.with({", - " 'Δ': ['0x01', '0xAF']", + " let ret = phi.take('org').take('eolang').take('bytes').copy()", + " ret = applied(ret, {", + " 0: object().with({", + " 'Δ': ['0x01', '0xAF']", + " })", + " })", + " return ret" + ] + ] + }, + { + "node": ".program.objects.object{.\"@_name\" == 'one-byte'}.javascript", + "method": "includes", + "args": [ + [ + " let ret = phi.take('org').take('eolang').take('bytes').copy()", + " ret = applied(ret, {", + " 0: object().with({", + " 'Δ': ['0x01']", + " })", + " })", + " return ret" + ] + ] + }, + { + "node": ".program.objects.object{.\"@_name\" == 'empty'}.javascript", + "method": "includes", + "args": [ + [ + " let ret = phi.take('org').take('eolang').take('bytes').copy()", + " ret = applied(ret, {", + " 0: object().with({", + " 'Δ': []", + " })", " })", " return ret" ] @@ -81,6 +117,12 @@ " float 3.5 > @", "# This is the default 64+ symbols comment in front of named abstract object.", "[] > b", - " 01-AF > @" + " 01-AF > @", + "# This is the default 64+ symbols comment in front of named abstract object.", + "[] > one-byte", + " 01- > @", + "# This is the default 64+ symbols comment in front of named abstract object.", + "[] > empty", + " -- > @" ] } diff --git a/package.json b/package.json index c0485bc..64e96bc 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "postinstall": "npm run install-eo2js && npm run install-eo2js-runtime", "test-eo2js": "cd eo2js && grunt", "test-eo2js-runtime": "cd eo2js-runtime && grunt", - "test": "npm run test-eo2js && npm run test-eo2js-runtime" + "test": "npm run test-eo2js-runtime && npm run test-eo2js" }, "author": "maxonfjvipon", "license": "MIT"