< >

beads-lang is a programming language created in 2016 by Edward de Jong.

 #574on PLDB 7Years Old 0Books 0Papers

Example from the web:
```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 ```

#### Language features

Feature Supported Token Example
Semantic Indentation âœ“
`// A comment`
`// A comment`