diff --git a/CHANGELOG.md b/CHANGELOG.md index aa9a6e2..258e96f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) with some edits, and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# 1.1.7 + +## What's Changed +* test: full code coverage w/ @mateonunez +* refactor: enforce code robustness + +## New Contributors +* @mateonunez made their first contribution in https://github.com/airscripts/analscript/pull/13 + +**Full Changelog**: https://github.com/airscripts/analscript/compare/1.1.6...1.1.7 + # 1.1.6 ## What's Changed diff --git a/README.md b/README.md index 5cbb853..150966f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

- Analscript is an esoteric programming language, that takes inspiration from the fallen Anal Lang and serves, the purpose to be a modern approach for writing anally fast stuff. + Analscript is a joke esoteric programming language, that takes inspiration from the fallen AnalLang and serves, the purpose to be a modern approach for writing anally fast stuff.

@@ -14,6 +14,7 @@ ## Contents - [Installation](#installation) - [Usage](#usage) +- [Resources](#resources) - [Contributing](#contributing) - [Support](#support) - [License](#license) @@ -35,7 +36,7 @@ analscript help This command will show you something like this: ``` -Analscript Version 1.1.6 +Analscript Version 1.1.7 Copyright (c) 2023 by Airscript Usage: @@ -58,17 +59,27 @@ Arguments: As you can see, you can as of this latest version, have access to a variety of commands. Use them to write or read your anally marvelous softwares! +## Resources +- [Esolang Wiki Page](https://esolangs.org/wiki/Analscript): the official Esolang Wiki page. +- [GitHub Page](https://ghio.airscript.it/analscript): the GitHub page for this repository. + ## Contributing Contributions and suggestions about how to improve this project are welcome! Just follow our [contributing guidelines](https://github.com/airscripts/analscript/blob/main/CONTRIBUTING.md). ## Support -If you want to support my work you can do it with the links below. - +If you want to support my work you can do it following me, leaving a star, sharing my projects or also with the links below. Choose what you find more suitable for you: -- [Support me on GitHub](https://github.com/sponsors/Airscripts) -- [Support me via ko-fi](https://ko-fi.com/airscript) -- [Support me via linktr.ee](https://linktr.ee/airscript) + + + GitHub Sponsors +  + + Kofi +  + + Linktree + *As of my personal preference, I'do go with GitHub Sponsors.* diff --git a/VERSION b/VERSION index ab67981..a5ba932 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.6 \ No newline at end of file +1.1.7 \ No newline at end of file diff --git a/analscript.js b/analscript.js index 3da3f4f..94001f5 100644 --- a/analscript.js +++ b/analscript.js @@ -1,4 +1,4 @@ -import { anallify, stringify } from './lib/stdlib.js'; +import { anallify, stringify } from './lib/std.js'; export { anallify, stringify }; export default { anallify, stringify }; diff --git a/assets/images/cover.png b/assets/images/cover.png new file mode 100644 index 0000000..a0fda09 Binary files /dev/null and b/assets/images/cover.png differ diff --git a/assets/images/github-sponsors.svg b/assets/images/github-sponsors.svg new file mode 100644 index 0000000..7743dcb --- /dev/null +++ b/assets/images/github-sponsors.svg @@ -0,0 +1 @@ +GitHub Sponsors \ No newline at end of file diff --git a/assets/images/kofi.svg b/assets/images/kofi.svg new file mode 100644 index 0000000..dae0275 --- /dev/null +++ b/assets/images/kofi.svg @@ -0,0 +1 @@ +Ko-fi \ No newline at end of file diff --git a/assets/images/linktree.svg b/assets/images/linktree.svg new file mode 100644 index 0000000..92d03f0 --- /dev/null +++ b/assets/images/linktree.svg @@ -0,0 +1 @@ +Linktree \ No newline at end of file diff --git a/cli.js b/cli.js index 67a9e2a..6f21ee8 100644 --- a/cli.js +++ b/cli.js @@ -1,12 +1,13 @@ #!/usr/bin/env node import help from './lib/help.js'; +import { graceful } from './lib/utils.js'; import { run, compile, anallify, stringify, -} from './lib/stdlib.js'; +} from './lib/std.js'; import { RUN, @@ -33,4 +34,4 @@ function cli() { process.stdout.write(`${output}\n`); } -cli(); +graceful(cli); diff --git a/lib/constants.js b/lib/constants.js index 355cbb1..837e157 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -4,3 +4,4 @@ export const COMPILE = 'compile'; export const ANALLIFY = 'anallify'; export const STRINGIFY = 'stringify'; export const ANAL_CHARACTERS = '🍑🍆'; +export const GRAMMAR = `${ANAL_CHARACTERS} `; diff --git a/lib/dictionary.js b/lib/dictionary.js index cdb4cad..bf7a9ad 100644 --- a/lib/dictionary.js +++ b/lib/dictionary.js @@ -2,6 +2,7 @@ export const ERROR = { fileNotFound: 'File not found.', missingArgument: 'Missing argument.', notString: 'Only strings are accepted', + notAcceptedByGrammar: 'There are some characters not accepted by grammar.', }; export const SUCCESS = { diff --git a/lib/help.js b/lib/help.js index f5b1bf4..41628c1 100644 --- a/lib/help.js +++ b/lib/help.js @@ -1,4 +1,4 @@ -const version = 'Analscript, version 1.1.6'; +const version = 'Analscript, version 1.1.7'; const copyright = '\nCopyright (c) 2023 by Airscript\n'; const usage = ` diff --git a/lib/regexp.js b/lib/regexp.js new file mode 100644 index 0000000..214133b --- /dev/null +++ b/lib/regexp.js @@ -0,0 +1,4 @@ +import { ANAL_CHARACTERS, GRAMMAR } from './constants.js'; + +export const matcher = new RegExp(`^[${GRAMMAR}]+$`, 'u'); +export const sequence = new RegExp(`${ANAL_CHARACTERS}`, 'g'); diff --git a/lib/std.js b/lib/std.js new file mode 100644 index 0000000..ea9c6d4 --- /dev/null +++ b/lib/std.js @@ -0,0 +1,83 @@ +import fs from 'node:fs'; + +import { checker } from './utils.js'; +import { ERROR, SUCCESS } from './dictionary.js'; +import { ANAL_CHARACTERS } from './constants.js'; +import { matcher, sequence } from './regexp.js'; + +export function anallify(string) { + if (!checker(string)) { + throw new Error(ERROR.notString); + } + + if (!string) { + throw new Error(ERROR.missingArgument); + } + + let anal = ''; + const characters = string.split(''); + + for (let index = 0; index < characters.length; index += 1) { + const character = characters[index]; + const fragment = ANAL_CHARACTERS.repeat(character.charCodeAt(0)); + anal = anal.concat(fragment).concat(' '); + } + + return anal.trimEnd(); +} + +export function stringify(anal) { + if (!checker(anal)) { + throw new Error(ERROR.notString); + } + + if (!anal) { + throw new Error(ERROR.missingArgument); + } + + if (!matcher.test(anal)) { + throw new Error(ERROR.notAcceptedByGrammar); + } + + let string = ''; + const fragments = anal.split(' '); + + for (let index = 0; index < fragments.length; index += 1) { + const fragment = fragments[index]; + const character = String.fromCharCode(((fragment.match(sequence)).length)); + string = string.concat(character); + } + + return string; +} + +export function run(file) { + let contents = null; + + try { + contents = fs.readFileSync(file, { encoding: 'utf-8' }); + } catch (error) { + throw new Error(ERROR.fileNotFound); + } + + return stringify(contents); +} + +export function compile(file) { + let contents = null; + const fileOptions = { encoding: 'utf-8' }; + + try { + contents = fs.readFileSync(file, fileOptions); + } catch (error) { + throw new Error(ERROR.fileNotFound); + } + + let filename = file.split('.'); + filename = filename.filter((element, index) => index < filename.length - 1); + filename = filename.join('.'); + + fs.writeFileSync(`${filename}.anal`, anallify(contents), fileOptions); + process.stdout.write(`${SUCCESS.compileSuccess}`); + return true; +} diff --git a/lib/stdlib.js b/lib/stdlib.js deleted file mode 100644 index 1a0e1c4..0000000 --- a/lib/stdlib.js +++ /dev/null @@ -1,78 +0,0 @@ -import fs from 'node:fs'; - -import checker from './utils.js'; -import { ERROR, SUCCESS } from './dictionary.js'; -import { ANAL_CHARACTERS } from './constants.js'; - -export function anallify(string) { - if (!checker(string)) { - process.stderr.write(`${ERROR.notString}\n`); - return process.exit(1); - } - - if (!string) { - process.stderr.write(`${ERROR.missingArgument}\n`); - return process.exit(1); - } - - let anal = ''; - const characters = string.split(''); - - for (let index = 0; index < characters.length; index += 1) { - const character = characters[index]; - const fragment = ANAL_CHARACTERS.repeat(character.charCodeAt(0)); - anal = anal.concat(fragment).concat(' '); - } - - return anal.trimEnd(); -} - -export function stringify(anal) { - if (!checker(anal)) { - process.stderr.write(`${ERROR.notString}\n`); - return process.exit(1); - } - - if (!anal) { - process.stderr.write(`${ERROR.missingArgument}\n`); - return process.exit(1); - } - - let string = ''; - const fragments = anal.split(' '); - const rule = new RegExp(`${ANAL_CHARACTERS}`, 'g'); - - for (let index = 0; index < fragments.length; index += 1) { - const fragment = fragments[index]; - const character = String.fromCharCode(((fragment.match(rule) || []).length)); - string = string.concat(character); - } - - return string; -} - -export function run(file) { - try { - const contents = fs.readFileSync(file, { encoding: 'utf-8' }); - return stringify(contents); - } catch (error) { - process.stderr.write(`${ERROR.fileNotFound}\n`); - return process.exit(1); - } -} - -export function compile(file) { - try { - const contents = fs.readFileSync(file, { encoding: 'utf-8' }); - - let filename = file.split('.'); - filename = filename.filter((element, index) => index < filename.length - 1); - filename = filename.join('.'); - - fs.writeFileSync(`${filename}.anal`, anallify(contents), { encoding: 'utf-8' }); - process.stdout.write(`${SUCCESS.compileSuccess}`); - } catch (error) { - process.stderr.write(`${ERROR.fileNotFound}\n`); - process.exit(1); - } -} diff --git a/lib/utils.js b/lib/utils.js index 526170d..ec47d2c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,3 +1,12 @@ -export default function checker(input) { +export function checker(input) { return typeof input === 'string'; } + +export function graceful(fn) { + try { + fn(); + } catch (error) { + process.stderr.write(`${error.message}\n`); + process.exit(1); + } +} diff --git a/package-lock.json b/package-lock.json index 86373e2..a0d88f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,26 @@ { "name": "analscript", - "version": "1.1.5", + "version": "1.1.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "analscript", - "version": "1.1.5", + "version": "1.1.7", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/airscripts" + }, + { + "type": "ko-fi", + "url": "https://ko-fi.com/airscript" + }, + { + "type": "linktree", + "url": "https://linktr.ee/airscript" + } + ], "license": "MIT", "bin": { "analscript": "cli.js" diff --git a/package.json b/package.json index 51b1d4c..237d63c 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,15 @@ { "type": "module", - "version": "1.1.6", + "version": "1.1.7", "name": "analscript", + "author": "Airscript", "main": "analscript.js", "description": "A modern approach for writing anally fast stuff.", + "keywords": ["analscript", "programming language", "esoteric", "cli", "anal"], + "homepage": "https://github.com/airscripts/analscript", + "bugs": { + "email": "support@airscript.it" + }, "scripts": { "test:std": "vitest", "test:ui": "vitest --ui", @@ -12,7 +18,24 @@ "bin": { "analscript": "cli.js" }, - "author": "Airscript", + "repository": { + "type": "git", + "url": "https://github.com/airscripts/analscript.git" + }, + "funding": [ + { + "type" : "github", + "url" : "https://github.com/sponsors/airscripts" + }, + { + "type" : "ko-fi", + "url" : "https://ko-fi.com/airscript" + }, + { + "type": "linktree", + "url": "https://linktr.ee/airscript" + } + ], "license": "MIT", "devDependencies": { "@vitest/coverage-v8": "^0.33.0", @@ -22,5 +45,5 @@ "eslint-plugin-import": "^2.28.0", "prettier": "^3.0.0", "vitest": "^0.33.0" - } + } } diff --git a/tests/hello.anus b/tests/hello.anus new file mode 100644 index 0000000..99f615c --- /dev/null +++ b/tests/hello.anus @@ -0,0 +1 @@ +Hello, Analscript! \ No newline at end of file diff --git a/tests/seeds.js b/tests/seeds.js index 2d3d790..bae9e1a 100644 --- a/tests/seeds.js +++ b/tests/seeds.js @@ -1,3 +1,7 @@ +export const NUMERIC_INPUT = 1; +export const EMPTY_STRING_INPUT = ''; +export const NOT_ACCEPTED_INPUT = '🍑🍆 a'; + export const STRINGIFY_CORRECT_OUTPUT = 'B'; export const STRINGIFY_WRONG_OUTPUT = '🍑🍆🍑🍆'; export const STRINGIFY_INPUT = '🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆'; @@ -9,3 +13,7 @@ export const ANALLIFY_CORRECT_OUTPUT = '🍑🍆🍑🍆🍑🍆🍑🍆🍑🍆 export const ANAL_FILE_LOCATION = 'tests/script.anal'; export const RUN_CORRECT_OUTPUT = 'Welcome to Analscript!'; export const RUN_WRONG_OUTPUT = 'Welcome to Jurassic Park!'; + +export const COMPILE_CORRECT_OUTPUT = true; +export const FILE_LOCATION = 'tests/hello.anus'; +export const COMPILED_FILE_LOCATION = 'tests/hello.anal'; diff --git a/tests/std.test.js b/tests/std.test.js new file mode 100644 index 0000000..b5ea901 --- /dev/null +++ b/tests/std.test.js @@ -0,0 +1,96 @@ +import fs from 'node:fs'; +import { test, expect, describe } from 'vitest'; + +import { + FILE_LOCATION, + NUMERIC_INPUT, + ANALLIFY_INPUT, + STRINGIFY_INPUT, + RUN_WRONG_OUTPUT, + ANAL_FILE_LOCATION, + EMPTY_STRING_INPUT, + NOT_ACCEPTED_INPUT, + RUN_CORRECT_OUTPUT, + ANALLIFY_WRONG_OUTPUT, + STRINGIFY_WRONG_OUTPUT, + COMPILE_CORRECT_OUTPUT, + COMPILED_FILE_LOCATION, + ANALLIFY_CORRECT_OUTPUT, + STRINGIFY_CORRECT_OUTPUT, +} from './seeds.js'; + +import { + run, + anallify, + stringify, + compile, +} from '../lib/std.js'; +import { ERROR } from '../lib/dictionary.js'; + +describe('Anallify', () => { + test('Encode string to anal', () => { + expect(anallify(ANALLIFY_INPUT)).toBe(ANALLIFY_CORRECT_OUTPUT); + expect(anallify(ANALLIFY_INPUT)).not.toBe(ANALLIFY_WRONG_OUTPUT); + }); + + test('Throw error if argument is not a string', () => { + expect(() => anallify(1)).toThrowError(Error(ERROR.notString)); + }); + + test('Throw error if argument is missing', () => { + expect(() => anallify(EMPTY_STRING_INPUT)).toThrowError( + Error(ERROR.missingArgument), + ); + }); +}); + +describe('Stringify', () => { + test('Decode anal to string', () => { + expect(stringify(STRINGIFY_INPUT)).toBe(STRINGIFY_CORRECT_OUTPUT); + expect(stringify(STRINGIFY_INPUT)).not.toBe(STRINGIFY_WRONG_OUTPUT); + }); + + test('Throw error if argument is not a string', () => { + expect(() => stringify(NUMERIC_INPUT)).toThrowError( + Error(ERROR.notString), + ); + }); + + test('Throw error if argument is missing', () => { + expect(() => stringify(EMPTY_STRING_INPUT)).toThrowError( + Error(ERROR.missingArgument), + ); + }); + + test('Throw error if there are grammar-refused characters', () => { + expect(() => stringify(NOT_ACCEPTED_INPUT)).toThrowError( + Error(ERROR.notAcceptedByGrammar), + ); + }); +}); + +describe('Run', () => { + test('Run .anal file', () => { + expect(run(ANAL_FILE_LOCATION)).toBe(RUN_CORRECT_OUTPUT); + expect(run(ANAL_FILE_LOCATION)).not.toBe(RUN_WRONG_OUTPUT); + }); + + test('Throw error if file is not found', () => { + expect(() => run(EMPTY_STRING_INPUT)).toThrowError( + Error(ERROR.fileNotFound), + ); + }); +}); + +describe('Compile', () => { + test('Compile file to .anal', () => { + expect(compile(FILE_LOCATION)).toBe(COMPILE_CORRECT_OUTPUT); + fs.rmSync(COMPILED_FILE_LOCATION); + }); + + test('Throw error if file is not found', () => { + expect(() => compile(EMPTY_STRING_INPUT)).toThrowError( + Error(ERROR.fileNotFound), + ); + }); +}); diff --git a/tests/stdlib.test.js b/tests/stdlib.test.js deleted file mode 100644 index b25089b..0000000 --- a/tests/stdlib.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import { test, expect } from 'vitest'; - -import { - ANALLIFY_INPUT, - STRINGIFY_INPUT, - RUN_WRONG_OUTPUT, - ANAL_FILE_LOCATION, - RUN_CORRECT_OUTPUT, - ANALLIFY_WRONG_OUTPUT, - STRINGIFY_WRONG_OUTPUT, - ANALLIFY_CORRECT_OUTPUT, - STRINGIFY_CORRECT_OUTPUT, -} from './seeds.js'; - -import { - run, - anallify, - stringify, -} from '../lib/stdlib.js'; - -test('Encode string to anal', () => { - expect(anallify(ANALLIFY_INPUT)).toBe(ANALLIFY_CORRECT_OUTPUT); - expect(anallify(ANALLIFY_INPUT)).not.toBe(ANALLIFY_WRONG_OUTPUT); -}); - -test('Decode anal to string', () => { - expect(stringify(STRINGIFY_INPUT)).toBe(STRINGIFY_CORRECT_OUTPUT); - expect(stringify(STRINGIFY_INPUT)).not.toBe(STRINGIFY_WRONG_OUTPUT); -}); - -test('Run .anal file', () => { - expect(run(ANAL_FILE_LOCATION)).toBe(RUN_CORRECT_OUTPUT); - expect(run(ANAL_FILE_LOCATION)).not.toBe(RUN_WRONG_OUTPUT); -}); diff --git a/tests/utils.test.js b/tests/utils.test.js new file mode 100644 index 0000000..7f3d514 --- /dev/null +++ b/tests/utils.test.js @@ -0,0 +1,23 @@ +import { + vi, + test, + expect, + describe, +} from 'vitest'; + +import { graceful } from '../lib/utils.js'; +import { EMPTY_STRING_INPUT } from './seeds.js'; + +describe('Graceful', () => { + test('Execute graceful', () => { + const fn = vi.fn(); + graceful(fn); + expect(fn).toBeCalled(); + }); + + test('Trigger graceful exit', () => { + const processExit = vi.spyOn(process, 'exit').mockImplementation(() => {}); + graceful(EMPTY_STRING_INPUT); + expect(processExit).toHaveBeenCalled(); + }); +});