Skip to content

Commit

Permalink
feature: goldstein: add ability to parse no async code with await
Browse files Browse the repository at this point in the history
  • Loading branch information
coderaiser committed May 6, 2024
1 parent db11881 commit 98f3048
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 36 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,22 @@ That absolutely fine, it will be converted to:
function hello() {}
```

### `asyn`-less `Function` with `await`

```gs
function hello() {
await world();
}
```

In js:

```js
async function hello() {
await world();
}
```

## How to contribute?

Clone the registry, create a new keyword with a prefix `keyword-`, then create directory `fixture` and put there two files with extensions `.js` and `.gs`. Half way done 🥳!
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@putout/plugin-declare": "^4.0.0",
"@putout/plugin-logical-expressions": "^5.0.0",
"@putout/plugin-try-catch": "^3.0.0",
"@putout/plugin-promises": "^14.1.0",
"@putout/printer": "^8.0.1",
"acorn": "^8.7.1",
"esbuild": "^0.20.1",
Expand All @@ -45,6 +46,7 @@
"devDependencies": {
"@babel/core": "^8.0.0-alpha.5",
"@cloudcmd/stub": "^4.0.1",
"@putout/plugin-goldstein": "./rules/goldstein",
"@putout/test": "^9.0.1",
"c8": "^9.1.0",
"check-dts": "^0.7.1",
Expand All @@ -58,8 +60,7 @@
"redlint": "^3.14.1",
"runsome": "^1.0.0",
"supertape": "^10.1.0",
"typescript": "^5.0.3",
"@putout/plugin-goldstein": "./rules/goldstein"
"typescript": "^5.0.3"
},
"engines": {
"node": ">=18"
Expand Down
2 changes: 2 additions & 0 deletions packages/goldstein/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {print} from '@putout/printer';
import tryCatchPlugin from '@putout/plugin-try-catch';
import declarePlugin from '@putout/plugin-declare';
import logicalExpressionsPlugin from '@putout/plugin-logical-expressions';
import promisesPlugin from '@putout/plugin-promises';
import {parse} from './parser.js';

export * from './parser.js';
Expand All @@ -19,6 +20,7 @@ export const compile = (source, options = {}) => {
['try-catch', tryCatchPlugin],
['declare', declarePlugin],
['logical-expressions', logicalExpressionsPlugin],
['promises', promisesPlugin],
],
});

Expand Down
39 changes: 35 additions & 4 deletions packages/goldstein/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ import {
test('goldstein: compile', (t) => {
const result = compile(`
fn hello() {
return 'world'
}
`);

const expected = 'function hello() {}\n';
const expected = montag`
function hello() {
return 'world';
}\n
`;

t.equal(result, expected);
t.end();
Expand Down Expand Up @@ -115,11 +120,15 @@ test('goldstein: compile: freeze', (t) => {

test('goldstein: compile: sourceType', (t) => {
const result = compile(montag`
export fn hello() {};
export fn hello() {
return 'world';
};
`);

const expected = montag`
export function hello() {};
export function hello() {
return 'world';
};
`;

Expand Down Expand Up @@ -162,11 +171,14 @@ test('goldstein: compile: curry', (t) => {
test('goldstein: compile: arrow', (t) => {
const result = compile(montag`
function hello() => {
return 'world';
}
`);

const expected = montag`
function hello() {}\n
function hello() {
return 'world';
}\n
`;

t.equal(result, expected);
Expand Down Expand Up @@ -320,3 +332,22 @@ test('goldstein: convert', (t) => {
t.equal(result, expected);
t.end();
});

test('goldstein: convert: no-async', (t) => {
const source = montag`
function hello() {
await world();
}
`;

const result = convert(source);

const expected = montag`
async function hello() {
await world();
}\n
`;

t.equal(result, expected);
t.end();
});
2 changes: 2 additions & 0 deletions packages/goldstein/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import keywordIf from '../keyword-if/index.js';
import keywordImport from '../keyword-import/index.js';
import keywordArrow from '../keyword-arrow/index.js';
import keywordAddArray from '../keyword-add-array/index.js';
import keywordNoAsync from '../keyword-no-async/index.js';

const defaultKeywords = {
keywordFn,
Expand All @@ -26,6 +27,7 @@ const defaultKeywords = {
keywordArrow,
keywordAddArray,
stringInterpolation,
keywordNoAsync,
};

export const keywords = defaultKeywords;
Expand Down
5 changes: 3 additions & 2 deletions packages/keyword-no-async/fixture/no-async.gs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
function hello() => {}
function world() {}
function hello() {
await world();
}
6 changes: 3 additions & 3 deletions packages/keyword-no-async/fixture/no-async.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
function hello() {}

function world() {}
async function hello() {
await world();
}
46 changes: 23 additions & 23 deletions packages/keyword-no-async/index.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import {tokTypes as tt} from 'acorn';
const {defineProperty} = Object;

export default function fn(Parser) {
return class extends Parser {
parseBlock(createNewLexicalScope, node, exitStrict) {
if (createNewLexicalScope === void 0)
createNewLexicalScope = true;

if (node === void 0)
node = this.startNode();
parseFunction(node, statement, allowExpressionBody, isAsync, forInit) {
return super.parseFunction(node, statement, allowExpressionBody, true, forInit);
}

checkUnreserved({start, end, name}) {
if (this.inGenerator && name === 'yield')
this.raiseRecoverable(start, `Cannot use 'yield' as identifier inside a generator`);

node.body = [];
// optionally parse arrow
this.eat(tt.arrow);
this.expect(tt.braceL);
if (this.inAsync && name === 'await')
this.raiseRecoverable(start, `Cannot use 'await' as identifier inside an async function`);

if (createNewLexicalScope)
this.enterScope(0);
if (this.currentThisScope().inClassFieldInit && name === 'arguments')
this.raiseRecoverable(start, `Cannot use 'arguments' in class field initializer`);

while (this.type !== tt.braceR) {
const stmt = this.parseStatement(null);
node.body.push(stmt);
}
if (this.inClassStaticBlock && (name === 'arguments' || name === 'await'))
this.raise(start, `Cannot use ${name} in class static initialization block`);

if (exitStrict)
this.strict = false;
if (this.keywords.test(name))
this.raise(start, `Unexpected keyword '` + name + `'`);

this.next();
if (this.options.ecmaVersion < 6 && this.input.slice(start, end).includes('\\'))
return;

if (createNewLexicalScope)
this.exitScope();
const re = this.strict ? this.reservedWordsStrict : this.reservedWords;

return this.finishNode(node, 'BlockStatement');
if (re.test(name) && !this.inAsync && name === 'await')
defineProperty(this, 'inAsync', {
value: true,
});
}
};
}
4 changes: 2 additions & 2 deletions packages/keyword-no-async/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import keywordFn from './index.js';

const test = createTest(import.meta.url, keywordFn);

test('goldstein: keyword: arrow', (t) => {
t.compile('arrow');
test('goldstein: keyword: no-async', (t) => {
t.compile('no-async');
t.end();
});

0 comments on commit 98f3048

Please sign in to comment.