funcity 0.1.0 → 0.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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A functional language interpreter with text processing.
4
4
 
5
+ ![funcity](./images/funcity.120.png)
6
+
5
7
  [![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
6
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
9
 
@@ -43,7 +45,7 @@ The following code may further interest you:
43
45
 
44
46
  ```funcity
45
47
  {{
46
- set printWeather (fun w cond w.sunny 'nice' 'bad')
48
+ set printWeather (fun w (cond w.sunny 'nice' 'bad'))
47
49
  }}
48
50
  Today is {{printWeather weather}} weather.
49
51
  ```
@@ -75,6 +77,8 @@ In other words, funcity is an interpreter that brings the power of functional pr
75
77
 
76
78
  ## Installation (CLI)
77
79
 
80
+ TODO:
81
+
78
82
  ```bash
79
83
  npm install -D funcity-cli
80
84
  ```
package/dist/index.cjs CHANGED
@@ -1,114 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const emptyLocation = {
4
- line: 0,
5
- column: 0
6
- };
7
- const emptyRange = {
8
- start: emptyLocation,
9
- end: emptyLocation
10
- };
11
- const specialFunctionMarker = /* @__PURE__ */ Symbol("$$special$$");
12
- const makeSpecialFunction = (f) => {
13
- f[specialFunctionMarker] = true;
14
- return f;
15
- };
16
- const isSpecialFunction = (f) => {
17
- var _a;
18
- return (_a = f[specialFunctionMarker]) != null ? _a : false;
19
- };
20
- const isConditionalTrue = (v) => {
21
- if (v === void 0 || v === null) {
22
- return false;
23
- }
24
- switch (typeof v) {
25
- case "boolean":
26
- return v;
27
- case "number":
28
- case "bigint":
29
- return v !== 0;
30
- default:
31
- return true;
32
- }
33
- };
34
- const asIterable = (v) => {
35
- if (typeof v[Symbol.iterator] === "function") {
36
- return v;
37
- } else {
38
- return void 0;
39
- }
40
- };
41
- const combineVariables = (...variablesList) => {
42
- const variables = /* @__PURE__ */ new Map();
43
- const appendVariables = (vs) => vs.forEach((v, k) => variables.set(k, v));
44
- const appendRecord = (vs) => Object.keys(vs).forEach((k) => variables.set(k, vs[k]));
45
- variablesList.forEach((vs) => {
46
- if (vs["forEach"] !== void 0) {
47
- appendVariables(vs);
48
- } else {
49
- appendRecord(vs);
50
- }
51
- });
52
- return variables;
53
- };
54
- const fromError = (error) => {
55
- var _a;
56
- if (error.message) {
57
- return error.message;
58
- } else if (typeof error.toString === "function") {
59
- return (_a = error.toString()) != null ? _a : "unknown";
60
- } else {
61
- return "unknown";
62
- }
63
- };
64
- const widerRange = (...ranges) => {
65
- let start = emptyRange.start;
66
- let end = emptyRange.end;
67
- for (const range of ranges) {
68
- if (range.start.line >= 1 && range.start.column >= 1) {
69
- if (start.line === 0 || start.column === 0) {
70
- start = range.start;
71
- } else if (range.start.line < start.line) {
72
- start = range.start;
73
- } else if (range.start.line === start.line && range.start.column < start.column) {
74
- start = range.start;
75
- }
76
- if (end.line === 0 || end.column === 0) {
77
- end = range.end;
78
- } else if (range.end.line > end.line) {
79
- end = range.end;
80
- } else if (range.end.line === end.line && range.end.column > end.column) {
81
- end = range.end;
82
- }
83
- }
84
- }
85
- return { start, end };
86
- };
87
- const locationEquals = (lhs, rhs) => lhs.line === rhs.line && lhs.column === rhs.column;
88
- const getLocationString = (range) => locationEquals(range.start, range.end) ? `${range.start.line}:${range.start.column}` : `${range.start.line}:${range.start.column}:${range.end.line}:${range.end.column}`;
89
- const printErrorString = (path, error) => {
90
- switch (error.type) {
91
- case "warning":
92
- console.warn(
93
- `${path}:${getLocationString(error.range)}: warning: ${error.description}`
94
- );
95
- break;
96
- case "error":
97
- console.error(
98
- `${path}:${getLocationString(error.range)}: error: ${error.description}`
99
- );
100
- return true;
101
- }
102
- return false;
103
- };
104
- const outputErrors = (path, errors) => {
105
- let isError = false;
106
- for (const error of errors) {
107
- const result = printErrorString(path, error);
108
- isError || (isError = result);
109
- }
110
- return isError;
111
- };
112
3
  const tokenizeString = (context) => {
113
4
  const start = context.cursor.location("start");
114
5
  context.cursor.skip(1);
@@ -320,20 +211,20 @@ const tokenizeCodeBlock = (context) => {
320
211
  });
321
212
  return tokens;
322
213
  };
323
- const createTokenizerCursor = (text) => {
214
+ const createTokenizerCursor = (script) => {
324
215
  let currentIndex = 0;
325
216
  let rawLine = 0;
326
217
  let rawColumn = 0;
327
218
  let lastLine = 0;
328
219
  let lastColumn = 0;
329
- const eot = () => currentIndex >= text.length;
330
- const getChar = (index) => text[(index != null ? index : 0) + currentIndex];
220
+ const eot = () => currentIndex >= script.length;
221
+ const getChar = (index) => script[(index != null ? index : 0) + currentIndex];
331
222
  const skip = (length) => {
332
223
  let lastch = "\0";
333
224
  while (length > 0) {
334
225
  lastColumn = rawColumn;
335
226
  lastLine = rawLine;
336
- const ch = text[currentIndex];
227
+ const ch = script[currentIndex];
337
228
  if (ch === "\r") {
338
229
  rawColumn = 0;
339
230
  } else if (ch === "\n" && lastch !== "\r") {
@@ -358,7 +249,7 @@ const createTokenizerCursor = (text) => {
358
249
  };
359
250
  const skipUntil = (word) => {
360
251
  while (!eot()) {
361
- const index = text.indexOf(word, currentIndex);
252
+ const index = script.indexOf(word, currentIndex);
362
253
  if (index === currentIndex) {
363
254
  skip(word.length);
364
255
  return true;
@@ -368,31 +259,31 @@ const createTokenizerCursor = (text) => {
368
259
  return false;
369
260
  };
370
261
  const assert = (word) => {
371
- if (text.length - currentIndex < word.length) {
262
+ if (script.length - currentIndex < word.length) {
372
263
  return false;
373
264
  }
374
- if (text.substring(currentIndex, currentIndex + word.length) === word) {
265
+ if (script.substring(currentIndex, currentIndex + word.length) === word) {
375
266
  return true;
376
267
  }
377
268
  return false;
378
269
  };
379
270
  const getRangeAndSkip = (length) => {
380
- const result = text.substring(currentIndex, currentIndex + length);
271
+ const result = script.substring(currentIndex, currentIndex + length);
381
272
  skip(result.length);
382
273
  return result;
383
274
  };
384
275
  const getUntil = (word) => {
385
- if (currentIndex >= text.length) {
276
+ if (currentIndex >= script.length) {
386
277
  return void 0;
387
278
  }
388
- const index = text.indexOf(word, currentIndex);
279
+ const index = script.indexOf(word, currentIndex);
389
280
  if (index >= 0) {
390
- const result = text.substring(currentIndex, index);
281
+ const result = script.substring(currentIndex, index);
391
282
  skip(index - currentIndex);
392
283
  return result;
393
284
  } else {
394
- const result = text.substring(currentIndex, text.length);
395
- skip(text.length - currentIndex);
285
+ const result = script.substring(currentIndex, script.length);
286
+ skip(script.length - currentIndex);
396
287
  return result;
397
288
  }
398
289
  };
@@ -459,6 +350,115 @@ const runTokenizer = (script, errors) => {
459
350
  }
460
351
  return tokens;
461
352
  };
353
+ const emptyLocation = {
354
+ line: 0,
355
+ column: 0
356
+ };
357
+ const emptyRange = {
358
+ start: emptyLocation,
359
+ end: emptyLocation
360
+ };
361
+ const specialFunctionMarker = /* @__PURE__ */ Symbol("$$special$$");
362
+ const makeSpecialFunction = (f) => {
363
+ f[specialFunctionMarker] = true;
364
+ return f;
365
+ };
366
+ const isSpecialFunction = (f) => {
367
+ var _a;
368
+ return (_a = f[specialFunctionMarker]) != null ? _a : false;
369
+ };
370
+ const isConditionalTrue = (v) => {
371
+ if (v === void 0 || v === null) {
372
+ return false;
373
+ }
374
+ switch (typeof v) {
375
+ case "boolean":
376
+ return v;
377
+ case "number":
378
+ case "bigint":
379
+ return v !== 0;
380
+ default:
381
+ return true;
382
+ }
383
+ };
384
+ const asIterable = (v) => {
385
+ if (typeof v[Symbol.iterator] === "function") {
386
+ return v;
387
+ } else {
388
+ return void 0;
389
+ }
390
+ };
391
+ const combineVariables = (...variablesList) => {
392
+ const variables = /* @__PURE__ */ new Map();
393
+ const appendVariables = (vs) => vs.forEach((v, k) => variables.set(k, v));
394
+ const appendRecord = (vs) => Object.keys(vs).forEach((k) => variables.set(k, vs[k]));
395
+ variablesList.forEach((vs) => {
396
+ if (vs["forEach"] !== void 0) {
397
+ appendVariables(vs);
398
+ } else {
399
+ appendRecord(vs);
400
+ }
401
+ });
402
+ return variables;
403
+ };
404
+ const fromError = (error) => {
405
+ var _a;
406
+ if (error.message) {
407
+ return error.message;
408
+ } else if (typeof error.toString === "function") {
409
+ return (_a = error.toString()) != null ? _a : "unknown";
410
+ } else {
411
+ return "unknown";
412
+ }
413
+ };
414
+ const widerRange = (...ranges) => {
415
+ let start = emptyRange.start;
416
+ let end = emptyRange.end;
417
+ for (const range of ranges) {
418
+ if (range.start.line >= 1 && range.start.column >= 1) {
419
+ if (start.line === 0 || start.column === 0) {
420
+ start = range.start;
421
+ } else if (range.start.line < start.line) {
422
+ start = range.start;
423
+ } else if (range.start.line === start.line && range.start.column < start.column) {
424
+ start = range.start;
425
+ }
426
+ if (end.line === 0 || end.column === 0) {
427
+ end = range.end;
428
+ } else if (range.end.line > end.line) {
429
+ end = range.end;
430
+ } else if (range.end.line === end.line && range.end.column > end.column) {
431
+ end = range.end;
432
+ }
433
+ }
434
+ }
435
+ return { start, end };
436
+ };
437
+ const locationEquals = (lhs, rhs) => lhs.line === rhs.line && lhs.column === rhs.column;
438
+ const getLocationString = (range) => locationEquals(range.start, range.end) ? `${range.start.line}:${range.start.column}` : `${range.start.line}:${range.start.column}:${range.end.line}:${range.end.column}`;
439
+ const printErrorString = (path, error) => {
440
+ switch (error.type) {
441
+ case "warning":
442
+ console.warn(
443
+ `${path}:${getLocationString(error.range)}: warning: ${error.description}`
444
+ );
445
+ break;
446
+ case "error":
447
+ console.error(
448
+ `${path}:${getLocationString(error.range)}: error: ${error.description}`
449
+ );
450
+ return true;
451
+ }
452
+ return false;
453
+ };
454
+ const outputErrors = (path, errors) => {
455
+ let isError = false;
456
+ for (const error of errors) {
457
+ const result = printErrorString(path, error);
458
+ isError || (isError = result);
459
+ }
460
+ return isError;
461
+ };
462
462
  const parseNumber = (cursor, _errors) => {
463
463
  const token = cursor.takeToken();
464
464
  return {
@@ -564,7 +564,7 @@ const parsePartialExpression = (cursor, errors) => {
564
564
  return node;
565
565
  }
566
566
  case "open": {
567
- cursor.takeToken();
567
+ cursor.skipToken();
568
568
  switch (token.symbol) {
569
569
  // Parenthesis surrounding expression list `( ... )` (Scope)
570
570
  case "(": {
@@ -588,7 +588,7 @@ const parsePartialExpression = (cursor, errors) => {
588
588
  )
589
589
  });
590
590
  } else {
591
- cursor.takeToken();
591
+ cursor.skipToken();
592
592
  range = widerRange(
593
593
  token.range,
594
594
  ...innerNodes.map((node) => node.range),
@@ -628,7 +628,7 @@ const parsePartialExpression = (cursor, errors) => {
628
628
  range
629
629
  });
630
630
  } else {
631
- cursor.takeToken();
631
+ cursor.skipToken();
632
632
  range = widerRange(
633
633
  token.range,
634
634
  ...itemNodes.map((node) => node.range),
@@ -721,7 +721,7 @@ const parseMultipleApplicationExpressions = (cursor, errors) => {
721
721
  }
722
722
  switch (token.kind) {
723
723
  case "eol": {
724
- cursor.takeToken();
724
+ cursor.skipToken();
725
725
  const expr2 = finalizeApplicationException(partialNodes, errors);
726
726
  if (expr2) {
727
727
  expressionList.push(expr2);
@@ -766,7 +766,7 @@ const drainEndOfLineAndPeek = (cursor) => {
766
766
  let token = cursor.peekToken();
767
767
  while (token) {
768
768
  if (token.kind === "eol") {
769
- cursor.takeToken();
769
+ cursor.skipToken();
770
770
  } else {
771
771
  break;
772
772
  }
@@ -791,7 +791,7 @@ const parseExpression = (cursor, errors) => {
791
791
  break;
792
792
  }
793
793
  if (token.kind === "eol") {
794
- cursor.takeToken();
794
+ cursor.skipToken();
795
795
  token = cursor.peekToken();
796
796
  break;
797
797
  }
@@ -889,7 +889,7 @@ const parseStatementArguments = (cursor, errors) => {
889
889
  break;
890
890
  }
891
891
  if (token.kind === "eol") {
892
- cursor.takeToken();
892
+ cursor.skipToken();
893
893
  break;
894
894
  }
895
895
  const partialNode = parsePartialExpression(cursor, errors);
@@ -945,7 +945,7 @@ const parseBlock = (cursor, errors) => {
945
945
  }
946
946
  switch (token.kind) {
947
947
  case "text": {
948
- cursor.takeToken();
948
+ cursor.skipToken();
949
949
  if (isInExpressionBlock) {
950
950
  errors.push({
951
951
  type: "error",
@@ -963,7 +963,7 @@ const parseBlock = (cursor, errors) => {
963
963
  }
964
964
  case "open": {
965
965
  if (token.symbol === "{{") {
966
- cursor.takeToken();
966
+ cursor.skipToken();
967
967
  if (isInExpressionBlock) {
968
968
  errors.push({
969
969
  type: "error",
@@ -981,7 +981,7 @@ const parseBlock = (cursor, errors) => {
981
981
  break;
982
982
  }
983
983
  case "close": {
984
- cursor.takeToken();
984
+ cursor.skipToken();
985
985
  if (!isInExpressionBlock) {
986
986
  errors.push({
987
987
  type: "error",
@@ -1005,7 +1005,7 @@ const parseBlock = (cursor, errors) => {
1005
1005
  }
1006
1006
  switch (token.name) {
1007
1007
  case "if": {
1008
- cursor.takeToken();
1008
+ cursor.skipToken();
1009
1009
  const args = parseStatementArguments(cursor, errors);
1010
1010
  if (args.length !== 1) {
1011
1011
  errors.push({
@@ -1030,7 +1030,7 @@ const parseBlock = (cursor, errors) => {
1030
1030
  break;
1031
1031
  }
1032
1032
  case "else": {
1033
- cursor.takeToken();
1033
+ cursor.skipToken();
1034
1034
  const args = parseStatementArguments(cursor, errors);
1035
1035
  if (args.length !== 0) {
1036
1036
  errors.push({
@@ -1072,7 +1072,7 @@ const parseBlock = (cursor, errors) => {
1072
1072
  break;
1073
1073
  }
1074
1074
  case "while": {
1075
- cursor.takeToken();
1075
+ cursor.skipToken();
1076
1076
  const args = parseStatementArguments(cursor, errors);
1077
1077
  if (args.length !== 1) {
1078
1078
  errors.push({
@@ -1095,7 +1095,7 @@ const parseBlock = (cursor, errors) => {
1095
1095
  break;
1096
1096
  }
1097
1097
  case "for": {
1098
- cursor.takeToken();
1098
+ cursor.skipToken();
1099
1099
  const args = parseStatementArguments(cursor, errors);
1100
1100
  if (args.length !== 2) {
1101
1101
  errors.push({
@@ -1128,7 +1128,7 @@ const parseBlock = (cursor, errors) => {
1128
1128
  break;
1129
1129
  }
1130
1130
  case "end": {
1131
- cursor.takeToken();
1131
+ cursor.skipToken();
1132
1132
  const args = parseStatementArguments(cursor, errors);
1133
1133
  if (args.length !== 0) {
1134
1134
  errors.push({
@@ -1200,7 +1200,7 @@ const parseBlock = (cursor, errors) => {
1200
1200
  break;
1201
1201
  }
1202
1202
  case "set": {
1203
- cursor.takeToken();
1203
+ cursor.skipToken();
1204
1204
  const args = parseStatementArguments(cursor, errors);
1205
1205
  if (args.length !== 2) {
1206
1206
  errors.push({
@@ -1281,9 +1281,16 @@ const createParserCursor = (tokens) => {
1281
1281
  index++;
1282
1282
  return token;
1283
1283
  };
1284
+ const skipToken = () => {
1285
+ if (index >= tokens.length) {
1286
+ return;
1287
+ }
1288
+ index++;
1289
+ };
1284
1290
  return {
1285
1291
  peekToken,
1286
- takeToken
1292
+ takeToken,
1293
+ skipToken
1287
1294
  };
1288
1295
  };
1289
1296
  const runParser = (tokens, errors) => {
@@ -1575,28 +1582,18 @@ const createReducerContext = (variables, errors) => {
1575
1582
  };
1576
1583
  return context;
1577
1584
  };
1578
- const unwrap = (results) => {
1579
- return results.flatMap((result) => {
1580
- if (result === void 0) {
1581
- return [];
1582
- } else {
1583
- return [result];
1584
- }
1585
- });
1586
- };
1587
1585
  async function runReducer(nodes, variables, errors) {
1588
1586
  const context = createReducerContext(variables, errors);
1589
- if (Array.isArray(nodes)) {
1590
- const resultList = [];
1591
- for (const node of nodes) {
1592
- const results = await reduceNode(context, node);
1593
- resultList.push(...results);
1587
+ const resultList = [];
1588
+ for (const node of nodes) {
1589
+ const results = await reduceNode(context, node);
1590
+ for (const result of results) {
1591
+ if (result !== void 0) {
1592
+ resultList.push(result);
1593
+ }
1594
1594
  }
1595
- return unwrap(resultList);
1596
- } else {
1597
- const results = await reduceNode(context, nodes);
1598
- return unwrap(results);
1599
1595
  }
1596
+ return resultList;
1600
1597
  }
1601
1598
  const _cond = makeSpecialFunction(async function(arg0, arg1, arg2) {
1602
1599
  const cond = await this.reduce(arg0);
@@ -1939,7 +1936,7 @@ const _bind = async (arg0, ...args) => {
1939
1936
  const predicate = arg0;
1940
1937
  return predicate.bind(void 0, ...args);
1941
1938
  };
1942
- const standardVariables = {
1939
+ const standardVariables = Object.freeze({
1943
1940
  undefined: void 0,
1944
1941
  null: null,
1945
1942
  true: true,
@@ -1978,13 +1975,21 @@ const standardVariables = {
1978
1975
  replace: _replace,
1979
1976
  regex: _regex,
1980
1977
  bind: _bind
1981
- };
1978
+ });
1982
1979
  const buildCandidateVariables = (...variablesList) => {
1983
1980
  return combineVariables(standardVariables, ...variablesList);
1984
1981
  };
1982
+ const runScriptOnce = async (script, variables, errors = []) => {
1983
+ const blocks = runTokenizer(script, errors);
1984
+ const nodes = runParser(blocks, errors);
1985
+ const results = await runReducer(nodes, variables, errors);
1986
+ const text = results.join("");
1987
+ return text;
1988
+ };
1985
1989
  exports.asIterable = asIterable;
1986
1990
  exports.buildCandidateVariables = buildCandidateVariables;
1987
1991
  exports.combineVariables = combineVariables;
1992
+ exports.createParserCursor = createParserCursor;
1988
1993
  exports.createReducerContext = createReducerContext;
1989
1994
  exports.emptyLocation = emptyLocation;
1990
1995
  exports.emptyRange = emptyRange;
@@ -1999,6 +2004,7 @@ exports.reduceExpressionNode = reduceExpressionNode;
1999
2004
  exports.reduceNode = reduceNode;
2000
2005
  exports.runParser = runParser;
2001
2006
  exports.runReducer = runReducer;
2007
+ exports.runScriptOnce = runScriptOnce;
2002
2008
  exports.runTokenizer = runTokenizer;
2003
2009
  exports.standardVariables = standardVariables;
2004
2010
  exports.widerRange = widerRange;