prathamt commited on
Commit
af139e1
·
verified ·
1 Parent(s): b11bed8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +184 -96
app.py CHANGED
@@ -3,10 +3,10 @@ import operator as op
3
  import re
4
  import gradio as gr
5
 
6
- # =========================
7
- # SAFE CALCULATION LOGIC
8
- # =========================
9
- OPS = {
10
  ast.Add: op.add,
11
  ast.Sub: op.sub,
12
  ast.Mult: op.mul,
@@ -17,77 +17,120 @@ OPS = {
17
  ast.USub: op.neg,
18
  }
19
 
20
- def eval_expr(expr):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  expr = expr.replace("×", "*").replace("÷", "/").replace("−", "-")
22
  tree = ast.parse(expr, mode="eval")
23
- return _eval(tree.body)
24
 
25
- def _eval(node):
26
- if isinstance(node, ast.Constant):
27
- return node.value
28
- elif isinstance(node, ast.BinOp):
29
- return OPS[type(node.op)](_eval(node.left), _eval(node.right))
30
- elif isinstance(node, ast.UnaryOp):
31
- return OPS[type(node.op)](_eval(node.operand))
32
- else:
33
- raise Exception("Invalid")
34
 
35
- # =========================
36
- # BUTTON FUNCTIONS
37
- # =========================
38
- def append(current, val):
39
  if current in ("0", "Error"):
40
  current = ""
41
 
42
- if val in "+-*/" and current.endswith(("+","-","*","/")):
43
- current = current[:-1]
 
 
 
44
 
45
- return current + val, current + val
46
 
47
- def calculate(current):
48
- try:
49
- result = eval_expr(current)
50
- return str(result), str(result)
51
- except:
52
- return "Error", "Error"
53
-
54
- def clear():
55
  return "0", "0"
56
 
57
  def backspace(current):
58
- if len(current) <= 1:
59
  return "0", "0"
60
- return current[:-1], current[:-1]
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  def toggle_sign(current):
63
- if current == "0":
64
- return "-0", "-0"
65
- if current.startswith("-"):
66
- return current[1:], current[1:]
67
- return "-" + current, "-" + current
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  def percent(current):
70
- try:
71
- value = eval_expr(current) / 100
72
- return str(value), str(value)
73
- except:
74
  return current, current
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def make_append(v):
77
  def fn(current):
78
- return append(current, v)
79
  return fn
80
 
81
- # =========================
82
- # CSS (EDIT HERE FOR THEME)
83
- # =========================
84
  CSS = """
85
- @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@300;400;600&display=swap');
86
-
87
- * {
88
- font-family: 'Orbitron', sans-serif !important;
89
- }
90
-
91
  body, .gradio-container {
92
  background: #000 !important;
93
  color: white !important;
@@ -95,95 +138,140 @@ body, .gradio-container {
95
 
96
  .phone {
97
  max-width: 390px;
98
- margin: auto;
99
- padding: 15px;
100
  }
101
 
102
- /* Display */
103
  .display textarea {
104
- background: black !important;
105
- color: white !important;
106
- font-size: 60px !important;
107
- text-align: right !important;
108
  border: none !important;
109
- height: 140px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
- /* Buttons */
113
  button {
114
- height: 75px !important;
115
- border-radius: 50% !important;
116
- font-size: 24px !important;
 
 
117
  border: none !important;
 
118
  }
119
 
120
- /* Colors */
121
  .digit button {
122
- background: #1c1c1c !important;
123
- color: white !important;
124
  }
125
 
126
  .utility button {
127
  background: #a5a5a5 !important;
128
- color: black !important;
129
  }
130
 
131
  .operator button {
132
- background: #ff9500 !important;
133
- color: white !important;
134
  }
135
 
136
  .equals button {
137
  background: #34c759 !important;
138
- color: white !important;
139
  }
140
 
141
- /* Zero button */
142
  .zero button {
143
- border-radius: 40px !important;
144
  }
145
  """
146
 
147
- # =========================
148
- # UI
149
- # =========================
150
- with gr.Blocks(css=CSS) as demo:
151
-
152
  with gr.Column(elem_classes="phone"):
153
-
154
- display = gr.Textbox(value="0", show_label=False, elem_classes="display")
 
 
 
 
 
 
155
  state = gr.State("0")
156
 
 
 
 
 
 
 
 
 
 
 
157
  # Row 1
158
  with gr.Row():
159
- gr.Button("C", elem_classes="utility").click(clear, None, [state, display])
160
- gr.Button("+/-", elem_classes="utility").click(toggle_sign, state, [state, display])
161
- gr.Button("%", elem_classes="utility").click(percent, state, [state, display])
162
- gr.Button("÷", elem_classes="operator").click(make_append("/"), state, [state, display])
 
 
 
 
163
 
164
  # Row 2
165
  with gr.Row():
166
- for i in ["7","8","9"]:
167
- gr.Button(i, elem_classes="digit").click(make_append(i), state, [state, display])
168
- gr.Button("×", elem_classes="operator").click(make_append("*"), state, [state, display])
 
 
169
 
170
  # Row 3
171
  with gr.Row():
172
- for i in ["4","5","6"]:
173
- gr.Button(i, elem_classes="digit").click(make_append(i), state, [state, display])
174
- gr.Button("−", elem_classes="operator").click(make_append("-"), state, [state, display])
 
 
175
 
176
  # Row 4
177
  with gr.Row():
178
- for i in ["1","2","3"]:
179
- gr.Button(i, elem_classes="digit").click(make_append(i), state, [state, display])
180
- gr.Button("+", elem_classes="operator").click(make_append("+"), state, [state, display])
 
 
181
 
182
  # Row 5
183
  with gr.Row():
184
- gr.Button("⌫", elem_classes="utility").click(backspace, state, [state, display])
185
- gr.Button("0", elem_classes="digit zero").click(make_append("0"), state, [state, display])
186
- gr.Button(".", elem_classes="digit").click(make_append("."), state, [state, display])
187
- gr.Button("=", elem_classes="equals").click(calculate, state, [state, display])
 
 
 
 
188
 
189
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
3
  import re
4
  import gradio as gr
5
 
6
+ # ----------------------------
7
+ # Safe arithmetic evaluator
8
+ # ----------------------------
9
+ ALLOWED_OPERATORS = {
10
  ast.Add: op.add,
11
  ast.Sub: op.sub,
12
  ast.Mult: op.mul,
 
17
  ast.USub: op.neg,
18
  }
19
 
20
+ def _safe_eval_node(node):
21
+ if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
22
+ return node.value
23
+
24
+ if isinstance(node, ast.BinOp) and type(node.op) in ALLOWED_OPERATORS:
25
+ return ALLOWED_OPERATORS[type(node.op)](
26
+ _safe_eval_node(node.left),
27
+ _safe_eval_node(node.right),
28
+ )
29
+
30
+ if isinstance(node, ast.UnaryOp) and type(node.op) in ALLOWED_OPERATORS:
31
+ return ALLOWED_OPERATORS[type(node.op)](_safe_eval_node(node.operand))
32
+
33
+ raise ValueError("Unsupported expression")
34
+
35
+ def safe_eval(expr: str):
36
  expr = expr.replace("×", "*").replace("÷", "/").replace("−", "-")
37
  tree = ast.parse(expr, mode="eval")
38
+ return _safe_eval_node(tree.body)
39
 
40
+ def format_result(value):
41
+ if isinstance(value, float) and value.is_integer():
42
+ return str(int(value))
43
+ return str(value)
 
 
 
 
 
44
 
45
+ # ----------------------------
46
+ # Calculator actions
47
+ # ----------------------------
48
+ def append_value(current, value):
49
  if current in ("0", "Error"):
50
  current = ""
51
 
52
+ if value in "+-*/":
53
+ if not current and value != "-":
54
+ return "0", "0"
55
+ if current.endswith(("+", "-", "*", "/", ".")):
56
+ current = current[:-1]
57
 
58
+ return current + value, current + value
59
 
60
+ def clear_display():
 
 
 
 
 
 
 
61
  return "0", "0"
62
 
63
  def backspace(current):
64
+ if current in ("0", "Error", ""):
65
  return "0", "0"
66
+ new_value = current[:-1]
67
+ if not new_value:
68
+ new_value = "0"
69
+ return new_value, new_value
70
+
71
+ def evaluate(current):
72
+ try:
73
+ result = safe_eval(current)
74
+ result = format_result(result)
75
+ return result, result
76
+ except:
77
+ return "Error", "Error"
78
 
79
  def toggle_sign(current):
80
+ if current in ("0", "Error", ""):
81
+ return "-", "-"
82
+
83
+ m = re.search(r"(-?\d+(?:\.\d+)?)$", current)
84
+ if not m:
85
+ return current, current
86
+
87
+ start, end = m.span()
88
+ num = m.group(0)
89
+
90
+ if num.startswith("-"):
91
+ num = num[1:]
92
+ else:
93
+ num = "-" + num
94
+
95
+ new_value = current[:start] + num
96
+ return new_value, new_value
97
 
98
  def percent(current):
99
+ if current in ("0", "Error", ""):
100
+ return "0", "0"
101
+
102
+ if current.endswith((".", "+", "-", "*", "/")):
103
  return current, current
104
 
105
+ new_value = current + "/100"
106
+ return new_value, new_value
107
+
108
+ def parens(current):
109
+ if current in ("0", "Error"):
110
+ current = ""
111
+
112
+ open_count = current.count("(")
113
+ close_count = current.count(")")
114
+
115
+ if not current or current.endswith(("+", "-", "*", "/", "(")):
116
+ ch = "("
117
+ elif open_count > close_count:
118
+ ch = ")"
119
+ else:
120
+ ch = "("
121
+
122
+ new_value = current + ch
123
+ return new_value, new_value
124
+
125
  def make_append(v):
126
  def fn(current):
127
+ return append_value(current, v)
128
  return fn
129
 
130
+ # ----------------------------
131
+ # UI
132
+ # ----------------------------
133
  CSS = """
 
 
 
 
 
 
134
  body, .gradio-container {
135
  background: #000 !important;
136
  color: white !important;
 
138
 
139
  .phone {
140
  max-width: 390px;
141
+ margin: 0 auto;
142
+ padding: 16px 14px 20px;
143
  }
144
 
 
145
  .display textarea {
146
+ background: #000 !important;
147
+ color: #fff !important;
 
 
148
  border: none !important;
149
+ box-shadow: none !important;
150
+ text-align: right !important;
151
+ font-size: 72px !important;
152
+ font-weight: 300 !important;
153
+ height: 170px !important;
154
+ padding: 10px 0 0 0 !important;
155
+ resize: none !important;
156
+ }
157
+
158
+ .divider {
159
+ height: 1px;
160
+ background: rgba(255,255,255,0.18);
161
+ margin: 10px 0 18px 0;
162
+ }
163
+
164
+ .topbar {
165
+ display: flex;
166
+ justify-content: space-between;
167
+ align-items: center;
168
+ color: rgba(255,255,255,0.8);
169
+ font-size: 18px;
170
+ margin-bottom: 8px;
171
+ opacity: 0.9;
172
  }
173
 
 
174
  button {
175
+ height: 82px !important;
176
+ min-height: 82px !important;
177
+ border-radius: 999px !important;
178
+ font-size: 30px !important;
179
+ font-weight: 500 !important;
180
  border: none !important;
181
+ box-shadow: none !important;
182
  }
183
 
 
184
  .digit button {
185
+ background: #272727 !important;
186
+ color: #fff !important;
187
  }
188
 
189
  .utility button {
190
  background: #a5a5a5 !important;
191
+ color: #000 !important;
192
  }
193
 
194
  .operator button {
195
+ background: #bfbfbf !important;
196
+ color: #000 !important;
197
  }
198
 
199
  .equals button {
200
  background: #34c759 !important;
201
+ color: #fff !important;
202
  }
203
 
 
204
  .zero button {
205
+ border-radius: 999px !important;
206
  }
207
  """
208
 
209
+ with gr.Blocks(css=CSS, title="Calculator") as demo:
 
 
 
 
210
  with gr.Column(elem_classes="phone"):
211
+ display = gr.Textbox(
212
+ value="0",
213
+ show_label=False,
214
+ interactive=False,
215
+ container=False,
216
+ lines=1,
217
+ elem_classes="display",
218
+ )
219
  state = gr.State("0")
220
 
221
+ gr.HTML("""
222
+ <div class="topbar">
223
+ <span>◔</span>
224
+ <span>⌕</span>
225
+ <span>√π</span>
226
+ <span style="color:#34c759">⌫</span>
227
+ </div>
228
+ <div class="divider"></div>
229
+ """)
230
+
231
  # Row 1
232
  with gr.Row():
233
+ with gr.Column(scale=1, min_width=0):
234
+ gr.Button("C", elem_classes=["utility"]).click(clear_display, None, [state, display])
235
+ with gr.Column(scale=1, min_width=0):
236
+ gr.Button("()", elem_classes=["utility"]).click(parens, state, [state, display])
237
+ with gr.Column(scale=1, min_width=0):
238
+ gr.Button("%", elem_classes=["utility"]).click(percent, state, [state, display])
239
+ with gr.Column(scale=1, min_width=0):
240
+ gr.Button("÷", elem_classes=["operator"]).click(make_append("/"), state, [state, display])
241
 
242
  # Row 2
243
  with gr.Row():
244
+ for label in ["7", "8", "9"]:
245
+ with gr.Column(scale=1, min_width=0):
246
+ gr.Button(label, elem_classes=["digit"]).click(make_append(label), state, [state, display])
247
+ with gr.Column(scale=1, min_width=0):
248
+ gr.Button("×", elem_classes=["operator"]).click(make_append("*"), state, [state, display])
249
 
250
  # Row 3
251
  with gr.Row():
252
+ for label in ["4", "5", "6"]:
253
+ with gr.Column(scale=1, min_width=0):
254
+ gr.Button(label, elem_classes=["digit"]).click(make_append(label), state, [state, display])
255
+ with gr.Column(scale=1, min_width=0):
256
+ gr.Button("−", elem_classes=["operator"]).click(make_append("-"), state, [state, display])
257
 
258
  # Row 4
259
  with gr.Row():
260
+ for label in ["1", "2", "3"]:
261
+ with gr.Column(scale=1, min_width=0):
262
+ gr.Button(label, elem_classes=["digit"]).click(make_append(label), state, [state, display])
263
+ with gr.Column(scale=1, min_width=0):
264
+ gr.Button("+", elem_classes=["operator"]).click(make_append("+"), state, [state, display])
265
 
266
  # Row 5
267
  with gr.Row():
268
+ with gr.Column(scale=1, min_width=0):
269
+ gr.Button("+/-", elem_classes=["utility"]).click(toggle_sign, state, [state, display])
270
+ with gr.Column(scale=2, min_width=0):
271
+ gr.Button("0", elem_classes=["digit", "zero"]).click(make_append("0"), state, [state, display])
272
+ with gr.Column(scale=1, min_width=0):
273
+ gr.Button(".", elem_classes=["digit"]).click(make_append("."), state, [state, display])
274
+ with gr.Column(scale=1, min_width=0):
275
+ gr.Button("=", elem_classes=["equals"]).click(evaluate, state, [state, display])
276
 
277
  demo.launch(server_name="0.0.0.0", server_port=7860)