Commits

camlspotter committed d31e732

update

Comments (0)

Files changed (12)

     token
     lexer
     reader
+    filter
     main
 
 lexer.cmi: parser.cmi
+let x = ( ...,
+          ...            -- +2; @(
+
+let f = List.iter lst ~f:(fun x -> (fun y -> ...
+  ... last+2; last+2; ...
+open Sexplib.Conv
+open Pos
+open Reader
+open Parser
+
+type 'a t = 
+  | Cons of 'a * 'a t lazy_t
+  | Null
+
+type 'a info = {
+  space : string;
+  token : 'a;
+  region : Region.t;
+  substr : string;
+}
+
+let streamer path = 
+  let ic = open_in path in
+  try
+    let reader = LexReader.create_from_channel ic in
+    let rec loop last_region = 
+      let token = LexReader.lex reader Lexer.token in
+      let region = LexReader.region reader in
+      let space_between = 
+        let last_end = (snd last_region).Position.pos_cnum in
+        LexReader.substring 
+          reader last_end ((fst region).Position.pos_cnum - last_end)
+      in
+      (* token's string *)
+      let substr = LexReader.current_substring reader in
+      
+      Cons ({ space = space_between; token; region; substr },
+            lazy (match token with
+            | EOF -> close_in ic; Null
+            | _ -> loop region))
+    in
+    loop Region.zero
+  with
+  | e -> 
+      close_in ic;
+      raise e
+
+let destr = function
+  | lazy Null -> None
+  | lazy (Cons (car, cdr)) -> Some (car, cdr)
 open Pos
 open Reader
 
+module Indent = struct
+  (* a *)
+  type t = 
+    | Add_to_last of int
+    | Diff of int 
+    | Set of int
+  with sexp
+end
+
+module Stack = struct
+  open Indent
+    
+  type k = KExpr | KParen | KLet of [`Top | `Local ] * int | KType of int | KNone | KStruct of int | KSig of int | KModule of int | KOpen | KBegin of int
+  with sexp
+
+  type elt = { k : k;
+               line : int;
+               indent : Indent.t }
+  with sexp
+
+  type t = elt list with sexp
+
+  let rec indent last_indent  = function
+    | [] -> 0
+    | { indent = Set n } :: _ -> n
+    | { indent = Add_to_last n } :: _ -> last_indent + n
+    | { indent = Diff n } :: ts -> indent last_indent ts + n
+end
+
+module State = struct
+  type t = {
+    bases : Stack.t;
+    orig_indent : int;
+    last_indent : int;
+    last_token : Parser.token option;
+  } with sexp
+
+  let init = { bases = []; orig_indent = 0; last_indent = 0; last_token = None; }
+
+  let indent t = Stack.indent t.last_indent t.bases
+end
+
+open State
+open Stack
+open Parser
+
+let rec is_top_let = function
+  | None -> true
+  | Some t ->
+      match t with
+      | COMMENT -> assert false (* COMMENT must be skipped *)
+      | STRUCT | SEMISEMI 
+
+      | UIDENT _|STRING _|OPTLABEL _|NATIVEINT _|LIDENT _|LABEL _|INT64 _|INT32 _
+      | INT _|FLOAT _|CHAR _|WITH|VIRTUAL|VAL|UNDERSCORE|TYPE|TRUE|TILDE|SIG|SHARP
+      | RPAREN|REC|RBRACKET|RBRACE|QUOTE|QUESTIONQUESTION|QUESTION|PRIVATE|OPEN
+      | OF|OBJECT|NEW|MUTABLE|MODULE|METHOD|MATCH|LET|LESS|LAZY|INHERIT|INCLUDE
+      | GREATERRBRACKET|GREATERRBRACE|GREATER|FUNCTOR|FUNCTION|FUN|FOR|FALSE
+      | EXTERNAL|EXCEPTION|EOF|END|DOTDOT|DOT|DONE|CONSTRAINT|COLONGREATER
+      | COLONCOLON|COLON|CLASS|BARRBRACKET|BARBAR|BAR|BANG|BACKQUOTE|ASSERT|AS|AND
+      | AMPERSAND|AMPERAMPER -> true
+
+      | _ -> false
+
+let rec unwind_top = function (* unwind the top *)
+  | { k = KLet (`Top, _) } :: bs -> bs
+  | ({ k = KModule _ } :: _ as bs) -> bs
+  | _ :: bs -> unwind_top bs
+  | [] -> []
+
+let token state region str (t : Parser.token) : State.t * State.t = 
+  let line = Region.lnum region in
+  let columns = Region.columns region in
+  let indent = state.last_indent in
+  let bases0 = state.bases in
+
+  let pre_bases, post_bases = match t with
+    | SEMISEMI ->
+        (* unwind the top *)
+        let bases = unwind_top bases0 in
+        bases, bases
+
+    | OPEN -> 
+        let bases = unwind_top bases0 in
+        bases, 
+        { k = KOpen; indent = Indent.Diff 2; line } :: bases
+
+    | MODULE ->
+        let bases = unwind_top bases0 in
+        bases, 
+        { k = KModule columns; indent = Indent.Diff 2; line } :: bases
+
+    | LET when is_top_let state.last_token ->
+        let bases = unwind_top bases0 in
+        bases, 
+        { k = KLet (`Top, columns); indent = Indent.Diff 2; line } :: bases
+
+    | LET ->
+        bases0, { k = KLet (`Local, columns); indent = Indent.Diff 2; line } :: bases0
+
+    | TYPE ->
+        let bases = unwind_top bases0 in
+        bases, 
+        { k = KType columns; indent = Indent.Diff 2; line } :: bases
+          
+    | AND -> 
+        (* for let *)
+        (* recover the last let *)
+        let rec f = function
+          | ({k = (KLet (_,columns) | KType columns | KModule columns) } :: bs as bases) -> 
+              { k = KNone; indent = Indent.Set columns; line }:: bs, bases
+          | [] -> [], []
+          | _ :: bs -> f bs
+        in
+        f bases0
+
+    | IN ->
+        (* in must paired with let *)
+        let rec f = function
+          | { k = KLet(_, columns) } :: bs -> 
+              { k = KNone; indent = Indent.Set columns; line } :: bs, bs
+          | [] -> [], []
+          | _ :: bs -> f bs
+        in
+        f bases0
+
+    | STRUCT ->
+        bases0, { k = KStruct indent; indent = Indent.Diff 2; line } :: bases0
+
+    | SIG ->
+        bases0, { k = KSig indent; indent = Indent.Diff 2; line } :: bases0
+
+    | BEGIN ->
+        bases0, { k = KBegin indent; indent = Indent.Diff 2; line } :: bases0
+
+    | END ->
+        let rec f = function
+          | { k = (KStruct columns | KSig columns | KBegin columns) } :: bs -> 
+              { k = KNone; indent = Indent.Set columns; line } :: bs, bs
+          | [] -> [], []
+          | _ :: bs -> f bs
+        in
+        f bases0
+
+(*
+    | EQUAL -> (* after let *)
+        let rec f = function
+          | { k = KLet } :: bs -> 
+              { k = KLet; indent = Indent.Diff 2; line } :: bs, 
+              { k = KLet; indent = Indent.Diff 2; line } :: bs
+          | [] -> [], []
+          | _ :: bs -> f bs
+        in
+        f bases0
+
+    | INT64 _ | INT32 _ |INT _ | LIDENT _ | UIDENT _ ->
+        let rec f = function
+          | { k = KExpr } :: _ -> bases0
+          | { k = KParen } :: _ -> { k = KExpr; indent =Indent.Set columns; line } :: bases0
+          | [] -> { k = KExpr; indent = Indent.Diff 2; line } :: bases0
+          | _ :: bs -> f bs
+        in
+        bases0, f bases0
+
+    | PLUS | PLUSDOT | MINUS | MINUSDOT | STAR ->
+        { k = KExpr; indent = Indent.Diff (-2); line} :: bases0, bases0
+          
+    | COMMA ->
+        let bases = 
+          let rec f = function
+            | ({ k = KExpr } :: bs as bases) -> bases
+            | { k = KParen } :: _ -> bases0
+            | [] -> []
+            | _ :: bs -> f bs
+          in
+          f bases0
+        in
+        bases, bases0
+*)
+          
+(*
+    | LPAREN ->
+        bases0, { k = KParen; indent = Indent.Set (columns + 2); line } :: bases0
+
+    | RPAREN ->
+        let rec f = function
+          | _ :: { k = KTop } :: bs -> bs
+          | { k = KParen }  :: bs -> bs
+          | [] -> []
+          | _ :: bs -> f bs
+        in
+        let bases = f bases0 in
+        bases, bases
+*)
+
+          | (UIDENT _|LIDENT _|INT64 _|INT32 _|INT _|STAR|RPAREN|PLUSDOT|PLUS|MINUSDOT|
+      MINUS|LPAREN|EQUAL|COMMA)
+
+    | STRING _|PREFIXOP _|OPTLABEL _|NATIVEINT _|LABEL _|INFIXOP4 _|
+      INFIXOP3 _|INFIXOP2 _|INFIXOP1 _|INFIXOP0 _|FLOAT _|CHAR _|COMMENT|WITH|
+      WHILE|WHEN|VIRTUAL|VAL|UNDERSCORE|TRY|TRUE|TO|TILDE|THEN|
+      SHARP|SEMI|REC|RBRACKET|RBRACE|QUOTE|QUESTIONQUESTION|QUESTION|
+      PRIVATE|OR|OF|OBJECT|NEW|MUTABLE|MINUSGREATER|METHOD|MATCH
+          |LESSMINUS|LESS|LBRACKETGREATER|LBRACKETLESS|LBRACKETBAR|LBRACKET|
+      LBRACELESS|LBRACE|LAZY|INITIALIZER|INHERIT|INCLUDE|IF|GREATERRBRACKET|
+      GREATERRBRACE|GREATER|FUNCTOR|FUNCTION|FUN|FOR|FALSE|EXTERNAL|EXCEPTION|EOF|
+      ELSE|DOWNTO|DOTDOT|DOT|DONE|DO|CONSTRAINT|COLONGREATER|COLONEQUAL|
+      COLONCOLON|COLON|CLASS|BARRBRACKET|BARBAR|BAR|BANG|BACKQUOTE|ASSERT|AS|
+      AMPERSAND|AMPERAMPER -> bases0, bases0
+  in
+  { state with bases = pre_bases },
+  { state with bases = post_bases; last_token = (if t <> COMMENT then Some t else state.last_token); }
+
+let update_state state new_line orig_region str t = 
+
+  (* if it is a new line, compute the line's indentation *)
+  let state' =
+    if new_line then
+      let pre, _ = token state orig_region str t in
+      pre
+    else
+      state
+  in
+
+  let ind = State.indent state' in
+
+  let fixed_region = Region.move_chars (ind - state.orig_indent) orig_region in
+ 
+  token state fixed_region str t
+
 let parse_args () = 
   let rev_paths = ref [] in
   let debug = ref false in
 
 let paths, debug = parse_args ()
 
-type kind = 
-  | K_TOPLET  (* top let *)
-  | K_LET     (* local let *)
-  | K_LPAREN 
-  | K_LBRACE 
-  | K_STRUCT 
-  | K_SIG 
-  | K_TRY 
-  | K_MATCH 
-  | K_TYPE 
-  | K_IF 
-  | K_THEN 
-  | K_ELSE 
-  | K_BEGIN 
-  | K_ARROW
-  | K_OPEN
-  | K_FUNCTION
-  | K_FUN
-  | K_WHILE
-  | K_FOR
-  | K_DO
-  | K_VAL
-  | K_WITH
-  | K_BAR
-  | K_NONE
-with sexp
-
-type stack = (kind * int) list with sexp
-
-let rec get_indent_chars = function
-  | [] -> 0
-  | (_,i)::bs -> i + get_indent_chars bs
-
-open Parser
-
-let pop f bases = 
-  try
-    let rec pop = function
-      | (token, _) :: bs when f token -> bs
-      | (_, _) :: bs -> pop bs
-      | [] -> []
-    in
-    pop bases
-  with
-  | Exit -> bases
-
-let pop' f bases = 
-  try
-    let rec pop = function
-      | (token, _) :: bs when f token -> bs, token
-      | (_, _) :: bs -> pop bs
-      | [] -> [], K_NONE
-    in
-    pop bases
-  with
-  | Exit -> bases, K_NONE
-
-let pop_before f bases = 
-  try
-    let rec pop = function
-      | ((token, _) :: _ as bs) when f token -> bs
-      | (_, _) :: bs -> pop bs
-      | [] -> []
-    in
-    pop bases
-  with
-  | Exit -> bases
-
-let rec is_top_let = function
-  | None -> true
-  | Some t ->
-      match t with
-      | COMMENT -> assert false (* COMMENT must be skipped *)
-      | STRUCT | SEMISEMI -> true
-
-      | BEGIN | COLONEQUAL
-      | COMMA
-      | DO
-      | DOWNTO
-      | ELSE
-      | EQUAL
-      | IF
-      | IN
-      | INFIXOP0 _
-      | INFIXOP1 _
-      | INFIXOP2 _
-      | INFIXOP3 _
-      | INFIXOP4 _
-      | INITIALIZER
-      | LBRACE
-      | LBRACELESS
-      | LBRACKET
-      | LBRACKETBAR
-      | LBRACKETLESS
-      | LBRACKETGREATER
-      | LESSMINUS
-      | LPAREN
-      | MINUS
-      | MINUSDOT
-      | MINUSGREATER
-      | OR
-      | PLUS
-      | PLUSDOT
-      | PREFIXOP _
-      | STAR
-      | THEN
-      | TO
-      | TRY
-      | WHEN
-      | WHILE -> false
-
-      | _ -> true
-;;
-
-let rec top bases = match bases with
-  | ((K_TOPLET | K_TYPE), _) :: bs -> bs
-  | ((K_STRUCT | K_SIG), _) :: _ -> bases
-  | _ :: bs -> top bs
-  | [] -> []
-
-let token bases ~prev = function
-  | EOF -> assert false
-
-  | MINUSGREATER -> 
-      bases, 
-      (K_ARROW, 2) :: bases
-  | IF -> 
-      bases, 
-      (K_IF, 2) :: bases
-  | THEN -> 
-      let bases = pop ((=) K_IF) bases in
-      bases,
-      (K_THEN, 2) :: bases
-  | ELSE -> 
-      let bases = pop ((=) K_THEN) bases in
-      bases,
-      (K_ELSE, 2) :: bases
-  | LPAREN -> 
-      bases, 
-      (K_LPAREN, 2) :: bases
-  | RPAREN ->
-      let bases = pop ((=) K_LPAREN) bases in
-      bases, bases
-  | LBRACE -> 
-      bases,
-      (K_LBRACE, 2) :: bases
-  | RBRACE -> 
-      let bases = pop ((=) K_LBRACE) bases in
-      bases, bases
-  | BEGIN  -> 
-      bases,
-      (K_BEGIN, 2) :: bases
-  | END -> 
-      let bases = pop ((=) K_BEGIN) bases in
-      bases, bases
-  | TYPE -> 
-      let bases = top bases in
-      bases, 
-      (K_TYPE, 2) :: bases
-  | VAL ->
-      let bases = top bases in
-      bases,
-      (K_VAL, 2) :: bases
-  | OPEN -> 
-      let bases = top bases in
-      bases, 
-      (K_OPEN, 2) :: bases
-  | FUN -> 
-      bases, 
-      (K_FUN, 2) :: bases
-  | FUNCTION -> 
-      bases, 
-      (K_FUNCTION, 2) :: bases
-  | BAR ->
-      (* BAR rewind just before WITH/FUNCTION but cannot exceed LPAREN *)
-      let bases, k = pop' (function K_WITH | K_FUNCTION | K_LPAREN -> true | _ -> false) bases in
-      bases, (k, 2) :: bases
-  | STRUCT -> 
-      bases, 
-      (K_STRUCT, 2) :: bases
-  | SIG  -> 
-      bases, 
-      (K_SIG, 2) :: bases
-  | TRY -> 
-      bases,
-      (K_TRY, 2) :: bases
-  | MATCH ->
-      bases,
-      (K_MATCH, 2) :: bases
-  | WITH ->
-      (* typedesc support *)
-      let bases = pop (function K_TRY | K_MATCH  -> true | K_TYPE -> raise Exit | _ -> false) bases in
-      bases,
-      (K_WITH, 2) :: bases
-  | LET when is_top_let prev -> 
-      let bases = top bases in
-      bases,
-      (K_TOPLET, 2) :: bases
-  | LET -> 
-      bases,
-      (K_LET, 2) :: bases
-  | IN -> 
-      let bases = pop ((=) K_LET) bases in
-      bases,
-      bases
-  | SEMISEMI -> 
-      let bases = top bases in
-      bases, bases
-  | WHILE ->
-      bases,
-      (K_WHILE, 2) :: bases
-  | DO ->
-      let bases = pop (function K_WHILE | K_FOR -> true | _ -> false) bases in
-      bases, 
-      (K_DO, 2) :: bases
-  | AND ->
-      let bases = pop_before (function K_LET | K_TYPE -> true | _ -> false) bases in
-      List.tl bases, bases
-      
-  |UIDENT _|STRING _|PREFIXOP _|OPTLABEL _|NATIVEINT _|LIDENT _|ASSERT|LABEL _
-  |INT64 _|INT32 _|INT _
-  |FLOAT _|CHAR _
-
-  | INFIXOP4 _| INFIXOP3 _| INFIXOP2 _| INFIXOP1 _| INFIXOP0 _
-      
-  |COMMENT
-
-  |WHEN|VIRTUAL|UNDERSCORE|TRUE|TO|TILDE|STAR
-  |SHARP|SEMI|REC|RBRACKET|QUOTE|QUESTIONQUESTION|QUESTION|PRIVATE|PLUSDOT|PLUS
-  |OR|OF|OBJECT|NEW|MUTABLE|MODULE|MINUSDOT|MINUS|METHOD|LESSMINUS|LESS
-  |LBRACKETGREATER|LBRACKETLESS|LBRACKETBAR|LBRACKET|LBRACELESS|LAZY
-  |INITIALIZER|INHERIT|INCLUDE|GREATERRBRACKET|GREATERRBRACE|GREATER|FUNCTOR
-  |FOR|FALSE|EXTERNAL|EXCEPTION|EQUAL|DOWNTO|DOTDOT|DOT|DONE|CONSTRAINT
-  |COMMA|COLONGREATER|COLONEQUAL|COLONCOLON|COLON|CLASS|BARRBRACKET|BARBAR|BANG
-  |BACKQUOTE|AS|AMPERSAND|AMPERAMPER -> bases, bases
-
-(*
-  | _ -> bases, bases
-*)
-  
-
-type state = {
-  bases : stack; (** indentation stack *)
-  last_orig_region : Region.t;            (** the last token's region *)
-  orig_indent : int;  
-  prev : Parser.token option
-}
-
 let _ = 
   List.iter (fun path ->
-    let ic = open_in path in
-    try
-      let reader = LexReader.create_from_channel ic in
-      let rec loop state = 
-        match LexReader.lex reader Lexer.token with
-        | EOF -> 
-            (* The last white space is gone *)
-            print_string "\n"
-        | t -> 
-            (* region from the source *)
-            let orig_region = LexReader.region reader in
+    let str = lazy (Filter.streamer path) in
 
-            (* space between the last token and the current *)
+    let rec loop last_orig_region state str = match Filter.destr str with
+      | None ->
+          (* The last white space is gone *)
+          print_string "\n"
+      | Some (i, str) ->
+          let t = i.Filter.token in
+          let orig_region = i.Filter.region in
+          let space_between = i.Filter.space in
+          let substr = i.Filter.substr in
+
+          let new_line = Region.lnum last_orig_region <>  Region.lnum orig_region in
+
+          let state = 
+            if new_line then { state with orig_indent = Region.columns orig_region } else state
+          in
+          
+          let pre, post = update_state state new_line orig_region str t in
+
+          let post = 
+            if new_line then 
+              { post with last_indent = State.indent pre }
+            else post
+          in
+          
+          (* printing *)
+
+          if new_line then begin
             let space_between = 
-              let last_end = (snd state.last_orig_region).Position.pos_cnum in
-              LexReader.substring 
-                reader last_end ((fst orig_region).Position.pos_cnum - last_end)
+              try 
+                let pos = String.rindex space_between '\n' in 
+                String.sub space_between 0 pos
+              with
+              | _ -> assert false
             in
+            print_endline space_between;
 
-            (* token's string *)
-            let substr = LexReader.current_substring reader in
+            let indent_string = String.make (State.indent pre) ' ' in
+            if debug then begin
+              print_string indent_string;
+              Format.printf "-- %s@." (Sexplib.Sexp.to_string_mach (Stack.sexp_of_t pre.bases))
+            end;
 
-            let new_line = Region.lnum state.last_orig_region <>  Region.lnum orig_region in
+            print_string indent_string;
+            print_string substr
+          end else begin
+            print_string space_between;
+            print_string substr;
+          end;
+          
+          loop orig_region post str
+    in
 
-            (* original indentation *)
-            let orig_indent = if new_line then Region.columns orig_region else state.orig_indent in
-
-            (* the first token of the line may change the indentation of the line *)
-            let bases_pre, bases = token state.bases ~prev:state.prev t in
-
-(*
-            (* fixed region *)
-            let fixed_region = Region.move_chars (indent - orig_indent) orig_region in
-
-            let bases = token tmp_bases ~prevs:state.prevs t fixed_region indent in
-*)
-            
-            let state' = 
-              { bases = bases;
-                last_orig_region = orig_region; (* or just line number ? *)
-                orig_indent = orig_indent;
-                prev = if t <> COMMENT then Some t else state.prev (* COMMENT must be skipped *)
-              }
-            in
-
-            if new_line then begin
-              let indent = get_indent_chars bases_pre in
-              let space_between = 
-                try 
-                  let pos = String.rindex space_between '\n' in 
-                  String.sub space_between 0 pos
-                with
-                | _ -> "XXX"
-              in
-
-              Printf.printf "%s\n" space_between;
-
-              if debug then begin
-                print_string (String.make indent ' ');
-                Format.printf "(* %s *)@." (Sexplib.Sexp.to_string_mach (sexp_of_stack bases_pre)); 
-              end;
-
-              print_string (String.make indent ' ');
-              Printf.printf "%s" substr;
-            end else begin
-              print_string space_between;
-              Printf.printf "%s" substr;
-            end;
-            loop state'
-      in
-      loop { bases = []; last_orig_region = Region.zero; orig_indent = 0; prev = None } 
-
-    with
-    | e -> 
-        close_in ic;
-        raise e
+    loop Region.zero State.init str
   ) paths
-

test2.ml

-let f = 
-  10 
-  + 2 -
-    3
-  + 4
-
-(* comment *)

test4.ml

-f 1 2 3 
-  4 5 6
+let               (* let: = cur+4 *)
+    x y = 1,
+  2
+
+let x 
+    y = 1,
+  2
+
+let x y
+    = 1,
+  2
+
+let x y =        (* =: = let+2 *)
+  1, 2
+
+let x y = 1
+  , 2            (* geez *)
+
+let x = let      (* let: = cur+4 *)
+    y =          (* =: = let+2 *)
+          2      
+        in       (* in: = let *)
+        1
+
+let f = fun
+  x
+-> 2
+
+let f = fun
+    x
+-> 2
+
+module X =
+  struct
+  end
+f 1 2 3
+  4 5 6
+  7 8 9
++ 1 2 3

test_expr_binop.ml

+let f x = 
+  1 + 2
+    3 -
+    4
+  * 5
+    6
+;;
+let 
+    f 
+      x 
+    = 
+  1
+
+and 
+    g
+      y
+    =
+  2
+
+let 
+    f x =
+  2
+;;
+
+let 
+    g 
+      x =
+  2
+let x =
+  let y = 10 in
+  x
+;;
+let x = ( 1,
+          2,
+          3 )
+
+let x = (
+  1,
+  2,
+  3
+)
+
+let x = f (fun x ->
+  1
+)
+
+let x = (f
+           g)
+
+let x = (
+  f g
+)
+
+let x = ( 1
+        , 2
+        , 3
+        )
+
+let x = f (fun x -> y
+  z
+)
+
+let z = let x = 
+          f ( (* com *) 1, 
+              2 )
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.