Directives are either transformative (pebblestream:from(Sheet1)) or annotative (pebblestream:sorted:asc).
Transformative directives look like this:
pebblestream:from(Accounts)
pebblestream:filter("Amount", ">0")
pebblestream:group-sum("Account ID", "Amount")
and go in a comment in the top-left cell of a sheet.
Multi-directive example
Purchases           
From To Amount Verified
0    1  30     TRUE
5    1  10     FALSE
9    2  20     TRUE
4    1  50     TRUE
5    2  90     FALSE
Transactions
Amount From To Verified
60     9    2  TRUE
-10    5    1  TRUE
Total
pebblestream:from(Purchases, Transactions)
pebblestream:filter("Verified")
pebblestream:sort("To")
pebblestream:group-sum("To", "Amount")
To Amount
1  70
2  80
The directives are executed in this order:
- from
- cartesian-product
- join
- outer-join
- filter
- sort
- group
- group-unique
- group-sum
- group-ratio
- header-map
- split
Column arguments are header value strings in double quotes ("Account", "Amount").
Sheet arguments are either in excel notation (Sheet1, '2010''s Data') or double quotes ("Sheet1", "2010's Data").
pebblestream:from(Sheet1, ...)
Stitches the sheets together (headers only appear once). Only the columns with headers the sheets have in common are included.
Example
Regional
Name ID Account
Alex 17 Checking
John 2  Savings
Local
ID Account
3  Checking
64 Brokerage
5  Brokerage
District
Name ID Balance Last Name
John 4  545     Jones
IDs
pebblestream:from(Regional, Local, District)
ID
17
2
3
64
5
4
O(cells)
pebblestream:cartesian-product(Sheet1, ...)
Output will have a row for every combination of rows in the input sheets.
Example
Cartesian-product example.xlsx
Customers
Customer ID Balance
c0          45
c1          3
c2          0
Discounts
Discount ID Percentage
d0          0.5
d1          0.1
Customers x Discounts
pebblestream:from(Customers)
pebblestream:cartesian-product(Discounts)
Customer ID Balance Discount ID Percentage
c0          45      d0          0.5
c0          45      d1          0.1
c1          3       d0          0.5
c1          3       d1          0.1
c2          0       d0          0.5
c2          0       d1          0.1
O(rows^sheets)
pebblestream:join(SheetRight, [ ["Key Left", "Key Right",] ...])
Does an inner join with SheetRight keyed on either the header names they have in common or the arguments, which come in pairs of first the left's header name and then the right's header name.
Example 1
Customers
Customer ID Name
0           John
1           James
2           Alex
Orders
Order ID Amount Customer ID
0        5      0
1        1      0
2        8      2
3        6      1
Customers + Orders
pebblestream:from(Customers)
pebblestream:join(Orders)
Name  Customer ID Order ID Amount
John  0           0        5
John  0           1        1
James 1           3        6
Alex  2           2        8
Example 2
Transfers
From To Amount
A0   A1 24
A0   A1 576
A2   A1 13
A1   A0 42
Taxable
Account From Account To Taxable
A0           A1         TRUE
A0           A2         FALSE
A1           A0         FALSE
A1           A2         TRUE
A2           A0         FALSE
A2           A1         TRUE
Transfers + Taxable
pebblestream:from(Transfers)
pebblestream:join(Taxable, "From", "Account From", "To", "Account To")
Amount From To Taxable
24     A0   A1 TRUE
576    A0   A1 TRUE
13     A2   A1 TRUE
42     A1   A0 FALSE
O(cells)
pebblestream:outer-join(SheetRight, [ ["Key Left", "Key Right",] ...])
Does a left outer join with SheetRight keyed on either the header names they have in common or the arguments, which come in pairs of first the left's header name and then the right's header name.
Example 1
Customers
Customer ID Name
0           John
1           James
2           Alex
3           Linda
Orders
Order ID Amount Customer ID
0        5      0
1        1      0
2        8      2
3        6      1
4        2      7
Customers + Orders
pebblestream:from(Customers)
pebblestream:outer-join(Orders)
Name  Customer ID Order ID Amount
John  0           0        5
John  0           1        1
James 1           3        6
Alex  2           2        8
Linda 3
Example 2
Transfers
From To Amount
A0   A1 24
A0   A1 576
A2   A1 13
A1   A0 42
A3   A0 78
Taxable
Account From Account To Taxable
A0           A1         TRUE
A0           A2         FALSE
A1           A0         FALSE
A1           A2         TRUE
A2           A0         FALSE
A2           A1         TRUE
A1           A3         FALSE
Transfers + Taxable
pebblestream:from(Transfers)
pebblestream:outer-join(Taxable, "From", "Account From", "To", "Account To")
Amount From To Taxable
24     A0   A1 TRUE
576    A0   A1 TRUE
13     A2   A1 TRUE
42     A1   A0 FALSE
78     A3   A0
O(cells)
pebblestream:filter("Active")
Only includes rows where "Active" is true.
pebblestream:filter("Amount", ">0", "Color", "blue", "Amount", "<="&"Balance", ...)
Only includes rows where "Amount" is greater than 0, "Color" is "blue", and "Amount" is less than or equal to the value in that row's "Balance" column.
If filter() appears multiple times, it includes rows where any of the filter() directives qualify (OR).
Simple example
Accounts
ID Verified Balance
7  TRUE     0
1  TRUE     1
5  FALSE    54
17 TRUE     35
Verified
pebblestream:from(Accounts)
pebblestream:filter("Verified")
ID
7
1
17
Complex example
Heights
Height Age Age last transaction
190    24  18
300    7   7
100    3   3
40     1   2
400    16  16
167    40  38
50     22  20
Unusual
pebblestream:from(Heights)
pebblestream:filter("Height", ">200")
pebblestream:filter("Height", "<60", "Age", ">3")
pebblestream:filter("Age last transaction", ">"&"Age")
Height Age Age last transaction
300    7   7
40     1   2
400    16  16
50     22  20
O(cells)
pebblestream:sort("Last name":desc, "First name":asc, ...)
Sorts the rows by "Last name" descending with ties broken by "First name" ascending. Default is ascending.
Can't be used with group.
Simple example
Accounts
ID Balance
5  44
1  40
8  848
3  12
Sorted
pebblestream:from(Accounts)
pebblestream:sort("ID")
ID Balance
1 40
3 12
5 44
8 848
Complex example
Transactions
First Last      Date
John  Jones     9/2/2020
Adam  Jones     1/1/2000
Cathy Albertson 5/7/1990
John  Jones     5/4/2013
Sorted
pebblestream:from(Transactions)
pebblestream:sort("Last", "First", "Date":desc)
First Last      Date
Cathy Albertson 5/7/1990
Adam  Jones     1/1/2000
John  Jones     9/2/2020
John  Jones     5/4/2013
O(cells*log(cells))
pebblestream:group("Account ID", "Date", ...)
All rows with matching "Account ID" and "Date" values are put next to each other.
Any of the following directives are calculated relative to their group:
Can't be used with sort, group-unique, group-sum, or group-ratio.
Multi-directive example
Group multi-directive example.xlsx
Transactions
Account ID Account User # Amount Coupon factor IP Address  Tags
1          2              894    0.9           127.0.38.9  home verified
1          1              45981  0.8           127.0.5.8   office verified
1          1              15     0.95          127.0.5.8   home
1          2              5958   1             127.0.48.2  verified home
2          1              255    0.7           127.0.59.12 verified mobile
Grouped
pebblestream:from(Transactions)
pebblestream:group("Account ID", "Account User #")
pebblestream:sum("Amount")
pebblestream:mult("Coupon factor")
pebblestream:equal("IP Address", "Same IP?")
pebblestream:regex("Tags", ".*\\bverified\\b.*", "Verified?")
pebblestream:coalesce()
Account ID Account User # Amount Coupon factor Same IP? Verified?
1          2              6852   0.9           FALSE    TRUE
1          1              45996  0.76          TRUE     FALSE
2          1              255    0.7           TRUE     TRUE
Solo example
Logins
Account ID Account User # Date
1          1              1/1/2015
3          1              5/6/2015
2          1              6/8/2015
1          2              2/20/2018
1          1              3/15/2020
2          1              4/15/2020
Logins by user
pebblestream:from(Logins)
pebblestream:group("Account ID", "Account User #")
Account ID Account User # Date
1          1              1/1/2015
1          1              3/15/2020
3          1              5/6/2015
2          1              6/8/2015
2          1              4/15/2020
1          2              2/20/2018
Users
pebblestream:from(Logins)
pebblestream:group("Account ID", "Account User #")
pebblestream:coalesce()
Account ID Account User #
1          1
3          1
2          1
1          2
O(cells)
pebblestream:coalesce()
After group calculations are performed, coalesce each group into a single row.
Requires group.
See "Group solo example" for an example.
O(1)
pebblestream:sum("Amount"[, "Result"])
The "Amount" rows are summed together. If "Result" is provided, a new column called "Result" is created with the value. Otherwise, the "Amount" column is updated.
Requires group.
Example
Transactions
First Last      Transfer
John  Jones     -60
Adam  Jones     186
Cathy Albertson 48794
John  Jones     545
Cathy Albertson 8777
Totals
pebblestream:from(Transactions)
pebblestream:group("Last", "First")
pebblestream:sum("Transfer")
pebblestream:coalesce()
First Last      Transfer
John  Jones     485
Adam  Jones     186
Cathy Albertson 57571
O(cells)
pebblestream:mult("Factor"[, "Result"])
The "Factor" rows are multiplied together. If "Result" is provided, a new column called "Result" is created with the value. Otherwise, the "Factor" column is updated.
Requires group.
Example
Discounts
Discount ID Account ID Discount
1           1          0.9
2           2          0.8
3           1          0.95
Account discounts
pebblestream:from(Discounts)
pebblestream:group("Account ID")
pebblestream:mult("Discount")
pebblestream:coalesce()
Account ID Discount
1          0.855
2          0.8
O(cells)
pebblestream:equal("Amount"[, "Result"])
The "Amount" rows are checked for whether they are all equal. If "Result" is provided, a new column called "Result" is created with the value. Otherwise, the "Amount" column is updated.
Requires group.
Example
Logins
Account ID Account User # IP Address
1          2              127.0.38.9
1          1              127.0.5.8
1          1              127.0.5.8
1          2              127.0.48.2
2          1              127.0.59.12
Same IP
pebblestream:from(Logins)
pebblestream:group("Account ID", "Account User #")
pebblestream:equal("IP Address", "Same IP?")
pebblestream:coalesce()
Account ID Account User # Same IP?
1          2              FALSE
1          1              TRUE
2          1              TRUE
O(cells)
pebblestream:ratio("Amount"[, "Result"])
The "Amount" rows are summed together and the ratio of each row compared to the total calculated. If "Result" is provided, a new column called "Result" is created with the value. Otherwise, the "Amount" column is updated.
Requires group.
Incompatible with coalesce.
Example
Transactions
First Last      Value
John  Jones     3
Adam  Jones     5
John  Jones     1
Cathy Albertson -1
Cathy Albertson 1
John  Jones     0
Ratio
pebblestream:from(Transactions)
pebblestream:group("Last", "First")
pebblestream:ratio("Value", "Ratio")
First Ratio
John  0.75
John  0.25
John  0.0
Adam  1.0
Cathy 0
Cathy 0
O(cells)
pebblestream:regex("Name", "[0-9]*MyRegex.*"[, "Result"])
Checks whether every row in "Name" matches the provided regular expression. If "Result" is provided, a new column called "Result" is created with the result. Otherwise, the "Name" column is updated.
Requires group.
Example
Transactions
Account ID Account User # Amount Tags
1          2              894    home verified
1          1              45981  office verified
1          1              15     home
1          2              5958   verified home
2          1              255    verified mobile
Transactions with verify info
pebblestream:from(Transactions)
pebblestream:group("Account ID", "Account User #")
pebblestream:regex("Tags", ".*\\bverified\\b.*", "User always verified?")
Account ID Account User # Amount User always verified?
1          2              894    TRUE
1          2              5958   TRUE
1          1              45981  FALSE
1          1              15     FALSE
2          1              255    TRUE
O(cells)
pebblestream:group-unique("Last name", "First name", ...)
All rows with the same "Last name" and "First name" are condensed to one and only columns "Last name" and "First name" are included.
Can't be used with group, group-sum, or group-ratio.
Example
Transactions
First Last      Date
John  Jones     9/2/2020
Adam  Jones     1/1/2000
Cathy Albertson 5/7/1990
John  Jones     5/4/2013
Cathy Albertson 12/12/1999
Names
pebblestream:from(Transactions)
pebblestream:group-unique("Last", "First")
First Last
John  Jones
Adam  Jones
Cathy Albertson
O(cells)
pebblestream:group-sum("Last name", "First name", ... "Amount")
All rows with the same "Last name" and "First name" are condensed to one with their "Amount"'s summed together. Only columns "Last name", "First name", and "Amount" are included.
Can't be used with group, group-unique, or group-ratio.
Example
Transactions
First Last      Transfer
John  Jones     -60
Adam  Jones     186
Cathy Albertson 48794
John  Jones     545
Cathy Albertson 8777
Totals
pebblestream:from(Transactions)
pebblestream:group-sum("Last", "First", "Transfer")
First Last      Transfer
John  Jones     485
Adam  Jones     186
Cathy Albertson 57571
O(cells)
pebblestream:group-ratio("Last name", "First name", ... "Amount")
All rows with the same "Last name" and "First name" are put next to each other with their "Amount" values replaced by their "Amount" value divided by their group's summed "Amount" value (0 if sum is 0).
Can't be used with group, group-unique, or group-sum.
Example
Deposits
First Last      Deposit
John  Jones     20
Adam  Jones     186
Cathy Albertson 48794
John  Jones     545
Cathy Albertson 8777
Ratio deposited
pebblestream:from(Deposits)
pebblestream:group-ratio("Last", "First", "Deposit")
First Last      Deposit
John Jones      0.03539823
John Jones      0.96460177
Adam Jones      1
Cathy Albertson 0.847544771
Cathy Albertson 0.152455229
O(cells)
pebblestream:header-map("Header Old", "Header New"[, ...])
Header Old is renamed to Header New, specified in pairs.
Example
Raw Accounts
ID Balance Last Name
0  4233    Jones
1  21      Smith
2  0       George
3  33      Jones
DB-friendly Accounts
pebblestream:from("Raw Accounts")
pebblestream:header-map("Balance", "VALUE", "Last Name", "OWNER_LAST")
ID OWNER_LAST VALUE
0  Jones      4233
1  Smith      21
2  George     0
3  Jones      33
O(cols)
pebblestream:split("Last name", "First name", ... SheetSplit)
SheetSplit in the output will be a list of sheets where each has a group of rows with the same "Last name" and "First name" values.
Example
Transactions
First Last      Date
John  Jones     9/2/2020
Adam  Jones     1/1/2000
Cathy Albertson 5/7/1990
John  Jones     5/4/2013
Cathy Albertson 12/12/1999
Splitter
pebblestream:from(Transactions)
pebblestream:split("First", "Last", SheetPerName)
First Last      Date
John  Jones     9/2/2020
Adam  Jones     1/1/2000
Cathy Albertson 5/7/1990
John  Jones     5/4/2013
Cathy Albertson 12/12/1999
SheetPerName (array of sheets)
[First Last  Date
 John  Jones 9/2/2020
 John  Jones 5/4/2013,
 First Last  Date
 Adam  Jones 1/1/2000,
 First Last      Date
 Cathy Albertson 5/7/1990
 Cathy Albertson 12/12/1999]
O(cells)
Annotative directives look like pebblestream:sorted:asc or pebblestream:id and go in a comment in the top row of a sheet. Depending on the directive, it may matter which column it's placed in.
- allowed-values
- div/0
- do-not-compile
- fail
- grouped
- header-count
- hint
- id
- identical-columns
- ignore
- match-rows
- not-blank
- out
- sorted
- stop
- stop-immediate
- unique
- warn
pebblestream:allowed-values[:value]+
An error will be thrown if this column has a value besides those listed. If value is a string, it should be written between "'s. For blank, put blank.
Allowed-values example.xlsx
O(rows)
pebblestream:div/0:(spreadsheet|worksheet|column)[:42]
Whenever a #DIV/0! would happen, 0 is returned. spreadsheet, worksheet, and column apply only to the spreadsheet, worksheet, or column in which they are present. If :<number> is provided at the end, that number is returned instead of 0.
Div/0 example 1.xlsx
Div/0 example 2.xlsx
O(1)
pebblestream:do-not-compile
This spreadsheet will not exist to the compiler. Any sheet referencing it will cause a compile error.
Do-not-compile example.xlsx
O(1)
pebblestream:fail
If any value in this column is TRUE, the runtime will throw an exception.
Fail example.xlsx
O(1)
pebblestream:grouped
If any value in this column appears multiple times but not next to each other, the runtime will throw an exception.
Grouped example.xlsx
O(rows)
pebblestream:header-count:2
Treats this sheet as having 2 header rows.
Header-count example.xlsx
O(1)
pebblestream:hint:key[:value]
Stores a map of Sheet name -> key -> list of values for each hint directive found in the sheet. If value isn't specified, uses nil.
Hint example.xlsx
O(1)
pebblestream:id[:name]
Every row has to be unique when looking at the cells in columns with id and the same name (where no name is also a name).
ID example 1.xlsx
ID example 2.xlsx
O(rows)
pebblestream:identical-columns:Sheet1[: ...]
Every column in Sheet1 ... with the same header name as this one must have the same values as this one.
Identical-columns example.xlsx
O(rows)
pebblestream:ignore
This sheet won't be generated by the runtime. Can only be used on isolated worksheets.
Hint example.xlsx
O(1)
pebblestream:match-rows:Sheet1
This sheet's last row will be dragged down by the runtime to match the number of rows in Sheet1.
Match-rows example.xlsx
O(rows_matching)
pebblestream:not-blank
The runtime will throw an exception if any value in this column is blank.
Not-blank example.xlsx
O(rows)
pebblestream:out
This sheet will be included in the output sheets when run by the runtime instead of whatever would have been output by default.
Out example.xlsx
O(1)
pebblestream:sorted[:(asc|desc)][:<number>]
The runtime will throw an exception if this column is not sorted, ascending by default. If multiple are placed in the same sheet, the columns will be considered multiple keys for the same sorted check, and they should each have a number to indicate their order for breaking ties (lower = earlier).
Sorted example.xlsx
O(rows)
pebblestream:stop
The runtime will drag down this worksheet row by row until a cell in this column is TRUE.
Stop example.xlsx
O(rows_until_stop)
pebblestream:stop-immediate
Same as pebblestream:stop, except when this column is TRUE, the rest of the row to the right is not completed.
Stop-immediate example.xlsx
O(rows_until_stop)
pebblestream:unique
Every row must have a unique value.
Unique example.xlsx
O(rows)
pebblestream:warn
When this column is TRUE, its address will be included in a :warnings entry of the runtime's returned output context.
Warn example.xlsx
O(1)