mast/parser/command_js.peg
Anish Lakhwara b268065d13 feat: done command parsing
starting to come together
I realized I'm going to have to seperate selection and filter state
And do actions based on that, some how show that different context
depending on which action is selected
2025-02-23 19:58:00 -08:00

163 lines
3.6 KiB
Plaintext

{
function makeCommand(type, filters, parts) {
const validParts = parts || [];
// Extract pure description (without attributes)
const description = validParts
.filter(part => part.type === 'text')
.map(part => part.value)
.join(' ')
.trim();
// Collect all attributes
const attributes = validParts
.filter(part => part.type !== 'text')
.filter(part => part !== null);
const project = validParts
.filter(part => part.type === 'project')
.map(part => part.value)
.join('')
.trim();
const tags = validParts
.filter(part => part.type === 'tag')
.map(part => part.value);
// Store original parts for reconstruction
const originalParts = validParts.filter(part => part !== null);
let validFilters;
if (!!filters) {
validFilters = filters.flat();
} else {
validFilters = [];
}
return {
type: type,
description: description || [],
attributes: attributes || [],
filters: validFilters,
project: project,
tags: JSON.stringify(tags),
parts: parts || [],
reconstruct: function() {
const filterStr = this.filters.map(f => f.reconstruct()).join(',');
const partsStr = this.parts.map(p => p.reconstruct()).join(' ');
return [filterStr, this.type, partsStr].filter(Boolean).join(' ');
}
};
}
}
Start = AddCommand / DoneCommand / ExplicitFilterCommand / ImplicitFilterCommand
// ADD COMMAND
AddCommand = "add" _ parts:(Part / _)+ EOF {
return makeCommand('add', null, parts.filter(p => p !== null));
}
// DONE COMMAND
DoneCommand = "done" _ filters:Filters EOF {
return makeCommand('done', filters, null);
}
ExplicitFilterCommand = filters:Filters _ "filter" _* moreFilters:Filters* EOF {
return makeCommand('filter', [...filters, ...moreFilters], null);
}
ImplicitFilterCommand = filters:Filters+ EOF {
return makeCommand('filter', filters, null);
}
Filters = first:Filter rest:(_ Filter)* {
return [first, ...rest.map(r => r[1])];
}
IdRange = start:Integer "-" end:Integer {
const ids = [];
for (let i = start; i <= end; i++) {
ids.push(i);
}
return ids;
}
SingleId = id:Integer {
return [id];
}
Filter = IdFilter / Tag / Project / Priority / Due
IdFilter = first:(IdRange / SingleId) rest:("," (IdRange / SingleId))* trailing:"," ? {
const ids = [first, ...rest.map(r => r[1])].flat();
return {
type: 'id',
ids: ids,
reconstruct: function() { return this.ids.join(','); }
};
}
Part = Attribute / TextPart
TextPart = chars:Word {
return {
type: "text",
value: chars,
reconstruct: function() { return this.value; }
};
}
Attribute = Due / Tag / Project / Priority
Due = "@" value:TimeValue {
return {
type: "due",
value: value,
reconstruct: function() { return `@${this.value}`; }
};
}
Project = ("pro:" / "project:" / "+") value:Word {
return {
type: "project",
value: value,
reconstruct: function() { return `+${this.value}`; }
};
}
Priority = "priority:" value:[HML] {
return {
type: "priority",
value: value,
reconstruct: function() { return `priority:${this.value}`; }
};
}
Tag = "#" value:Word {
return {
type: "tag",
value: value,
reconstruct: function() { return `#${this.value}`; }
};
}
Integer = digits:[0-9]+ {
return parseInt(digits.join(''), 10);
}
TimeValue = chars:[0-9:]+ ("am" / "pm")? {
return chars.join('') + (text() || '');
}
Word = chars:[a-zA-Z0-9_-]+ {
return chars.join('');
}
_ = [ \t]+ {
return null;
}
EOF = !.