๐Ÿ“ฆ MaxwellKnight / interpreter-ts

๐Ÿ“„ environment.ts ยท 132 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132import { NULL, StringObj } from "./interfaces/object";
import { ArrayObj, ErrorObj, IntegerObj, Obj, ObjectType } from "./interfaces/object";

class Environment {
	#env: Map<string, Obj>;
	#parent: Environment | null;

	constructor(parent: Environment | null = null) {
		this.#env = new Map<string, Obj>;
		this.#parent = parent;
	}

	public get(key: string): Obj | undefined {
		const obj = this.#env.get(key);
		if (!obj && this.#parent)
			return this.#parent.get(key);
		return obj;
	}

	public getEnv(): Map<string, Obj> {
		return this.#env;
	}

	public getParent(): Environment | null {
		return this.#parent;
	}

	public set(key: string, obj: Obj): Obj {
		const env = Environment.retrieve(key, this);
		env.getEnv().set(key, obj);
		return obj;
	}

	public assign(key: string, obj: Obj): Obj {
		this.#env.set(key, obj);
		return obj;
	}

	static retrieve(key: string, env: Environment): Environment {
		let current: Environment | null = env;
		while (current !== null) {
			if (current.getEnv().has(key)) return current;
			current = current.getParent();
		}
		return env;
	}
}

const builtin_len = (...args: Obj[]): Obj => {
	if (args.length != 1) {
		return ErrorObj.create("Invalid argument count `len` takes 1, got:", [String(args.length)]);
	}
	switch (args[0].type) {
		case ObjectType.STRING_OBJ:
			return new IntegerObj(args[0].value.toString().length);
		case ObjectType.ARRAY_OBJ:
			return new IntegerObj((args[0] as ArrayObj).elements.length);
		default:
			return ErrorObj.create("Unsupported argument to `len`, got:", [args[0].type])
	}
};

const builtin_first = (...args: Obj[]): Obj => {
	if (args.length != 1) {
		return ErrorObj.create("Invalid argument count `first` takes 1, got:", [String(args.length)]);
	}
	if (!(args[0] instanceof ArrayObj))
		return ErrorObj.create("`first` accepts only arrays as argument, got:", [args[0].type])
	if (args[0].elements.length > 0)
		return args[0].elements[0];

	return NULL;
};

const builtin_last = (...args: Obj[]): Obj => {
	if (args.length != 1) {
		return ErrorObj.create("Invalid argument count `last` takes 1, got:", [String(args.length)]);
	}
	if (!(args[0] instanceof ArrayObj))
		return ErrorObj.create("`last` accepts only arrays as argument, got:", [args[0].type])
	if (args[0].elements.length > 0)
		return args[0].elements[args[0].elements.length - 1];

	return NULL;
};

const builtin_rest = (...args: Obj[]): Obj => {
	if (args.length != 1) {
		return ErrorObj.create("Invalid argument count `rest` takes 1, got:", [String(args.length)]);
	}
	if (!(args[0] instanceof ArrayObj))
		return ErrorObj.create("`rest` accepts only arrays as argument, got:", [args[0].type])
	if (args[0].elements.length > 0)
		return new ArrayObj(args[0].elements.slice(1));

	return NULL;
};

const builtin_yo_mama = (...args: Obj[]): Obj => {
	if (args.length !== 0) {
		return ErrorObj.create("Invalid argument count: `yo_mama` takes 0 arguments, got:", [String(args.length)]);
	}

	const jokes = [
		"Yo mama so fat, when she fell I didn't laugh, but the sidewalk cracked up.",
		"Yo mama so fat, when she skips a meal, the stock market drops.",
		"Yo mama so fat, it took me two buses and a train to get to her good side.",
		"Yo mama so fat, when she goes camping, the bears hide their food.",
		"Yo mama so fat, if she buys a fur coat, a whole species goes extinct.",
	];

	const randomIndex = Math.floor(Math.random() * jokes.length);
	return new StringObj(jokes[randomIndex]);
};


const builtin_print = (...args: Obj[]): Obj => {
	const strs = args.map(arg => arg.type === ObjectType.STRING_OBJ ? arg.stringify().slice(1, - 1) : arg.stringify());
	console.log(strs.join(" "));
	return NULL;
};

export {
	Environment,
	builtin_first,
	builtin_last,
	builtin_len,
	builtin_print,
	builtin_rest,
	builtin_yo_mama
}