content: module RestyScript.AST (
      RSVal(..), traverse
  ) where
  
  import Text.ParserCombinators.Parsec.Pos (SourcePos)
  
  -- RestyScript AST datatype
  data RSVal = SetOp !String !RSVal !RSVal
             | Query ![RSVal]
             | Select ![RSVal] -- Select entries
             | From ![RSVal]
             | Where !RSVal
             | Limit !RSVal
             | Offset !RSVal
             | TypeCast !RSVal !RSVal
             | OrderBy ![RSVal]
             | OrderPair !RSVal !RSVal
             | GroupBy !RSVal
             | Alias !RSVal !RSVal
             | Column !RSVal
             | Model !RSVal
             | Symbol !String
             | Type !String
             | Keyword !String  -- for asc and desc
             | QualifiedColumn !RSVal !RSVal
             | Integer !Int
             | Float !Double
             | String !String
             | Variable !SourcePos !String
             | FuncCall !RSVal ![RSVal]
             | Compare !String !RSVal !RSVal
             | Arith !String !RSVal !RSVal
             | Minus !RSVal
             | Plus !RSVal
             | Not !RSVal
             | Or !RSVal !RSVal
             | And !RSVal !RSVal
             | Empty
             | Null
             | AnyColumn
             | Action ![RSVal]
             | Delete !RSVal !RSVal -- Delete model cond
             | Update !RSVal !RSVal !RSVal -- Update model assign cond
             | Assign !RSVal !RSVal -- Assign col expr
             | HttpCmd !String !RSVal !RSVal -- HttpCmd method url content
             | Object ![RSVal]  -- Object [pair]
             | Pair !RSVal !RSVal -- Pair key value
             | Array ![RSVal]  -- Array [elem]
             | Concat !RSVal !RSVal
             | Distinct ![RSVal]
             | All ![RSVal]
             | RSTrue
             | RSFalse
             | Capture [(RSVal, RSVal)]
             | ArrayIndex !RSVal !RSVal
                 deriving (Eq, Show)
  
  traverse :: (RSVal->a) -> (a->a->a) -> RSVal -> a
  traverse visit merge node =
      let mergeAll = foldl1 merge
          cur = visit node
          self = traverse visit merge
      in case node of
          SetOp _ lhs rhs -> mergeAll [cur, self lhs, self rhs]
          Query q -> mergeAll $ cur : map self q
          Select s -> mergeAll $ cur : map self s
          From f -> mergeAll $ cur : map self f
          Where w -> merge cur $ self w
          Limit l -> merge cur $ self l
          Offset o -> merge cur $ self o
          TypeCast e t -> mergeAll [cur, self e, self t]
          OrderBy o -> mergeAll $ cur : map self o
          OrderPair col dir -> mergeAll [cur, self col, self dir]
          GroupBy g -> merge cur (self g)
  
          Alias col alias -> mergeAll [cur, self col, self alias]
          Column c -> merge cur (self c)
          Model m -> merge cur (self m)
          QualifiedColumn lhs rhs -> mergeAll [cur, self lhs, self rhs]
          Variable _ _ -> visit node
          FuncCall f args -> mergeAll $ cur : map self (f:args)
  
          Compare _ lhs rhs -> mergeAll [cur, self lhs, self rhs]
          Arith _ lhs rhs -> mergeAll [cur, self lhs, self rhs]
  
          Or lhs rhs -> mergeAll [cur, self lhs, self rhs]
          And lhs rhs -> mergeAll [cur, self lhs, self rhs]
  
          Minus val -> mergeAll [cur, self val]
          Plus val -> mergeAll [cur, self val]
          Not val -> mergeAll [cur, self val]
  
          Action cmds -> mergeAll $ cur : map self cmds
          Delete model cond -> mergeAll [cur, self model, self cond]
          Update model assign cond -> mergeAll [cur, self model, self assign, self cond]
          Assign col expr -> mergeAll [cur, self col, self expr]
          HttpCmd meth url content -> mergeAll [cur, self url, self content]
          Object ps -> mergeAll $ cur : map self ps
          Array xs -> mergeAll $ cur : map self xs
          Pair k v -> mergeAll [cur, self k, self v]
          Concat a b -> mergeAll [cur, self a, self b]
          AnyColumn -> cur
          Empty -> cur
          Null -> cur
          String _ -> cur
          Float _ -> cur
          Integer _ -> cur
          Symbol _ -> cur
          Type _ -> cur
          Keyword _ -> cur
          RSTrue -> cur
          RSFalse -> cur
          All _ -> cur
          Distinct _ -> cur
          Capture sig -> mergeAll $ cur : map aux sig
              where aux (a, b) = merge (self a) (self b)
          ArrayIndex array index -> mergeAll [cur, self array, self index]
          -- otherwise -> cur
  
  