goldstein 2.6.0 → 3.1.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,18 @@
1
+ 2023.04.18, v3.1.0
2
+
3
+ feature:
4
+ - 4236346 goldstein: add ability to use arrow in FunctionDeclaration
5
+ - 8e2197f goldstein: add ability tot pass keywords and 🐊Putout optionts
6
+
7
+ 2023.04.02, v3.0.0
8
+
9
+ feature:
10
+ - 3eeeab7 goldstein: add ability to simplify logical expressions during compilation
11
+ - 7959e57 goldstein: add ability to compile import gs to js
12
+ - c61e500 goldstein: improve compiler, disable bundling
13
+ - 6cd279e goldstein: enable support of ifStatments with no round braces
14
+ - 0ba6f9c major: goldstein: compile: use @putout/pinter instead of recast, parse: provide Babel AST
15
+
1
16
  2023.04.01, v2.6.0
2
17
 
3
18
  feature:
package/README.md CHANGED
@@ -43,6 +43,32 @@ export {
43
43
  };
44
44
  ```
45
45
 
46
+ Let's do a bit more!
47
+
48
+ ```gs
49
+ const a = () => throw 'hello';
50
+
51
+ if a > 2 {
52
+ log('hello');
53
+ }
54
+
55
+ fn hello() {
56
+ console.log('hello');
57
+ }
58
+ ```
59
+
60
+ Will give us:
61
+
62
+ ```js
63
+ const a = () => {
64
+ throw 'hello';
65
+ };
66
+
67
+ if (a > 2) {
68
+ log('hello');
69
+ }
70
+ ```
71
+
46
72
  ## API
47
73
 
48
74
  ### `compile(source)`
@@ -73,9 +99,56 @@ function hello() {
73
99
  `;
74
100
  ```
75
101
 
102
+ By default, all keywords mentioned in the next section used, but you can limit the list setting with `keywords` option.
103
+ You can add any keywords, and even create your own:
104
+
105
+ ```js
106
+ import {
107
+ compile,
108
+ keywords,
109
+ } from 'goldstein';
110
+
111
+ const source = `
112
+ fn hello() {
113
+ return id('hello');
114
+ }
115
+ `;
116
+
117
+ const {keywordFn} = keywords;
118
+
119
+ compile(source, {
120
+ keywords: [
121
+ keywordFn,
122
+ function id(Parser) {
123
+ const {keywordTypes} = Parser.acorn;
124
+ return class extends Parser {
125
+ };
126
+ },
127
+ ],
128
+ rules: {
129
+ declare: ['on', {
130
+ declarations: {
131
+ id: 'const id = (a) => a',
132
+ },
133
+ }],
134
+ },
135
+ });
136
+
137
+ // returns
138
+ `
139
+ const id = (a) => a;
140
+
141
+ function hello() {
142
+ return id('hello');
143
+ }
144
+ `;
145
+ ```
146
+
147
+ You can declare variables with using [`@putout/operator-declare`](https://github.com/coderaiser/putout/tree/master/packages/operator-declare).
148
+
76
149
  ### `parse(source)`
77
150
 
78
- When you need to get **JavaScript** ESTree AST use `parse`:
151
+ When you need to get **JavaScript** Babel AST use `parse`:
79
152
 
80
153
  ```js
81
154
  import {parse} from 'goldstein';
@@ -89,7 +162,8 @@ parse(`
89
162
  return "Hello " + text
90
163
  }
91
164
  `);
92
- // returns ESTree AST
165
+
166
+ // returns Babel AST
93
167
  ```
94
168
 
95
169
  ## Keywords
@@ -133,7 +207,7 @@ Is the same as:
133
207
 
134
208
  ```js
135
209
  function hello() {
136
- if (!(text !== 'world')) {
210
+ if (text === 'world') {
137
211
  return '';
138
212
  }
139
213
 
@@ -237,6 +311,41 @@ inc(5);
237
311
  6
238
312
  ```
239
313
 
314
+ ### `Import`
315
+
316
+ When you import `.gs` files during compile step it will be replaced with `.js`:
317
+
318
+ ```gs
319
+ // hello.js
320
+ export const hello = () => 'world';
321
+
322
+ // index.js
323
+ import hello from './hello.gs';
324
+ ```
325
+
326
+ Will be converted to:
327
+
328
+ ```js
329
+ // index.js
330
+ import hello from './hello.js';
331
+ ```
332
+
333
+ ### `FunctionDeclaration` with `Arrow`
334
+
335
+ If you mistakenly put `=>` in function declaration:
336
+
337
+ ```gs
338
+ function hello() => {
339
+ }
340
+ ```
341
+
342
+ That absolutely fine, it will be converted to:
343
+
344
+ ```js
345
+ function hello() {
346
+ }
347
+ ```
348
+
240
349
  ## How to contribute?
241
350
 
242
351
  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/bin/gs.js CHANGED
@@ -24,7 +24,7 @@ const outfile = compiledName.replace('~', '');
24
24
 
25
25
  esbuild.buildSync({
26
26
  entryPoints: [compiledName],
27
- bundle: true,
27
+ bundle: false,
28
28
  write: true,
29
29
  outfile,
30
30
  mainFields: ['main'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goldstein",
3
- "version": "2.6.0",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "JavaScript with no limits",
@@ -25,8 +25,10 @@
25
25
  "fix:lint": "madrun fix:lint"
26
26
  },
27
27
  "dependencies": {
28
+ "@putout/printer": "^1.16.0",
28
29
  "acorn": "^8.7.1",
29
30
  "esbuild": "^0.17.14",
31
+ "estree-to-babel": "^5.0.1",
30
32
  "putout": "^29.1.11",
31
33
  "try-catch": "^3.0.1"
32
34
  },
@@ -1,6 +1,6 @@
1
- import putout, {
2
- print,
3
- } from 'putout';
1
+ import {transform} from 'putout';
2
+
3
+ import {print} from '@putout/printer';
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';
@@ -10,31 +10,51 @@ import keywordThrow from '../keyword-throw/index.js';
10
10
  import stringInterpolation from '../string-interpolation/index.js';
11
11
  import keywordCurry from '../keyword-curry/index.js';
12
12
  import keywordFreeze from '../keyword-freeze/index.js';
13
+ import keywordIf from '../keyword-if/index.js';
14
+ import keywordImport from '../keyword-import/index.js';
15
+
16
+ import estreeToBabel from 'estree-to-babel';
17
+
18
+ const defaultKeywords = {
19
+ keywordFn,
20
+ keywordGuard,
21
+ keywordTry,
22
+ keywordShould,
23
+ keywordThrow,
24
+ keywordCurry,
25
+ keywordFreeze,
26
+ keywordIf,
27
+ keywordImport,
28
+ stringInterpolation,
29
+ };
30
+
31
+ export const keywords = defaultKeywords;
13
32
 
14
- export const parse = (source) => {
15
- const {parse} = extendParser([
16
- keywordFn,
17
- keywordGuard,
18
- keywordTry,
19
- keywordShould,
20
- keywordThrow,
21
- keywordCurry,
22
- keywordFreeze,
23
- stringInterpolation,
24
- ]);
33
+ export const parse = (source, keywords = defaultKeywords) => {
34
+ const {parse} = extendParser(Object.values(keywords));
25
35
 
26
- return parse(source);
36
+ return estreeToBabel(parse(source));
27
37
  };
28
38
 
29
- export const compile = (source) => {
39
+ export const compile = (source, options = {}) => {
30
40
  const ast = parse(source);
31
- const jsCode = print(ast);
32
- const {code} = putout(jsCode, {
41
+
42
+ transform(ast, source, {
43
+ rules: {
44
+ ...options.rules,
45
+ },
33
46
  plugins: [
34
47
  'try-catch',
35
48
  'declare',
49
+ 'logical-expressions',
36
50
  ],
37
51
  });
38
52
 
39
- return code;
53
+ const {keywords} = options;
54
+
55
+ return fixEmpty(print(ast, {keywords}));
56
+ };
57
+
58
+ const fixEmpty = (source) => {
59
+ return source.replace(';;', ';');
40
60
  };
@@ -0,0 +1,43 @@
1
+ import {
2
+ tokTypes as tt,
3
+ } from 'acorn';
4
+
5
+ export default function fn(Parser) {
6
+ return class extends Parser {
7
+ parseBlock(createNewLexicalScope, node, exitStrict) {
8
+ if (createNewLexicalScope === void 0)
9
+ createNewLexicalScope = true;
10
+
11
+ if (node === void 0)
12
+ node = this.startNode();
13
+
14
+ node.body = [];
15
+
16
+ // optionally parse arrow
17
+ this.eat(tt.arrow);
18
+ this.expect(tt.braceL);
19
+
20
+ if (createNewLexicalScope) {
21
+ this.enterScope(0);
22
+ }
23
+
24
+ while (this.type !== tt.braceR) {
25
+ const stmt = this.parseStatement(null);
26
+ node.body.push(stmt);
27
+ }
28
+
29
+ if (exitStrict) {
30
+ this.strict = false;
31
+ }
32
+
33
+ this.next();
34
+
35
+ if (createNewLexicalScope) {
36
+ this.exitScope();
37
+ }
38
+
39
+ return this.finishNode(node, 'BlockStatement');
40
+ }
41
+ };
42
+ }
43
+
@@ -1,6 +1,4 @@
1
- import {
2
- tokTypes as tt,
3
- } from '../operator/index.js';
1
+ import {tokTypes as tt} from '../operator/index.js';
4
2
 
5
3
  export default function fn(Parser) {
6
4
  return class extends Parser {
@@ -0,0 +1,34 @@
1
+ import {tokTypes} from '../operator/index.js';
2
+
3
+ const empty = [];
4
+
5
+ export default function keywordImport(Parser) {
6
+ return class extends Parser {
7
+ parseImport(node) {
8
+ this.next();
9
+
10
+ // import '...'
11
+ if (this.type === tokTypes.string) {
12
+ node.specifiers = empty;
13
+ node.source = this.parseExprAtom();
14
+ } else {
15
+ node.specifiers = this.parseImportSpecifiers();
16
+ this.expectContextual('from');
17
+
18
+ node.source = this.type === tokTypes.string ? this.parseLiteral(this.value) : this.unexpected();
19
+ }
20
+
21
+ const {raw, value} = node.source;
22
+
23
+ if (value.endsWith('.gs')) {
24
+ node.source.raw = raw.replace('.gs', '.js');
25
+ node.source.value = value.replace(/\.gs$/, '.js');
26
+ }
27
+
28
+ this.semicolon();
29
+
30
+ return this.finishNode(node, 'ImportDeclaration');
31
+ }
32
+ };
33
+ }
34
+
@@ -19,11 +19,13 @@ export default function keywordThrow(Parser) {
19
19
  const expression = this.parseExpression();
20
20
 
21
21
  assign(node, {
22
- operator: 'throw',
23
- argument: expression,
22
+ body: [{
23
+ type: 'ThrowStatement',
24
+ argument: expression,
25
+ }],
24
26
  });
25
27
 
26
- return super.finishNode(node, 'UnaryExpression');
28
+ return super.finishNode(node, 'BlockStatement');
27
29
  }
28
30
  };
29
31
  }
@@ -15,4 +15,5 @@ export const extendParser = (keywords) => {
15
15
  const createParse = (parser) => (a) => parser.parse(a, {
16
16
  ecmaVersion: 'latest',
17
17
  sourceType: 'module',
18
+ locations: true,
18
19
  });