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 +2 -1
- package/package.json +1 -1
- package/src/compiler.js +7 -2
- package/src/interpreter.js +59 -1
- package/src/keywords.js +2 -1
- package/src/utils.js +5 -5
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!) (
|
249
|
+
(apply) (case) (assert) (log!) (log-string!) (log-char!) (clear!)
|
250
|
+
(void) (fez-manual)
|
250
251
|
```
|
package/package.json
CHANGED
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
|
-
|
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
|
-
)
|
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);`
|
package/src/interpreter.js
CHANGED
@@ -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
|
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) =>
|
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) {
|