From f63877f247c8a77219350b25c867c526cb95e164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agn=C3=A8s=20Toulet?= <35176601+AgnesToulet@users.noreply.github.com> Date: Thu, 26 Mar 2020 10:26:47 +0100 Subject: [PATCH] Core: Add go-datemath library (#22978) --- go.mod | 1 + go.sum | 2 + pkg/extensions/main.go | 1 + .../timberio/go-datemath/.gitignore | 1 + .../timberio/go-datemath/CONTRIBUTING.md | 16 + .../github.com/timberio/go-datemath/LICENSE | 13 + .../github.com/timberio/go-datemath/README.md | 29 + .../timberio/go-datemath/datemath.go | 352 ++++++++ .../timberio/go-datemath/datemath.l | 144 ++++ .../timberio/go-datemath/datemath.l.go | 296 +++++++ .../timberio/go-datemath/datemath.y | 300 +++++++ .../timberio/go-datemath/datemath.y.go | 776 ++++++++++++++++++ vendor/github.com/timberio/go-datemath/go.mod | 3 + vendor/github.com/timberio/go-datemath/go.sum | 0 vendor/modules.txt | 2 + 15 files changed, 1936 insertions(+) create mode 100644 vendor/github.com/timberio/go-datemath/.gitignore create mode 100644 vendor/github.com/timberio/go-datemath/CONTRIBUTING.md create mode 100644 vendor/github.com/timberio/go-datemath/LICENSE create mode 100644 vendor/github.com/timberio/go-datemath/README.md create mode 100644 vendor/github.com/timberio/go-datemath/datemath.go create mode 100644 vendor/github.com/timberio/go-datemath/datemath.l create mode 100644 vendor/github.com/timberio/go-datemath/datemath.l.go create mode 100644 vendor/github.com/timberio/go-datemath/datemath.y create mode 100644 vendor/github.com/timberio/go-datemath/datemath.y.go create mode 100644 vendor/github.com/timberio/go-datemath/go.mod create mode 100644 vendor/github.com/timberio/go-datemath/go.sum diff --git a/go.mod b/go.mod index 2b15c3005ab..d0ae2ea2013 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 github.com/stretchr/testify v1.4.0 github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf + github.com/timberio/go-datemath v0.1.1-0.20200323150745-74ddef604fff github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329 github.com/uber/jaeger-client-go v2.20.1+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index 6ad50a695e2..3bc0a49ba4d 100644 --- a/go.sum +++ b/go.sum @@ -286,6 +286,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= +github.com/timberio/go-datemath v0.1.1-0.20200323150745-74ddef604fff h1:QCdUBuN+iKWAB9HqPTkBwyKPPUHDobJ2AuELSNZwd4o= +github.com/timberio/go-datemath v0.1.1-0.20200323150745-74ddef604fff/go.mod h1:m7kjsbCuO4QKP3KLfnxiUZWiOiFXmxj30HeexjL3lc0= github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329 h1:VBsKFh4W1JEMz3eLCmM9zOJKZdDkP5W4b3Y4hc7SbZc= github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329/go.mod h1:OBcG9bn7sHtXgarhUEb3OfCnNsgtGnkVf41ilSZ3K3E= github.com/uber/jaeger-client-go v2.20.1+incompatible h1:HgqpYBng0n7tLJIlyT4kPCIv5XgCsF+kai1NnnrJzEU= diff --git a/pkg/extensions/main.go b/pkg/extensions/main.go index a88f67ff9ed..a80d432dbbb 100644 --- a/pkg/extensions/main.go +++ b/pkg/extensions/main.go @@ -14,6 +14,7 @@ import ( _ "github.com/robfig/cron" _ "github.com/robfig/cron/v3" _ "github.com/stretchr/testify/require" + _ "github.com/timberio/go-datemath" _ "gopkg.in/square/go-jose.v2" ) diff --git a/vendor/github.com/timberio/go-datemath/.gitignore b/vendor/github.com/timberio/go-datemath/.gitignore new file mode 100644 index 00000000000..26520536f94 --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/.gitignore @@ -0,0 +1 @@ +y.output diff --git a/vendor/github.com/timberio/go-datemath/CONTRIBUTING.md b/vendor/github.com/timberio/go-datemath/CONTRIBUTING.md new file mode 100644 index 00000000000..62fb4e295a0 --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/CONTRIBUTING.md @@ -0,0 +1,16 @@ +# Contributing + +Bug fixes and other contributions via pull request greatly welcomed! + +This library relies on [goyacc](https://godoc.org/golang.org/x/tools/cmd/goyacc) and +[golex](https://godoc.org/modernc.org/golex) for parsing and evaluating datemath grammar. + +To install, run: + +* `go get golang.org/x/tools/cmd/goyacc modernc.org/golex` + +After modifying either the `datemath.l` or `datemath.y` you can rerun `go generate`. + +When in doubt on semantics of the library, [Elasticsearch's +implementation](https://www.elastic.co/guide/en/elasticsearch/reference/7.3/common-options.html#date-math) should be +considered the canonical specification. diff --git a/vendor/github.com/timberio/go-datemath/LICENSE b/vendor/github.com/timberio/go-datemath/LICENSE new file mode 100644 index 00000000000..740f831482b --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2019, Timber Technologies, Inc. + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/vendor/github.com/timberio/go-datemath/README.md b/vendor/github.com/timberio/go-datemath/README.md new file mode 100644 index 00000000000..71923de932c --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/README.md @@ -0,0 +1,29 @@ +# go-datemath + +[![GoDoc](https://godoc.org/github.com/timberio/go-datemath?status.svg)](http://godoc.org/github.com/timberio/go-datemath) +[![Circle CI](https://circleci.com/gh/timberio/go-datemath.svg?style=svg)](https://circleci.com/gh/timberio/go-datemath) +[![Go Report Card](https://goreportcard.com/badge/github.com/timberio/go-datemath)](https://goreportcard.com/report/github.com/timberio/go-datemath) +[![coverage](https://gocover.io/_badge/github.com/timberio/go-datemath?0 "coverage")](http://gocover.io/github.com/timberio/go-datemath) + +This library provides support for parsing datemath expressions compatibly with [Elasticsearch datemath +expressions](https://www.elastic.co/guide/en/elasticsearch/reference/7.3/common-options.html#date-math). These are +useful for allowing users to specify, and for encoding, relative dates. Examples: + +* `now+15m`: 15 minutes from now +* `now-1w+1d`: one day after on week ago +* `2015-05-05T00:00:00||+1M`: one month after 2019-05-05 + +These expressions will seem familiar if you have used Grafana or Kibana. + +Example usage: + +```go +expr, _ := datemath.Parse("now-15m") +fmt.Println(t.Time(datemath.WithNow(now))) +``` + +See [package documentation](http://godoc.org/github.com/timberio/go-datemath) for usage and more examples. + +## Development / Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/vendor/github.com/timberio/go-datemath/datemath.go b/vendor/github.com/timberio/go-datemath/datemath.go new file mode 100644 index 00000000000..c89bbc008bc --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/datemath.go @@ -0,0 +1,352 @@ +// Requires golang.org/x/tools/cmd/goyacc and modernc.org/golex +// +//go:generate goyacc -o datemath.y.go datemath.y +//go:generate golex -o datemath.l.go datemath.l + +/* +Package datemath provides an expression language for relative dates based on Elasticsearch's date math. + +This package is useful for letting end-users describe dates in a simple format similar to Grafana and Kibana and for +persisting them as relative dates. + +The expression starts with an anchor date, which can either be "now", or an ISO8601 date string ending with ||. This +anchor date can optionally be followed by one or more date math expressions, for example: + + now+1h Add one hour + now-1d Subtract one day + now/d Round down to the nearest day + +The supported time units are: + y Years + M Months + w Weeks + d Days + b Business Days (excludes Saturday and Sunday by default, use WithBusinessDayFunc to override) + h Hours + H Hours + m Minutes + s Seconds + +Compatibility with Elasticsearch datemath + +This package aims to be a superset of Elasticsearch's expressions. That is, any datemath expression that is valid for +Elasticsearch should evaluate in the same way here. + +Currently the package does not support expressions outside of those also considered valid by Elasticsearch, but this may +change in the future to include additional functionality. +*/ +package datemath + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +func init() { + // have goyacc parser return more verbose syntax error messages + yyErrorVerbose = true +} + +var missingTimeZone = time.FixedZone("MISSING", 0) + +type timeUnit rune + +const ( + timeUnitYear = timeUnit('y') + timeUnitMonth = timeUnit('M') + timeUnitWeek = timeUnit('w') + timeUnitDay = timeUnit('d') + timeUnitBusinessDay = timeUnit('b') + timeUnitHour = timeUnit('h') + timeUnitMinute = timeUnit('m') + timeUnitSecond = timeUnit('s') +) + +func (u timeUnit) String() string { + return string(u) +} + +// Expression represents a parsed datemath expression +type Expression struct { + input string + + mathExpression +} + +type mathExpression struct { + anchorDateExpression anchorDateExpression + adjustments []timeAdjuster +} + +func newMathExpression(anchorDateExpression anchorDateExpression, adjustments []timeAdjuster) mathExpression { + return mathExpression{ + anchorDateExpression: anchorDateExpression, + adjustments: adjustments, + } +} + +// MarshalJSON implements the json.Marshaler interface +// +// It serializes as the string expression the Expression was created with +func (e Expression) MarshalJSON() ([]byte, error) { + return []byte(strconv.Quote(e.String())), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface +// +// Parses the datemath expression from a JSON string +func (e *Expression) UnmarshalJSON(data []byte) error { + s, err := strconv.Unquote(string(data)) + if err != nil { + return err + } + + expression, err := Parse(s) + if err != nil { + return nil + } + + *e = expression + return nil +} + +// String returns a the string used to create the expression +func (e Expression) String() string { + return e.input +} + +// Options represesent configurable behavior for interpreting the datemath expression +type Options struct { + // Use this this time as "now" + // Default is `time.Now()` + Now time.Time + + // Use this location if there is no timezone in the expression + // Defaults to time.UTC + Location *time.Location + + // Use this weekday as the start of the week + // Defaults to time.Monday + StartOfWeek time.Weekday + + // Rounding to period should be done to the end of the period + // Defaults to false + RoundUp bool + + BusinessDayFunc func(time.Time) bool +} + +// WithNow use the given time as "now" +func WithNow(now time.Time) func(*Options) { + return func(o *Options) { + o.Now = now + } +} + +// WithStartOfWeek uses the given weekday as the start of the week +func WithStartOfWeek(day time.Weekday) func(*Options) { + return func(o *Options) { + o.StartOfWeek = day + } +} + +// WithLocation uses the given location as the timezone of the date if unspecified +func WithLocation(l *time.Location) func(*Options) { + return func(o *Options) { + o.Location = l + } +} + +// WithRoundUp sets the rounding of time to the end of the period instead of the beginning +func WithRoundUp(b bool) func(*Options) { + return func(o *Options) { + o.RoundUp = b + } +} + +// WithBusinessDayFunc use the given fn to check if a day is a business day +func WithBusinessDayFunc(fn func(time.Time) bool) func(*Options) { + return func(o *Options) { + o.BusinessDayFunc = fn + } +} + +func isNotWeekend(t time.Time) bool { + return t.Weekday() != time.Saturday && t.Weekday() != time.Sunday +} + +// Time evaluate the expression with the given options to get the time it represents +func (e Expression) Time(opts ...func(*Options)) time.Time { + options := Options{ + Now: time.Now(), + Location: time.UTC, + StartOfWeek: time.Monday, + } + for _, opt := range opts { + opt(&options) + } + + t := e.anchorDateExpression(options) + for _, adjustment := range e.adjustments { + t = adjustment(t, options) + } + return t +} + +// Parse parses the datemath expression which can later be evaluated +func Parse(s string) (Expression, error) { + lex := newLexer([]byte(s)) + lexWrapper := newLexerWrapper(lex) + + yyParse(lexWrapper) + + if len(lex.errors) > 0 { + return Expression{}, fmt.Errorf(strings.Join(lex.errors, "\n")) + } + + return Expression{input: s, mathExpression: lexWrapper.expression}, nil +} + +// MustParse is the same as Parse() but panic's on error +func MustParse(s string) Expression { + e, err := Parse(s) + if err != nil { + panic(err) + } + return e +} + +// ParseAndEvaluate is a convience wrapper to parse and return the time that the expression represents +func ParseAndEvaluate(s string, opts ...func(*Options)) (time.Time, error) { + expression, err := Parse(s) + if err != nil { + return time.Time{}, err + } + + return expression.Time(opts...), nil +} + +type anchorDateExpression func(opts Options) time.Time + +func anchorDateNow(opts Options) time.Time { + return opts.Now.In(opts.Location) +} + +func anchorDate(t time.Time) func(opts Options) time.Time { + return func(opts Options) time.Time { + location := t.Location() + if location == missingTimeZone { + location = opts.Location + } + + return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), location) + } +} + +type timeAdjuster func(time.Time, Options) time.Time + +func addUnits(factor int, u timeUnit) func(time.Time, Options) time.Time { + return func(t time.Time, options Options) time.Time { + switch u { + case timeUnitYear: + return t.AddDate(factor, 0, 0) + case timeUnitMonth: + return t.AddDate(0, factor, 0) + case timeUnitWeek: + return t.AddDate(0, 0, 7*factor) + case timeUnitDay: + return t.AddDate(0, 0, factor) + case timeUnitBusinessDay: + + fn := options.BusinessDayFunc + if fn == nil { + fn = isNotWeekend + } + + increment := 1 + if factor < 0 { + increment = -1 + } + + for i := factor; i != 0; i -= increment { + t = t.AddDate(0, 0, increment) + for !fn(t) { + t = t.AddDate(0, 0, increment) + } + } + + return t + + case timeUnitHour: + return t.Add(time.Duration(factor) * time.Hour) + case timeUnitMinute: + return t.Add(time.Duration(factor) * time.Minute) + case timeUnitSecond: + return t.Add(time.Duration(factor) * time.Second) + default: + panic(fmt.Sprintf("unknown time unit: %s", u)) + } + } +} + +func truncateUnits(u timeUnit) func(time.Time, Options) time.Time { + var roundDown = func(t time.Time, options Options) time.Time { + switch u { + case timeUnitYear: + return time.Date(t.Year(), 1, 1, 0, 0, 0, 0, t.Location()) + case timeUnitMonth: + return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()) + case timeUnitWeek: + diff := int(t.Weekday() - options.StartOfWeek) + if diff < 0 { + return time.Date(t.Year(), t.Month(), t.Day()+diff-1, 0, 0, 0, 0, t.Location()) + } + return time.Date(t.Year(), t.Month(), t.Day()-diff, 0, 0, 0, 0, t.Location()) + case timeUnitDay: + return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + case timeUnitHour: + return t.Truncate(time.Hour) + case timeUnitMinute: + return t.Truncate(time.Minute) + case timeUnitSecond: + return t.Truncate(time.Second) + default: + panic(fmt.Sprintf("unknown time unit: %s", u)) + } + } + + return func(t time.Time, options Options) time.Time { + if options.RoundUp { + return addUnits(1, u)(roundDown(t, options), options).Add(-time.Millisecond) + } + return roundDown(t, options) + } +} + +func daysIn(m time.Month, year int) int { + return time.Date(year, m+1, 0, 0, 0, 0, 0, time.UTC).Day() +} + +// lexerWrapper wraps the golex generated wrapper to store the parsed expression for later and provide needed data to +// the parser +type lexerWrapper struct { + lex yyLexer + + expression mathExpression +} + +func newLexerWrapper(lex yyLexer) *lexerWrapper { + return &lexerWrapper{ + lex: lex, + } +} + +func (l *lexerWrapper) Lex(lval *yySymType) int { + return l.lex.Lex(lval) +} + +func (l *lexerWrapper) Error(s string) { + l.lex.Error(s) +} diff --git a/vendor/github.com/timberio/go-datemath/datemath.l b/vendor/github.com/timberio/go-datemath/datemath.l new file mode 100644 index 00000000000..aecb5253970 --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/datemath.l @@ -0,0 +1,144 @@ +/* +This file is used with golex to generate a lexer that has a signature compatible with goyacc. + +Many constants referred to below are defined by goyacc when creating template.y.go + +See https://godoc.org/modernc.org/golex for more about golex +*/ + +%{ + +package datemath + +import ( + "bytes" + "fmt" + "strconv" +) + +const ( + // 0 is expected by the goyacc generated parser to indicate EOF + eofCode = 0 +) + +// lexer holds the state of the lexer +type lexer struct { + src *bytes.Reader + + buf []byte + current byte + + pos int + + errors []string +} + +func newLexer(b []byte) *lexer { + l := &lexer{ + src: bytes.NewReader(b), + } + // queue up a byte + l.next() + return l +} + +func (l *lexer) Error(s string) { + l.errors = append(l.errors, fmt.Sprintf("%s at character %d starting with %q", s, l.pos, string(l.buf))) +} + +func (l *lexer) next() { + if l.current != 0 { + l.buf = append(l.buf, l.current) + } + l.current = 0 + if b, err := l.src.ReadByte(); err == nil { + l.current = b + } + l.pos++ +} +func (l *lexer) Lex(lval *yySymType) int { +%} + +/* give some regular expressions more semantic names for use below */ +eof \0 + +/* tell golex how to determine the current start condition */ +%yyt l.startCondition +/* tell golex how to determine the current byte */ +%yyc l.current +/* tell golex how to advance to the next byte */ +%yyn l.next() + +%% + // runs before each token is parsed + l.buf = l.buf[:0] + +[0-9] + i, err := strconv.ParseInt(string(l.buf), 10, 0) + if err != nil { + panic(fmt.Sprintf("could not parse digit as number: %s", err)) + } + lval.i = int(i) + return tDIGIT + +"now" + return tNOW + +"+" + return tPLUS + +"-" + return tMINUS + +":" + return tCOLON + +"||" + return tPIPES + +"/" + return tBACKSLASH + +[yMwdbhHms] + switch l.buf[0] { + case 'y': + lval.unit = timeUnitYear + case 'M': + lval.unit = timeUnitMonth + case 'w': + lval.unit = timeUnitWeek + case 'b': + lval.unit = timeUnitBusinessDay + case 'd': + lval.unit = timeUnitDay + case 'h', 'H': + lval.unit = timeUnitHour + case 'm': + lval.unit = timeUnitMinute + case 's': + lval.unit = timeUnitSecond + default: + panic(fmt.Sprintf("unknown time unit: %q", l.buf[0])) + } + return tUNIT + +\. + return tDOT + +"T" + return tTIME_DELIMITER + +"Z" + return tUTC + +{eof} + return eofCode + +. + return tINVALID_TOKEN + +%% + +// should never get here +panic("scanner internal error") +} diff --git a/vendor/github.com/timberio/go-datemath/datemath.l.go b/vendor/github.com/timberio/go-datemath/datemath.l.go new file mode 100644 index 00000000000..eb9e8301463 --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/datemath.l.go @@ -0,0 +1,296 @@ +// Code generated by golex. DO NOT EDIT. + +/* +This file is used with golex to generate a lexer that has a signature compatible with goyacc. + +Many constants referred to below are defined by goyacc when creating template.y.go + +See https://godoc.org/modernc.org/golex for more about golex +*/ + +package datemath + +import ( + "bytes" + "fmt" + "strconv" +) + +const ( + // 0 is expected by the goyacc generated parser to indicate EOF + eofCode = 0 +) + +// lexer holds the state of the lexer +type lexer struct { + src *bytes.Reader + + buf []byte + current byte + + pos int + + errors []string +} + +func newLexer(b []byte) *lexer { + l := &lexer{ + src: bytes.NewReader(b), + } + // queue up a byte + l.next() + return l +} + +func (l *lexer) Error(s string) { + l.errors = append(l.errors, fmt.Sprintf("%s at character %d starting with %q", s, l.pos, string(l.buf))) +} + +func (l *lexer) next() { + if l.current != 0 { + l.buf = append(l.buf, l.current) + } + l.current = 0 + if b, err := l.src.ReadByte(); err == nil { + l.current = b + } + l.pos++ +} +func (l *lexer) Lex(lval *yySymType) int { + + /* give some regular expressions more semantic names for use below */ + /* tell golex how to determine the current start condition */ + /* tell golex how to determine the current byte */ + /* tell golex how to advance to the next byte */ + +yystate0: + + // runs before each token is parsed + l.buf = l.buf[:0] + + goto yystart1 + +yystate1: + l.next() +yystart1: + switch { + default: + goto yyabort + case l.current == '+': + goto yystate4 + case l.current == '-': + goto yystate5 + case l.current == '.': + goto yystate6 + case l.current == '/': + goto yystate7 + case l.current == ':': + goto yystate9 + case l.current == 'H' || l.current == 'M' || l.current == 'b' || l.current == 'd' || l.current == 'h' || l.current == 'm' || l.current == 's' || l.current == 'w' || l.current == 'y': + goto yystate10 + case l.current == 'T': + goto yystate11 + case l.current == 'Z': + goto yystate12 + case l.current == '\x00': + goto yystate2 + case l.current == 'n': + goto yystate13 + case l.current == '|': + goto yystate16 + case l.current >= '0' && l.current <= '9': + goto yystate8 + case l.current >= '\x01' && l.current <= '\t' || l.current >= '\v' && l.current <= '*' || l.current == ',' || l.current >= ';' && l.current <= 'G' || l.current >= 'I' && l.current <= 'L' || l.current >= 'N' && l.current <= 'S' || l.current >= 'U' && l.current <= 'Y' || l.current >= '[' && l.current <= 'a' || l.current == 'c' || l.current >= 'e' && l.current <= 'g' || l.current >= 'i' && l.current <= 'l' || l.current >= 'o' && l.current <= 'r' || l.current >= 't' && l.current <= 'v' || l.current == 'x' || l.current == 'z' || l.current == '{' || l.current >= '}' && l.current <= 'ÿ': + goto yystate3 + } + +yystate2: + l.next() + goto yyrule12 + +yystate3: + l.next() + goto yyrule13 + +yystate4: + l.next() + goto yyrule3 + +yystate5: + l.next() + goto yyrule4 + +yystate6: + l.next() + goto yyrule9 + +yystate7: + l.next() + goto yyrule7 + +yystate8: + l.next() + goto yyrule1 + +yystate9: + l.next() + goto yyrule5 + +yystate10: + l.next() + goto yyrule8 + +yystate11: + l.next() + goto yyrule10 + +yystate12: + l.next() + goto yyrule11 + +yystate13: + l.next() + switch { + default: + goto yyrule13 + case l.current == 'o': + goto yystate14 + } + +yystate14: + l.next() + switch { + default: + goto yyabort + case l.current == 'w': + goto yystate15 + } + +yystate15: + l.next() + goto yyrule2 + +yystate16: + l.next() + switch { + default: + goto yyrule13 + case l.current == '|': + goto yystate17 + } + +yystate17: + l.next() + goto yyrule6 + +yyrule1: // [0-9] + { + + i, err := strconv.ParseInt(string(l.buf), 10, 0) + if err != nil { + panic(fmt.Sprintf("could not parse digit as number: %s", err)) + } + lval.i = int(i) + return tDIGIT + } +yyrule2: // "now" + { + + return tNOW + } +yyrule3: // "+" + { + + return tPLUS + } +yyrule4: // "-" + { + + return tMINUS + } +yyrule5: // ":" + { + + return tCOLON + } +yyrule6: // "||" + { + + return tPIPES + } +yyrule7: // "/" + { + + return tBACKSLASH + } +yyrule8: // [yMwdbhHms] + { + + switch l.buf[0] { + case 'y': + lval.unit = timeUnitYear + case 'M': + lval.unit = timeUnitMonth + case 'w': + lval.unit = timeUnitWeek + case 'b': + lval.unit = timeUnitBusinessDay + case 'd': + lval.unit = timeUnitDay + case 'h', 'H': + lval.unit = timeUnitHour + case 'm': + lval.unit = timeUnitMinute + case 's': + lval.unit = timeUnitSecond + default: + panic(fmt.Sprintf("unknown time unit: %q", l.buf[0])) + } + return tUNIT + } +yyrule9: // \. + { + + return tDOT + } +yyrule10: // "T" + { + + return tTIME_DELIMITER + } +yyrule11: // "Z" + { + + return tUTC + } +yyrule12: // {eof} + { + + return eofCode + } +yyrule13: // . + if true { // avoid go vet determining the below panic will not be reached + + return tINVALID_TOKEN + } + panic("unreachable") + +yyabort: // no lexem recognized + // + // silence unused label errors for build and satisfy go vet reachability analysis + // + { + if false { + goto yyabort + } + if false { + goto yystate0 + } + if false { + goto yystate1 + } + } + + // should never get here + panic("scanner internal error") +} diff --git a/vendor/github.com/timberio/go-datemath/datemath.y b/vendor/github.com/timberio/go-datemath/datemath.y new file mode 100644 index 00000000000..bc8e605fa5f --- /dev/null +++ b/vendor/github.com/timberio/go-datemath/datemath.y @@ -0,0 +1,300 @@ +/* +This file is used with goyacc to generate a parser. + +See https://godoc.org/golang.org/x/tools/cmd/goyacc for more about goyacc. +*/ + +%{ +package datemath + +import ( + "fmt" + "math" + "time" +) + +var epoch = time.Unix(0, 0).In(time.UTC) + +// convert a list of significant digits to an integer +// assumes most to least significant +// e.g. 5,2,3 -> 523 +func digitsToInt(digits ...int) int { + n := 0 + for i := range digits { + n += digits[i] * int(math.Pow10(len(digits)-i-1)) + } + return n +} +%} + +/* set of valid tokens; generated constants used by lexer */ +%token tNOW tPLUS tMINUS tPIPES tBACKSLASH tTIME_DELIMITER tCOLON tDOT tUNIT tUTC tDIGIT tINVALID_TOKEN + +/* Go variables to hold the corresponding token values */ +%union { +i64 int64 +i int +unit timeUnit +month time.Month + +expression mathExpression +anchorDateExpression anchorDateExpression +timeAdjuster timeAdjuster +timeAdjusters []timeAdjuster + +location *time.Location +time time.Time +} + +/* associate tokens with Go types */ +%type tUNIT +%type sign factor number year day hour minute second nanoseconds tDIGIT +%type month +%type expression +%type