dewbead

Pure-Dart parser for dewbead lines — the single-line output format of the dewdrop schedule-extraction model. Turns one line into a structured ParsedEvent. No runtime dependencies.

Design: two stages, NOW isolated

The model never knows the current time. So does the parser's stage 1:

  • parse(String) -> AstResult — pure syntax, never touches NOW.
  • resolve(DewbeadAst, DateTime now, {String? sourceText}) -> ParsedEvent — the only place NOW is used (weekday/week math, carry, year inference, endpoint computation, review flags).
  • parseDewbead(String, DateTime now, {String? sourceText}) -> ParseResult — runs both.

Parsing never throws: failures come back as Err(reason, rawInput).

Grammar (summary)

line       = "N" | type body
type       = "E" (event) | "T" (todo)
body       = when ["@" location] ">" title
when       = datetime ["~" endpoint]      // "~" is event-only
datetime   = t-anchor | n-anchor | absolute
t-anchor   = "t" [±Nd|±Nw] [".WD"] [hh:mm]   // date anchor; no h/m
n-anchor   = "n" {±N(d|h|m)}                 // now anchor; no clock
absolute   = (yyyy-)mm-dd [hh:mm]
endpoint   = "+"… duration | hh:mm (date from start) | absolute
  • All-day when no clock is present (n-anchors are never all-day).
  • Week = Monday start (ISO-8601). t+Nw = +N*7 days.
  • ClockEndpoint earlier than start rolls to the next day (22:00~02:00).
  • AbsoluteEndpoint with omitted year infers from start, not NOW.

Usage

import 'package:dewbead/dewbead.dart';

void main() {
  // Fixed `now` here so the output is deterministic; in your app pass DateTime.now().
  final now = DateTime(2026, 3, 10, 9, 0); // Tuesday, local
  switch (parseDewbead('Et+1d15:00~+1h@회의실>스탠드업', now)) {
    case Ok(:final value):
      print(value.start);    // 2026-03-11 15:00:00.000
      print(value.end);      // 2026-03-11 16:00:00.000
      print(value.location); // 회의실
    case Err(:final reason):
      print('parse failed: $reason');
  }
}

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.

Known limitations (v1)

  • No per-month day/leap validation: 02-30 rolls over via DateTime normalization (→ 03-02).
  • Model-owned review flags (vague_time, etc.) are not yet carried on the line; only code flags (inferredDate, inferredTitle, partialInfo) are emitted. inferredTitle normalization aggressiveness is still being tuned.

See dewbead-v1.md for the full format specification.

Libraries

dewbead
Parse dewdrop output lines (dewbead) into structured calendar events.