dewbead 0.2.0
dewbead: ^0.2.0 copied to clipboard
Pure-Dart parser for dewbead schedule documents — turns dewdrop model output into structured, timezone-safe ParsedEvents. No runtime dependencies.
dewbead #
Pure-Dart parser for dewbead lines — the single-line output format of the
dewdrop schedule-extraction model. Turns a document into structured
ParsedEvents. No runtime dependencies.
Design: two stages, NOW isolated #
The model never knows the current time, and neither does the parser's stage 1:
parse(String document)— pure syntax, never touches NOW. Splits on;;and returns({List<DewbeadAst> asts, List<RecordError> errors}).resolve(DewbeadAst, DateTime now, {String? sourceText}) -> ParsedEvent— the only place NOW is used (day/week/month math, carry, year inference, endpoint computation, review flags).parseDewbead(String document, DateTime now, {String? sourceText}) -> ParseResult— runs both, returning{ events, errors }.
Parsing never throws: per-record failures come back as RecordError(reason, rawInput) in ParseResult.errors, while the records that parsed are in
ParseResult.events.
Grammar (summary) #
document = "N" | record {";;" record}
record = type datetime ["~" endpoint] ["@" location] ">" title
type = "E" (event) | "T" (todo)
datetime = date_part ["/" time_part] // "/" present => has a time
date_part = "t" [±N] // today + N days
| "w" [±N] "." weekday // week ±N, weekday required
| "mo" [±N] "." dom // month ±N, day required
| (yyyy-)mm-dd // ISO absolute
time_part = hh:mm // absolute clock
| "n" {±N(d|h|m)} // from NOW (t offset 0 only)
endpoint = "+"… duration | datetime // "~" is event-only
- All-day when there is no
/time_part. - Anchor is the unit:
t+1= 1 day,w+1= 1 week,mo+1= 1 month. - Week = Monday start (ISO-8601).
- Endpoints are
duration(~+90m) or a fulldatetimewith an explicit date (~t+2/02:00). There is no clock-only inheritance and no midnight-rollover correction — cross-day ranges name their end date. - No past-time correction:
w.mon/mo.5resolve as-is even if already past. AbsoluteMM-DDstill infers a year (rolls forward if past).
Usage #
import 'package:dewbead/dewbead.dart';
void main() {
// Fixed `now` here for deterministic output; in your app pass DateTime.now().
final now = DateTime(2026, 3, 10, 9, 0); // Tuesday, local
final result = parseDewbead('Et+1/15:00~+1h@회의실>스탠드업;;Tw.fri>보고서', now);
for (final e in result.events) {
print('${e.type.name} ${e.title} ${e.start ?? e.end}');
}
// event 스탠드업 2026-03-11 15:00:00.000
// todo 보고서 2026-03-13 00:00:00.000
for (final err in result.errors) {
print('parse failed: ${err.reason} on "${err.rawInput}"');
}
}
Time zones #
start/end are naive local DateTimes carrying the wall-clock in NOW's
zone. The library never converts to UTC, so toIso8601String() never emits
Z. If you need an explicit zone, apply it at your app boundary.
Review flags #
resolve attaches code-owned hints to each event:
partialInfo— empty title.inferredDate— a year was filled into anMM-DD, or anmoday was clamped to the month's last day.inferredTitle— the (normalized) title is absent from the suppliedsourceText. Only evaluated whensourceTextis provided; omitting it skips the check, so the flag is never set in that case.
Known limitations #
- No per-month day/leap validation at parse time:
02-30rolls over viaDateTimenormalization (→03-02).moanchors clamp to month-end instead. inferredTitlenormalization aggressiveness is still being tuned.
See dewbead-v2.md for the full format specification. The legacy v1 format is
documented in dewbead-v1.md and is available in the 0.1.0 release.