From 9ba419479378a5e827f9e815a4faaf38983b1e13 Mon Sep 17 00:00:00 2001 From: historia Date: Fri, 26 Jul 2024 05:21:32 -0400 Subject: Removing < and > operators because they do not have much real world use. Replacing these with ranges indicated with a dash as documented in the README. Partially implemented a shunting yard algorithm to parse parentheses but will likely simplify this and remove support for parentheses outside of date groups. The complex example cases with parens aren't realistic. --- src/then.nim | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 19 deletions(-) (limited to 'src/then.nim') diff --git a/src/then.nim b/src/then.nim index 67700a4..be3e813 100644 --- a/src/then.nim +++ b/src/then.nim @@ -1,15 +1,20 @@ -import std/[times, os, strutils, strbasics, strscans, sets], parseopt +import std/[times, os, strutils, strbasics, strscans, sets, re, deques], parseopt const VERSION = "0.1" -const defaultConfigDir = expandTilde("~/.config/then/") -const defaultConfigFile = defaultConfigDir & "then.toml" -const defaultCalendarFile = defaultConfigDir & "calendar.then" +const configDir = expandTilde("~/.config/then/") +const defaultConfigFile = configDir & "then.toml" +const defaultCalendarFile = configDir & "calendar.then" + +type + Token = object + token: string + precedence: int # Creates a default configuration file if it doesn't exist proc createDefaultConfigFile() = if not fileExists(defaultConfigFile): - if not dirExists(defaultConfigDir): - createDir(defaultConfigDir) + if not dirExists(configDir): + createDir(configDir) writeFile(defaultConfigFile, """ [config] directory = "~/.config/then/" @@ -18,10 +23,9 @@ proc createDefaultConfigFile() = # Creates a default calendar file if it doesn't exist proc createDefaultCalendarFile() = if not fileExists(defaultCalendarFile): - if not dirExists(defaultConfigDir): - createDir(defaultConfigDir) + if not dirExists(configDir): + createDir(configDir) writeFile(defaultCalendarFile, """ - # Default calendar file # Add your events here """) @@ -52,7 +56,12 @@ proc printVersion() = proc checkType(cond: string): string = if cond.len == 4 and parseInt(cond) != 0: return "year" if cond.len in [1, 2] and parseInt(cond) != 0: return "day" - if cond in ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].toHashSet: return "month" + if cond in DefaultLocale.MMM.toHashSet or cond in DefaultLocale.MMMM.toHashSet: return "month" + if cond in DefaultLocale.ddd.toHashSet or cond in DefaultLocale.dddd.toHashSet: return "dayofweek" + if find(cond, re"\d{1,2}:\d{2}") > -1: return "time" + if find(cond, re"\dw") > -1: return "firstweek" + if find(cond, re"\dl") > -1: return "lastweek" + if find(cond, re"J%\d([-+]\d)?") > -1: return "julian" return "unknown" proc checkCond(cond: string, day: DateTime): bool = @@ -65,6 +74,15 @@ proc checkCond(cond: string, day: DateTime): bool = of "month": if cond == day.format("MMM"): return true if cond == day.format("MMMM"): return true + of "dayofweek": + if cond == day.format("ddd"): return true + if cond == day.format("dddd"): return true + of "time": + return true + of "firstweek": + discard +# Uh oh we didn't think about this hard enough + of "unknown": echo "UNKNOWN THING ", cond return false @@ -74,21 +92,88 @@ proc getConditions(dateStr: string): seq[string] = conditions = dateStr.rsplit(' ') return conditions +# TODO simplify?? Would it be easier to identify conditions and replace them with TRUE/FALSE then just process the string? + +# Shunting yard to parse conditions, parens, and logical operators +# Does NOT parse out individual conditions yet because some individual +# conditions (like 1W) require context of surrounding conditions. +# The logic around parentheses is a little weird because conditions like +# "<(March 15)" contain parentheses that we don't want to throw out +proc parseDateToEvents(dateStr: string): seq[string] = + var holdingStack = initDeque[Token]() + var resultStack = initDeque[Token]() + var newToken: string = "" + for c in dateStr: + echo c + case c: + of '(': + if newToken.len == 1: + if newToken[0] == '>' or newToken[0] == '<': + newToken.add(c) + else + echo "ERROR: newToken exists but we got an open paren?" + else: + holdingStack.addLast(Token(token: "(", precedence: 0)) + of ')': + if newToken.len > 0: # Closing token like <(March 15). Push the token. + resultStack.addLast(Token(token: newToken, precedence: 0)) + newToken = "" + else: # Logical closing paren. Pop stack until (. + while holdingStack.len > 0 and holdingStack.peekLast.token != "(": + if holdingStack.len == 0: + echo "UH OH, ERROR MISMATCHED PARENS" + resultStack.addLast(holdingStack.popLast) + discard holdingStack.popLast # Pop the ( + of '!': + holdingStack.addLast(Token(token: "!", precedence: 3)) + of '|': + while holdingStack.len > 0 and holdingStack.peekLast.precedence > 2: + resultStack.addLast(holdingStack.popLast) + holdingStack.addLast(Token(token: "|", precedence: 2)) + of ' ': + if newToken.len > 1 and newToken[1] == '(' and newToken[newToken.len-1] != ')': # Middle of token like <(March 15). + newToken.add(c) + else: # End of new token, implicit & + # Add newToken to result + resultStack.addLast(Token(token: newToken, precedence: 0)) + newToken = "" + # Add implicit & to holding + while holdingStack.len > 0 and holdingStack.peekLast.precedence > 1: + resultStack.addLast(holdingStack.popLast) + holdingStack.addLast(Token(token:"&", precedence: 1)) + of '>','<',':','-','a'..'z','A'..'Z','0'..'9': + newToken.add(c) + else: + echo "Weird character in dateStr: ", c + discard + # Push last token + if newToken.len > 0: + resultStack.addLast(Token(token: newToken, precedence: 0)) + # Clear the holding stack + while holdingStack.len > 0: + var t = holdingStack.popLast + if t.token != "(": + resultStack.addLast(t) + echo resultStack + + proc getEvents(cal: seq[string], day: DateTime): seq[string] = var events: seq[string] for line in cal: - var (success, dateStr, eventStr) = scanTuple(line, "$+,$*") + var (success, dateStr, eventStr) = scanTuple(line, "$*,$*") if success: dateStr.strip eventStr.strip - let conditions = getConditions(dateStr) - var match = true - for c in conditions: - if checkCond(c, day) == false: - match = false - break - if match: - events.add(eventStr) + discard parseDateToEvents(dateStr) + #let conditions = getConditions(dateStr) + # var match = true + # for c in conditions: + # c = c.toLower.capitalizeAscii + # if checkCond(c, day) == false: + # match = false + # break + # if match: + # events.add(eventStr) else: echo "WARNING: Cannot parse line: ", line return events -- cgit v1.2.3