diff --git a/lib/lex.go b/lib/lex.go index 885ec08..29de485 100644 --- a/lib/lex.go +++ b/lib/lex.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "strings" + "time" "unicode" ) @@ -43,6 +44,8 @@ func (t tokenType) String() string { return "t_variable" case t_bool: return "t_bool" + case t_duration: + return "t_duration" default: panic(fmt.Sprintf("unknown token type: %v", t)) } @@ -64,6 +67,7 @@ const ( t_imaginary_number // an imaginary number t_variable // e.g. @var_name, a variable name. t_bool // a boolean token (true|false) + t_duration // a duration (e.g.: 1s, 2h45m, 900ms) ) type stateFn func(*lexer) stateFn @@ -404,7 +408,8 @@ func lexNumber(l *lexer) stateFn { imaginary := l.accept("i") r := l.next() if isAlphaNumeric(r) { - return lexErrorf("unexpected alphanum in lexNumber: %c", r) + l.keep(r) + return lexDuration } l.unread(r) if imaginary { @@ -415,6 +420,63 @@ func lexNumber(l *lexer) stateFn { return lexRoot } +func lexDuration(l *lexer) stateFn { + r := l.next() + switch { + case r == '\n', r == ';': + _, err := time.ParseDuration(string(l.buf)) + if err == nil { + l.emit(t_duration) + return lexRoot + } + l.emit(t_string) + return lexRoot + case unicode.IsSpace(r): + _, err := time.ParseDuration(string(l.buf)) + if err == nil { + l.emit(t_duration) + return lexRoot + } + l.keep(r) + return lexNameOrString + case r == ':': + _, err := time.ParseDuration(string(l.buf)) + if err == nil { + l.emit(t_duration) + } else { + l.emit(t_string) + } + l.keep(r) + l.emit(t_object_separator) + return lexRoot + case isSpecial(r): + _, err := time.ParseDuration(string(l.buf)) + if err == nil { + l.emit(t_duration) + } else { + l.emit(t_string) + } + l.unread(r) + return lexRoot + case r == '\\': + l.unread(r) + return lexNameOrString + case r == eof: + _, err := time.ParseDuration(string(l.buf)) + if err == nil { + l.emit(t_duration) + } else { + l.emit(t_string) + } + return nil + case unicode.IsGraphic(r): + l.keep(r) + return lexDuration + default: + return lexErrorf("unhandled character type in lexDuration: %c", r) + } +} + func isAlphaNumeric(r rune) bool { return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) } diff --git a/lib/tests/lex/11.in b/lib/tests/lex/11.in new file mode 100644 index 0000000..81184fd --- /dev/null +++ b/lib/tests/lex/11.in @@ -0,0 +1,10 @@ +300ms +-1.5h +2h45m +9ns +18us +45µs +900ms +275s +89m +927h diff --git a/lib/tests/lex/11.out b/lib/tests/lex/11.out new file mode 100644 index 0000000..aa09ebb --- /dev/null +++ b/lib/tests/lex/11.out @@ -0,0 +1,10 @@ +{t_duration 300ms} +{t_duration -1.5h} +{t_duration 2h45m} +{t_duration 9ns} +{t_duration 18us} +{t_duration 45µs} +{t_duration 900ms} +{t_duration 275s} +{t_duration 89m} +{t_duration 927h}