goldstein 1.4.0 → 2.0.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,21 @@
1
+ 2022.06.22, v2.0.0
2
+
3
+ feature:
4
+ - goldstein: keyword: safe -> try
5
+
6
+
7
+ 2022.06.22, v1.6.0
8
+
9
+ feature:
10
+ - goldstein: guard keyword: add ability to omit parens
11
+
12
+
13
+ 2022.06.22, v1.5.0
14
+
15
+ feature:
16
+ - goldstein: keyword if: add ability to omit parens
17
+
18
+
1
19
  2022.06.22, v1.4.0
2
20
 
3
21
  feature:
package/README.md CHANGED
@@ -77,7 +77,7 @@ Here is the list.
77
77
 
78
78
  You can use `fn` to declare a `function`:
79
79
 
80
- ```gs
80
+ ```rust
81
81
  fn hello() {
82
82
  return 'world';
83
83
  }
@@ -95,9 +95,9 @@ function hello() {
95
95
 
96
96
  Applies not to `IfCondition`:
97
97
 
98
- ```gs
98
+ ```swift
99
99
  fn hello() {
100
- guard (text !== "world") else {
100
+ guard text !== "world" else {
101
101
  return ""
102
102
  }
103
103
 
@@ -117,12 +117,14 @@ function hello() {
117
117
  }
118
118
  ```
119
119
 
120
- ### `safe`
120
+ ### `try`
121
+
122
+ `try` can be used as an expression.
121
123
 
122
124
  Applies [`tryCatch`](https://github.com/coderaiser/try-catch):
123
125
 
124
126
  ```gs
125
- const [error, result] = safe hello(1, 2, 3);
127
+ const [error, result] = try hello(1, 2, 3);
126
128
  ```
127
129
 
128
130
  Is the same as:
@@ -135,7 +137,7 @@ const [error, result] = tryCatch(1, 2, 3);
135
137
  and
136
138
 
137
139
  ```gs
138
- const [error, result] = safe await hello(1, 2, 3);
140
+ const [error, result] = try await hello(1, 2, 3);
139
141
  ```
140
142
 
141
143
  Is the same as:
@@ -145,6 +147,16 @@ import tryToCatch from 'try-catch';
145
147
  const [error, result] = await tryToCatch(1, 2, 3);
146
148
  ```
147
149
 
150
+ ### `if`
151
+
152
+ You can omit parens. But you must use braces in this case.
153
+
154
+ ```rust
155
+ if a > 3 {
156
+ hello();
157
+ }
158
+ ```
159
+
148
160
  ## How to contribute?
149
161
 
150
162
  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.4.0",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "commitType": "colon",
6
6
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
@@ -4,13 +4,13 @@ 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
8
 
9
9
  export const compile = (source) => {
10
10
  const {parse} = extendParser([
11
11
  keywordFn,
12
12
  keywordGuard,
13
- keywordSafe,
13
+ keywordTry,
14
14
  ]);
15
15
 
16
16
  const ast = parse(source);
@@ -33,7 +33,7 @@ export default function newSpeak(Parser) {
33
33
  } = tokTypes;
34
34
 
35
35
  const node = super.startNode();
36
- super.expect(parenL);
36
+ const isParenL = super.eat(parenL);
37
37
 
38
38
  node.test = {
39
39
  type: 'UnaryExpression',
@@ -42,7 +42,11 @@ export default function newSpeak(Parser) {
42
42
  argument: super.parseExpression(),
43
43
  };
44
44
 
45
- super.expect(parenR);
45
+ const isParenR = super.eat(parenR);
46
+
47
+ if (isParenL !== isParenR)
48
+ this.raise(this.start, `Use both parens ('(', ')') or none`);
49
+
46
50
  super.expect(_else);
47
51
 
48
52
  node.consequent = this.parseStatement();
@@ -0,0 +1,27 @@
1
+ import {
2
+ tokTypes as tt,
3
+ } from '../operator/index.js';
4
+
5
+ export default function fn(Parser) {
6
+ return class extends Parser {
7
+ parseIfStatement(node) {
8
+ this.next();
9
+
10
+ const isParenL = this.eat(tt.parenL);
11
+ node.test = this.parseExpression();
12
+ const isParenR = this.eat(tt.parenR);
13
+
14
+ if (!isParenL && !isParenR && this.type !== tt.braceL)
15
+ this.raise(this.start, `Use braces ('{', '}') when omit parens ('(', ')')`);
16
+
17
+ if (isParenL !== isParenR)
18
+ this.raise(this.start, `Use both parens ('(', ')') or none`);
19
+
20
+ node.consequent = this.parseStatement('if');
21
+ node.alternate = this.eat(tt._else) ? this.parseStatement('if') : null;
22
+
23
+ return this.finishNode(node, 'IfStatement');
24
+ }
25
+ };
26
+ }
27
+
@@ -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,24 @@ 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
-
44
27
  const node = super.startNode();
28
+
29
+ if (this.type === tt.braceL)
30
+ return this.parseUglyTry(node);
31
+
45
32
  const expression = this.parseExpression();
46
33
 
47
34
  if (isCallExpression(expression))
@@ -74,10 +61,42 @@ export default function newSpeak(Parser) {
74
61
  };
75
62
 
76
63
  else
77
- this.raise(this.start, `After 'safe' only 'await' and 'function call' can come`);
64
+ this.raise(this.start, `After 'try' only '{', 'await' and 'function call' can come`);
78
65
 
79
66
  return super.finishNode(node, 'ExpressionStatement');
80
67
  }
68
+ parseUglyTry(node) {
69
+ node.block = this.parseBlock();
70
+ node.handler = null;
71
+
72
+ if (this.type === tt._catch) {
73
+ const clause = this.startNode();
74
+ this.next();
75
+
76
+ if (this.eat(tt.parenL)) {
77
+ clause.param = this.parseBindingAtom();
78
+ const simple = clause.param.type === 'Identifier';
79
+
80
+ this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);
81
+ this.checkLValPattern(clause.param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);
82
+ this.expect(tt.parenR);
83
+ } else {
84
+ clause.param = null;
85
+ this.enterScope(0);
86
+ }
87
+
88
+ clause.body = this.parseBlock(false);
89
+ this.exitScope();
90
+ node.handler = this.finishNode(clause, 'CatchClause');
91
+ }
92
+
93
+ node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
94
+
95
+ if (!node.handler && !node.finalizer)
96
+ this.raise(node.start, 'Missing catch or finally clause');
97
+
98
+ return this.finishNode(node, 'TryStatement');
99
+ }
81
100
  };
82
101
  }
83
102
 
@@ -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