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 +19 -0
- package/README.md +37 -8
- package/package.json +1 -1
- package/packages/goldstein/index.js +8 -2
- package/packages/keyword-if/index.js +1 -0
- package/packages/keyword-should/index.js +91 -0
- package/packages/keyword-throw/index.js +30 -0
- package/packages/{keyword-safe → keyword-try}/index.js +44 -24
- package/packages/operator/index.js +1 -0
- package/packages/operator/scopeflags.js +21 -0
- package/packages/string-interpolation/index.js +65 -0
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
|
+

|
|
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
|
|
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
|
-
```
|
|
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
|
-
```
|
|
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
|
-
### `
|
|
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] =
|
|
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] =
|
|
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
|
-
```
|
|
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
|
@@ -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
|
|
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
|
-
|
|
16
|
+
keywordTry,
|
|
17
|
+
keywordShould,
|
|
18
|
+
keywordThrow,
|
|
19
|
+
stringInterpolation,
|
|
14
20
|
]);
|
|
15
21
|
|
|
16
22
|
const ast = parse(source);
|
|
@@ -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
|
-
|
|
4
|
-
|
|
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.
|
|
36
|
-
return this.
|
|
19
|
+
if (this.type === keywordTypes.try)
|
|
20
|
+
return this.parseTryStatement();
|
|
37
21
|
|
|
38
22
|
return super.parseExprAtom(refDestructuringErrors, forInit);
|
|
39
23
|
}
|
|
40
24
|
|
|
41
|
-
|
|
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 '
|
|
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
|
|
|
@@ -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
|
+
|