SLet (Old 2)/Implementation

Save as SLet.py
There is no bug afaik.
# SLet interpreter attempt
# by islptng
# https://esolangs.org/wiki/SLet
from math import floor
from random import randint
class Pair:
	former = None
	latter = None
	def __init__(self, former, latter):
		self.former = former
		self.latter = latter
class Set:
	content = []
	def __init__(self):
		self.content = []
	def exist(self, value):
		return value in self.content
	def append(self, value):
		if type(value) == Pair:
			if value.latter == None: return
		if not self.exist(value):
			self.content.append(value)
		def to_num(x):
			if type(x) == int:
				return x
			elif type(x) == float:
				return x
			elif type(x) == Pair:
				return x.former
			elif type(x) == Set:
				return len(x.content)
			elif type(x) == str or type(x) == list:
				return len(x)
		def recursivenum(x):
			while type(x) != int and type(x) != float:
				x = to_num(x)
			return x
		self.content.sort(key=lambda x: recursivenum(x))
def union(set1, set2):
	result = Set()
	try:
		for x in set1.content:
			result.append(x)
	except: result.append(set1)
	try:
		for x in set2.content:
			result.append(x)
	except: result.append(set2)
	return result
def intersect(set1, set2):
	result = Set()
	for x in set1.content:
		if set2.exist(x):
			result.append(x)
	return result
def isfalse(x):
	if type(x) == bool:
		return not x
	elif type(x) == int:
		return x == 0
	elif type(x) == float:
		return x == 0.0
	elif type(x) == Pair:
		return isfalse(x.former) and isfalse(x.latter)
	elif type(x) == Set:
		return len(x.content) == 0
def filter1(set1, condition):
	result = Set()
	for x in set1.content:
		if not isfalse(condition(x)):
			result.append(x)
	return result
def pack(value):
	result = Set()
	result.append(value)
	return result
def unpack(set1):
	return set1.content[0]
def isnumber(x):
	return type(x) == int or type(x) == float
def packifnum(x):
	if isnumber(x):
		return pack(x)
	else:
		return x
def plus(level, num1, num2):
	if isnumber(num1) and isnumber(num2) and isnumber(level):
		if level == 0:
			return num1 + num2
		elif level == 1:
			return num1 * num2
		elif level == 2:
			return num1 ** num2
		result = num1
		for i in range(num2-1):
			result = plus(level-1, result, result)
		return result
	result = Set()
	for leveli in packifnum(level).content:
		for num1i in packifnum(num1).content:
			for num2i in packifnum(num2).content:
				result.append(plus(leveli, num1i, num2i))
	return result
def negate(num):
	if isnumber(num):
		return -num
	result = Set()
	for numi in packifnum(num).content:
		result.append(-numi)
	return result
def divide(num):
	if isnumber(num):
		return 1/num
	result = Set()
	for numi in packifnum(num).content:
		result.append(1/numi)
	return result
def issubset(set1, set2):
	for x in packifnum(set1).content:
		if not set2.exist(x):
			return False
	return True
	
def analyze(srcstr):
	class Tree:
		content = [None]
		depth = 0
		def dig(self):
			res = self.content
			for i in range(self.depth): res = res[-1]
			res.pop()
			res.append([None])
			self.depth += 1
		def push(self,val):
			res = self.content
			for i in range(self.depth): res = res[-1]
			res.pop()
			res.append(val)
			res.append(None)
		def pop(self):
			res = self.content
			for i in range(self.depth-1): res = res[-1]
			res.append(None)
			res = res[-2]
			res.pop()
			self.depth -= 1
		def finish(self):
			res = self.content
			for i in range(self.depth): res = res[-1]
			res.pop()
			return self.content
	# the 1st step we get rid of quotes and comments
	tcsrc = ""
	incomment = False
	inescape = False
	intext = False
	currentxt = ""
	for i in srcstr:
		if intext and not incomment:
			if inescape:
				inescape = False
				if i == 'n': currentxt += chr(10)
				elif i == 't': currentxt += '\t'
				else: currentxt += i
			else:
				if i == '\\':
					inescape = True
				elif i == '!':
					intext = False
					tcsrc += '['
					for j in range(len(currentxt)-1):
						tcsrc += str(ord(currentxt[j])) + ','
					tcsrc += str(ord(currentxt[len(currentxt)-1]))+'!'
				else: currentxt += i
			continue
		if i == '(':
			incomment = True
			continue
		if i == ')':
			incomment = False
			continue
		if i == '"' and not incomment:
			intext = True
			currentxt = ""
			continue
		if incomment: continue
		tcsrc += i
	srcstr = tcsrc.replace("\t","").replace("\n","").replace(" ","")
	srcstr = srcstr.replace("{","").replace("}","") # Don't forget we have no bracklets!
	# the 2nd step we tokenize it
	wdict = {'#':2,'@':1,'*':3,'~':2,';':0,':':1,"'":0,'`':1,
			 '|':-1,'&':-1,'^':1,'_':1,'=':1,'?':3,'+':3,'-':1,'/':1,'%':2,'<':1,'>':1,'$':2,
			 '[':-1,']':3,'\\':1,'!':0}
	currentxt = ""
	predlist = []
	for i in srcstr:
		if i in wdict:
			if currentxt != "":
				predlist.append(currentxt)
				currentxt = ""
			predlist.append(i)
		elif i == ',':
			if currentxt != "":
				predlist.append(currentxt)
				currentxt = ""
		else:
			currentxt += i
	# the 3rd step we analyze it
	res = Tree()
	tablist = [-1]
	for i in predlist:
		if i == '!':
			tablist.pop()
			res.pop()
		while tablist[-1] == 0:
			tablist.pop()
			res.pop()
		if i != '!':
			tablist[-1] -= 1
			if i in wdict:
				res.dig()
				tablist.append(wdict[i])
			res.push(i)
	return res.finish()[0]
def printable(val):
	if type(val) == Set:
		res = "{"
		for i in val.content:
			res += printable(i) + ", "
		if res == "{":
			return "Ø"
		return res[:-2]+"}"
	if type(val) == Pair:
		return "(%s %s)" % (printable(val.former), printable(val.latter))
	return str(val)
class SLetProcedure:
	variables = {}
	def __init__(self,getchar,getnum,putchar,putnum):
		self.getchar = getchar
		self.getnum = getnum
		self.putchar = putchar
		self.putnum = putnum
	def execute(self, instruction):
		if type(instruction) == str:
			try:
				return int(instruction)
			except:
				try:
					return float(instruction)
				except:
					if instruction in self.variables:
						return self.variables[instruction]
					else:
						raise NameError("Undefined variable: "+instruction)
		elif type(instruction) == list:
			if type(instruction[0]) == list:
				for i in instruction:
					self.execute(i)
				return None
			if instruction[0] == '#':
				self.variables[instruction[1]] = self.execute(instruction[2])
			elif instruction[0] == '@':
				return self.execute(self.execute(instruction[1]))
			elif instruction[0] == '*':
				set1 = self.execute(instruction[1])
				inst = instruction[3][1:]
				var = instruction[2]
				for x in set1.content:
					self.variables[var] = x
					self.execute(inst)
			elif instruction[0] == '~':
				while not isfalse(self.execute(instruction[1])):
					self.execute(self.execute(instruction[2]))
			elif instruction[0] == ';':
				self.getchar()
			elif instruction[0] == ':':
				self.putchar(self.execute(instruction[1]))
			elif instruction[0] == "'":
				return self.getnum()
			elif instruction[0] == '`':
				return self.putnum(self.execute(instruction[1]))
			elif instruction[0] == '|':
				result = Set()
				for i in instruction[1:]:
					result = union(result, self.execute(i))
				return result
			elif instruction[0] == '&':
				result = self.execute(instruction[1])
				for i in instruction[2:]:
					result = intersect(result, self.execute(i))
				return result
			elif instruction[0] == '^':
				return pack(self.execute(instruction[1]))
			elif instruction[0] == '_':
				param = self.execute(instruction[1])
				try:
					return unpack(param)
				except:
					if type(param) == int or type(param) == float:
						return floor(param)
					elif type(param) == Pair:
						return randint(param.former, param.latter)
					else:
						raise TypeError("Cannot unpack non-set value: "+printable(param))
			elif instruction[0] == '=':
				return len(self.execute(instruction[1]).content)
			elif instruction[0] == '?':
				set1 = self.execute(instruction[1])
				var = instruction[2]
				# condition is a bit tricky, we need a lambda function to evaluate it
				def condition(param):
					self.variables[var] = param
					return self.execute(instruction[3])
				return filter1(set1, condition)
			elif instruction[0] == '+':
				return plus(self.execute(instruction[1]), self.execute(instruction[2]), self.execute(instruction[3]))
			elif instruction[0] == '-':
				return negate(self.execute(instruction[1]))
			elif instruction[0] == '/':
				return divide(self.execute(instruction[1]))
			elif instruction[0] == '%':
				return Pair(self.execute(instruction[1]), self.execute(instruction[2]))
			elif instruction[0] == '<':
				return self.execute(instruction[1]).former
			elif instruction[0] == '>':
				return self.execute(instruction[1]).latter
			elif instruction[0] == '$':
				return issubset(self.execute(instruction[1]), self.execute(instruction[2]))
			elif instruction[0] == '[':
				result = Set()
				for i in range(len(instruction)-1):
					result.append(Pair(i, self.execute(instruction[i+1])))
				return result
			elif instruction[0] == ']':
				from1 = self.execute(instruction[1])
				to1 = self.execute(instruction[2])
				step1 = self.execute(instruction[3])
				if step1 > 0: judge = lambda a,b: a < b
				else: judge = lambda a,b: a > b
				result = Set()
				while judge(from1,to1):
					result.append(from1)
					from1 += step1
				return result
			elif instruction[0] == '\\':
				if instruction[1][0] == '[':
					return instruction[1][1:]
				return instruction[1]
IDLE:
from SLet import *
inbuffer = ""
def iputchar(x):
	print(chr(x), end="")
def iputnum(x):
	print(x, end=" ")
def igetchar():
	global inbuffer
	if len(inbuffer) == 0:
		try: a = input()
		except EOFError: a = "\0"
		inbuffer += a + '\n'
	t = inbuffer[0]
	inbuffer = inbuffer[1:]
	return t
def igetnum():
	global inbuffer
	originalnum = ""
	t = 'a'
	while t not in '0123456789.': t = igetchar()
	while t in '0123456789.':
		originalnum += t
		t = igetchar()
	inbuffer = t + inbuffer
	try:
		return int(originalnum)
	except:
		return float(originalnum)
show = """SLet 2.0.1 - IDLE 1.0 (Nov.28 2024) by islptng
Type ".info" for the online document,
 ".def" to see a list of variables,
 or ".exit" to quit. """
print(show)
idle = SLetProcedure(igetchar,igetnum,iputchar,iputnum)
text = ""
while text != ".exit":
	if text == ".def":
		print("Name Value\n--------------------")
		for i in idle.variables.keys():
			print(i,"\t",printable(idle.variables[i]))
		print("--------------------")
		text = ""
		continue
	elif text == ".info":
		import webbrowser
		webbrowser.open("https://esolangs.org/wiki/SLet")
		text = ""
		continue
	try:
		text = analyze("[" + text + "!")
		error = True
		try:
			rtv = idle.execute(text)
			error = False
		except Exception as e: print(type(e),e)
		if not error:
			print("=",printable(rtv.content[0].latter))
	except:
		pass
	print("\n --> ", end="")
	text = input()