diff --git a/compare/main.go b/compare/main.go index ed9503b..5a643e1 100644 --- a/compare/main.go +++ b/compare/main.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/kshvmdn/fsql/query" + "../query" ) // Alpha compares two strings a and b. diff --git a/main.go b/main.go index e89c4b6..3fe4f8d 100644 --- a/main.go +++ b/main.go @@ -11,8 +11,8 @@ import ( "strings" "time" - cmp "github.com/kshvmdn/fsql/compare" - "github.com/kshvmdn/fsql/query" + cmp "./compare" + "./query" ) const ( @@ -145,21 +145,21 @@ func main() { } if q.HasAttribute("mode") { - fmt.Printf("%s", info.Mode()) + fmt.Printf("%s", q.PerformTransformations("mode",info.Mode())) if q.HasAttribute("size", "time", "name") { fmt.Print("\t") } } if q.HasAttribute("size") { - fmt.Printf("%d", info.Size()) + fmt.Printf("%s", q.PerformTransformations("size",info.Size())) if q.HasAttribute("time", "name") { fmt.Print("\t") } } if q.HasAttribute("time") { - fmt.Printf("%s", info.ModTime().Format(time.Stamp)) + fmt.Printf("%s", q.PerformTransformations("time",info.ModTime().Format(time.Stamp)) ) if q.HasAttribute("name") { fmt.Print("\t") } @@ -167,7 +167,7 @@ func main() { if q.HasAttribute("name") { // TODO: Only show file name, instead of the full path? - fmt.Printf("%s", path) + fmt.Printf("%s", q.PerformTransformations("name",path)) } fmt.Printf("\n") diff --git a/query/parser.go b/query/parser.go index 417f7b5..cf619c5 100644 --- a/query/parser.go +++ b/query/parser.go @@ -59,6 +59,7 @@ func (p *parser) showAllAttributes() (bool, error) { func (p *parser) parse(input string) (*Query, error) { p.tokenizer = NewTokenizer(input) q := new(Query) + q.Transformations = make(map[string][]Function) all, err := p.showAllAttributes() if err != nil { @@ -68,7 +69,7 @@ func (p *parser) parse(input string) (*Query, error) { q.Attributes = allAttributes } else { q.Attributes = make(map[string]bool) - err := p.parseAttributes(&q.Attributes) + err := p.parseAttributes(&q.Attributes,&q.Transformations) if err != nil { return nil, err } @@ -123,24 +124,67 @@ func (p *parser) parse(input string) (*Query, error) { } // Parse the list of attributes provided to the SELECT clause. -func (p *parser) parseAttributes(attributes *map[string]bool) error { +func (p *parser) parseAttributes(attributes *map[string]bool, transformations *map[string][]Function) error { attribute := p.expect(Identifier) if attribute == nil { return p.currentError() } if attribute.Raw == "*" || attribute.Raw == "all" { *attributes = allAttributes - } else if _, ok := allAttributes[attribute.Raw]; !ok { - return &ErrUnknownToken{attribute.Raw} - } else { + } else{ + p.current = attribute + attribute, err := p.parseAttribute(transformations) + if err != nil{ + return err + } + if _, ok := allAttributes[attribute.Raw]; !ok { + return &ErrUnknownToken{attribute.Raw} + } (*attributes)[attribute.Raw] = true + } if p.expect(Comma) == nil { return nil } - return p.parseAttributes(attributes) + return p.parseAttributes(attributes, transformations) +} + +// Parses all the transformation applied to given attribute recursively +func (p *parser) parseAttribute( transformations *map[string][]Function) (*Token,error) { + identifier := p.expect(Identifier) + var currFunction Function + if identifier != nil { + if p.expect(OpenParen) != nil { + currFunction = Function{Name: identifier.Raw, Arguments: make([]string,0)} + attribute,err := p.parseAttribute( transformations ) + if attribute == nil{ + return attribute,err + } + for{ + if token := p.expect(Identifier); token != nil{ + currFunction.Arguments = append(currFunction.Arguments,token.Raw) + continue + } else if token := p.expect(Comma); token != nil{ + continue + } else if token := p.expect(CloseParen); token != nil{ + if _,ok := (*transformations)[attribute.Raw]; !ok{ + (*transformations)[attribute.Raw] = make([]Function,0) + } + (*transformations)[attribute.Raw] = append((*transformations)[attribute.Raw], currFunction) + return attribute,err + } + return nil, p.currentError() + } + } else{ + if _,ok := allAttributes[identifier.Raw]; !ok{ + return nil, &ErrUnknownToken{identifier.Raw} + } + return identifier,nil + } + } + return nil, p.currentError() } // Parse the list of directories passed to the FROM clause. Expects that diff --git a/query/query.go b/query/query.go index 7d138e4..f81d63b 100644 --- a/query/query.go +++ b/query/query.go @@ -3,13 +3,41 @@ package query import ( "fmt" "os" + "bytes" ) +// Function entity used for defining transformations on attributes +type Function struct { + Name string + Arguments []string +} + + // Query represents an input query. type Query struct { Attributes map[string]bool Sources map[string][]string ConditionTree *ConditionNode // Root node of this query's condition tree. + Transformations map[string][]Function +} + +// PrintTransformations function prints all transformations parsed from given query +func (q *Query) PrintTransformations() { + for _, functions := range q.Transformations{ + for _, function := range functions{ + fmt.Println(function.String()) + } + } +} + +// Return string representation of a given function +func (f *Function) String() string{ + var buffer bytes.Buffer + buffer.WriteString(f.Name) + for _,v := range f.Arguments{ + buffer.WriteString(fmt.Sprintf(", %s",v)) + } + return buffer.String() } // HasAttribute checks if the query's attribute map contains the provided diff --git a/query/transformations.go b/query/transformations.go new file mode 100644 index 0000000..3d58cee --- /dev/null +++ b/query/transformations.go @@ -0,0 +1,77 @@ +package query + +import ( + "fmt" + "os" + "strings" + "path/filepath" +) + +// Function mapping of transformations +var allTransformations = map[string]func (interface{}, []string) interface{}{ + "format": format, + "upper": upper, + "fullpath": fullpath, +} + +//format function +func format(inp interface{}, args []string) interface{}{ + + if size,ok := inp.(int64); ok{ + if args[0] == "kb"{ + return fmt.Sprintf("%fkb",float64(size)/(1<<10)) + } else if args[0] == "mb"{ + return fmt.Sprintf("%fmb",float64(size)/(1<<20)) + } else if args[0] == "gb"{ + return fmt.Sprintf("%fgb",float64(size)/(1<<30)) + } + } else{ + fmt.Fprintf(os.Stderr,"type for attribute:%s for function: format is not implemented\n",inp) + os.Exit(1) + } + return "" +} + +//Upper Function +func upper(inp interface{}, args []string) interface{}{ + if str,ok := inp.(string); ok{ + return strings.ToUpper(str) + } + fmt.Fprintf(os.Stderr,"type for attribute:%s for function: format is not implemented\n",inp) + os.Exit(1) + return "" +} + +//Fullpath function +func fullpath(inp interface{}, args []string) interface{}{ + if str,ok := inp.(string); ok{ + res,_:=filepath.Abs(str) + return res + } + fmt.Fprintf(os.Stderr,"type for attribute:%s for function: format is not implemented\n",inp) + os.Exit(1) + return "" +} + +//PerformTransformations fetchs all transformation to be applied on a given attribute and applies them one by one +func (q *Query) PerformTransformations(attribute string, in interface{}) string{ + functions,ok := q.Transformations[attribute] + res := in + if ok{ + for _, function := range functions{ + if funcImpl,ok:=allTransformations[function.Name];ok{ + res = funcImpl(res, function.Arguments) + } else{ + fmt.Fprintf(os.Stderr,"Given function:%s is not implemented\n",function.Name) + os.Exit(1) + } + } + } + + if v,ok:=res.(int64);ok{ + return fmt.Sprintf("%d",v) + } else if v,ok:=res.(string);ok{ + return v + } + return "" +} \ No newline at end of file