fez-lisp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/src/enums.js ADDED
@@ -0,0 +1,79 @@
1
+ // AST enums
2
+ export const WORD = 'w'
3
+ export const APPLY = 'f'
4
+ export const VALUE = 'v'
5
+ export const TYPE = 't'
6
+ export const ATOM = 'a'
7
+ // tokeniser enums
8
+ export const PLACEHOLDER = '.'
9
+ // keywords aliases
10
+ export const KEYWORDS = {
11
+ BIT_TYPE: 'Bit',
12
+ STRING_TYPE: 'String',
13
+ NUMBER_TYPE: 'Number',
14
+ BOOLEAN_TYPE: 'Boolean',
15
+ FUNCTION_TYPE: 'Function',
16
+ ARRAY_TYPE: 'Array',
17
+ CHAR_CODE_TYPE: 'Char-Code',
18
+ CHAR_TYPE: 'Char',
19
+
20
+ CAST_TYPE: 'type',
21
+ CONCATENATION: 'concatenate',
22
+ ARRAY_OR_STRING_LENGTH: 'length',
23
+ IS_ARRAY: 'Array?',
24
+ IS_NUMBER: 'Number?',
25
+ IS_STRING: 'String?',
26
+ IS_FUNCTION: 'Function?',
27
+
28
+ ADDITION: '+',
29
+ SUBTRACTION: '-',
30
+ MULTIPLICATION: '*',
31
+ DIVISION: '/',
32
+ REMAINDER_OF_DIVISION: 'mod',
33
+
34
+ BITWISE_AND: '&',
35
+ BITWISE_OR: '|',
36
+ BITWISE_NOT: '~',
37
+ BITWISE_XOR: '^',
38
+ BITWISE_LEFT_SHIFT: '<<',
39
+ BITWISE_RIGHT_SHIFT: '>>',
40
+ BITWISE_UNSIGNED_RIGHT_SHIFT: '>>>',
41
+ ATOM: 'Atom',
42
+ IS_ATOM: 'Atom?',
43
+ FIRST_ARRAY: 'car',
44
+ REST_ARRAY: 'cdr',
45
+ GET_ARRAY: 'get',
46
+
47
+ BLOCK: 'do',
48
+
49
+ MERGE: 'merge',
50
+ ANONYMOUS_FUNCTION: 'lambda',
51
+
52
+ IF: 'if',
53
+ UNLESS: 'unless',
54
+ WHEN: 'when',
55
+ OTHERWISE: 'otherwise',
56
+ CONDITION: 'cond',
57
+
58
+ NOT: 'not',
59
+ EQUAL: '=',
60
+ LESS_THAN: '<',
61
+ GREATHER_THAN: '>',
62
+ GREATHER_THAN_OR_EQUAL: '>=',
63
+ LESS_THAN_OR_EQUAL: '<=',
64
+ AND: 'and',
65
+ OR: 'or',
66
+ CALL_FUNCTION: 'apply',
67
+ DEFINE_VARIABLE: 'let',
68
+
69
+ PIPE: 'go',
70
+ THROW_ERROR: 'throw',
71
+ TAIL_CALLS_OPTIMISED_RECURSIVE_FUNCTION: 'let*',
72
+ IMMUTABLE_FUNCTION: 'safety',
73
+ NOT_COMPILED_BLOCK: 'void',
74
+ LOG: 'log!',
75
+
76
+ TEST_CASE: 'case',
77
+ TEST_BED: 'assert',
78
+ SERIALISE: 'serialise',
79
+ }
@@ -0,0 +1,29 @@
1
+ import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './enums.js'
2
+ import { keywords } from './tokeniser.js'
3
+ import { stringifyArgs } from './utils.js'
4
+
5
+ export const evaluate = (expression, env) => {
6
+ const [first, ...rest] = Array.isArray(expression) ? expression : [expression]
7
+ if (first == undefined) return []
8
+ switch (first[TYPE]) {
9
+ case WORD: {
10
+ const word = env[first[VALUE]]
11
+ if (word == undefined)
12
+ throw new ReferenceError(`Undefined variable ${first[VALUE]}.`)
13
+ return word
14
+ }
15
+ case APPLY:
16
+ const apply = env[first[VALUE]]
17
+ if (typeof apply !== 'function')
18
+ throw new TypeError(`${first[VALUE]} is not a (function).`)
19
+ return apply(rest, env)
20
+ case ATOM:
21
+ return first[VALUE]
22
+ default:
23
+ throw new ReferenceError(
24
+ `Attempting to acces Undefined near ${stringifyArgs(expression)}`
25
+ )
26
+ }
27
+ }
28
+ export const run = (tree, env = {}) =>
29
+ keywords[KEYWORDS.BLOCK](tree, { ...keywords, ...env })
package/src/parser.js ADDED
@@ -0,0 +1,45 @@
1
+ import { APPLY, ATOM, TYPE, VALUE, WORD } from './enums.js'
2
+ import { escape } from './utils.js'
3
+
4
+ export const parse = (source) => {
5
+ const tree = []
6
+ let head = tree,
7
+ stack = [tree],
8
+ acc = ''
9
+ for (let i = 0; i < source.length; ++i) {
10
+ const cursor = source[i]
11
+ if (cursor === '"') {
12
+ acc += '"'
13
+ ++i
14
+ while (source[i] !== '"') {
15
+ if (source[i] === '\\') acc += escape(source[++i])
16
+ else acc += source[i]
17
+ ++i
18
+ }
19
+ }
20
+ if (cursor === '(') {
21
+ head.push([])
22
+ stack.push(head)
23
+ head = head.at(-1)
24
+ } else if (cursor === ')' || cursor === ' ') {
25
+ let token = acc
26
+ acc = ''
27
+ if (token) {
28
+ if (!head.length) head.push({ [TYPE]: APPLY, [VALUE]: token })
29
+ else if (token.match(/^"([^"]*)"/))
30
+ head.push({
31
+ [TYPE]: ATOM,
32
+ [VALUE]: token.substring(1, token.length - 1),
33
+ })
34
+ else if (token.match(/^-?[0-9]\d*(\.\d+)?$/))
35
+ head.push({
36
+ [TYPE]: ATOM,
37
+ [VALUE]: Number(token),
38
+ })
39
+ else head.push({ [TYPE]: WORD, [VALUE]: token })
40
+ }
41
+ if (cursor === ')') head = stack.pop()
42
+ } else acc += cursor
43
+ }
44
+ return tree
45
+ }