-
-
Notifications
You must be signed in to change notification settings - Fork 85
/
beads-lang.scroll
268 lines (226 loc) · 7.99 KB
/
beads-lang.scroll
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
import ../code/conceptPage.scroll
id beads-lang
name beads-lang
appeared 2016
creators Edward de Jong
tags pl
website http://beadslang.org/
fileExtensions beads
country United States
originCommunity Edward de Jong
reference https://docs.google.com/spreadsheets/d/12sTu7RT-s_QlAupY1v-3DfI1Mm9NEX5YMWWTDAKHLfc/edit#gid=0
example
beads level 1 program calculator
// flutter version available at: on github, look for: flutter-calculator-demo
// article: https://itnext.io/building-a-calculator-app-in-flutter-824254704fe6
const
C_OP = #3E424D // keycap fill for an operator like C
C_DIGIT = #6E6E6E // keycap fill for a digit
C_ARITH = #1A4C6E // keycap fill for arithmetic buttons
// warning: you must use the same unicode math chars in case statements later in arithmetic()
KEYCAPS = [ 'C', '±', '%', '÷', // tried \u207A\u2215\u208B instead of ± but it is ugly
'1', '2', '3', 'x',
'4', '5', '6', '−', // \u2212 is the minus sign
'7', '8', '9', '+',
'', '0', '.', '='] // '⌫' is U+232B, for future undo key
KEYCOLORS = [C_OP, C_OP, C_OP, C_ARITH,
C_DIGIT, C_DIGIT, C_DIGIT, C_ARITH,
C_DIGIT, C_DIGIT, C_DIGIT, C_ARITH,
C_DIGIT, C_DIGIT, C_DIGIT, C_ARITH,
C_OP, C_DIGIT, C_OP, C_ARITH]
SPACING = 3 // points between each cell
HAIR = "\u2009" // a thin space 200A is even thinner
record a_term
ss : str // the string containing the contents of the term
op : str // the operator in keycap string form '+', '-'...
record a_state
terms : array of a_term
termx : num // which term we are on
chain_op : str // chain operator char for repeated = presses
chain_val: num // chain value to repeat
fresh : yesno // if this is Y, then next digit will clear existing value
var g : a_state
calc main_init
g.terms[1].ss = ""
g.termx = 1
g.fresh = Y
vert slice main_draw
var cellsize = if b.box.width > b.box.height then b.box.height/8 else b.box.width/4
var result_v = cellsize*2 // need to enhance compiler so that expressions don't get converted
var keys_v = cellsize*5
var keys_maxh = min(b.box.width, cellsize*8) // don't go wider than 8 squares wide (double)
draw_rect(b.box, fill:#121F30)
skip 10 al
add result_v px d_result
add keys_v px d_keys(keys_maxh)
skip 10 al
draw d_result
draw_rect(b.box, fill:#0D161F)
// build the string out of the active terms
var s : str = ""
loop array:g.terms index:i
g.terms[i].ss &=> s
if g.terms[i].op <> U
HAIR & g.terms[i].op & HAIR &=> s // append the operator
if (s == "")
s = "0" // when nothing is entered into our expression, call it zero
draw_str(b.box, s, size:b.box.height*0.5, just:RIGHT, indent:20 pt, color:WHITE)
horz slice d_keys(
totwidth -- max width we allow for the grid, might be all of the space
)
// in landscape mode, we don't want the key grid to get too wide, looks bad
skip 10 al
add totwidth px d_keygrid
skip 10 al
table d_keygrid
horz slice
skip SPACING pt
loop reps:4
add 10 al
skip SPACING pt
vert slice
skip SPACING pt
loop reps:5
add 10 al
skip SPACING pt
// inside grid cell draw function, b has properties b.box, cell_seq, cell:a_xy, nrows, ncols
cell
draw_rect(b.box, fill:KEYCOLORS[b.cell_seq], corner:6 pt)
draw_str(b.box, KEYCAPS[b.cell_seq], size:b.box.height, color:WHITE)
track EV_TAP
// respond to the command
case b.cell_seq
| 1 // clear
do_clear
| 2 // plusminus - change the sign of the current term
sign_change(g.termx)
| 3 // percent - divide the current term by 100
do_percent
| 4, 8, 12, 16 // arithmetic operations
do_arith(KEYCAPS[b.cell_seq])
| 17 // future feature - backspace
nop // do_backspace
| 19 // period
do_period
| 20 // equals
do_equals
else
// must be a digit
add_digit(KEYCAPS[b.cell_seq])
// calc do_backspace
// log "backspace"
// reserved for future undo functionality
// this will test ability to read code and extend it
calc do_percent
// if the current term is empty do nothing
// apple's calculator takes the sequence 900+% and makes it 900^2 which is nutty
if g.terms[g.termx].ss <> ""
if g.termx > 1
// when we have two terms, like 300 + 20% we take 20% of the first term and replace
g.terms[g.termx].ss = to_str(eval(g.termx)*eval(g.termx-1)/100)
else
// we only have 1 term, so just divide it by 100
g.terms[g.termx].ss = to_str(eval(g.termx)/100)
g.fresh = Y
calc eval (
termx -- term index to evaluate
) : num // convert a term to a floating point number
var ss : str = g.terms[termx].ss
if ss == ""
return 0
return to_num(ss)
calc do_clear // clear the current term to blank.
// if the user has entered 123+, there is an empty current term will do nothing
g.terms[g.termx].ss = ""
calc sign_change(
tx -- term index
)
var old : str = g.terms[tx].ss
if old == ""
// we have no operand yet in the current term, so
// either ignore it or change previous operand's sign
// this is what apple's calculator does
if tx > 1
sign_change(tx-1) // change previous operand's sign. kinda weird really.
elif str_begins(old, "-")
g.terms[tx].ss = str_subset(old, from:2) // strip the minus
else
g.terms[tx].ss = '-' & old // prepend a minus
calc do_period
// ignore attempts to add more than one period
var list : array of num
str_find(g.terms[g.termx].ss, ".", list)
if tree_count(list) == 0
add_digit(".") // no period yet, so append one
calc do_equals
var val = eval(1) // start with the first term by itself
var val2
// if there is no second or later term use the chain operator and value
if tree_count(g.terms) < 2
// use repeat if we have one
if g.chain_op <> U
val = arithmetic(g.chain_op, val, g.chain_val)
else
// two or more terms to process
loop from:1 index:tx while:g.terms[tx+1].ss <> "" and g.terms[tx].op <> U
val2 = eval(tx+1)
// remember the last operator we used as our chaining value
g.chain_op = g.terms[tx].op
g.chain_val = val2
val = arithmetic(g.chain_op, val, val2)
// calculation done, convert the value back as if we entered it
trunc g.terms // zap the array
g.terms[1].ss = to_str(val) // replace our value
g.termx = 1
g.fresh = Y
calc arithmetic(
operand : str -- operation like "+", must match keycap
term1 : num
term2 : num
) : num -- resulting value
var result
case operand // note: these operators must match the keycaps
| '+'
term1 + term2 => result
| '−'
term1 - term2 => result
| 'x'
term1 * term2 => result
| '÷'
term1 / term2 => result
else
result = ERR
return result
calc add_digit(
digit : str // digit to append to current term
)
// if we are starting fresh, then erase what was there before
// we also replace the previous string if it was a leading zero
if g.fresh or g.terms[g.termx].ss == "0"
g.terms[g.termx].ss = "" // clear whatever was there
digit &=> g.terms[g.termx].ss
g.fresh = N
calc do_arith(
operand : str // '+', etc
)
if g.terms[g.termx].ss == ""
// we have no term, so treat that as replacing the previously entered operation
// and if this is the very beginning and we have no prior operation, ignore it
if g.termx == 1
// starting with a plus on an empty term is ignored
return
// multiple operators in a row, rewrite the previous operator
g.terms[g.termx-1].op = operand
else
// we did have a term, advance to the next term
g.terms[g.termx].op = operand
inc g.termx
g.terms[g.termx].ss = "" // empty term
lineCommentToken //
hasLineComments true
// A comment
hasComments true
// A comment
hasSemanticIndentation true
domainName beadslang.org
registered 2016