aboutsummaryrefslogtreecommitdiff
path: root/sheet.ml
diff options
context:
space:
mode:
Diffstat (limited to 'sheet.ml')
-rwxr-xr-xsheet.ml300
1 files changed, 300 insertions, 0 deletions
diff --git a/sheet.ml b/sheet.ml
new file mode 100755
index 0000000..773c784
--- /dev/null
+++ b/sheet.ml
@@ -0,0 +1,300 @@
+type cell = int * int
+
+type search = [
+ | `Pattern of ScTypes.result
+ | `Next
+ | `Previous
+]
+
+module Raw = struct
+
+ exception Cycle
+
+ module Map = Map.Make(struct
+ type t = cell
+ let compare (x1, y1) (x2, y2) = Pervasives.compare (y1, x1) (y2, x2)
+ end)
+
+ type content = {
+ expr : Expression.t; (** The expression *)
+ value : ScTypes.result; (** The content evaluated *)
+ sink : Cell.Set.t; (** All the cell which references this one *)
+ }
+
+ and t = content Map.t
+
+ (** An empty cell which does contains nothing *)
+ let empty_cell = {
+ expr = Expression.load @@ UTF8.empty;
+ value = ScTypes.Result ScTypes.Undefined;
+ sink = Cell.Set.empty;
+ }
+
+
+ let create = Map.empty
+
+ let get_value (id: cell) t = begin
+ try (Map.find id t).value
+ with Not_found -> ScTypes.Result ScTypes.Undefined
+ end
+
+ let get_expr (id: cell) t = begin
+ try (Map.find id t).expr
+ with Not_found -> Expression.load @@ UTF8.empty
+ end
+
+ (** Extract a value from a reference. *)
+ let get_ref (from:cell) (t:t) : ScTypes.refs -> ScTypes.types = begin
+
+ let extract_values = begin function
+ | ScTypes.Result v -> v
+ | ScTypes.Error e -> raise e
+ end in
+
+ begin function
+ | ScTypes.Cell c ->
+ let coord = Cell.to_pair c in
+ if coord = from then raise Cycle; extract_values (get_value coord t)
+ | ScTypes.Range _ as r ->
+ ScTypes.Refs.collect r
+ |> List.map (fun x -> if x = from then raise Cycle; extract_values (get_value x t))
+ |> (fun x -> ScTypes.List x)
+ end
+ end
+
+ (** Update the value for the given cell *)
+ let update cell content t = begin
+ let new_val = Expression.eval content.expr (get_ref cell t) in
+ if not (ScTypes.Result.(=) new_val content.value) then
+ Some (Map.add cell { content with value = new_val } t)
+ else
+ (* If there is no changes, do not update the map *)
+ None
+ end
+
+ (** Parse all the successors from [init] and call [f] for each of them.
+ [f] is called only once for each successor.
+ @return all the successors collected
+ *)
+ let successors (f:(cell -> content -> t -> t option)) (init:content) (state:Cell.Set.t * t) = begin
+ let rec fold cell (succ, t) = begin
+ if (Cell.Set.mem cell succ) then
+ (* The element has already been parsed, do not cycle *)
+ (succ, t)
+ else (
+ (* Map.find cannot raise Not_found here *)
+ let content = Map.find cell t in
+ match f cell content t with
+ | None -> (succ, t)
+ | Some x -> Cell.Set.fold fold content.sink (Cell.Set.add cell succ, x)
+ )
+ end in
+ Cell.Set.fold fold init.sink state
+ end
+
+ (** Remove the cell from the sheet *)
+ let remove_element (id:cell) t : t * content option = begin
+
+ (** Remove the references from each sources.
+ If the sources is not referenced anywhere, and is Undefined, remove it
+ *)
+ let remove_ref cell t = begin
+ try let c = Map.find cell t in
+
+ (* Remove all the refs which points to the removed cell *)
+ let sink' = Cell.Set.filter ((<>) id) c.sink in
+ if Cell.Set.is_empty sink' && not (Expression.is_defined c.expr) then (
+ Map.remove cell t )
+ else
+ Map.add cell {c with sink = sink'} t
+ with Not_found -> t
+ end in
+
+ begin try
+ let c = Map.find id t in
+ let t' =
+ (** Remove the references from each sources *)
+ let sources = Expression.collect_sources c.expr in
+ Cell.Set.fold remove_ref sources t in
+
+ (** If there is no references on the cell, remove it *)
+ if Cell.Set.is_empty c.sink then (
+ Map.remove id t', None)
+ else (
+ let c = { empty_cell with sink = c.sink } in
+ Map.add id c t', (Some c)
+ )
+ with Not_found -> t, None
+ end
+ end
+
+ let remove id t = begin
+ match remove_element id t with
+ | t, None -> Cell.Set.empty, t
+ | t, Some content ->
+ (** Update all the successors *)
+ successors update content (Cell.Set.singleton id, t)
+ end
+
+ let add_element id f t = begin
+
+ (** Add the references in each sources.
+ If the sources does not exists, create it.
+ *)
+ let add_ref cell t = begin
+ let c =
+ try Map.find cell t
+ with Not_found -> empty_cell in
+ let c' = { c with sink = Cell.Set.add id c.sink} in
+ Map.add cell c' t
+ end in
+
+ let t', cell = remove_element id t in
+ let cell' = match cell with
+ | None -> empty_cell
+ | Some x -> x in
+
+ let content = f cell' t' in
+
+ let sources = Expression.collect_sources content.expr in
+ let updated = Map.add id content t'
+ |> Cell.Set.fold add_ref sources
+ in
+
+ (** Update the value for each sink already evaluated *)
+ successors update content (Cell.Set.singleton id, updated)
+ end
+
+ let add id expression t = begin
+ if not (Expression.is_defined expression) then
+ (Cell.Set.empty, t)
+ else
+ let f cell t = { cell with
+ expr = expression ;
+ value = Expression.eval expression (get_ref id t)
+ } in
+ add_element id f t
+ end
+
+
+ let paste id shift content t = begin
+ let expr = Expression.shift shift content.expr in
+ let f cell t =
+ { cell with
+ expr = expr ;
+ value = Expression.eval expr (get_ref id t)
+ } in
+ add_element id f t
+ end
+
+ exception Found of (int * int)
+
+ let search pattern t = begin
+
+ let _search key content = if content.value = pattern then raise (Found key) in
+ try
+ Map.iter _search t;
+ None
+ with Found key -> Some key
+ end
+
+ let get_sink id t =
+ try (Map.find id t).sink
+ with Not_found -> Cell.Set.empty
+
+ let fold f a t = begin
+ Map.fold (fun key content a -> f a key (content.expr, content.value)) t a
+ end
+
+end
+
+type yank =cell * Raw.content
+
+type t = {
+ selected: Selection.t; (* The selected cell *)
+ data: Raw.t;
+ history: t list; (* Unlimited history *)
+ yank: (cell * Raw.content) list
+}
+
+let undo t = begin match t.history with
+ | [] -> None
+ | hd::tl -> Some { hd with selected = t.selected }
+end
+
+let move direction t =
+ let position = Selection.extract t.selected in
+ let position' = begin match direction with
+ | Actions.Left quant -> Tools.Tuple2.replace1 (max 1 ((fst position) - quant)) position
+ | Actions.Right quant -> Tools.Tuple2.replace1 ((fst position) + quant) position
+ | Actions.Up quant -> Tools.Tuple2.replace2 (max 1 ((snd position) - quant)) position
+ | Actions.Down quant -> Tools.Tuple2.replace2 ((snd position) + quant) position
+ | Actions.Absolute (x, y)-> (x, y)
+ end in
+ if position = position' then
+ None
+ else
+ Some {t with selected = Selection.create position'}
+
+let delete t = begin
+ let count, data' = Selection.fold (fun (count, c) t ->
+ (count + 1, snd @@ Raw.remove t c)) (0, t.data) t.selected in
+ let t' = { t with
+ data = data';
+ history = t::t.history
+ } in
+ t', count
+end
+
+let yank t = begin
+
+ let shift = Selection.shift t.selected in
+ let origin = shift (0, 0) in
+ let _yank (count, extracted) cell = begin
+ let content =
+ try let content = (Raw.Map.find cell t.data) in
+ { content with Raw.expr = Expression.shift origin content.Raw.expr }
+ with Not_found -> Raw.empty_cell in
+
+ count + 1, (shift cell,content)::extracted
+ end in
+
+ let count, yanked = Selection.fold _yank (0, []) t.selected in
+ let t' = { t with yank = List.rev yanked; } in
+ t', count
+end
+
+let paste t = begin
+ (* Origin of first cell *)
+ let (shift_x, shift_y) as shift = Selection.extract t.selected in
+
+ let _paste (count, t) ((x, y), content) = begin
+ count + 1, snd @@ Raw.paste (shift_x + x, shift_y + y) shift content t
+ end in
+
+ let count, data' = List.fold_left _paste (0, t.data) t.yank in
+ let t' = { t with data = data'; history = t::t.history } in
+ t', count
+end
+
+let add expression t = begin
+ let id = Selection.extract t.selected in
+ let cells, data' = Raw.add id expression t.data in
+ cells, { t with data = data'; history = t::t.history}
+end
+
+let search action t = begin match action with
+ | `Pattern pattern ->
+ begin match Raw.search pattern t.data with
+ | None -> None
+ | Some x -> Some {t with selected = Selection.create x}
+ end
+ | _ -> None
+end
+
+let create data = {
+ data = data;
+ selected = Selection.create (1, 1);
+ history = [];
+ yank = [];
+}