fez-lisp 1.1.23 → 1.1.25

Sign up to get free protection for your applications and to get access to all the features.
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) {