Compare commits

...

2 Commits

Author SHA1 Message Date
cf21e4c7a7 Implement basic types and task wrapper 2025-01-14 17:13:55 +01:00
739a5d9721 Implement the tasks grammar 2025-01-14 17:13:32 +01:00
5 changed files with 296 additions and 3 deletions

View File

@@ -1,6 +1,119 @@
start = task
start = _ @(task / line) _
task = "-" _ "[" status:. "]" { return status; }
/**************************************************************************************************************************************/
line = data:.+ {
return {
type: "line",
data: data.join("")
}
}
/**************************************************************************************************************************************/
task = "-" _ "[" status:. "]" _ label:label meta:meta* {
return {
type: "task",
data: {
status,
label,
meta
}
};
}
label = text:([^⏬🔽🔼⏫🔺➕⏳🛫📅✅❌🔁🏁🆔⛔⏰]*) {
return text.join("").trim()
}
meta = _ @(date / recurrence / delete / priority / dependency / reminder) _
/**************************************************************************************************************************************/
priority = priority:priorityIcon {
return {
feature: "priority",
priority
}
}
/**************************************************************************************************************************************/
delete = type:deleteIcon _ action:[a-zA-Z]+ {
return {
feature: "delete",
action: action.join("").trim()
}
}
/**************************************************************************************************************************************/
reminder = reminderIcon _ time:(longTime / shortTime)? {
return {
feature: "reminder",
time
}
}
longTime = hour:[0-9]|1..2| ":" minute:[0-9]|1..2| {
return `${hour.join("").padStart(2, '0')}:${minute.join("").padStart(2, '0')}`;
}
shortTime = hour:[0-9]|1..2| {
return `${hour.join("").padStart(2, '0')}:00`;
}
/**************************************************************************************************************************************/
recurrence = type:recurrenceIcon _ rec:[a-zA-Z0-9 ]+ {
return {
feature: "recurrence",
rule: rec.join("").trim()
}
}
/**************************************************************************************************************************************/
date = type:dateIcon _ date:dateLiteral {
return {
feature: "date",
type,
date
};
}
dateLiteral = year:([0-9]|4|) "-" month:([0-9]|1..2|) "-" day:([0-9]|1..2|) {
return `${year.join("")}-${month.join("").padStart(2, '0')}-${day.join("").padStart(2, '0')}`;
}
/**************************************************************************************************************************************/
dependency = type:dependencyIcon _ deps:([a-zA-Z0-9]+)|..,(_ "," _)| {
return {
feature: "dependency",
type,
deps: deps.map(x => x.join(""))
}
}
/**************************************************************************************************************************************/
allIcons = dependencyIcon / recurrenceIcon / deleteIcon / reminderIcon / dateIcon / priority
dependencyIcon = "🆔" / "⛔"
recurrenceIcon = "🔁"
deleteIcon = "🏁"
reminderIcon = "⏰"
dateIcon = ""
/"⏳"
/"🛫"
/"📅"
/"✅"
/"❌"
priorityIcon = "⏬"
/"🔽"
/"🔼"
/"⏫"
/"🔺"
_ "whitespace"
= [ \t\n\r]*
= [ \t\r]*

15
src/parser/index.ts Normal file
View File

@@ -0,0 +1,15 @@
import {parse as rawParse} from "../generated/grammar/task";
import { ParseResult } from "../types/grammar/task";
import { Task } from "../types/task";
import { LazyTask } from "./task";
export function parse(line: string): Task|undefined {
const item = rawParse(line) as ParseResult;
if (item.type === 'task') {
return new LazyTask(item.data);
}
return undefined;
}

88
src/parser/task.ts Normal file
View File

@@ -0,0 +1,88 @@
import { ParsedTask, ParsedTaskDate, ParsedTaskDependency, ParsedTaskMeta } from "../types/grammar/task";
import { Task, TaskPriority } from "../types/task";
export class LazyTask implements Task {
#parsed: ParsedTask;
#dates: ParsedTaskDate[];
constructor(task: ParsedTask) {
this.#parsed = task;
this.#dates = task.meta.filter(x => x.feature === 'date');
}
get status(): string {
return this.#parsed.status;
}
get label(): string {
return this.#parsed.label;
}
get priority(): TaskPriority {
const priority = this.#parsed.meta.find(x => x.feature === 'priority')?.priority;
if(!priority) {
return "normal";
}
return {
'⏬': 'lowest',
'🔽': 'low',
'🔼': 'medium',
'⏫': 'high',
'🔺': 'highest'
}[priority] as TaskPriority;
}
get createdDate(): string|undefined {
return this.#dates.find(d => d.type === '')?.date;
}
get startDate(): string|undefined {
return this.#dates.find(d => d.type === '🛫')?.date;
}
get scheduledDate(): string|undefined {
return this.#dates.find(d => d.type === '⏳')?.date;
}
get dueDate(): string|undefined {
return this.#dates.find(d => d.type === '📅')?.date;
}
get completedDate(): string|undefined {
return this.#dates.find(d => d.type === '✅')?.date;
}
get cancelledDate(): string|undefined {
return this.#dates.find(d => d.type === '❌')?.date;
}
get recurrencyRule(): string|undefined {
return this.#parsed.meta.find(x => x.feature === 'recurrence')?.rule;
}
get onDelete(): string|undefined {
return this.#parsed.meta.find(x => x.feature === 'delete')?.action;
}
get id(): string|undefined {
return this.#features<ParsedTaskDependency>("dependency").find(x => x.type === '🆔')?.deps?.[0];
}
#features <T = ParsedTaskMeta>(feature: ParsedTask['meta'][0]['feature']): T[] {
return this.#parsed.meta.filter(x => x.feature === feature) as T[]
}
get dependsOn(): string[] {
return this.#features<ParsedTaskDependency>("dependency").find(x => x.type === '⛔')?.deps || [];
}
get reminder(): string|undefined {
return this.#parsed.meta.find(x => x.feature === 'reminder')?.time;
}
toString() {
return `Task[status=${this.status}, label=${this.label}, priority=${this.priority}, created=${this.createdDate}, start=${this.startDate}, scheduled=${this.scheduledDate}, due=${this.dueDate}, completed=${this.completedDate}, cancelled=${this.cancelledDate}, recurrence=${this.recurrencyRule}, onDelete=${this.onDelete}, id=${this.id}, dependsOn=${this.dependsOn}, reminder=${this.reminder}]`
}
}

53
src/types/grammar/task.ts Normal file
View File

@@ -0,0 +1,53 @@
export type ParseResult = {
type: 'line';
data: string;
} | {
type: 'task';
data: ParsedTask;
};
export type ParsedTask = {
status: string;
label: string;
meta: ParsedTaskMeta[];
}
export type ParsedTaskMeta =
| ParsedTaskPriority
| ParsedTaskRecurrence
| ParsedTaskDelete
| ParsedTaskDate
| ParsedTaskDependency
| ParsedTaskReminder;
export type ParsedTaskPriority = {
feature: 'priority';
priority: '⏬'|'🔽'|'🔼'|'⏫'|'🔺';
}
export type ParsedTaskRecurrence = {
feature: 'recurrence';
rule: string;
}
export type ParsedTaskDelete = {
feature: 'delete';
action: string;
}
export type ParsedTaskDate = {
feature: 'date';
type: ''|'⏳'|'🛫'|'📅'|'✅'|'❌';
date: string;
}
export type ParsedTaskDependency = {
feature: 'dependency';
type: '🆔'|'⛔';
deps: string[];
}
export type ParsedTaskReminder = {
feature: 'reminder';
time: string;
}

24
src/types/task.ts Normal file
View File

@@ -0,0 +1,24 @@
export type Task = {
status: string;
label: string;
priority: TaskPriority;
createdDate?: string;
startDate?: string;
scheduledDate?: string;
dueDate?: string;
completedDate?: string;
cancelledDate?: string;
recurrencyRule?: string;
onDelete?: string;
id?: string;
dependsOn?: string[];
reminder?: string;
}
export type TaskPriority =
| 'lowest'
| 'low'
| 'normal'
| 'medium'
| 'high'
| 'highest';