This repository has been archived by the owner on Nov 22, 2023. It is now read-only.
forked from gek169/funbas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
2089 lines (1861 loc) · 74.4 KB
/
index.html
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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>153</title>
<script></script>
</head>
<body>
<!-- Content -->
<center>
<h1>
Tutorials
</h1>
<hr>
<img src="logo.png" alt="logo" style="width:100%">
</center>
<hr>
<!-- Index-->
<div style="margin-left:10%;">
<a href="../index.html">Main page</a><br>
</div>
<hr>
<div style="margin-left:10%;">
<a href="#preface">Preface</a><br>
<a href="#requirements">System Requirements</a><br>
<a href="#compile">Compilation</a><br>
<a href="#install">Installation</a><br>
<a href="#funbas">Compiling and Running the tutorial programs</a><br>
<a href="#tut1">Hello World</a><br>
<a href="#tut2">Control Flow, Functions</a><br>
<a href="#tut3">Arrays- A Calculator Program</a><br>
<a href="#tut4">Structs and Loops</a><br>
<a href="#tut5">Object Oriented Programming and Codegen Constants</a><br>
<a href="#tut6">Playing Sounds and Music</a><br>
<a href="#tut7">Snake!</a><br>
<a href="#tut8">3d Graphics- Hello World Triangle</a><br>
<a href="#tut9">3d Graphics- Spheres, Cones and Cylinders</a><br>
<a href="#tut10">Program 2- Mancala</a><br>
<a href="#tut11">Metaprogramming Introduction</a><br>
<a href="#tut12">Worksheet- A replacement for C macros</a><br>
<a href="#tut13">Using Codegen Code to make pre-rendered graphics</a><br>
<a href="#license">License</a><br>
</div>
<hr>
<p>
Welcome, beloved.
<br><i>Saying, Blessed are they whose iniquities are forgiven, and whose sins are covered.</i> - Romans 4:7
</p>
<p>Welcome, Conquerors to the Seabass Metaprogramming Language Tutorial Series!</p>
<h1>This tutorial series is still in development...</h1>
<hr>
<center>
<h3>All Glory to the Lord Jesus Christ, who is, and who was, and who is to come, for ever. Amen.</h3>
</center>
<hr>
<center>
<h2 id="preface">Preface</h2>
</center>
<hr>
<p>The Seabass metaprogramming language is a gift I have been given by our Lord Jesus Christ.</p>
<p>These tutuorials are written so that you may learn how to write useful, interesting programs
in the language even if you are a beginner.</p>
<p>This repository, every byte (Saving for accidentally included compiled binaries), is provided to you under the CC0 license- which is to say, it is
public domain software with virtually zero restriction, not even attribution. May your joy be full.</p>
<hr>
<center>
<h2 id="requirements">System Requirements</h2>
</center>
<hr>
<h3>In order to install the `cbas` tool, your computer must meet these requirements</h3>
<ol>
<li>A compatible C compiler invocable from the command line as `cc`</li>
<li>64 bit</li>
<li>Byte addressable memory</li>
<li>A C standard library with some extension functions (such as strdup)</li>
</ol>
<p>If you are using a <a href="https://www.microsoft.com/en-us/windows">malware</a> <a href="https://en.wikipedia.org/wiki/MacOS">operating</a> system, please be advised that
you may have great difficulty using this software. Please consider removing it and replacing it with an actual operating system.</p>
<p>Installing the usermode `toc` standard library is somewhat more burdensome:</p>
<ol>
<li>Roughly POSIX compatible</li>
<li>A complete pthreads implementation including pthread barrier</li>
<li>A Berkley sockets implementation with non-blocking I/O extensions.</li>
</ol>
<p>Your C compiler and standard library must support the following to properly support the `toc` translator:</p>
<ol>
<li>The "Labels as Values" extension. GCC and Clang both support it.</li>
<li>Tail call optimization.</li>
</ol>
<p>The tutorial programs will require the following:</p>
<ol>
<li>OpenGL 1.1, with development headers on your system.</li>
<li>SDL2 (+mixer) with development headers on your system.</li>
</ol>
<p>Additionally, I expect that you are adept with using your system's commandline tools, such as `cd` (Change Directory).</p>
<p>This tutorial will not cover how to use the commandline, how to use git, etcetera.</p>
<hr>
<center>
<h2 id="compile">Compilation</h2>
</center>
<hr>
<ol>
<li>Download/clone the <a href="https://github.com/gek169/seabass">development repository</a> somewhere.</li>
<li>`cd` into it.</li>
<li>execute `cc *.c -o cbas` or the like. Optimizations are recommended, i.e. `cc -O3 *.c -o cbas`</li>
<li>invoke `./cbas` and ensure you see the giant ASCII heart print out.</li>
<li>try the feature test program- run `./cbas featuretest.cbas`. Make sure it passes the tests.</li>
<li>cd into the tests directory and invoke `../cbas vm_test.cbas`. Make sure it says codegen main executed successfully.</li>
</ol>
<p>If all these completed successfully, you have successfully compiled the `cbas` tool.</p>
<hr>
<center>
<h2 id="install">Installation</h2>
</center>
<hr>
<p>copy the `cbas` program somewhere on your system's PATH- such as `/usr/bin/` or `/usr/local/bin/`.</p>
<p>Please also copy the contents of `library` (but not the folder itself) to `/usr/include/cbas/`</p>
<p>You can perform the latter step automatically by executing `make install_stdlib`</p>
<p>Once you have finished those steps, try compiling the directory listing program-</p>
<pre><code>
cbas tests2/dirlist.cbas
cc -O3 auto_out.c -lm -lpthread
#recommended for compilers that support it:
#cc -O3 -s auto_out.c -o dirlist -lm -lpthread -fwrapv
</code></pre>
<p>(note that `-lm` and `-lpthread` should be replaced by equivalent linker flags to link to your math library and pthreads, respectively)</p>
<p>After that's finished try testing it out by executing `./dirlist .`. You should see the contents of the current directory printed.</p>
<p>There are a bunch of other test programs written and I suggest trying multiple of them to make sure your setup is working- notably the
`sockets_test.cbas` program. Most of these test programs are in the `tests2` directory.</p>
<p>Once you have adequately confirmed that the test programs work, you have successfully installed Seabass.</p>
<p></p>
<hr>
<center>
<h2 id="funbas">Compiling and Running the Tutorial programs</h2>
</center>
<hr>
<p>The repository containing this html file contains several `.cbas` programs. cd into the top level
directory of the repository. You compile and run each program like this:</p>
<pre><code>
cbas NAME.cbas
cc -O3 auto_out.c -o NAME -lm -lSDL2 -lGL -lSDL2_mixer -fwrapv
./NAME
</code></pre>
<p>try it with the snake program.</p>
<pre><code>
cbas snake.cbas
cc -O3 auto_out.c -o snake -lm -lSDL2 -lGL -lSDL2_mixer -fwrapv
./snake
</code></pre>
<p>It is played with the arrow keys. You should hear some music playing.</p>
<img src="tutorial_images/snake_test_1.png" alt="logo" style="width:600px"><br>
<img src="tutorial_images/snake_test_2.png" alt="logo" style="width:600px"><br>
<p>Hit escape to quit.</p>
<hr>
<center>
<h2 id="tut1">Hello World</h2>
</center>
<hr>
<p>In this tutorial we will be writing a hello world program in Seabass using the `fun` library
(the library for doing fun programming!).</p>
<p>Start by creating an empty file ending in `.cbas` in the directory. Use a text editor (Not a word processor).</p>
<p>Here is the boilerplate you will need for each program:</p>
<pre><code>
#include "funbas.hbas"
fn appInit():
openWindow(
"Joy!",
1280,
720
);
setSwapInterval(1); //enable Vsync
end
fn appUpdate():
clearScreen(0.1,0.1,0.3);
swapDisplay();
end
fn appClose():
closeWindow;
end
fn onClick(int btn, int state):
end
fn onTextInput(char* text):
end
fn onTextEdit(char* text, int start, int length):
end
fn onKey(int kc, char state):
if(state == 0 && kc == KEY_ESCAPE)
appClose();
sys_exit(0);
end
end
fn onKeyRepeat(int kc, char state):
end
fn onResize(int w, int h):
glViewport(0,0,width,height);
end
fn onScroll(int x, int y):
end
</code></pre>
<p>Let me explain what each part of the boiler plate does.</p>
<pre><code>
//this is a comment!
//this command tells the compiler to paste the contents of the `funbas.hbas` file here.
//the quotes means it's loading from the current directory.
#include "funbas.hbas"
//this is a function- the basic building-block of your programs. This one is called `appInit` and
//it takes zero arguments.
//this function is called when our program starts.
fn appInit():
//here we are calling a function taking three arguments.
openWindow(
"Joy!",
1280,
720
);
//this is a function taking a single argument. It enables a feature known as "vsync" which
//basically helps stop screen tearing.
setSwapInterval(1); //enable Vsync
end
//this function is our "main loop"- it is called to render every frame.
//right now, all it does is clear the screen and update the display.
fn appUpdate():
clearScreen(0.1,0.1,0.3);
swapDisplay();
end
//this function is called if the user clicks the "X" button on the window that we opened.
fn appClose():
//if a function takes zero arguments, you can call it without parentheses.
closeWindow;
end
//this function is called whenever a button on the mouse is pressed or released.
fn onClick(int btn, int state):
end
//this function is used for doing text input- if you want to make something
//where the user types.
fn onTextInput(char* text):
end
//This one is for doing software text input. It's used by people who write
//fancy foreign languages like japanese.
fn onTextEdit(char* text, int start, int length):
end
//when a key is pressed or released, this is called.
fn onKey(int kc, char state):
if(state == 0 && kc == KEY_ESCAPE)
appClose();
sys_exit(0);
end
end
//You know how when you're typing and you hold down a key,
//it repeats the same character over and over? This is for that.
fn onKeyRepeat(int kc, char state):
end
//This is called when the window is resized.
fn onResize(int w, int h):
//this command tells the graphics library the dimensions of the window.
//width and height are global variables defined in `fun.hbas`
glViewport(0,0,width,height);
end
//this is called when the mouse wheel is scrolled.
//it's both X and Y because some mice let you scroll
//in two directions.
fn onScroll(int x, int y):
end
</code></pre>
<p>Try putting that code into a file called `tut1.cbas`. Compile and run it like so:</p>
<pre><code>
cbas tut1.cbas
cc -O3 auto_out.c -o tut1 -lm -lSDL2 -lGL -lSDL2_mixer -fwrapv
./tut1
</code></pre>
<p>(Note that depending on what system you're on the exact compiler flags may differ.)</p>
<p>You should see something like this:</p>
<img src="tutorial_images/boilerplate_result1.png" alt="logo" style="width:600px">
<p>(The window should be resizable too- try resizing it)</p>
<h3>Compiler Errors</h3>
<p>If you tipe someting in rong or make a mistak, you may see an error like this:</p>
<pre><code>
gek@whitebox:~/work/funbas$ cbas tut1.cbas
<CBAS: Finished Tokenization>
expression statement requires a semicolon.
The place I was trying to find a semicolon was:
File:Line:Col
tut1.cbas:35:5
~~~~
Note that the line number and column number
are where the parser invoked parse_error.
(the actual error may be slightly before,
or on the previous line)
I recommend looking near the location provided,
not just that exact spot!
</code></pre>
<p>In the error above, notice the `File:Line:Col` notation. This indicates
where the error occurred. Note that <b>Compiler errors are not always guaranteed
to point at exactly where the mistake is.</b> and furthermore <b>Not all compiler
errors emit a file, line, and column number.</b></p>
<p>During the course of these tutorials and beyond, you may encounter some
pretty unusual errors. It can be pretty frustrating. Here are some tips:</p>
<ol>
<li>Try ending every line in a semicolon, even if you don't need to.</li>
<li>If you get an error that says it's in a file that doesn't exist on line 0:0, read it carefully. It may
be from auto-generated code at compiletime, or it may be from semantic analysis.
If you cannot discern what the error means from the message, try inserting errors
into your code until the message changes to determine which function and which
lines the error is in. </li>
<li>If you get a file, line, and column number which points to the first
non-whitespace non-comment piece of text immediately after a function, the error is likely inside the function
body.</li>
<li>if you get an error from calling your system's C compiler,
you probably messed up your compiler command somehow. If it persists
even after checking and re-checking,
<b>verify that the snake program compiles and runs correctly- see above.</b>
<br>
You may also want to try the `funbas.cbas` program, which tests 3d rendering and sound.
</li>
</ol>
<p><b>Back to business...</b></p>
<p>If that worked, congratulations! We're almost at hello world.</p>
<p>First, we need to pick a font to render it in. two unencumbered public
domain fonts are provided:<br>
jupiteroid<br>
unitblock
</p>
<p>They are inside of the `fonts` folder in this repository. You can try either one (or both)</p>
<p>Look inside each folder inside of `fonts` and note the names of the `.ttf` files. Pick one to be your font.</p>
<p>Next, we need to load that font. Start be declaring a global variable to store it:</p>
<pre><code>
byte* ourFont = 0; //the variable used to store our font!
fn appInit():
openWindow(
"Joy!",
1280,
720
);
setSwapInterval(1); //enable Vsync
end
// bla bla rest of code....
</code></pre>
<p>Now we have to *load* the font. I decided to use Jupiteroid Bold. We will use `loadFont`</p>
<pre><code>
byte* ourFont = 0; //the variable used to store our font!
//its name is `ourFont` and it is of type `byte*`
fn appInit():
openWindow(
"Joy!",
1280,
720
);
setSwapInterval(1); //enable Vsync
//load our font! (IMPORTANT!!!! Must be AFTER openWindow)
ourFont = loadFont(
//relative path to the font's .ttf file
"fonts/jupiteroid/Bold.ttf",
//this is how tall each character will be, in pixels.
128
);
end
//...
// bla bla rest of code....
//...
//cleanup
fn appClose():
//clean up!
free(ourFont);
closeWindow;
end
</code></pre>
<p><b>Note how the type of font is a `byte*`</b>. This is called a "pointer". This will become imporatant later.</p>
<p>Compile the program again and make sure it's still working. If it fails, you probably
typed in the name of the directory wrong.</p>
<p>Now we're actually going to render some text! Go to your appUpdate function
and enter the `beginUI` and `endUI` calls. These demarcate where we are going to render 2D graphics.</p>
<pre><code>
fn appUpdate():
clearScreen(0.1,0.1,0.3);
beginUI();
endUI();
swapDisplay();
end
</code></pre>
<p>Now decide what color you want your hello world to be. Red? Green? Blue? You'll need it as three
numbers between 0-1 (R,G,B). I chose this bluish white color (0.8, 0.8, 1):</p>
<pre><code>
fn appUpdate():
clearScreen(0.1,0.1,0.3);
beginUI();
//GL command to set the current color.
glColor3f(0.8,0.8,1);
endUI();
swapDisplay();
end
</code></pre>
<p>Now it's finally time to render our hello world! Here's the code for our `appUpdate` function:</p>
<pre><code>
fn appUpdate():
clearScreen(0.1,0.1,0.3);
beginUI();
glColor3f(0.8,0.8,1);
renderText(
//this is our text! the `\n` means `newline`.
"Hello World!\nEnjoying the view?",
ourFont,
100, //this is the X coordinate of the bottom left corner of the first character.
//we choose 100 pixels from the left edge.
128, //this is the Y coordinate. We choose 128 pixels from the top of the screen.
128 //this is the amount of "vertical spacing" that a newline introduces.
);
endUI();
swapDisplay();
end
</code></pre>
<p>Compile and run it. This is what you should see:</p>
<img src="tutorial_images/helloworld.png" alt="logo" style="width:800px">
<h3>BONUS CONTENT</h3>
<p>Try adding more text to the screen.</p>
<p>Try making it so when you press KEY_A the text disappears.</p>
<p></p>
<p></p>
<p></p>
<hr>
<center>
<h2 id="tut2">Control Flow, Functions</h2>
</center>
<hr>
<p>In this tutorial we will be learning about how Control Flow is done in Seabass.</p>
<p>Control flow is how you get your program to do do anything other than execute
a linear sequence of instructions. Seabass provides these constructs:</p>
<ol>
<li>if- execute a block of code if an expression is non-zero.</li>
<li>elif/elseif- execute a block of code if an expression is non-zero
if a previous block was not executed in an `if/elif/else` chain.</li>
<li>else- execute a block of code if no statement in an `if/elif/else` chain
was execute.</li>
<li>while- execute in a loop so long as an expression evaluates as non-zero.</li>
<li>for- execute an initial expression, then run loop iterations as long
as a condition is true, finishing each execution with a third expression.</li>
<li>goto/jump- jump to some part of the code which has a name.</li>
<li>switch- based on an integer expression, jump to one of a variety of labels.</li>
<li>break- exit the current loop.</li>
<li>continue- continue the current loop.</li>
<li>functions- A body of code which accepts zero or more arguments and optionally returns a value.</li>
<li>methods- special functions which reference a struct object as `this` as a secret first argument.</li>
</ol>
<p>These language constructs are, broadly, what you will use to write your programs
in seabass.</p>
<p>This tutorial will aim to teach you how to use if/elif/else and switch.</p>
<p>Here's a small program, `controlflow.cbas` to demonstrate control flow in
the language:</p>
<pre><code>
#include "funbas.hbas"
byte* ourFont = 0; //the variable used to store our font!
fn appInit():
openWindow(
"Joy!",
1280,
720
);
setSwapInterval(1); //enable Vsync
//load our font! (Must be AFTER openWindow)
ourFont = loadFont(
"fonts/jupiteroid/Bold.ttf",
64
);
end
int keypressed = 0;
uint keypresses = 2;
fn appUpdate():
clearScreen(0.1,0.1,0.3);
beginUI();
glColor3f(0.8,0.8,1);
if(keypressed)
if(keypressed == KEY_H)
renderText(
"You are pressing H!",
ourFont,100,128,64
);
elif(keypressed == KEY_J)
renderText(
"You are pressing J!",
ourFont,100,128,64
);
else
renderText(
"Good!",
ourFont,100,128,64
);
end
else
renderText(
"Try pressing a key.",
ourFont,100,128,64
);
end
//demo switch....
switch(keypresses%3) mod_0, mod_1, mod_2;
:mod_0
renderText(
"Hey...",
ourFont,100,200,64
);
jump after
:mod_1
renderText(
"There!",
ourFont,100,200,64
);
jump after
:mod_2
:after
endUI();
swapDisplay();
end
fn appClose():
free(ourFont);
closeWindow;
end
fn onClick(int btn, int state):
end
fn onTextInput(char* text):
end
fn onTextEdit(char* text, int start, int length):
end
fn onKey(int kc, char state):
if(state == 0 && kc == KEY_ESCAPE)
appClose();
sys_exit(0);
end
if(state == 1)
keypressed = kc;
keypresses++;
elif(keypressed == kc)
keypressed = 0;
end
end
fn onKeyRepeat(int kc, char state):
end
fn onResize(int w, int h):
glViewport(0,0,width,height);
end
fn onScroll(int x, int y):
end
</code></pre>
<p>The code performs the following functions:</p>
<ol>
<li>When the user is holding down a key, a different message prints, and it varies based on the key being pressed.</li>
<li>Depending on how many times the user has pressed a key, another message displays either "Hey..." or "There!"</li>
</ol>
<p>In order to understand how, let's talk about the language constructs used in this demo:</p>
<h3>The if-elif-else Chain</h3>
<p>An if-elif-else chain is of the following form:</p>
<code><pre>
if(EXPRESSION)
STATEMENTS
elif(EXPRESSION)
STATEMENTS
else
STATEMENTS
end
</code></pre>
<p>Note that you may have as many `elif`s as you like, or none at all. However, you may at most
one `else` in a chain, and you may either have it or not.</p>
<p>These are valid:</p>
<code><pre>
int a
if(1)
a = 1 + 1
end
if(a != 2)
a = 12
else //this will execute
//you may have nested if-elif-else chains inside of any if/elif/else clause.
if(a == 3)
a--
elif(a == 2) //this executes
a--; a++;
else
sys_exit(0);
end
end
int b
if(a-1) //this will execute
doStuff();
elif(a)
doOtherStuff();
b = 17
end
int c
if(b-17)
doStuff();
elif(0) //never executes
doOtherStuff();
else //this will execute
doYetMoreStuff();
c = 97
end
if(c-97)
//do nothing...
elif(c && 0)
//do nothing...
elif(0-0)
//do nothing...
else //this executes
doStuff();
end
</code></pre>
<p>An if/elif statement accepts an integer expression between its parentheses. if the expression
is non-zero, its body executes. Otherwise, it attempts to execute the next `elif` in the chain.</p>
<p>Note that this syntax is <b>not</b> whitespace dependent. The ending of an `if/elif` clause
is determined by the presence of `end`, `elif`, or `else`. </p>
<h3>The Switch Statement</h3>
<p>Switch in seabass accepts an integer expression in parentheses followed by a list of labels:</p>
<code><pre>
int a = 3;
switch(a) label0 label1 label2 label3;
//code here never executes....
println("You will never see me!");
sys_exit(1);
:label0
a = 0;
goto last
:label1
a = 10;
goto last
:label2
a = 20;
goto last
:label3 //this code will be executed...
a = 100;
:last
//code.....
</code></pre>
<p>Note that it is a runtime error for switch to have an expression evaluating to an integer greater than
or equal to the number of labels, or negative.</p>
<h3>goto/jump</h3>
<p>Seabass allows you to do `jumps` between different parts of your program:</p>
<code><pre>
goto skip
println("This will never execute!!!!");
:skip
</pre></code>
<p>Note that you may jump out of a scope, but not deeper into one:</p>
<code><pre>
int a = 12;
//later...
if(a == 12)
println("Skipping ahead!");
goto skip
end
println("Today we will be learning about the history of underwater basket weaving...");
println("It all started when a man found some weird reeds...");
//insert boring lesson...
:skip
println("Now for the deserts...");
</pre></code>
<p>Here is a detailed example:</p>
<code><pre>
int a = 12;
goto secret_club //ERROR! Jumps into a scope.
if(a != 12)
:secret_club
a = 12;
goto else_clause //ERROR! Jumps into a scope.
else
:else_clause
a = 24;
goto secret_club //ERROR! Jumps into a scope.
end
</pre></code>
<p>Note that switch may not even jump out of its scope, let alone into a scope:</p>
<code><pre>
int a = ((uint)rand)%3;
if(1)
switch(a) lab0, lab1, lab2; //ERROR! lab0 and lab1 are not in the same scope!
if(0)
:lab0
println("Hiya!");
end
:lab2 //Switch can go here just fine (same scope)
println("Whoya!");
goto lab1 //OK
end
:lab1
println("Huzzah!");
</pre></code>
<p></p>
<p></p>
<p></p>
<p></p>
<h3>BONUS CONTENT</h3>
<p>Try modifying the example program to print out more unique messages for different keys. Maybe KEY_BACKSPACE for instance.</p>
<p>Try making the `switch` handle more numbers of key presses and print out more messages. Perhaps 5.</p>
<p></p>
<p></p>
<p></p>
<p></p>
<hr>
<center>
<h2 id="tut3">Arrays- A Calculator Program</h2>
</center>
<hr>
<p>In this tutorial we will be learning about Arrays and writing a calculator program.</p>
<h3>Declaring an array</h3>
<p>To declare an array variable, enter a type name followed by an integer surrounded by square brackets:</p>
<code><pre>
int[3] myintegers;
</code></pre>
<p>Note that you can do integer math inside of an array declaration:</p>
<code><pre>
int[1+2] myintegers;
int[(1<<1) + 1] myintegers2;
</code></pre>
<p>And, if you have declared a `codegen` integer variable, you can use it as well:</p>
<code><pre>
//at global scope....
codegen int MY_ARR_SIZE = 3;
int[MY_ARR_SIZE] myintegers;
</code></pre>
<h3>The Calculator</h3>
<p>Time to do some number crunching. Here's the code:</p>
<code><pre>
#include "funbas.hbas"
byte* ourFont = 0; //the variable used to store (a pointer to) our font!
i64[2] numbers; //the numbers for the current calculation. 64 bit signed integers.
uint n_numbers_entered = 0; //the number of numbers entered.
byte[257] cur_number_entry; //the text buffer for holding the current number entry.
byte number_entry_counter = 0; //Digits in current number typed.
int errflag =0; //if we divide by zero, this will be set.
fn appInit():
openWindow(
"Joy!",
1280,
720
);
setSwapInterval(1); //enable Vsync
//load our font! (Must be AFTER openWindow)
ourFont = loadFont(
"fonts/jupiteroid/Bold.ttf",
64
);
memclear(cur_number_entry,257);
end
fn appUpdate():
char[50] pbuf //print buffer- used for printing numbers.
clearScreen(0.1,0.1,0.3); //color to clear the screen to...
//begin 2D rendering...
beginUI();
//color for text...
glColor3f(0.8,0.8,1);
//greeting message. \n means "newline"
renderText("Welcome to Calculotron!\nUse the numberpad\nIf you don't have one\nuse the number keys\nand A(+)/S(-)/M(*)/D(/)\nC to clear.", ourFont, 612, 64,64);
//if the first number has been entered, display it.
if(n_numbers_entered >= 1)
itoa(pbuf, numbers[0]);
renderText(pbuf, ourFont, 10, 128,64);
end
//if the second number has been entered, display it too.
if(n_numbers_entered >= 2)
itoa(pbuf, numbers[1]);
renderText(pbuf, ourFont, 10, 128+64,64);
end
//if there is something in the current entry field, display it as well.
if(number_entry_counter)
//renderText("Current Entry:", ourFont, 250, 330,64);
renderText(cur_number_entry, ourFont, 300, 450,64);
end
//if an error has occurred, report it to the user and tell them what to do...
if(errflag)
renderText("<ERROR> Divide by Zero!\nPress Enter or `c`\nto continue...", ourFont, 300, 450,64);
end
//end 2D rendering...
endUI();
//update the display...
swapDisplay();
end
fn appClose():
free(ourFont);
closeWindow;
end
fn onClick(int btn, int state):
end
fn onTextInput(char* text):
end
fn onTextEdit(char* text, int start, int length):
end
//function for finishing a number entry
fn finishNumber:
//check for the special case that we typed just a minus sign...
//we want to make it "-1"
if(number_entry_counter == 1 && cur_number_entry[0] == '-')
cur_number_entry[number_entry_counter++] = '1';
end
//if we haven't typed anything, and we haven't entered both numbers,
//we want to make the number zero.
if(number_entry_counter == 0 && n_numbers_entered < 2)
numbers[n_numbers_entered++] = 0;
elif(n_numbers_entered < 2)
//if we have entered fewer than 2 numbers, add the number
//to the list of numbers.
numbers[n_numbers_entered++] = atoi(cur_number_entry);
else
//If both numbers have been entered, we want to replace the current
//number with the number being typed.
numbers[1] = atoi(cur_number_entry);
end
//reset the number entry...
memclear(cur_number_entry, 257);
number_entry_counter = 0;
end
fn onKey(int kc, char state):
//escape is our quit key...
if(state == 0 && kc == KEY_ESCAPE)
appClose();
sys_exit(0);
end
//This is a bit complicated...
if(
state == 0 && //a key has been released AND....
(
(errflag && //a divide by zero has happened AND...
( //enter/backspace was entered...
kc == KEY_RETURN ||
kc == KEY_KP_ENTER ||
kc == KEY_BACKSPACE
)
)
|| //OR
kc == KEY_C //the C (clear) key was pressed....
)
)
errflag = 0; //set error flag to zero.
memclear(cur_number_entry, 257); //clear the text entry buffer...
number_entry_counter = 0; //set the number entry position to zero.
n_numbers_entered = 0; //reset the number of numbers entered.
end
//if a key has been released and we are not in the error state...
if(state == 0 && !errflag)
//if it's a number key....
if(kc == KEY_0 || kc == KEY_KP_0)
cur_number_entry[number_entry_counter++] = '0';
elif(kc == KEY_1 || kc == KEY_KP_1)
cur_number_entry[number_entry_counter++] = '1';
elif(kc == KEY_2 || kc == KEY_KP_2)
cur_number_entry[number_entry_counter++] = '2';
elif(kc == KEY_3 || kc == KEY_KP_3)
cur_number_entry[number_entry_counter++] = '3';
elif(kc == KEY_4 || kc == KEY_KP_4)
cur_number_entry[number_entry_counter++] = '4';
elif(kc == KEY_5 || kc == KEY_KP_5)
cur_number_entry[number_entry_counter++] = '5';
elif(kc == KEY_6 || kc == KEY_KP_6)
cur_number_entry[number_entry_counter++] = '6';
elif(kc == KEY_7 || kc == KEY_KP_7)
cur_number_entry[number_entry_counter++] = '7';
elif(kc == KEY_8 || kc == KEY_KP_8)
cur_number_entry[number_entry_counter++] = '8';
elif(kc == KEY_9 || kc == KEY_KP_9)
cur_number_entry[number_entry_counter++] = '9';
//if we are entering the first character and we press `-` then the number is negative...
elif(
(//a minus key was pressed
kc == KEY_MINUS ||
kc == KEY_KP_MINUS
) && //AND
number_entry_counter == 0 && //the entry buffer is empty
(n_numbers_entered < 2) //and we have entered fewer than two numbers...
)
cur_number_entry[number_entry_counter++] = '-'; //the new number starts with minus.
//If backspace has been released and we have characters in the entry buffer,
//delete a character from that buffer.
elif(kc == KEY_BACKSPACE && number_entry_counter)
cur_number_entry[--number_entry_counter] = 0;
//BUT if the text entry buffer is empty, then we delete a number instead.
elif(kc == KEY_BACKSPACE && n_numbers_entered)
n_numbers_entered--;
//if a number entry is in progress and the user presses enter
//we finish the current number being entered.
elif((kc == KEY_KP_ENTER || kc == KEY_RETURN) && number_entry_counter)
finishNumber();
//but if we are not entering a number, and enter is pressed, and the number
//of numbers entered is zero, we should reset the state of the calculator.
elif((kc == KEY_KP_ENTER || kc == KEY_RETURN) && n_numbers_entered == 0)
memclear(cur_number_entry, 257);
number_entry_counter = 0;
n_numbers_entered = 0;
//we already know errflag is zero, so we dont need to reset it.
//If a plus key has been pressed (the equals key is also a plus key,
//and a stands for add)
elif((kc == KEY_EQUALS || kc == KEY_KP_PLUS || kc == KEY_A))
//If the user has not typed in any numbers, we want the add/sub/mul/div keys