brittainscript 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {brittainscript-0.1.0 → brittainscript-0.2.0}/LICENSE +1 -1
  2. {brittainscript-0.1.0 → brittainscript-0.2.0}/PKG-INFO +2 -2
  3. {brittainscript-0.1.0 → brittainscript-0.2.0}/brittainscript.egg-info/PKG-INFO +2 -2
  4. {brittainscript-0.1.0 → brittainscript-0.2.0}/brittainscript.egg-info/SOURCES.txt +9 -1
  5. brittainscript-0.2.0/core/gui_backend.py +450 -0
  6. {brittainscript-0.1.0 → brittainscript-0.2.0}/core/parser.py +143 -20
  7. {brittainscript-0.1.0 → brittainscript-0.2.0}/core/parsetab.py +43 -43
  8. brittainscript-0.2.0/libs/datetime.bs +50 -0
  9. brittainscript-0.2.0/libs/gui.bs +156 -0
  10. brittainscript-0.2.0/libs/io.bs +28 -0
  11. brittainscript-0.2.0/libs/terminal.bs +49 -0
  12. {brittainscript-0.1.0 → brittainscript-0.2.0}/pyproject.toml +1 -1
  13. brittainscript-0.2.0/tests/test_datetime_builtin.py +54 -0
  14. brittainscript-0.2.0/tests/test_gui_builtins.py +43 -0
  15. brittainscript-0.2.0/tests/test_parser_builtins.py +39 -0
  16. {brittainscript-0.1.0 → brittainscript-0.2.0}/Documentation/README.md +0 -0
  17. {brittainscript-0.1.0 → brittainscript-0.2.0}/brittainscript.egg-info/dependency_links.txt +0 -0
  18. {brittainscript-0.1.0 → brittainscript-0.2.0}/brittainscript.egg-info/entry_points.txt +0 -0
  19. {brittainscript-0.1.0 → brittainscript-0.2.0}/brittainscript.egg-info/requires.txt +0 -0
  20. {brittainscript-0.1.0 → brittainscript-0.2.0}/brittainscript.egg-info/top_level.txt +0 -0
  21. {brittainscript-0.1.0 → brittainscript-0.2.0}/core/__init__.py +0 -0
  22. {brittainscript-0.1.0 → brittainscript-0.2.0}/core/lexer.py +0 -0
  23. {brittainscript-0.1.0 → brittainscript-0.2.0}/core/main.py +0 -0
  24. {brittainscript-0.1.0 → brittainscript-0.2.0}/libs/__init__.py +0 -0
  25. {brittainscript-0.1.0 → brittainscript-0.2.0}/libs/convert.bs +0 -0
  26. {brittainscript-0.1.0 → brittainscript-0.2.0}/libs/math.bs +0 -0
  27. {brittainscript-0.1.0 → brittainscript-0.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 AstraStudios
3
+ Copyright (c) 2026 Luke Brittain
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brittainscript
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: The BrittainScript programming language interpreter
5
5
  Author-email: Luke Brittain <luke.brittain@gmail.com>
6
6
  License: MIT License
7
7
 
8
- Copyright (c) 2024 AstraStudios
8
+ Copyright (c) 2026 Luke Brittain
9
9
 
10
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
11
11
  of this software and associated documentation files (the "Software"), to deal
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brittainscript
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: The BrittainScript programming language interpreter
5
5
  Author-email: Luke Brittain <luke.brittain@gmail.com>
6
6
  License: MIT License
7
7
 
8
- Copyright (c) 2024 AstraStudios
8
+ Copyright (c) 2026 Luke Brittain
9
9
 
10
10
  Permission is hereby granted, free of charge, to any person obtaining a copy
11
11
  of this software and associated documentation files (the "Software"), to deal
@@ -8,10 +8,18 @@ brittainscript.egg-info/entry_points.txt
8
8
  brittainscript.egg-info/requires.txt
9
9
  brittainscript.egg-info/top_level.txt
10
10
  core/__init__.py
11
+ core/gui_backend.py
11
12
  core/lexer.py
12
13
  core/main.py
13
14
  core/parser.py
14
15
  core/parsetab.py
15
16
  libs/__init__.py
16
17
  libs/convert.bs
17
- libs/math.bs
18
+ libs/datetime.bs
19
+ libs/gui.bs
20
+ libs/io.bs
21
+ libs/math.bs
22
+ libs/terminal.bs
23
+ tests/test_datetime_builtin.py
24
+ tests/test_gui_builtins.py
25
+ tests/test_parser_builtins.py
@@ -0,0 +1,450 @@
1
+ # gui_backend.py -- tkinter bridge for the BrittainScript gui library
2
+ #
3
+ # Widgets are handed to BrittainScript code as integer ids so scripts only
4
+ # ever hold plain numbers. Callbacks are BrittainScript function names passed
5
+ # as strings; when tkinter fires an event we call back into the interpreter
6
+ # through the invoker set by parser.py.
7
+ #
8
+ # tkinter is imported lazily so environments without a display can still run
9
+ # non-GUI scripts.
10
+
11
+ tk = None
12
+ messagebox = None
13
+ simpledialog = None
14
+
15
+ callback_invoker = None
16
+
17
+ widgets = {}
18
+ next_id = 1
19
+ root_window = None
20
+
21
+
22
+ def set_callback_invoker(invoker):
23
+ global callback_invoker
24
+ callback_invoker = invoker
25
+
26
+
27
+ def _load_tk():
28
+ global tk, messagebox, simpledialog
29
+ if tk is not None:
30
+ return True
31
+ try:
32
+ import tkinter as tk_module
33
+ from tkinter import messagebox as messagebox_module
34
+ from tkinter import simpledialog as simpledialog_module
35
+ except ImportError:
36
+ print("GUI error: tkinter is not available on this system")
37
+ return False
38
+ tk = tk_module
39
+ messagebox = messagebox_module
40
+ simpledialog = simpledialog_module
41
+ return True
42
+
43
+
44
+ def _register(widget):
45
+ global next_id
46
+ widget_id = next_id
47
+ next_id += 1
48
+ widgets[widget_id] = widget
49
+ return widget_id
50
+
51
+
52
+ def _lookup(widget_id, kinds=None):
53
+ widget = widgets.get(widget_id)
54
+ if widget is None:
55
+ print(f"GUI error: no widget with id {widget_id}")
56
+ return None
57
+ if kinds and not isinstance(widget, kinds):
58
+ print(f"GUI error: widget {widget_id} does not support this operation")
59
+ return None
60
+ return widget
61
+
62
+
63
+ def _run_callback(name, args):
64
+ if callback_invoker is None:
65
+ print("GUI error: no callback invoker set")
66
+ return
67
+ callback_invoker(name, list(args))
68
+
69
+
70
+ def gui_window(args):
71
+ if not _load_tk():
72
+ return None
73
+ global root_window
74
+ title, width, height = args
75
+ if root_window is None or not _window_alive(root_window):
76
+ window = tk.Tk()
77
+ root_window = window
78
+ else:
79
+ window = tk.Toplevel(root_window)
80
+ window.title(str(title))
81
+ window.geometry(f"{int(width)}x{int(height)}")
82
+ return _register(window)
83
+
84
+
85
+ def _window_alive(window):
86
+ try:
87
+ return bool(window.winfo_exists())
88
+ except Exception:
89
+ return False
90
+
91
+
92
+ def gui_title(args):
93
+ window = _lookup(args[0])
94
+ if window is not None:
95
+ window.title(str(args[1]))
96
+ return None
97
+
98
+
99
+ def gui_close(args):
100
+ window = _lookup(args[0])
101
+ if window is not None:
102
+ window.destroy()
103
+ return None
104
+
105
+
106
+ def gui_run(args):
107
+ if root_window is None or not _window_alive(root_window):
108
+ print("GUI error: create a window before calling run()")
109
+ return None
110
+ root_window.mainloop()
111
+ return None
112
+
113
+
114
+ def gui_frame(args):
115
+ parent = _lookup(args[0])
116
+ if parent is None:
117
+ return None
118
+ return _register(tk.Frame(parent))
119
+
120
+
121
+ def gui_label(args):
122
+ parent = _lookup(args[0])
123
+ if parent is None:
124
+ return None
125
+ return _register(tk.Label(parent, text=str(args[1])))
126
+
127
+
128
+ def gui_button(args):
129
+ parent = _lookup(args[0])
130
+ if parent is None:
131
+ return None
132
+ callback_name = str(args[2])
133
+ button = tk.Button(parent, text=str(args[1]),
134
+ command=lambda: _run_callback(callback_name, []))
135
+ return _register(button)
136
+
137
+
138
+ def gui_entry(args):
139
+ parent = _lookup(args[0])
140
+ if parent is None:
141
+ return None
142
+ return _register(tk.Entry(parent))
143
+
144
+
145
+ def gui_textbox(args):
146
+ parent = _lookup(args[0])
147
+ if parent is None:
148
+ return None
149
+ return _register(tk.Text(parent, width=int(args[1]), height=int(args[2])))
150
+
151
+
152
+ def gui_checkbox(args):
153
+ parent = _lookup(args[0])
154
+ if parent is None:
155
+ return None
156
+ variable = tk.BooleanVar(parent, value=False)
157
+ checkbox = tk.Checkbutton(parent, text=str(args[1]), variable=variable)
158
+ checkbox.bs_variable = variable
159
+ return _register(checkbox)
160
+
161
+
162
+ def gui_slider(args):
163
+ parent = _lookup(args[0])
164
+ if parent is None:
165
+ return None
166
+ slider = tk.Scale(parent, from_=args[1], to=args[2], orient='horizontal')
167
+ return _register(slider)
168
+
169
+
170
+ def gui_canvas(args):
171
+ parent = _lookup(args[0])
172
+ if parent is None:
173
+ return None
174
+ canvas = tk.Canvas(parent, width=int(args[1]), height=int(args[2]),
175
+ background='white', highlightthickness=0)
176
+ return _register(canvas)
177
+
178
+
179
+ def gui_pack(args):
180
+ widget = _lookup(args[0])
181
+ if widget is not None:
182
+ widget.pack(padx=4, pady=4)
183
+ return None
184
+
185
+
186
+ def gui_place(args):
187
+ widget = _lookup(args[0])
188
+ if widget is not None:
189
+ widget.place(x=int(args[1]), y=int(args[2]))
190
+ return None
191
+
192
+
193
+ def gui_grid(args):
194
+ widget = _lookup(args[0])
195
+ if widget is not None:
196
+ widget.grid(row=int(args[1]), column=int(args[2]), padx=4, pady=4)
197
+ return None
198
+
199
+
200
+ def gui_gettext(args):
201
+ widget = _lookup(args[0])
202
+ if widget is None:
203
+ return None
204
+ if isinstance(widget, tk.Entry):
205
+ return widget.get()
206
+ if isinstance(widget, tk.Text):
207
+ return widget.get('1.0', 'end-1c')
208
+ try:
209
+ return widget.cget('text')
210
+ except Exception:
211
+ print(f"GUI error: widget {args[0]} has no text")
212
+ return None
213
+
214
+
215
+ def gui_settext(args):
216
+ widget = _lookup(args[0])
217
+ if widget is None:
218
+ return None
219
+ text = str(args[1])
220
+ if isinstance(widget, tk.Entry):
221
+ widget.delete(0, 'end')
222
+ widget.insert(0, text)
223
+ elif isinstance(widget, tk.Text):
224
+ widget.delete('1.0', 'end')
225
+ widget.insert('1.0', text)
226
+ else:
227
+ try:
228
+ widget.config(text=text)
229
+ except Exception:
230
+ print(f"GUI error: widget {args[0]} has no text")
231
+ return None
232
+
233
+
234
+ def gui_setcolor(args):
235
+ widget = _lookup(args[0])
236
+ if widget is None:
237
+ return None
238
+ try:
239
+ widget.config(background=str(args[1]), foreground=str(args[2]))
240
+ except Exception:
241
+ try:
242
+ widget.config(background=str(args[1]))
243
+ except Exception as error:
244
+ print(f"GUI error: could not set color: {error}")
245
+ return None
246
+
247
+
248
+ def gui_setfont(args):
249
+ widget = _lookup(args[0])
250
+ if widget is None:
251
+ return None
252
+ try:
253
+ widget.config(font=(str(args[1]), int(args[2])))
254
+ except Exception as error:
255
+ print(f"GUI error: could not set font: {error}")
256
+ return None
257
+
258
+
259
+ def gui_ischecked(args):
260
+ widget = _lookup(args[0])
261
+ if widget is None:
262
+ return None
263
+ variable = getattr(widget, 'bs_variable', None)
264
+ if variable is None:
265
+ print(f"GUI error: widget {args[0]} is not a checkbox")
266
+ return None
267
+ return bool(variable.get())
268
+
269
+
270
+ def gui_getvalue(args):
271
+ widget = _lookup(args[0])
272
+ if widget is None:
273
+ return None
274
+ try:
275
+ return widget.get()
276
+ except Exception:
277
+ print(f"GUI error: widget {args[0]} has no value")
278
+ return None
279
+
280
+
281
+ def gui_setvalue(args):
282
+ widget = _lookup(args[0])
283
+ if widget is None:
284
+ return None
285
+ try:
286
+ widget.set(args[1])
287
+ except Exception:
288
+ print(f"GUI error: widget {args[0]} has no value")
289
+ return None
290
+
291
+
292
+ def _canvas(args):
293
+ return _lookup(args[0], (tk.Canvas,)) if _load_tk() else None
294
+
295
+
296
+ def gui_drawline(args):
297
+ canvas = _canvas(args)
298
+ if canvas is None:
299
+ return None
300
+ return canvas.create_line(args[1], args[2], args[3], args[4],
301
+ fill=str(args[5]), width=2)
302
+
303
+
304
+ def gui_drawrect(args):
305
+ canvas = _canvas(args)
306
+ if canvas is None:
307
+ return None
308
+ return canvas.create_rectangle(args[1], args[2], args[3], args[4],
309
+ fill=str(args[5]), outline=str(args[5]))
310
+
311
+
312
+ def gui_drawoval(args):
313
+ canvas = _canvas(args)
314
+ if canvas is None:
315
+ return None
316
+ return canvas.create_oval(args[1], args[2], args[3], args[4],
317
+ fill=str(args[5]), outline=str(args[5]))
318
+
319
+
320
+ def gui_drawtext(args):
321
+ canvas = _canvas(args)
322
+ if canvas is None:
323
+ return None
324
+ return canvas.create_text(args[1], args[2], text=str(args[3]),
325
+ fill=str(args[4]))
326
+
327
+
328
+ def gui_move(args):
329
+ canvas = _canvas(args)
330
+ if canvas is not None:
331
+ canvas.move(args[1], args[2], args[3])
332
+ return None
333
+
334
+
335
+ def gui_erase(args):
336
+ canvas = _canvas(args)
337
+ if canvas is not None:
338
+ canvas.delete(args[1])
339
+ return None
340
+
341
+
342
+ def gui_clearcanvas(args):
343
+ canvas = _canvas(args)
344
+ if canvas is not None:
345
+ canvas.delete('all')
346
+ return None
347
+
348
+
349
+ def gui_onkey(args):
350
+ window = _lookup(args[0])
351
+ if window is None:
352
+ return None
353
+ callback_name = str(args[1])
354
+ window.bind('<Key>',
355
+ lambda event: _run_callback(callback_name, [event.keysym]))
356
+ return None
357
+
358
+
359
+ def gui_onclick(args):
360
+ widget = _lookup(args[0])
361
+ if widget is None:
362
+ return None
363
+ callback_name = str(args[1])
364
+ widget.bind('<Button-1>',
365
+ lambda event: _run_callback(callback_name, [event.x, event.y]))
366
+ return None
367
+
368
+
369
+ def gui_after(args):
370
+ if root_window is None or not _window_alive(root_window):
371
+ print("GUI error: create a window before calling after()")
372
+ return None
373
+ callback_name = str(args[1])
374
+ root_window.after(int(args[0]), lambda: _run_callback(callback_name, []))
375
+ return None
376
+
377
+
378
+ def gui_alert(args):
379
+ if not _load_tk():
380
+ return None
381
+ messagebox.showinfo(str(args[0]), str(args[1]))
382
+ return None
383
+
384
+
385
+ def gui_confirm(args):
386
+ if not _load_tk():
387
+ return None
388
+ return bool(messagebox.askyesno(str(args[0]), str(args[1])))
389
+
390
+
391
+ def gui_prompt(args):
392
+ if not _load_tk():
393
+ return None
394
+ answer = simpledialog.askstring(str(args[0]), str(args[1]))
395
+ return answer if answer is not None else ""
396
+
397
+
398
+ BUILTINS = {
399
+ 'guiwindow': (gui_window, 3),
400
+ 'guititle': (gui_title, 2),
401
+ 'guiclose': (gui_close, 1),
402
+ 'guirun': (gui_run, 0),
403
+ 'guiframe': (gui_frame, 1),
404
+ 'guilabel': (gui_label, 2),
405
+ 'guibutton': (gui_button, 3),
406
+ 'guientry': (gui_entry, 1),
407
+ 'guitextbox': (gui_textbox, 3),
408
+ 'guicheckbox': (gui_checkbox, 2),
409
+ 'guislider': (gui_slider, 3),
410
+ 'guicanvas': (gui_canvas, 3),
411
+ 'guipack': (gui_pack, 1),
412
+ 'guiplace': (gui_place, 3),
413
+ 'guigrid': (gui_grid, 3),
414
+ 'guigettext': (gui_gettext, 1),
415
+ 'guisettext': (gui_settext, 2),
416
+ 'guisetcolor': (gui_setcolor, 3),
417
+ 'guisetfont': (gui_setfont, 3),
418
+ 'guiischecked': (gui_ischecked, 1),
419
+ 'guigetvalue': (gui_getvalue, 1),
420
+ 'guisetvalue': (gui_setvalue, 2),
421
+ 'guidrawline': (gui_drawline, 6),
422
+ 'guidrawrect': (gui_drawrect, 6),
423
+ 'guidrawoval': (gui_drawoval, 6),
424
+ 'guidrawtext': (gui_drawtext, 5),
425
+ 'guimove': (gui_move, 4),
426
+ 'guierase': (gui_erase, 2),
427
+ 'guiclearcanvas': (gui_clearcanvas, 1),
428
+ 'guionkey': (gui_onkey, 2),
429
+ 'guionclick': (gui_onclick, 2),
430
+ 'guiafter': (gui_after, 2),
431
+ 'guialert': (gui_alert, 2),
432
+ 'guiconfirm': (gui_confirm, 2),
433
+ 'guiprompt': (gui_prompt, 2),
434
+ }
435
+
436
+
437
+ def is_gui_builtin(name):
438
+ return name in BUILTINS
439
+
440
+
441
+ def call_builtin(name, args):
442
+ handler, arity = BUILTINS[name]
443
+ if len(args) != arity:
444
+ print(f"Error: {name}() expects {arity} argument{'s' if arity != 1 else ''}")
445
+ return None
446
+ try:
447
+ return handler(args)
448
+ except Exception as error:
449
+ print(f"GUI error: {error}")
450
+ return None