Syntax
This page lists the rules of the format. For examples, see Diary Format.
Lines
- Files are UTF-8 text.
- Windows line endings work. Chow writes Unix line endings.
- Whitespace is spaces or tabs.
;starts a comment that runs to the end of the line.- Blank lines are ignored.
- Servings, recipe lines, and the lines of a day are indented. A continued nutrient list is indented further. Everything else starts in the first column.
- A serving or unit can contain spaces. It ends at
=, atx, or at the end of the line.
Grammar
The grammar is in EBNF. ws is one or more spaces or tabs.
diary = [ marker ] { line } ;
marker = "format" ws "2" [ trail ] newline ; (* the root file only *)
line = blank | comment | include | food_block | recipe_block
| container | target | timed | day_block ;
comment = ws? ";" text newline ;
include = "include" ws path [ trail ] newline ;
food_block = food_header serving_line { serving_line } ; (* at least one serving *)
food_header = "food" ws handle ws name [ ws ";" ws tags ] newline ;
serving_line = ws serving ws "=" [ ws nutrients ] [ trail ] newline
{ continuation } ;
continuation = deeper nutrients [ trail ] newline ; (* no "=" *)
recipe_block = date ws "recipe" ws handle ws name [ trail ] newline
{ recipe_line } ; (* at least one ingredient *)
recipe_line = ws ( "container" ws container_id
| "weight" ws number ws weight_unit
| "servings" ws number
| handle ws portion ) [ trail ] newline ;
container = "container" ws container_id ws name ws number ws weight_unit
[ trail ] newline ;
target = date ws "target" ws nutrients [ trail ] newline
{ continuation } ;
timed = date ws record ;
day_block = date [ trail ] newline { ws record } ; (* a date alone on its line *)
record = time ws ( entry | inline | burn | reading ) ;
entry = handle ws portion [ trail ] newline ;
inline = name ws serving ws "=" [ ws nutrients ] [ trail ] newline
{ continuation } ;
burn = "burn" ws number ws "kcal" ws name [ trail ] newline ;
reading = "metric" ws metric_id ws number ws metric_unit [ trail ] newline ;
portion = number ws "x" ws serving (* 2 x 170 g *)
| serving ; (* 340 g, 1 tsp *)
serving = number ws unit ; (* "170 g", "1 medium onion" *)
unit = word { ws word } ;
handle = idchar { idchar } ; (* not a keyword *)
container_id = idchar { idchar } ;
metric_id = idchar { idchar } ;
idchar = letter | digit | "_" | "-" ; (* ASCII *)
metric_unit = char { char } ; (* no whitespace *)
weight_unit = unit ; (* one that folds to g, kg, mg, oz or lb *)
name = '"' { char - '"' } '"' ;
tags = tag { ws tag } ;
tag = key ":" ( bareword | '"' { char - '"' } '"' ) ;
nutrients = nutrient { ws nutrient } ;
nutrient = ( number | number "%" ) ws nutrient_id ; (* "150 kcal", "15% iron" *)
nutrient_id = idchar { idchar } ;
date = digit digit digit digit "-" digit digit "-" digit digit ;
time = digit digit ":" digit digit ; (* 24-hour, HH:MM, local *)
number = digit { digit } [ "." digit { digit } ] ; (* never negative *)
trail = ws? ";" text ;
Errors
Chow rejects a diary with any of these, and reports the file and line:
- A line that matches no record
- A date that isn’t on the calendar, or a time outside
00:00to23:59 - A number, portion, or serving it can’t read
- An unknown nutrient id, or a percentage of a nutrient with no daily value
- A
basisother thanfda2016orrdi1993 - A burn in a unit other than
kcal - An unknown metric, or a unit the metric doesn’t accept
- A serving outside a food, or a food with no serving
- A reserved word as a handle
- An inline entry with
x - A
formatline anywhere but the top ofmain.chow - Two foods or recipes with the same handle
- Two servings of one food that are the same
- Two containers with the same id, or two targets on the same date
- An
includethat loops back to its own file, or names a file Chow can’t read - A recipe with no ingredient, a repeated
container,weight, orservingsline, acontainerwithoutweight, or aweightnot in a weight unit - A
servingscount or weight that isn’t above zero
An entry with an unknown handle or a portion that doesn’t match is not an error. It is unresolved.