fez-lisp 1.1.23 → 1.1.25

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
@@ -246,5 +246,6 @@ console.log(fez(tree(`(+ (|> 1 (+ 2) (* 3) (- 1)) (- (* (+ 1 2) 3) 1))`)))
246
246
  (/) (+) (*) (-) (=) (<) (>) (>=) (<=) (&) (~) (|) (^) (<<) (>>) (>>>)
247
247
  (|>) (mod) (let) (if) (unless) (not) (and) (or) (cond) (atom?) (lambda)
248
248
  (car) (cdr) (cons) (length) (do) (array) (array:set!) (array:get)
249
- (apply) (case) (assert) (log!) (log-string!) (clear!) (void) (fez-manual)
249
+ (apply) (case) (assert) (log!) (log-string!) (log-char!) (clear!)
250
+ (void) (fez-manual)
250
251
  ```
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "fez-lisp",
3
3
  "description": "Lisp interpreted & compiled to JavaScript",
4
4
  "author": "AT290690",
5
- "version": "1.1.23",
5
+ "version": "1.1.25",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/compiler.js CHANGED
@@ -94,6 +94,7 @@ const Helpers = {
94
94
  return args.at(-1) ? 1 : 0
95
95
  }`,
96
96
  logEffect: `logEffect=(msg)=>{console.log(msg);return msg}`,
97
+ logCharEffect: `logCharEffect=(msg)=>{console.log(String.fromCharCode(msg));return msg}`,
97
98
  logStringEffect: `logStringEffect=(msg)=>{console.log(msg.map(x=>String.fromCharCode(x)).join(''));return msg}`,
98
99
  clearEffect: `clearEffect=()=>{console.clear();return 0}`,
99
100
  array_cons: `array_cons=(A, ...B)=> B.reduce((a, b) => a.concat(b), A)`,
@@ -223,14 +224,18 @@ const compile = (tree, Drill) => {
223
224
  const vars = InnerDrills.Variables.size
224
225
  ? `var ${[...InnerDrills.Variables].join(',')};`
225
226
  : ''
226
- return `((${parseArgs(
227
+ const args = parseArgs(
227
228
  functionArgs.map((node, index) =>
228
229
  node[VALUE] === PLACEHOLDER
229
230
  ? leaf(node[TYPE], `_${index}`)
230
231
  : leaf(node[TYPE], node[VALUE])
231
232
  ),
232
233
  InnerDrills
233
- )})=>{${vars}return ${evaluatedBody.toString().trimStart()}});`
234
+ )
235
+ // const $ = [${args}];
236
+ return `((${args})=>{${vars}return ${evaluatedBody
237
+ .toString()
238
+ .trimStart()}});`
234
239
  }
235
240
  case KEYWORDS.AND:
236
241
  return `((${parseArgs(Arguments, Drill, '&&')}) ? 1 : 0);`
@@ -317,9 +317,11 @@ const keywords = {
317
317
  } ${stringifyArgs(args)})`
318
318
  )
319
319
  const localEnv = Object.create(env)
320
+ // localEnv[KEYWORDS.BLOCK] = block[KEYWORDS.BLOCK]
320
321
  for (let i = 0; i < props.length; ++i) {
322
+ const value = evaluate(props[i], scope)
321
323
  Object.defineProperty(localEnv, params[i][VALUE], {
322
- value: evaluate(props[i], scope),
324
+ value,
323
325
  writable: true
324
326
  })
325
327
  }
@@ -857,6 +859,25 @@ const keywords = {
857
859
  console.log(expression.map((x) => String.fromCharCode(x)).join(''))
858
860
  return expression
859
861
  },
862
+ [KEYWORDS.LOG_CHAR]: (args, env) => {
863
+ if (args.length !== 1)
864
+ throw new RangeError(
865
+ `Invalid number of arguments to (${
866
+ KEYWORDS.LOG_CHAR
867
+ }) (= 1 required) (${KEYWORDS.LOG_CHAR} ${stringifyArgs(args)})`
868
+ )
869
+ const expression = evaluate(args[0], env)
870
+ if (typeof expression !== 'number')
871
+ throw new TypeError(
872
+ `Argument of (${KEYWORDS.LOG_CHAR}) must be a (${
873
+ KEYWORDS.NUMBER_TYPE
874
+ }) but got (${expression}) (${KEYWORDS.LOG_CHAR} ${stringifyArgs(
875
+ args
876
+ )})`
877
+ )
878
+ console.log(String.fromCharCode(expression))
879
+ return expression
880
+ },
860
881
  [KEYWORDS.CLEAR_CONSOLE]: (args) => {
861
882
  if (args.length)
862
883
  throw new RangeError(
@@ -902,7 +923,16 @@ keywords[KEYWORDS.DOC] = (args, env) => {
902
923
  .map((x) => `(${x.join(' ')})`)
903
924
  }
904
925
  }
926
+
905
927
  export const deSuggar = (ast) => {
928
+ if (ast.length === 0)
929
+ throw new SyntaxError(
930
+ `Top level ${KEYWORDS.NUMBER_TYPE} need to be wrapped in a (${KEYWORDS.IDENTITY})`
931
+ )
932
+ // for (const node of ast)
933
+ // if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
934
+ // throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
935
+ let prev = undefined
906
936
  const evaluate = (exp) => {
907
937
  const [first, ...rest] = isLeaf(exp) ? [exp] : exp
908
938
  if (first != undefined) {
@@ -914,6 +944,33 @@ export const deSuggar = (ast) => {
914
944
  case APPLY:
915
945
  {
916
946
  switch (first[VALUE]) {
947
+ case KEYWORDS.BLOCK:
948
+ {
949
+ if (
950
+ prev == undefined ||
951
+ (prev &&
952
+ prev[TYPE] === APPLY &&
953
+ prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
954
+ )
955
+ throw new SyntaxError(
956
+ `Can only use (${KEYWORDS.BLOCK}) as a body of a (${KEYWORDS.ANONYMOUS_FUNCTION})`
957
+ )
958
+ }
959
+ break
960
+ // case KEYWORDS.DEFINE_VARIABLE:
961
+ // {
962
+ // if (
963
+ // rest[1] &&
964
+ // rest[1][0] &&
965
+ // rest[1][0][TYPE] === APPLY &&
966
+ // rest[1][0][VALUE] === KEYWORDS.BLOCK
967
+ // ) {
968
+ // throw new SyntaxError(
969
+ // `Can't use (${KEYWORDS.BLOCK}) in (${KEYWORDS.DEFINE_VARIABLE})`
970
+ // )
971
+ // }
972
+ // }
973
+ break
917
974
  case KEYWORDS.PIPE:
918
975
  {
919
976
  if (rest.length < 1)
@@ -957,6 +1014,7 @@ export const deSuggar = (ast) => {
957
1014
  }
958
1015
  break
959
1016
  }
1017
+ prev = first
960
1018
  }
961
1019
  break
962
1020
  default:
package/src/keywords.js CHANGED
@@ -5,9 +5,9 @@ export const WORD = 1
5
5
  export const ATOM = 2
6
6
  export const PLACEHOLDER = '.'
7
7
  export const KEYWORDS = {
8
- RECURSION: 'rec',
9
8
  NUMBER_TYPE: 'number',
10
9
  ARRAY_TYPE: 'array',
10
+ IDENTITY: 'identity',
11
11
  ARRAY_LENGTH: 'length',
12
12
  IS_ATOM: 'atom?',
13
13
  ADDITION: '+',
@@ -50,6 +50,7 @@ export const KEYWORDS = {
50
50
  TEST_BED: 'assert',
51
51
  LOG: 'log!',
52
52
  LOG_STRING: 'log-string!',
53
+ LOG_CHAR: 'log-char!',
53
54
  CLEAR_CONSOLE: 'clear!',
54
55
  DOC: 'fez-manual'
55
56
  }
package/src/utils.js CHANGED
@@ -9,6 +9,7 @@ export const logError = (error) =>
9
9
  export const logSuccess = (output) => console.log(output, '\x1b[0m')
10
10
  export const replaceStrings = (source) => {
11
11
  const quotes = source.match(/"(.*?)"/g)
12
+ // TODO handle escaping
12
13
  if (quotes)
13
14
  for (const q of quotes)
14
15
  source = source.replaceAll(
@@ -20,7 +21,10 @@ export const replaceStrings = (source) => {
20
21
  )
21
22
  return source
22
23
  }
23
- export const replaceQuotes = (source) => source.replaceAll(/\'\(/g, '(array ')
24
+ export const replaceQuotes = (source) =>
25
+ source.replaceAll(/\'\(/g, '(array ').replaceAll(/\(\)/g, '(array)')
26
+ // export const replaceEmptyArrays = (source) =>
27
+ // source
24
28
  export const removeNoCode = (source) =>
25
29
  source
26
30
  .replace(/;.+/g, '')
@@ -233,10 +237,6 @@ export const fez = (source, options = {}) => {
233
237
  if (!options.mutation) code = removeMutation(code)
234
238
  if (!code.length && options.throw) throw new Error('Nothing to parse!')
235
239
  const parsed = deSuggar(LISP.parse(code))
236
- if (parsed.length === 0 && options.throw)
237
- throw new Error(
238
- 'Top level expressions need to be wrapped in a (do) block'
239
- )
240
240
  const ast = [...treeShake(parsed, std), ...parsed]
241
241
  // if (options.check) typeCheck(ast)
242
242
  if (options.compile) {