From 0b4756aae5db2d79ecf3a4d500de4e9bd9be590a Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 19 Oct 2023 22:54:46 +0300 Subject: [PATCH] feature: goldstein: add if let --- README.md | 10 ++- packages/convert/index.spec.js | 1 + packages/goldstein/index.spec.js | 21 +++++++ packages/keyword-if/fixture/if-let.gs | 5 ++ packages/keyword-if/fixture/if-let.js | 9 +++ packages/keyword-if/index.js | 88 ++++++++++++++++++++++----- packages/keyword-if/index.spec.js | 5 ++ 7 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 packages/keyword-if/fixture/if-let.gs create mode 100644 packages/keyword-if/fixture/if-let.js diff --git a/README.md b/README.md index c85fb5e..d784de8 100644 --- a/README.md +++ b/README.md @@ -344,12 +344,20 @@ Object.freeze({ You can omit parens. But you must use braces in this case. -```rust +```swift if a > 3 { hello(); } ``` +Also you can use `if let` syntax: + +```swift +if let x = a?.b { + print(x); +} +``` + ### `throw expression` You can use [throw as expression](https://github.com/tc39/proposal-throw-expressions), just like that: diff --git a/packages/convert/index.spec.js b/packages/convert/index.spec.js index be8dddb..2de7790 100644 --- a/packages/convert/index.spec.js +++ b/packages/convert/index.spec.js @@ -44,3 +44,4 @@ test('goldstein: convert: add-array', (t) => { t.equal(result, expected); t.end(); }); + diff --git a/packages/goldstein/index.spec.js b/packages/goldstein/index.spec.js index 58a9bd8..19fe724 100644 --- a/packages/goldstein/index.spec.js +++ b/packages/goldstein/index.spec.js @@ -249,6 +249,27 @@ test('goldstein: parse: append array', (t) => { t.end(); }); +test('goldstein: parse: if let', (t) => { + const result = compile(montag` + if let a = b?.c { + log(a); + } + `); + + const expected = montag` + { + let a = b?.c; + + if (a) { + log(a); + } + }\n + `; + + t.equal(result, expected); + t.end(); +}); + test('goldstein: parse: import', (t) => { const result = compile(montag` import hello from './hello.gs'; diff --git a/packages/keyword-if/fixture/if-let.gs b/packages/keyword-if/fixture/if-let.gs new file mode 100644 index 0000000..fef186a --- /dev/null +++ b/packages/keyword-if/fixture/if-let.gs @@ -0,0 +1,5 @@ +let a = 5; + +if let x = john.info?.name { + print(x); +} \ No newline at end of file diff --git a/packages/keyword-if/fixture/if-let.js b/packages/keyword-if/fixture/if-let.js new file mode 100644 index 0000000..1e795ce --- /dev/null +++ b/packages/keyword-if/fixture/if-let.js @@ -0,0 +1,9 @@ +let a = 5; + +{ + let x = john.info?.name; + + if (x) { + print(x); + } +} diff --git a/packages/keyword-if/index.js b/packages/keyword-if/index.js index 950fda5..5d0fc03 100644 --- a/packages/keyword-if/index.js +++ b/packages/keyword-if/index.js @@ -3,27 +3,85 @@ import {setGoldsteinIf} from '../types/if.js'; export default function fn(Parser) { return class extends Parser { - parseIfStatement(node) { + parseIfStatement() { this.next(); - const isParenL = this.eat(tt.parenL); - node.test = this.parseExpression(); - const isParenR = this.eat(tt.parenR); - - if (!isParenL && !isParenR && this.type !== tt.braceL) - this.raise(this.start, `Use braces ('{', '}') when omit parens ('(', ')')`); - - if (isParenL !== isParenR) - this.raise(this.start, `Use both parens ('(', ')') or none`); + if (this.isContextual('let')) + return createIfLet.call(this, { + isParenL, + }); - node.consequent = this.parseStatement('if'); - node.alternate = this.eat(tt._else) ? this.parseStatement('if') : null; + const test = this.parseExpression(); - setGoldsteinIf(node); - - return this.finishNode(node, 'IfStatement'); + return createIf.call(this, { + test, + isParenL, + }); } }; } +function check({isParenL, isParenR}) { + if (!isParenL && !isParenR && this.type !== tt.braceL) + this.raise(this.start, `Use braces ('{', '}') when omit parens ('(', ')')`); + + if (isParenL !== isParenR) + this.raise(this.start, `Use both parens ('(', ')') or none`); +} + +function createIfLet({isParenL}) { + this.next(); + this.eat(tt.assign); + + const assignmentExpression = this.parseExpression(); + const isParenR = this.eat(tt.parenR); + + check.call(this, { + isParenL, + isParenR, + }); + + const ifNode = createIf.call(this, { + test: assignmentExpression.left, + isParenL, + }); + + const node = { + loc: {}, + range: [], + body: [{ + type: 'VariableDeclaration', + kind: 'let', + declarations: [{ + type: 'VariableDeclarator', + id: assignmentExpression.left, + init: assignmentExpression.right, + }], + }, ifNode], + }; + + return this.finishNode(node, 'BlockStatement'); +} + +function createIf({test, isParenL}) { + const node = { + test, + }; + + const isParenR = this.eat(tt.parenR); + + check.call(this, { + isParenL, + isParenR, + }); + + node.consequent = this.parseStatement('if'); + node.alternate = this.eat(tt._else) ? this.parseStatement('if') : null; + node.loc = {}; + node.range = []; + + setGoldsteinIf(node); + + return this.finishNode(node, 'IfStatement'); +} diff --git a/packages/keyword-if/index.spec.js b/packages/keyword-if/index.spec.js index 9548609..a8c077a 100644 --- a/packages/keyword-if/index.spec.js +++ b/packages/keyword-if/index.spec.js @@ -8,6 +8,11 @@ test('goldstein: keyword: if', (t) => { t.end(); }); +test('goldstein: keyword: if: let', (t) => { + t.compile('if-let'); + t.end(); +}); + test('goldstein: keyword: if: no brace', (t) => { t.raise('no-brace', `Use braces ('{', '}') when omit parens ('(', ')') (2:4)`); t.end();