goldstein 1.6.0 → 2.2.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.
package/ChangeLog CHANGED
@@ -1,3 +1,22 @@
1
+ 2022.06.27, v2.2.0
2
+
3
+ feature:
4
+ - goldstein: add support of throw expressions
5
+
6
+
7
+ 2022.06.25, v2.1.0
8
+
9
+ feature:
10
+ - goldstein: integrate 'should'
11
+ - add should keyword (#5)
12
+
13
+
14
+ 2022.06.22, v2.0.0
15
+
16
+ feature:
17
+ - goldstein: keyword: safe -> try
18
+
19
+
1
20
  2022.06.22, v1.6.0
2
21
 
3
22
  feature:
package/README.md CHANGED
@@ -9,11 +9,13 @@
9
9
  [CoverageURL]: https://coveralls.io/github/coderaiser/goldstein?branch=master
10
10
  [CoverageIMGURL]: https://coveralls.io/repos/coderaiser/goldstein/badge.svg?branch=master&service=github
11
11
 
12
+ ![image](https://user-images.githubusercontent.com/1573141/175353192-9867d3ba-beaf-46d5-adbc-e2eb736bfef1.png)
13
+
12
14
  > *"You haven't a real appreciation of Newspeak, Winston," he said almost sadly. "Even when you write it you're still thinking in Oldspeak. I've read some of those pieces that you write in The Times occasionally. They're good enough, but they're translations. In your heart you'd prefer to stick to Oldspeak, with all its vagueness and its useless shades of meaning. You don't grasp the beauty of the destruction of words. Do you know that Newspeak is the only language in the world whose vocabulary gets smaller every year?"*
13
15
  >
14
16
  > *(c) “1984”, George Orwell*
15
17
 
16
- JavaScript with no limits.
18
+ JavaScript with no limits 🤫.
17
19
 
18
20
  Language ruled by the users, [create an issue](https://github.com/coderaiser/goldstein/issues/new/choose) with ideas of a new language construction and what is look like in JavaScript, and most likely we implement it :).
19
21
 
@@ -49,7 +51,7 @@ import {compile} from 'goldstein';
49
51
 
50
52
  compile(`
51
53
  fn hello() {
52
- guard (text !== "world") else {
54
+ guard text !== "world" else {
53
55
  return ""
54
56
  }
55
57
 
@@ -77,7 +79,7 @@ Here is the list.
77
79
 
78
80
  You can use `fn` to declare a `function`:
79
81
 
80
- ```gs
82
+ ```rust
81
83
  fn hello() {
82
84
  return 'world';
83
85
  }
@@ -95,7 +97,7 @@ function hello() {
95
97
 
96
98
  Applies not to `IfCondition`:
97
99
 
98
- ```gs
100
+ ```swift
99
101
  fn hello() {
100
102
  guard text !== "world" else {
101
103
  return ""
@@ -117,12 +119,14 @@ function hello() {
117
119
  }
118
120
  ```
119
121
 
120
- ### `safe`
122
+ ### `try`
123
+
124
+ `try` can be used as an expression.
121
125
 
122
126
  Applies [`tryCatch`](https://github.com/coderaiser/try-catch):
123
127
 
124
128
  ```gs
125
- const [error, result] = safe hello(1, 2, 3);
129
+ const [error, result] = try hello(1, 2, 3);
126
130
  ```
127
131
 
128
132
  Is the same as:
@@ -135,7 +139,7 @@ const [error, result] = tryCatch(1, 2, 3);
135
139
  and
136
140
 
137
141
  ```gs
138
- const [error, result] = safe await hello(1, 2, 3);
142
+ const [error, result] = try await hello(1, 2, 3);
139
143
  ```
140
144
 
141
145
  Is the same as:
@@ -145,16 +149,41 @@ import tryToCatch from 'try-catch';
145
149
  const [error, result] = await tryToCatch(1, 2, 3);
146
150
  ```
147
151
 
152
+ ### `should`
153
+
154
+ `should` can be used as an expression (just like [`try`](https://github.com/coderaiser/goldstein/edit/master/README.md#try)).
155
+ This keyword is useful if you want to prevent a function call (also async) to throw an error because you don't need to have any result and the real execution is just optional (so runs if supported).
156
+
157
+ ```gs
158
+ should hello()
159
+ ```
160
+
161
+ Is the same as:
162
+
163
+ ```gs
164
+ try hello();
165
+ ```
166
+
167
+ > ☝️ *Warning: this feature can be helpful but also dangerous especially if you're debugging your application. In fact, this is made to be used as an optional function call (ex. should load content, but not necessary and knowing this feature is optional), if you call a function in this way while debugging, no error will be printed and the application will continue run as nothing happened.*
168
+
148
169
  ### `if`
149
170
 
150
171
  You can omit parens. But you must use braces in this case.
151
172
 
152
- ```gs
173
+ ```rust
153
174
  if a > 3 {
154
175
  hello();
155
176
  }
156
177
  ```
157
178
 
179
+ ### `throw expression`
180
+
181
+ You can use [throw as expression](https://github.com/tc39/proposal-throw-expressions), just like that:
182
+
183
+ ```js
184
+ const a = () => throw 'hello';
185
+ ```
186
+
158
187
  ## How to contribute?
159
188
 
160
189
  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 🥳!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goldstein",
3
- "version": "1.6.0",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "commitType": "colon",
6
6
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
@@ -4,13 +4,19 @@ import putout, {
4
4
  import {extendParser} from '../parser/index.js';
5
5
  import keywordFn from '../keyword-fn/index.js';
6
6
  import keywordGuard from '../keyword-guard/index.js';
7
- import keywordSafe from '../keyword-safe/index.js';
7
+ import keywordTry from '../keyword-try/index.js';
8
+ import keywordShould from '../keyword-should/index.js';
9
+ import keywordThrow from '../keyword-throw/index.js';
10
+ import stringInterpolation from '../string-interpolation/index.js';
8
11
 
9
12
  export const compile = (source) => {
10
13
  const {parse} = extendParser([
11
14
  keywordFn,
12
15
  keywordGuard,
13
- keywordSafe,
16
+ keywordTry,
17
+ keywordShould,
18
+ keywordThrow,
19
+ stringInterpolation,
14
20
  ]);
15
21
 
16
22
  const ast = parse(source);
@@ -8,6 +8,7 @@ export default function fn(Parser) {
8
8
  this.next();
9
9
 
10
10
  const isParenL = this.eat(tt.parenL);
11
+
11
12
  node.test = this.parseExpression();
12
13
  const isParenR = this.eat(tt.parenR);
13
14
 
@@ -0,0 +1,91 @@
1
+ import {types} from 'putout';
2
+ import {
3
+ addKeyword,
4
+ TokenType,
5
+ tokTypes as tt,
6
+ } from '../operator/index.js';
7
+
8
+ const {
9
+ isCallExpression,
10
+ isAwaitExpression,
11
+ } = types;
12
+
13
+ export default function newSpeak(Parser) {
14
+ const {keywordTypes} = Parser.acorn;
15
+ keywordTypes.should = new TokenType('should', {
16
+ keyword: 'should',
17
+ });
18
+
19
+ return class extends Parser {
20
+ parse() {
21
+ this.keywords = addKeyword('should', this.keywords);
22
+ return super.parse();
23
+ }
24
+ parseStatement(context, topLevel, exports) {
25
+ if (this.type === keywordTypes.should) {
26
+ return this.parseShould();
27
+ }
28
+
29
+ return super.parseStatement(context, topLevel, exports);
30
+ }
31
+
32
+ parseShould() {
33
+ this.next();
34
+
35
+ const node = super.startNode();
36
+
37
+ if (this.type === tt.braceL)
38
+ return this.raise(this.start, `After 'should' only 'await' and 'function call' can come, brakets are not supported`);
39
+
40
+ const expression = this.parseExpression();
41
+
42
+ if (isCallExpression(expression))
43
+ node.expression = {
44
+ type: 'TryStatement',
45
+ block: {
46
+ type: 'BlockStatement',
47
+ body: [{
48
+ type: 'ExpressionStatement',
49
+ expression: {
50
+ type: 'CallExpression',
51
+ callee: expression.callee,
52
+ arguments: expression.arguments.slice(),
53
+ },
54
+ }],
55
+ },
56
+ handler: {
57
+ type: 'CatchClause',
58
+ body: {
59
+ type: 'BlockStatement',
60
+ body: [],
61
+ },
62
+ },
63
+ };
64
+
65
+ else if (isAwaitExpression(expression))
66
+ node.expression = {
67
+ type: 'TryStatement',
68
+ block: {
69
+ type: 'BlockStatement',
70
+ body: [{
71
+ type: 'ExpressionStatement',
72
+ expression,
73
+ }],
74
+ },
75
+ handler: {
76
+ type: 'CatchClause',
77
+ body: {
78
+ type: 'BlockStatement',
79
+ body: [],
80
+ },
81
+ },
82
+ };
83
+
84
+ else
85
+ this.raise(this.start, `After 'should' only 'await' and 'function call' can come`);
86
+
87
+ return super.finishNode(node, 'ExpressionStatement');
88
+ }
89
+ };
90
+ }
91
+
@@ -0,0 +1,30 @@
1
+ const {assign} = Object;
2
+
3
+ export default function keywordThrow(Parser) {
4
+ const {keywordTypes} = Parser.acorn;
5
+
6
+ return class extends Parser {
7
+ parseExprAtom(refDestructuringErrors, forInit) {
8
+ if (this.type === keywordTypes.throw) {
9
+ return this.parseThrowExpression();
10
+ }
11
+
12
+ return super.parseExprAtom(refDestructuringErrors, forInit);
13
+ }
14
+
15
+ parseThrowExpression() {
16
+ this.next();
17
+
18
+ const node = super.startNode();
19
+ const expression = this.parseExpression();
20
+
21
+ assign(node, {
22
+ operator: 'throw',
23
+ argument: expression,
24
+ });
25
+
26
+ return super.finishNode(node, 'UnaryExpression');
27
+ }
28
+ };
29
+ }
30
+
@@ -1,7 +1,9 @@
1
1
  import {types} from 'putout';
2
2
  import {
3
- addKeyword,
4
- TokenType,
3
+ BIND_LEXICAL,
4
+ BIND_SIMPLE_CATCH,
5
+ SCOPE_SIMPLE_CATCH,
6
+ tokTypes as tt,
5
7
  } from '../operator/index.js';
6
8
 
7
9
  const {
@@ -9,39 +11,25 @@ const {
9
11
  isAwaitExpression,
10
12
  } = types;
11
13
 
12
- // why not 'try'?
13
- // because acorn internals should be copied, and added tests.
14
- // there is no such thing as this.previous(), only this.next() 🤷‍
15
-
16
14
  export default function newSpeak(Parser) {
17
15
  const {keywordTypes} = Parser.acorn;
18
- keywordTypes.safe = new TokenType('safe', {
19
- keyword: 'safe',
20
- });
21
16
 
22
17
  return class extends Parser {
23
- parse() {
24
- this.keywords = addKeyword('safe', this.keywords);
25
-
26
- return super.parse();
27
- }
28
- parseStatement(context, topLevel, exports) {
29
- if (this.type === keywordTypes.safe)
30
- return this.parseSafe();
31
-
32
- return super.parseStatement(context, topLevel, exports);
33
- }
34
18
  parseExprAtom(refDestructuringErrors, forInit) {
35
- if (this.type === keywordTypes.safe)
36
- return this.parseSafe();
19
+ if (this.type === keywordTypes.try)
20
+ return this.parseTryStatement();
37
21
 
38
22
  return super.parseExprAtom(refDestructuringErrors, forInit);
39
23
  }
40
24
 
41
- parseSafe() {
25
+ parseTryStatement() {
42
26
  this.next();
43
27
 
44
28
  const node = super.startNode();
29
+
30
+ if (this.type === tt.braceL)
31
+ return this.parseUglyTry(node);
32
+
45
33
  const expression = this.parseExpression();
46
34
 
47
35
  if (isCallExpression(expression))
@@ -74,10 +62,42 @@ export default function newSpeak(Parser) {
74
62
  };
75
63
 
76
64
  else
77
- this.raise(this.start, `After 'safe' only 'await' and 'function call' can come`);
65
+ this.raise(this.start, `After 'try' only '{', 'await' and 'function call' can come`);
78
66
 
79
67
  return super.finishNode(node, 'ExpressionStatement');
80
68
  }
69
+ parseUglyTry(node) {
70
+ node.block = this.parseBlock();
71
+ node.handler = null;
72
+
73
+ if (this.type === tt._catch) {
74
+ const clause = this.startNode();
75
+ this.next();
76
+
77
+ if (this.eat(tt.parenL)) {
78
+ clause.param = this.parseBindingAtom();
79
+ const simple = clause.param.type === 'Identifier';
80
+
81
+ this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);
82
+ this.checkLValPattern(clause.param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);
83
+ this.expect(tt.parenR);
84
+ } else {
85
+ clause.param = null;
86
+ this.enterScope(0);
87
+ }
88
+
89
+ clause.body = this.parseBlock(false);
90
+ this.exitScope();
91
+ node.handler = this.finishNode(clause, 'CatchClause');
92
+ }
93
+
94
+ node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
95
+
96
+ if (!node.handler && !node.finalizer)
97
+ this.raise(node.start, 'Missing catch or finally clause');
98
+
99
+ return this.finishNode(node, 'TryStatement');
100
+ }
81
101
  };
82
102
  }
83
103
 
@@ -1,4 +1,5 @@
1
1
  export * from 'acorn';
2
+ export * from './scopeflags.js';
2
3
 
3
4
  export function addKeyword(keyword, keywords) {
4
5
  const str = keywords
@@ -0,0 +1,21 @@
1
+ // Each scope gets a bitset that may contain these flags
2
+ export const
3
+ SCOPE_TOP = 1,
4
+ SCOPE_FUNCTION = 2,
5
+ SCOPE_ASYNC = 4,
6
+ SCOPE_GENERATOR = 8,
7
+ SCOPE_ARROW = 16,
8
+ SCOPE_SIMPLE_CATCH = 32,
9
+ SCOPE_SUPER = 64,
10
+ SCOPE_DIRECT_SUPER = 128,
11
+ SCOPE_CLASS_STATIC_BLOCK = 256,
12
+ SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK;
13
+
14
+ // Used in checkLVal* and declareName to determine the type of a binding
15
+ export const
16
+ BIND_NONE = 0, // Not a binding
17
+ BIND_VAR = 1, // Var-style binding
18
+ BIND_LEXICAL = 2, // Let- or const-style binding
19
+ BIND_FUNCTION = 3, // Function declaration
20
+ BIND_SIMPLE_CATCH = 4, // Simple (identifier pattern) catch binding
21
+ BIND_OUTSIDE = 5; // Special case for function names as bound inside the function
@@ -0,0 +1,65 @@
1
+ import {template} from 'putout';
2
+ const {assign} = Object;
3
+
4
+ export default function stringInterpolation(Parser) {
5
+ return class extends Parser {
6
+ parseLiteral(value) {
7
+ const chars = value.split('');
8
+
9
+ let literalOpened = false;
10
+ let parenthesis = 0;
11
+ const out = [];
12
+ let isTemplateLiteral = false;
13
+
14
+ for (let [index, char] of chars.entries()) {
15
+ if (char === '(') {
16
+ // keep count of parenthesis
17
+ parenthesis++;
18
+
19
+ // check if previous token was "/" (only if literal is not opened yet)
20
+ const prev = index - 1;
21
+
22
+ if (chars[prev] === '/' && !literalOpened) {
23
+ // set previous char to "$"
24
+ out[prev] = '$';
25
+ // set current char to "{"
26
+ char = '{';
27
+
28
+ // set literalOpened to true
29
+ literalOpened = true;
30
+ // match TemplateLiteral instead of StringLiteral
31
+ isTemplateLiteral = true;
32
+ }
33
+ } else if (char === ')') {
34
+ parenthesis--;
35
+
36
+ if (parenthesis === 0 && literalOpened) {
37
+ char = '}';
38
+
39
+ // reset literalOpened to false
40
+ literalOpened = false;
41
+ }
42
+ }
43
+
44
+ out.push(char);
45
+ }
46
+
47
+ if (isTemplateLiteral) {
48
+ const node = this.startNode();
49
+ this.next();
50
+
51
+ const {quasis, expressions} = template.ast('`' + out.join('') + '`');
52
+
53
+ assign(node, {
54
+ quasis,
55
+ expressions,
56
+ });
57
+
58
+ return this.finishNode(node, 'TemplateLiteral');
59
+ }
60
+
61
+ return super.parseLiteral(out.join(''));
62
+ }
63
+ };
64
+ }
65
+