Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions example/example.ox
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ kb-clean-and-press: 24kg 5x3 "each arm"
kb-turkish-getup: 24kg 5x1 "each arm"
@end

2024-01-18 * run: 30min "easy pace"
2024-01-18 * run: PT30M "easy pace"

@session
2024-01-19 * Full Body
Expand All @@ -120,7 +120,7 @@ pullup: BW 4x8
kb-swing: 24kg 4x20
@end

2024-01-20 * run: 45min "long slow distance"
2024-01-20 * run: PT45M "long slow distance"

# Week 2 - Volume Phase
@session
Expand All @@ -144,7 +144,7 @@ kb-swing: 32kg 10x15
box-jump: BW 10x5
@end

2024-01-25 * run: 25min
2024-01-25 * run: PT25M

@session
2024-01-26 * KB Focus
Expand Down Expand Up @@ -177,7 +177,7 @@ burpee: BW 5x15
box-jump: BW 5x5
@end

2024-02-01 * run: 35min
2024-02-01 * run: PT35M

# Week 4 - Mixed Work
@session
Expand All @@ -196,7 +196,7 @@ kb-turkish-getup: 32kg 5x1 "each arm"
kb-snatch: 32kg 5x3 "each arm"
@end

2024-02-04 * run: 40min "tempo run"
2024-02-04 * run: PT40M "tempo run"

@session
2024-02-05 * Circuit Training
Expand Down Expand Up @@ -227,7 +227,7 @@ kb-snatch: 24kg 8x6 "each arm"
kb-clean-and-press: 24kg 6x5 "each arm"
@end

2024-02-10 * run: 30min
2024-02-10 * run: PT30M

# Week 6 - Deload
@session
Expand All @@ -250,7 +250,7 @@ kb-swing: 24kg 5x15
kb-turkish-getup: 24kg 5x1 "each arm"
@end

2024-02-15 * run: 20min "easy"
2024-02-15 * run: PT20M "easy"

# Week 7 - 5/3/1 Cycle 1 Week 1
@session
Expand All @@ -275,7 +275,7 @@ deadlift: 185lb/210lb/235lb 5/5/8
kb-swing: 32kg 5x15
@end

2024-02-22 * run: 35min
2024-02-22 * run: PT35M

@session
2024-02-23 * KB & Plyometrics
Expand Down Expand Up @@ -307,7 +307,7 @@ deadlift: 200lb/225lb/250lb 3/3/7
kb-swing: 32kg 5x15
@end

2024-02-29 * run: 40min
2024-02-29 * run: PT40M

@session
2024-03-01 * KB Heavy
Expand Down Expand Up @@ -338,7 +338,7 @@ note: "Cycle 1 Week 3"
deadlift: 215lb/240lb/265lb 5/3/8 "grip was tough on the last set"
@end

2024-03-07 * run: 45min "felt strong"
2024-03-07 * run: PT45M "felt strong"

@session
2024-03-08 * Conditioning
Expand Down Expand Up @@ -368,7 +368,7 @@ note: "Cycle 1 Week 4 - Deload"
deadlift: 135lb/155lb/175lb 3x5
@end

2024-03-14 * run: 25min "recovery pace"
2024-03-14 * run: PT25M "recovery pace"

@session
2024-03-15 * Light KB
Expand Down Expand Up @@ -398,7 +398,7 @@ deadlift: 195lb/220lb/245lb 5/5/10
kb-swing: 32kg 5x20
@end

2024-03-21 * run: 30min
2024-03-21 * run: PT30M

@session
2024-03-22 * KB Workout
Expand Down Expand Up @@ -429,7 +429,7 @@ note: "Cycle 2 Week 2"
deadlift: 210lb/235lb/260lb 3/3/9
@end

2024-03-28 * run: 40min
2024-03-28 * run: PT40M

@session
2024-03-29 * Full Body Circuit
Expand Down Expand Up @@ -461,7 +461,7 @@ deadlift: 225lb/250lb/275lb 5/3/10 "new rep PR!"
kb-swing: 32kg 5x20
@end

2024-04-04 * run: 35min
2024-04-04 * run: PT35M

@session
2024-04-05 * KB & Plyometrics
Expand All @@ -487,7 +487,7 @@ box-jump: BW 8x5
burpee: BW 5x20
@end

2024-04-10 * run: 50min "long run"
2024-04-10 * run: PT50M "long run"

@session
2024-04-11 * KB Intensive
Expand Down Expand Up @@ -519,7 +519,7 @@ deadlift: 245lb 5x3
box-jump: BW 5x3
@end

2024-04-17 * run: 30min
2024-04-17 * run: PT30M

@session
2024-04-18 * KB Complex
Expand Down Expand Up @@ -551,7 +551,7 @@ overhead-press: 95lb 5x10
pullup: BW 5x10
@end

2024-04-24 * run: 45min
2024-04-24 * run: PT45M

@session
2024-04-25 * KB & Bodyweight
Expand Down Expand Up @@ -589,7 +589,7 @@ deadlift: 155lb/185lb/215lb/245lb/275lb/285lb 5/5/3/3/1/1 "new 1RM: 285lb"
kb-swing: 32kg 5x15
@end

2024-05-02 * run: 30min "recovery"
2024-05-02 * run: PT30M "recovery"

@session
2024-05-03 * KB Celebration
Expand Down
72 changes: 72 additions & 0 deletions tests/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,78 @@ class TestRepSchemes:
# Can add if needed.


def _parse_str(content: str):
"""Parse a raw .ox string and return (tree, diagnostics)."""
import tree_sitter_ox
from tree_sitter import Language, Parser
from ox.lint import collect_diagnostics

language = Language(tree_sitter_ox.language())
parser = Parser(language)
tree = parser.parse(bytes(content, encoding="utf-8"))
return tree, collect_diagnostics(tree)


class TestDurationToken:
"""Test that ISO 8601 PT duration strings are accepted by the grammar."""

def test_minutes_only(self):
_, diags = _parse_str("2025-01-10 * run: PT30M\n")
assert not diags

def test_minutes_and_seconds(self):
_, diags = _parse_str("2025-01-10 * run: PT30M15S\n")
assert not diags

def test_hours_only(self):
_, diags = _parse_str("2025-01-10 * run: PT1H\n")
assert not diags

def test_hours_and_minutes(self):
_, diags = _parse_str("2025-01-10 * run: PT1H30M\n")
assert not diags

def test_hours_minutes_seconds(self):
_, diags = _parse_str("2025-01-10 * run: PT1H30M15S\n")
assert not diags

def test_fractional_seconds(self):
_, diags = _parse_str("2025-01-10 * run: PT30M15.5S\n")
assert not diags

def test_seconds_only(self):
_, diags = _parse_str("2025-01-10 * run: PT45S\n")
assert not diags

def test_old_time_format_rejected(self):
_, diags = _parse_str("2025-01-10 * run: 30min\n")
assert len(diags) > 0


class TestWeighInEntry:
"""Test that weigh_in_entry nodes parse correctly."""

def test_weight_only(self):
_, diags = _parse_str("2025-01-10 W 185lb\n")
assert not diags

def test_weight_with_timestamp(self):
_, diags = _parse_str("2025-01-10 W 185lb T06:30\n")
assert not diags

def test_weight_with_scale(self):
_, diags = _parse_str('2025-01-10 W 185lb "bathroom scale"\n')
assert not diags

def test_weight_with_timestamp_and_scale(self):
_, diags = _parse_str('2025-01-10 W 83.5kg T06:30 "home scale"\n')
assert not diags

def test_kg_weight(self):
_, diags = _parse_str("2025-01-10 W 83.5kg\n")
assert not diags


class TestQueryEntryParsing:
"""Test that query_entry nodes are parsed into StoredQuery objects."""

Expand Down
28 changes: 22 additions & 6 deletions tree-sitter-ox/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ module.exports = grammar({
$.exercise_block,
$.template_block,
$.note_entry,
$.query_entry
$.query_entry,
$.weigh_in_entry,
),

comment: ($) => /#[^\n]*/,
Expand All @@ -46,6 +47,17 @@ module.exports = grammar({
"\n"
),

// Weigh-in entry: date W weight optional(T06:30) optional("scale name")
weigh_in_entry: ($) =>
seq(
field("date", $.date),
"W",
field("weight", $.weight),
optional(field("time_of_day", $.time_of_day)),
optional(field("scale", $.quoted_string)),
"\n"
),

// Query entry: date query "name" "SQL"
query_entry: ($) =>
seq(
Expand Down Expand Up @@ -120,7 +132,7 @@ module.exports = grammar({

date: ($) => /\d{4}-\d{2}-\d{2}/,

flag: ($) => choice("*", "!", "W"),
flag: ($) => choice("*", "!"),

// Item name (before colon)
item: ($) => /[^\s:]+/,
Expand All @@ -134,13 +146,13 @@ module.exports = grammar({
// Text until newline (for metadata values, standalone notes)
text_until_newline: ($) => /[^\n]+/,

// Details: combination of weights, reps, time, distance, quoted notes
// Details: combination of weights, reps, duration, distance, quoted notes
details: ($) =>
repeat1(
choice(
field("weight", $.weight),
field("rep_scheme", $.rep_scheme),
field("time", $.time),
field("duration", $.duration),
field("distance", $.distance),
field("note", $.quoted_string)
)
Expand All @@ -158,8 +170,12 @@ module.exports = grammar({

rep_scheme: ($) => /(\d+x\d+)|(\d+(\/\d+)+)/, // 4x4 or 5/5/5

// Time units: curated from pint's default_en.txt
time: ($) => /\d+(\.\d+)?(s|sec|second|min|minute|h|hr|hour|d|day|week|month|yr|year)/,
// ISO 8601 duration: PT followed by at least one component
// Examples: PT30M, PT30M15S, PT1H, PT1H30M, PT1H30M15S, PT30M15.5S
duration: ($) => /PT(\d+H(\d+M(\d+(\.\d+)?S)?)?|\d+M(\d+(\.\d+)?S)?|\d+(\.\d+)?S)/,

// 24-hour time of day with T prefix: T06:30
time_of_day: ($) => /T\d{2}:\d{2}/,

// Distance units: curated from pint's default_en.txt
distance: ($) => /\d+(\.\d+)?(m|meter|metre|km|kilometer|cm|centimeter|mm|millimeter|in|inch|ft|foot|yd|yard|mi|mile|nmi)/,
Expand Down
Loading