Skip to main content

tdsl_parser/
ast.rs

1/// Source span for error reporting.
2#[derive(Debug, Clone, PartialEq, Eq)]
3pub struct Span {
4    pub start: usize,
5    pub end: usize,
6}
7
8/// A node annotated with its source location.
9#[derive(Debug, Clone, PartialEq)]
10pub struct Spanned<T> {
11    pub node: T,
12    pub span: Span,
13}
14
15/// Root of the AST.
16#[derive(Debug, Clone, PartialEq)]
17pub struct File {
18    pub statements: Vec<Spanned<Statement>>,
19}
20
21/// DSL の各トップレベル文(statement)に対応する enum。
22#[derive(Debug, Clone, PartialEq)]
23pub enum Statement {
24    Timeline(TimelineBlock),
25    Lane(LaneDecl),
26    Group(GroupDecl),
27    Span(SpanDecl),
28    Event(EventDecl),
29    EventRange(EventRangeDecl),
30    Import(ImportBlock),
31    Map(MapBlock),
32    Template(TemplateBlock),
33    Apply(ApplyBlock),
34}
35
36// ─── Timeline ───────────────────────────────────────────────
37
38/// `timeline "名前" { ... }` ブロックのAST表現。
39#[derive(Debug, Clone, PartialEq)]
40pub struct TimelineBlock {
41    pub name: String,
42    pub title: Option<String>,
43    pub unit: Option<String>,
44    pub range: Option<RangeExpr>,
45    pub calendar: Option<String>,
46    pub color_map: Vec<(String, String)>,
47}
48
49/// 時刻リテラル。年・月・日の3精度を保持する。
50///
51/// `Year(y)` は `YYYY` または `-YYYY`(紀元前)、
52/// `YearMonth(y, m)` は `YYYY-MM`、
53/// `Date(y, m, d)` は `YYYY-MM-DD` に対応する。
54/// 紀元前(負の年)は文法上 year 精度のみ許容される(仕様書 §1.3)。
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum TimeValue {
57    Year(i64),
58    YearMonth(i64, u8),
59    Date(i64, u8, u8),
60}
61
62impl TimeValue {
63    /// 年部分のみ取り出す(month/day 精度の情報は失われる)。
64    pub fn year(&self) -> i64 {
65        match self {
66            TimeValue::Year(y) => *y,
67            TimeValue::YearMonth(y, _) => *y,
68            TimeValue::Date(y, _, _) => *y,
69        }
70    }
71
72    pub fn month(&self) -> Option<u8> {
73        match self {
74            TimeValue::Year(_) => None,
75            TimeValue::YearMonth(_, m) => Some(*m),
76            TimeValue::Date(_, m, _) => Some(*m),
77        }
78    }
79
80    pub fn day(&self) -> Option<u8> {
81        match self {
82            TimeValue::Year(_) | TimeValue::YearMonth(_, _) => None,
83            TimeValue::Date(_, _, d) => Some(*d),
84        }
85    }
86
87    /// 比較用のタプル `(year, month_or_0, day_or_0)`。
88    /// `Eq` の意味(精度の違いを保持)と整合させるため、`PartialOrd` 実装には依らず
89    /// 呼び出し側でこの関数を使って明示的にタプル順序で比較する。
90    pub fn to_sortable(&self) -> (i64, u8, u8) {
91        match self {
92            TimeValue::Year(y) => (*y, 0, 0),
93            TimeValue::YearMonth(y, m) => (*y, *m, 0),
94            TimeValue::Date(y, m, d) => (*y, *m, *d),
95        }
96    }
97}
98
99impl std::fmt::Display for TimeValue {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        match self {
102            TimeValue::Year(y) => write!(f, "{y}"),
103            TimeValue::YearMonth(y, m) => write!(f, "{y:04}-{m:02}"),
104            TimeValue::Date(y, m, d) => write!(f, "{y:04}-{m:02}-{d:02}"),
105        }
106    }
107}
108
109/// `start..end` 形式の時間範囲式。
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct RangeExpr {
112    pub start: TimeValue,
113    pub end: TimeValue,
114}
115
116// ─── Lane ───────────────────────────────────────────────────
117
118/// `lane "ラベル" as id { ... }` 宣言のAST表現。
119#[derive(Debug, Clone, PartialEq)]
120pub struct LaneDecl {
121    pub label: String,
122    pub alias: Option<String>,
123    pub kind: Option<String>,
124    pub order: Option<i64>,
125}
126
127/// `group "名前" { lane ... }` 宣言のAST表現。
128#[derive(Debug, Clone, PartialEq)]
129pub struct GroupDecl {
130    pub label: String,
131    pub lanes: Vec<LaneDecl>,
132}
133
134// ─── Items ──────────────────────────────────────────────────
135
136/// `span <lane> <start>..<end> "ラベル" { ... }` 宣言のAST表現。
137#[derive(Debug, Clone, PartialEq)]
138pub struct SpanDecl {
139    pub lane_ref: String,
140    pub start: TimeValue,
141    pub end: TimeValue,
142    pub label: String,
143    pub props: ItemProps,
144}
145
146/// `event <lane> <time> "ラベル" { ... }` 宣言のAST表現。
147#[derive(Debug, Clone, PartialEq)]
148pub struct EventDecl {
149    pub lane_ref: String,
150    pub time: TimeValue,
151    pub label: String,
152    pub props: ItemProps,
153}
154
155/// `event_range <lane> <start>..<end> "ラベル" { ... }` 宣言のAST表現。
156#[derive(Debug, Clone, PartialEq)]
157pub struct EventRangeDecl {
158    pub lane_ref: String,
159    pub start: TimeValue,
160    pub end: TimeValue,
161    pub label: String,
162    pub props: ItemProps,
163}
164
165/// アイテム共通の省略可能プロパティ(`tags`, `source`, `id`, `origin`)。
166#[derive(Debug, Clone, PartialEq, Default)]
167pub struct ItemProps {
168    pub tags: Vec<String>,
169    pub source: Option<SourceRef>,
170    pub id: Option<String>,
171    pub origin: Option<String>,
172}
173
174/// `source <prefix>:<qid>` 形式の出典参照(例: `source wd:Q7209`)。
175#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct SourceRef {
177    pub prefix: String,
178    pub qid: String,
179}
180
181// ─── Import ─────────────────────────────────────────────────
182
183/// `import <source_type> as <alias> { ... }` ブロックのAST表現。
184#[derive(Debug, Clone, PartialEq)]
185pub struct ImportBlock {
186    pub source_type: String,
187    pub alias: Option<String>,
188    pub items: Vec<ImportItem>,
189    pub policy: Option<ReimportPolicy>,
190}
191
192/// `import` ブロック内の個別エントリ(`entity` または `query`)。
193#[derive(Debug, Clone, PartialEq)]
194pub enum ImportItem {
195    /// `entity QXXX as alias` — 単一エンティティのインポート。
196    Entity { qid: String, alias: Option<String> },
197    /// `query "SPARQL" as alias` — SPARQL クエリで複数エンティティを一括インポート。
198    Query {
199        query: String,
200        alias: Option<String>,
201    },
202}
203
204/// フィールド別インポート優先度戦略。
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum FieldStrategy {
207    /// 手動設定値を優先する。
208    Manual,
209    /// Wikidata 取得値を優先する。
210    Wikidata,
211    /// 両方をマージする。
212    Merge,
213}
214
215/// `policy field_priority { ... }` ブロックのAST表現。
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217pub struct FieldPriorityConfig {
218    pub label: FieldStrategy,
219    pub time: FieldStrategy,
220    pub tags: FieldStrategy,
221}
222
223impl Default for FieldPriorityConfig {
224    fn default() -> Self {
225        Self {
226            label: FieldStrategy::Manual,
227            time: FieldStrategy::Wikidata,
228            tags: FieldStrategy::Merge,
229        }
230    }
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum ReimportPolicy {
235    MergeBySource,
236    OverwriteImported,
237    KeepManual,
238    FieldPriority(FieldPriorityConfig),
239}
240
241// ─── Template / Apply ───────────────────────────────────────
242
243/// Named reusable map pattern.
244#[derive(Debug, Clone, PartialEq)]
245pub struct TemplateBlock {
246    pub name: String,
247    pub alias: Option<String>,
248    pub target_type: MapTargetType,
249    pub props: Vec<MapProp>,
250}
251
252/// Applies a template to an import alias with optional overrides.
253#[derive(Debug, Clone, PartialEq)]
254pub struct ApplyBlock {
255    pub template_alias: String,
256    pub import_alias: String,
257    /// Overriding props (currently only lane).
258    pub overrides: Vec<MapProp>,
259}
260
261// ─── Map ────────────────────────────────────────────────────
262
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum MapTargetType {
265    Span,
266    Event,
267    EventRange,
268}
269
270#[derive(Debug, Clone, PartialEq)]
271pub struct MapBlock {
272    pub source_ref: String,
273    pub target_type: MapTargetType,
274    pub props: Vec<MapProp>,
275}
276
277#[derive(Debug, Clone, PartialEq)]
278pub enum MapProp {
279    Lane(String),
280    Start(MapExpr),
281    End(MapExpr),
282    Time(MapExpr),
283    Label(LabelExpr),
284    Tags(Vec<String>),
285    Filter(FilterExpr),
286    /// `expand claim(P39);` — expands multiple non-deprecated statements into separate items.
287    Expand(ClaimCall),
288}
289
290#[derive(Debug, Clone, PartialEq)]
291pub struct MapExpr {
292    pub fallbacks: Vec<MapFallback>,
293}
294
295/// A single fallback element in a `??` chain: either a claim expression or an integer literal.
296#[derive(Debug, Clone, PartialEq)]
297pub enum MapFallback {
298    Claim(ClaimExpr),
299    Literal(i64),
300}
301
302#[derive(Debug, Clone, PartialEq)]
303pub struct ClaimExpr {
304    pub claim: ClaimCall,
305    /// Qualifier property to access (e.g. `"P580"` for `.qualifier(P580)`).
306    /// When `Some`, the qualifier snak of the main claim is resolved instead of the mainsnak.
307    pub qualifier: Option<String>,
308    pub accessor: Option<String>,
309    /// Year offset applied after claim resolution (e.g. `+1`, `-30`).
310    pub offset: Option<i32>,
311}
312
313#[derive(Debug, Clone, PartialEq, Eq)]
314pub struct ClaimCall {
315    pub property: String,
316}
317
318#[derive(Debug, Clone, PartialEq)]
319pub struct LabelExpr {
320    pub fallbacks: Vec<LabelRef>,
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
324pub struct LabelRef {
325    pub lang: String,
326}
327
328// ─── Filter expressions (for map `filter` clause) ───────────
329
330#[derive(Debug, Clone, PartialEq)]
331pub enum FilterExpr {
332    And(Box<FilterExpr>, Box<FilterExpr>),
333    Or(Box<FilterExpr>, Box<FilterExpr>),
334    Not(Box<FilterExpr>),
335    Compare {
336        lhs: FilterOperand,
337        op: CompareOp,
338        rhs: FilterOperand,
339    },
340    StringMatch {
341        lhs: LabelRef,
342        op: StringMatchOp,
343        rhs: String,
344    },
345}
346
347#[derive(Debug, Clone, Copy, PartialEq, Eq)]
348pub enum CompareOp {
349    Eq,
350    NotEq,
351    Lt,
352    Le,
353    Gt,
354    Ge,
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358pub enum StringMatchOp {
359    Contains,
360    StartsWith,
361}
362
363#[derive(Debug, Clone, PartialEq)]
364pub enum FilterOperand {
365    Claim(ClaimExpr),
366    Int(i64),
367    Null,
368}