-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathibelarus.nim
309 lines (276 loc) · 10.3 KB
/
ibelarus.nim
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import deques, strformat, strutils, math, sequtils
# Used for comparisons
const epsilon = 5.96e-08
type
CommandStream = ref object
position: int
command: string
proc getChar(stream: CommandStream): char =
if stream.position < stream.command.len:
result = stream.command[stream.position]
inc stream.position
type
StackItemKind = enum
siNumber,
siList,
StackItem = ref object
case kind: StackItemKind # the ``kind`` field is the discriminator
of siNumber: numberVal: float # 0.0 by default
of siList: listVal: seq[char] # empty by default
# Implements echo/repr for our StackItem
proc `$`(si: StackItem): string =
case si.kind:
of siNumber: result = &"StackItem(siNumber, numberVal: {si.numberVal:.9f})"
of siList: result = "StackItem(siList, listVal: @[" & si.listVal.join(", ") & "])"
# Define what it means to be true for a StackItem of kind: siNumber
converter toBool(x: StackItem): bool =
case x.kind:
of siNumber: return abs(x.numberVal) > epsilon
of siList: quit(1)
# Initialize empty stack
# element [len(stack)] refers to the TOP of the stack
var stack = initDeque[StackItem]()
var command = "a@"
#var command = "1 1 5 4 1 5 4 5 5b@"
var commandStream = CommandStream(command: command)
var operationMode: int = 0
var register: array['a'..'z', StackItem] # yep crazy shit like that is part of nim-lang
const repl = "(Bienvenue!)\"((Input:)\"'@#!@) (#2-?(2!2$\"#!@)(#1+!@)(4!4$1+$@)@) #!@"
register['a'] = StackItem(kind: siList, listVal: cast[seq[char]](repl))
# use like "1 1 5 4 1 5 4 5 5b@" x y z x y z x y z
const triangle = "GHIJKLMNOol-2!*nk-2!*+mj-2!*+_Xil-2!*hk-2!*+gj-2!*+_Yoi-2!*nh-2!*+mg-2!*+_Zxy+z+2/Sssx-*sy-*sz-*_"
register['b'] = StackItem(kind: siList, listVal: cast[seq[char]](triangle))
# use like 1 1 5 4 1 5 4 5 5 1 1 5 4 1 5 4 5 5 2 c@
# | --------- coordinates --------- |n|execute
# stack like this x y z x y z x y z (n times) n
# uses registers t for the sum, r to store n, p for recursion
const multiple_triangles = "(r1-Rr0>(b@t+Tp@)(t\")(4!4$1+$@)@)P2!1>(0TRp@)((n must be bigger than 0, terminating)\")(4!4$1+$@)@"
register['c'] = StackItem(kind: siList, listVal: cast[seq[char]](multiple_triangles))
#use with d@
#uses register s for side length
# 8 dreiecke, jedes eck kommt in 4 vor.
# ecken: 0|0|0 0|s|0 s|0|0 s|s|0 s/2|s/2|±s/sqrt(2)
const octahedron = "(Seitenlaenge des Oktaeders:)\"'2!2!*2*3_*\"S0 0 0 0 s 0 s2/ s2/ s2_/ 0 0 0 s 0 0 s2/ s2/ s2_/ s 0 0 s s 0 s2/ s2/ s2_/ s s 0 0 s 0 s2/ s2/ s2_/ 0 0 0 0 s 0 s2/ s2/ s2_/~ 0 0 0 s 0 0 s2/ s2/ s2_/~ s 0 0 s s 0 s2/ s2/ s2_/~ s s 0 0 s 0 s2/ s2/ s2_/~ 8c@"
register['d'] = StackItem(kind: siList, listVal: cast[seq[char]](octahedron))
#NOTE: Error: unhandled exception: Empty deque. [IndexError]
# this is in spec "If an error occurs, the calculator stops its execution and gives an error message"
const debug = false
#############
# Main loop #
#############
var i = 0
while (var ch = getChar(commandStream); ch) != '\0':
inc i
# if i == 50: break
if debug and ch != ' ':
echo commandStream.command
echo spaces(commandStream.position-1) & "\e[1;34m^\e[00m"
case operationMode:
# Decimal Place Construction Mode
# REMARK: Fully implemented
of low(int)..(-2):
case ch:
of '0'..'9':
stack[^1].numberVal += parseFloat($ch) * pow(10, float(operationMode+1))
operationMode -= 1
of '.':
stack.addLast(StackItem(kind: siNumber))
operationMode = -2
else:
operationMode = 0
commandStream.position -= 1
# Whole Number Construction Mode
# REMARK: Fully implemented
of -1:
case ch:
of '0'..'9':
stack[^1].numberVal = stack[^1].numberVal * 10 + parseFloat($ch)
of '.':
operationMode = -2
else:
operationMode = 0
commandStream.position -= 1
# Execution Mode
of 0:
case ch:
of '0'..'9':
stack.addLast(StackItem(kind: siNumber, numberVal: parseFloat($ch)))
operationMode = -1
of '.':
stack.addLast(StackItem(kind: siNumber))
operationMode = -2
of '(':
stack.addLast(StackItem(kind: siList))
operationMode = 1
of 'a'..'z':
stack.addLast(register[ch])
of 'A'..'Z':
register[ch.toLowerAscii()] = stack.popLast()
of '=', '<', '>':
#NOTE: order: a = b, a < b, a > b
let a = stack.popLast()
let b = stack.popLast()
var erg: bool = false
if a.kind == siNumber and b.kind == siNumber:
case ch:
of '=': #NOTE:"works as long as we dont compare zero" (should we fix this?)
erg = (abs(a.numberVal - b.numberVal) <= epsilon * max(a.numberVal, b.numberVal))
of '>':
erg = (a.numberVal - b.numberVal > epsilon)
of '<':
erg = (a.numberVal - b.numberVal < -epsilon)
else:
discard
elif a.kind == siList and b.kind == siList:
case ch:
of '=':
erg = ($a.listVal == $b.listVal)
of '<':
erg = ($a.listVal < $b.listVal)
of '>':
erg = ($a.listVal > $b.listVal)
else:
discard
if a.kind == siNumber and b.kind == siList and ch == '<': erg = true
elif a.kind == siList and b.kind == siNumber and ch == '>': erg = true
if erg:
stack.addLast(StackItem(kind: siNumber, numberVal:1))
else:
stack.addLast(StackItem(kind: siNumber, numberVal:0))
of '?':
let a = stack.popLast()
case a.kind:
of siNumber:
if a: stack.addLast(StackItem(kind: siNumber))
else: stack.addLast(StackItem(kind: siNumber, numberVal: 1.0))
of siList:
if len(a.listVal) == 0: stack.addLast(StackItem(kind: siNumber, numberVal: 1.0))
else: stack.addLast(StackItem(kind: siNumber))
of '+', '-', '*', '/', '&', '|':
let b = stack.popLast() # Intentionally b then a, since this is the order for - and /
let a = stack.popLast()
if a.kind == siNumber and b.kind == siNumber:
case ch
of '+':
stack.addLast(StackItem(kind: siNumber, numberVal: a.numberVal + b.numberVal))
of '-':
stack.addLast(StackItem(kind: siNumber, numberVal: a.numberVal - b.numberVal))
of '*':
stack.addLast(StackItem(kind: siNumber, numberVal: a.numberVal * b.numberVal))
of '/':
# Should we compare to epsilon here? We needn't but in the spirit?
if b.numberVal == 0: # This would be inf or nan, so according to spec => empty list
stack.addLast(StackItem(kind: siList))
else:
stack.addLast(StackItem(kind: siNumber, numberVal: a.numberVal / b.numberVal))
of '&':
if a and b: stack.addLast(StackItem(kind: siNumber, numberVal: 1.0)) # True
else: stack.addLast(StackItem(kind: siNumber, numberVal: 0.0)) # False
of '|':
if a or b: stack.addLast(StackItem(kind: siNumber, numberVal: 1.0)) # True
else: stack.addLast(StackItem(kind: siNumber, numberVal: 0.0)) # False
else:
discard
else: # If one of them is not a number, push an empty list
stack.addLast(StackItem(kind: siList))
of '~':
case stack[^1].kind:
of siNumber: stack[^1].numberVal *= -1.0
of siList: stack[^1] = StackItem(kind: siList)
of '%':
case stack[^1].kind:
of siNumber:
let f = stack[^1].numberVal
let r = round(f)
let a = r - f
assert(abs(a) <= 0.5)
stack.addLast(StackItem(kind:siNumber, numberVal: a))
of siList: stack.addLast(StackItem(kind: siList))
of '_':
if stack[^1].kind == siNumber and stack[^1].numberVal > 0:
stack[^1].numberVal = sqrt(stack[^1].numberVal)
of '!':
if stack[^1].kind == siNumber:
let v = stack.popLast()
var n = int(round(v.numberVal))-1
if n <= len(stack) and n > 0:
var b = deepCopy(stack[^n])
stack.addLast(b)
else:
stack.addLast(v)
of '$':
let l = stack.popLast()
if l.kind == siNumber:
var n = int(round(l.numberVal))-1
if n <= len(stack) and n >= 0:
var s : seq[StackItem]
for i in 0..n-1:
s.add(stack.popLast())
stack.popLast() #delete item
for i in countdown(n-1,0):
stack.addLast(s[i])
of '@':
if stack[^1].kind == siList:
commandStream.command.insert(join(stack.popLast().listVal), commandStream.position)
#commandstream.command = commandstream.command[0..commandstream.position-1] & join(stack.popLast().listVal) & commandstream.command[commandstream.position..^1]
of '\\':
if stack[^1].kind == siList:
commandstream.command = commandstream.command & join(stack.popLast().listVal)
of '#':
stack.addLast(StackItem(kind: siNumber, numberVal: float(len(stack))))
of '\'':
let input = readLine(stdin)
try:
let number = parseFloat(input)
stack.addLast(StackItem(kind: siNumber, numberVal: number))
except:
# not a valid float
var parens = 0
for i in 0..<len(input):
case input[i]:
of '(': parens += 1
of ')': parens -= 1
else: discard
if parens != 0:
stack.addLast(StackItem(kind: siList))
else:
if input[0] == '(' and input[^1] == ')':
stack.addLast(StackItem(kind: siList, listVal: cast[seq[char]](input[1..^2])))
else:
stack.addLast(StackItem(kind: siList, listVal: cast[seq[char]](input)))
of '"':
let l = stack.popLast()
if l.kind == siNumber:
echo l.numberVal
else:
echo l.listVal.join("")
else:
discard
# List Construction Mode
# REMARK: Fully implemented
of 1..high(int):
case ch:
of '(':
var top = stack.popLast()
top.listVal.add('(')
stack.addLast(top)
operationMode += 1
of ')':
if operationMode > 1:
var top = stack.popLast()
top.listVal.add(')')
stack.addLast(top)
operationMode -= 1
else:
var top = stack.popLast()
top.listVal.add(ch)
stack.addLast(top)
# Debug stuff
if debug and ch != ' ':
echo "Stack:"
for i in countdown(len(stack)-1, 0):
echo " ", stack[i]
#echo "\nRegisters:"
#for i in low(register)..high(register):
# if register[i] != nil: echo " ", i, ": ", register[i]
echo "=".repeat(0x40)