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 +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) {
|