Advertisement
Guest User

potential fix for scope, 27.02

a guest
Feb 27th, 2020
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.70 KB | None | 0 0
  1. from functools import reduce
  2. #'(+ 1 2 (+ 2 3))' -> ['+', 1, 2, ['+', 2, 3]]
  3. # parsing
  4.  
  5.  
  6.  
  7. #'(car (quote (+ 42 13)))' -> ['(','car','(','quote','(','+','42','13',')',')',')']
  8.  
  9. class LispInterException(ValueError):pass
  10.  
  11. def tokenize(line):
  12. word_holder = ''
  13. tokens = []
  14. for i in line:
  15. if i == '(' or i == ')':
  16. if word_holder == '':
  17. tokens.append(i)
  18. else:
  19. tokens.append(word_holder)
  20. word_holder = ''
  21. tokens.append(i)
  22. elif i == ' ':
  23. if word_holder != '':
  24. tokens.append(word_holder)
  25. word_holder = ''
  26. else:
  27. word_holder += i
  28. if word_holder != '':
  29. tokens.append(word_holder)
  30. return tokens
  31.  
  32.  
  33. #tokens: '(','42','(','13',')','666',')'
  34. #stack: '(','42',['13']
  35.  
  36. def parse(array): #via stack
  37. stack = []
  38. for i in array:
  39. if i != ')':
  40. if i.isdigit():
  41. stack.append(int(i))
  42. else:
  43. stack.append(i)
  44. else:
  45. resulting_list = []
  46. while stack and stack[-1] != '(':
  47. resulting_list.append(stack.pop())
  48. if stack:
  49. stack.pop()
  50. else:
  51. raise LispInterException("Exception: Missing opening skobochka")
  52. stack.append(resulting_list[::-1])
  53. return stack.pop()
  54.  
  55.  
  56. def to_str(final_list, count, result): #making result look like lisp code
  57. print(final_list)
  58. if count == len(final_list):
  59. return result[:-1] + ') '
  60. else:
  61. if final_list[count] == '\'' or final_list[count] == ',':
  62. return to_str(final_list, count + 1, result)
  63. elif type(final_list[count]) == list:
  64. return to_str(final_list, count + 1, result + to_str(final_list[count], 0, '('))
  65. elif type(final_list[count]) == int:
  66. return to_str(final_list, count + 1, result + str(final_list[count]) + ' ')
  67. else:
  68. return to_str(final_list, count + 1, result + final_list[count] + ' ')
  69.  
  70. global_d = {}
  71. local_d = {}
  72.  
  73. def tracing(function):
  74. def traced_function(*args):
  75. print(function.__name__, ' '.join(str(i) for i in args))
  76. result = function(*args)
  77. print('result for function ', function.__name__, ' '.join(str(i) for i in args), ' is ', result)
  78. return result
  79. return traced_function
  80.  
  81. @tracing
  82. def my_apply(procedure, arguments, global_d, local_d):
  83. if procedure == '+': #plus
  84. #print('summ func')
  85. if not all(isinstance(x, int) for x in arguments):
  86. raise LispInterException("Exception: you can only use 'Summ' with int elements")
  87. return reduce(lambda x,y: x + y, arguments, 0)
  88.  
  89. elif procedure == '-': #minus
  90. #print('minus func')
  91. if not all(isinstance(x, int) for x in arguments):
  92. raise LispInterException("Exception: you can only use 'Minus' with int elements")
  93. if len(arguments) == 1: #Lisp returns -x if it's (- x)
  94. return arguments[0] * -1
  95. else:
  96. return reduce(lambda x,y: x - y, arguments)
  97.  
  98. elif procedure == '*': #multiply
  99. #print('multiply func')
  100. if not all(isinstance(x, int) for x in arguments):
  101. raise LispInterException("Exception: you can only use 'Multiply' with int elements")
  102. return reduce(lambda x,y: x * y, arguments, 1)
  103.  
  104. elif procedure == '/': #divide
  105. #print('divide func')
  106. if not all(isinstance(x, int) for x in arguments):
  107. raise LispInterException("Exception: you can only use 'Divide' with int elements")
  108. if len(procedure) == 1:
  109. return 'Error: You can\'t call eval for the empty division symbol'
  110. else:
  111. return reduce(lambda x,y: x / y, arguments)
  112.  
  113. elif procedure == 'null': #null
  114. #print('null func')
  115. if not len(arguments) == 1:
  116. raise LispInterException("Exception: you can only use 'Null' with single argument")
  117. if type(arguments) == list and not arguments:
  118. return '#t'
  119. else:
  120. return '#f'
  121.  
  122. elif procedure == '=': #equal
  123. #print('equal func')
  124. if not all(isinstance(x, int) for x in arguments):
  125. raise LispInterException("Exception: you can only use 'Equal' with int elements")
  126. for i in range(len(arguments) - 1):
  127. if arguments[i] != arguments[i + 1]:
  128. return '#f'
  129. return '#t'
  130.  
  131. elif procedure == '/=': #not equal
  132. #print('not equal func')
  133. if not all(isinstance(x, int) for x in arguments):
  134. raise LispInterException("Exception: you can only use 'Not equal' with int elements")
  135. for i in range(len(arguments) - 1):
  136. if arguments[i] == arguments[i + 1]:
  137. return '#f'
  138. return '#t'
  139.  
  140. elif procedure == '>': #more than
  141. #print('more than func')
  142. if not all(isinstance(x, int) for x in arguments):
  143. raise LispInterException("Exception: you can only use 'More than' with int elements")
  144. if not all(isinstance(x, int) for x in arguments):
  145. raise LispInterException("Exception: not int value in 'More than' function")
  146. for i in range(len(arguments) - 1):
  147. if not arguments[i] > arguments[i + 1]:
  148. return '#f'
  149. return '#t'
  150.  
  151. elif procedure == '<': #less than
  152. #print('less than func')
  153. if not all(isinstance(x, int) for x in arguments):
  154. raise LispInterException("Exception: you can only use 'Less than' with int elements")
  155. if not all(isinstance(x, int) for x in arguments):
  156. raise LispInterException("Exception: not int value in 'Less than' function")
  157. for i in range(len(arguments) - 1):
  158. if not arguments[i] < arguments[i + 1]:
  159. return '#f'
  160. return '#t'
  161.  
  162. elif procedure == '>=': #more or equal than
  163. #print('more or equal than func')
  164. if not all(isinstance(x, int) for x in arguments):
  165. raise LispInterException("Exception: you can only use 'More or equal than' with int elements")
  166. if not all(isinstance(x, int) for x in arguments):
  167. raise LispInterException("Exception: not int value in 'Equal or more than' function")
  168. if arguments[0] >= arguments[1]:
  169. return '#t'
  170. else:
  171. return '#f'
  172.  
  173. elif procedure == '<=': #less or equal than
  174. #print('less or equal func')
  175. if not all(isinstance(x, int) for x in arguments):
  176. raise LispInterException("Exception: you can only use 'Less or equal than' with int elements")
  177. if not all(isinstance(x, int) for x in arguments):
  178. raise LispInterException("Exception: not int value in 'Equal or less than' function")
  179. if arguments[0] <= arguments[1]:
  180. return '#t'
  181. else:
  182. return '#f'
  183.  
  184. elif procedure == 'cons': #car + cdr (cons 1 2) -> '(1.2) | (cons 1 '(b c d)) -> '(1 b c d)
  185. #print('cons func')
  186. if len(arguments) != 2:
  187. raise LispInterException("Exception: wrong amount of arguments in 'cons' function")
  188. a = arguments[0]
  189. b = arguments[1]
  190. if type(b) != list:
  191. raise LispInterException("Exception: this intepreter can't handle pairs")
  192. else:
  193. return [a] + b
  194.  
  195. elif procedure == 'list': #Creating lists
  196. #print('list func')
  197. return arguments
  198.  
  199. elif procedure == 'car': #head of list
  200. #print('car func')
  201. return arguments[0][0]
  202.  
  203. elif procedure == 'cdr': #list without head
  204. #print('cdr func')
  205. return arguments[0][1:]
  206.  
  207. elif type(procedure) == list and procedure[0] == 'lambda': #making lambda expression ((lambda (params) (body)) params-value)
  208. #print('lambda func')
  209. local_d = procedure[3]
  210. actuals = [i for i in arguments]
  211. for i,j in enumerate(procedure[1]):
  212. local_d[j] = actuals[i]
  213. return (my_eval(procedure[2], global_d, local_d))
  214.  
  215. else:
  216. raise LispInterException('Exception: you gave some bullshit to the interperter')
  217.  
  218. inbuilt_functions = ['+', '-', '*', '/', 'null', '=', '/=', '>', '<', '>=', '<=', 'cons', 'list', 'car', 'cdr', 'lambda']
  219.  
  220. @tracing
  221. def my_eval(expr, global_d, local_d):
  222. if type(expr) == int: #displaying int number
  223. #print('displaying int')
  224. return expr
  225.  
  226. elif expr == 'pseudoclear': #kinda clearing the terminal
  227. for i in range(50):
  228. print()
  229. return ''
  230.  
  231. elif expr == '#t' or expr == '#f': #displaying true/false
  232. #print('displaying bool')
  233. return expr
  234.  
  235. elif expr == 'global': #displaying global dict
  236. return global_d
  237.  
  238. elif expr == 'clear-global': #Clearing global dict
  239. global_d.clear()
  240. return 'global dictionary is cleared'
  241.  
  242. elif expr == 'exit': #exit key
  243. exit('Exiting terminal')
  244.  
  245. elif type(expr) == str: #Calling variable
  246. #print('looking for variable')
  247. if expr in local_d:
  248. return local_d[expr]
  249. elif expr in global_d:
  250. return global_d[expr]
  251. else:
  252. if expr in inbuilt_functions:
  253. return expr
  254. else:
  255. raise LispInterException('Exception: variable not defined')
  256.  
  257. elif type(expr) == list and expr[0] == 'if': #if function
  258. #print('if func')
  259. if my_eval(expr[1], global_d, local_d) == '#t':
  260. return my_eval(expr[2], global_d, local_d)
  261. else:
  262. return my_eval(expr[3], global_d, local_d)
  263.  
  264. elif type(expr) == list and expr[0] == 'quote': #quoting expression into list
  265. #print('quote func')
  266. if len(expr) != 2:
  267. raise LispInterException('Exception: \'quote\' function can only work with 1 argument')
  268. return expr[1]
  269.  
  270. elif type(expr) == list and expr[0] == 'let': #local namespace parallel (let ((var1 value1) (var2 value2)) body)
  271. #print('let func')
  272. local_d_new = local_d.copy() #переносим все предыдущие значения в новый дикт, так как надо изолировать дикт в параметрах подаваемый на данный шаг рекурсии и подаваемый на следующий шаг рекурсии
  273. for i in expr[1]: #Заполнение словаря новыми переменными из текущей итерации рекурсии
  274. local_d_new[i[0]] = my_eval(i[1], global_d, local_d)
  275. return my_eval(expr[2], global_d, local_d_new)
  276.  
  277. elif type(expr) == list and expr[0] == 'let*': #local namespace one after another (let* ((var1 value1) (var2 value2)) body)
  278. #print('*let func')
  279. for i in expr[1]: #Создание новой копии словаря после каждого добавления переменной, чтобы в последующих шагах, если использовалась перезаданная переменная из прошлого шага, было использовано новое значение
  280. local_d = local_d.copy()
  281. local_d[i[0]] = my_eval(i[1], global_d, local_d)
  282. return my_eval(expr[2], global_d, local_d)
  283.  
  284. elif type(expr) == list and expr[0] == 'lambda': #если подается (lambda (x) x)
  285. #print('single lambda func')
  286. expr = expr + [local_d]
  287. return expr
  288.  
  289. elif type(expr) == list and expr[0] == 'define':
  290. #print('define func')
  291. if type(expr[1]) == str: #для присваивания атому
  292. global_d[expr[1]] = my_eval(expr[2], global_d, local_d)
  293. return 'defined a variable'
  294. else: #Для присваивания функции
  295. if not all(isinstance(n, str) for n in expr[1]):
  296. raise LispInterException('Exception: define - not an identifier, identifier with default, or keyword for procedure argument')
  297. else:
  298. global_d[expr[1][0]] = ['lambda', expr[1][1:], expr[2], local_d]
  299. return 'defined a function'
  300.  
  301. else:
  302. return my_apply(my_eval(expr[0], global_d, local_d), [my_eval(i, global_d, local_d) for i in expr[1:]], global_d, local_d)
  303. #
  304.  
  305.  
  306. #Прогнать парсинг с отладочной печатью
  307. #Написать функцию самостоятельно перегона из стринга в инт, из инта в стринг
  308. #Доделать всю арифметику, а также car cdr quote cons if
  309.  
  310. # REPL = Read Eval Print Loop
  311.  
  312. def repl():
  313. #global_d = {}
  314. while True:
  315. expr = input("MICRO-LISP: ")
  316. try:
  317. result = (my_eval(parse(tokenize(expr)), global_d, {}))
  318. if type(result) == int or type(result) == str or type(result) == dict:
  319. print(result)
  320. else:
  321. print(to_str(result, 0, '(')[:-1])
  322. except LispInterException as e:
  323. print (e)
  324.  
  325. repl()
  326.  
  327. #Добавить в необходимые места ексепшены (например, не хватает открывающейся скобки)
  328. #Сделать Define
  329. #Реализовать специальную конструкцию If, чтобы експрешион ((if (= 1 1) (lambda (x) (+ 1 x)) (lambda (y) (+ 2 y))) 42) выполнялся (прописать myeval...)
  330. #Реализовать интерпретатор без рекурсии
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement