prathamt commited on
Commit
450ebd0
·
verified ·
1 Parent(s): ddc5c38

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -179
app.py CHANGED
@@ -3,10 +3,10 @@ import operator as op
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,124 +17,75 @@ ALLOWED_OPERATORS = {
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
- @import url('https://fonts.googleapis.com/css2?family=VT323:wght@300;400;600&display=swap');
135
 
136
  * {
137
- font-family: 'VT323', sans-serif !important;
138
  }
139
 
140
  body, .gradio-container {
@@ -144,130 +95,90 @@ body, .gradio-container {
144
 
145
  .phone {
146
  max-width: 390px;
147
- margin: 0 auto;
148
- padding: 16px 14px 20px;
149
  }
150
 
 
151
  .display textarea {
152
- background: #000 !important;
153
- color: #fff !important;
154
- border: none !important;
155
- box-shadow: none !important;
156
  text-align: right !important;
157
- font-size: 72px !important;
158
- font-weight: 300 !important;
159
- height: 170px !important;
160
- padding: 10px 0 0 0 !important;
161
- resize: none !important;
162
- }
163
-
164
- .divider {
165
- height: 1px;
166
- background: rgba(255,255,255,0.18);
167
- margin: 10px 0 18px 0;
168
- }
169
-
170
- .topbar {
171
- display: flex;
172
- justify-content: space-between;
173
- align-items: center;
174
- color: rgba(255,255,255,0.8);
175
- font-size: 18px;
176
- margin-bottom: 8px;
177
- opacity: 0.9;
178
  }
179
 
 
180
  button {
181
- height: 82px !important;
182
- min-height: 82px !important;
183
- border-radius: 999px !important;
184
- font-size: 30px !important;
185
- font-weight: 500 !important;
186
  border: none !important;
187
- box-shadow: none !important;
188
  }
189
 
 
190
  .digit button {
191
- background: #272727 !important;
192
- color: #fff !important;
193
  }
194
 
195
  .utility button {
196
  background: #a5a5a5 !important;
197
- color: #000 !important;
198
  }
199
 
200
  .operator button {
201
- background: #bfbfbf !important;
202
- color: #000 !important;
203
  }
204
 
205
  .equals button {
206
  background: #34c759 !important;
207
- color: #fff !important;
208
  }
209
  """
210
 
211
- with gr.Blocks(css=CSS, title="Calculator") as demo:
 
 
 
 
212
  with gr.Column(elem_classes="phone"):
213
- display = gr.Textbox(
214
- value="0",
215
- show_label=False,
216
- interactive=False,
217
- container=False,
218
- lines=1,
219
- elem_classes="display",
220
- )
221
- state = gr.State("0")
222
 
223
- gr.HTML("""
224
- <div class="divider"></div>
225
- """)
226
 
227
  # Row 1
228
  with gr.Row():
229
- with gr.Column(scale=1, min_width=0):
230
- gr.Button("C", elem_classes=["utility"]).click(clear_display, None, [state, display])
231
- with gr.Column(scale=1, min_width=0):
232
- gr.Button("()", elem_classes=["utility"]).click(parens, state, [state, display])
233
- with gr.Column(scale=1, min_width=0):
234
- gr.Button("%", elem_classes=["utility"]).click(percent, state, [state, display])
235
- with gr.Column(scale=1, min_width=0):
236
- gr.Button("÷", elem_classes=["operator"]).click(make_append("/"), state, [state, display])
237
 
238
  # Row 2
239
  with gr.Row():
240
- for label in ["7", "8", "9"]:
241
- with gr.Column(scale=1, min_width=0):
242
- gr.Button(label, elem_classes=["digit"]).click(make_append(label), state, [state, display])
243
- with gr.Column(scale=1, min_width=0):
244
- gr.Button("×", elem_classes=["operator"]).click(make_append("*"), state, [state, display])
245
 
246
  # Row 3
247
  with gr.Row():
248
- for label in ["4", "5", "6"]:
249
- with gr.Column(scale=1, min_width=0):
250
- gr.Button(label, elem_classes=["digit"]).click(make_append(label), state, [state, display])
251
- with gr.Column(scale=1, min_width=0):
252
- gr.Button("−", elem_classes=["operator"]).click(make_append("-"), state, [state, display])
253
 
254
  # Row 4
255
  with gr.Row():
256
- for label in ["1", "2", "3"]:
257
- with gr.Column(scale=1, min_width=0):
258
- gr.Button(label, elem_classes=["digit"]).click(make_append(label), state, [state, display])
259
- with gr.Column(scale=1, min_width=0):
260
- gr.Button("+", elem_classes=["operator"]).click(make_append("+"), state, [state, display])
261
 
262
  # Row 5
263
  with gr.Row():
264
- with gr.Column(scale=1, min_width=0):
265
- gr.Button("+/-", elem_classes=["utility"]).click(toggle_sign, state, [state, display])
266
- with gr.Column(scale=2, min_width=0):
267
- gr.Button("0", elem_classes=["digit", "zero"]).click(make_append("0"), state, [state, display])
268
- with gr.Column(scale=1, min_width=0):
269
- gr.Button(".", elem_classes=["digit"]).click(make_append("."), state, [state, display])
270
- with gr.Column(scale=1, min_width=0):
271
- gr.Button("=", elem_classes=["equals"]).click(evaluate, state, [state, display])
272
 
273
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
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
  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 (VT323 FONT)
83
+ # =========================
84
  CSS = """
85
+ @import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
86
 
87
  * {
88
+ font-family: 'VT323', monospace !important;
89
  }
90
 
91
  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: 26px !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
 
142
+ # =========================
143
+ # UI
144
+ # =========================
145
+ with gr.Blocks(css=CSS) as demo:
146
+
147
  with gr.Column(elem_classes="phone"):
 
 
 
 
 
 
 
 
 
148
 
149
+ display = gr.Textbox(value="0", show_label=False, elem_classes="display")
150
+ state = gr.State("0")
 
151
 
152
  # Row 1
153
  with gr.Row():
154
+ gr.Button("C", elem_classes="utility").click(clear, None, [state, display])
155
+ gr.Button("+/-", elem_classes="utility").click(toggle_sign, state, [state, display])
156
+ gr.Button("%", elem_classes="utility").click(percent, state, [state, display])
157
+ gr.Button("÷", elem_classes="operator").click(make_append("/"), state, [state, display])
 
 
 
 
158
 
159
  # Row 2
160
  with gr.Row():
161
+ for i in ["7","8","9"]:
162
+ gr.Button(i, elem_classes="digit").click(make_append(i), state, [state, display])
163
+ gr.Button("×", elem_classes="operator").click(make_append("*"), state, [state, display])
 
 
164
 
165
  # Row 3
166
  with gr.Row():
167
+ for i in ["4","5","6"]:
168
+ gr.Button(i, elem_classes="digit").click(make_append(i), state, [state, display])
169
+ gr.Button("−", elem_classes="operator").click(make_append("-"), state, [state, display])
 
 
170
 
171
  # Row 4
172
  with gr.Row():
173
+ for i in ["1","2","3"]:
174
+ gr.Button(i, elem_classes="digit").click(make_append(i), state, [state, display])
175
+ gr.Button("+", elem_classes="operator").click(make_append("+"), state, [state, display])
 
 
176
 
177
  # Row 5
178
  with gr.Row():
179
+ gr.Button("⌫", elem_classes="utility").click(backspace, state, [state, display])
180
+ gr.Button("0", elem_classes="digit").click(make_append("0"), state, [state, display])
181
+ gr.Button(".", elem_classes="digit").click(make_append("."), state, [state, display])
182
+ gr.Button("=", elem_classes="equals").click(calculate, state, [state, display])
 
 
 
 
183
 
184
  demo.launch(server_name="0.0.0.0", server_port=7860)