From 0e6090799e62171dbe45d65814bd1ee9393569cd Mon Sep 17 00:00:00 2001 From: David Moon Date: Fri, 8 Nov 2024 14:10:49 -0500 Subject: [PATCH 1/5] fix typo causing spurious degrouted marks and shifting whitespace --- src/core/structure/marks/Marks.re | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/structure/marks/Marks.re b/src/core/structure/marks/Marks.re index b122128c..33b3f3fe 100644 --- a/src/core/structure/marks/Marks.re +++ b/src/core/structure/marks/Marks.re @@ -138,7 +138,8 @@ module Cell = { cursor: Options.merge(~f=Path.Cursor.union, l.cursor, r.cursor), obligs: Path.Map.union((_, t, _) => Some(t), l.obligs, r.obligs), dirty: Path.Map.union((_, (), ()) => Some(), l.dirty, r.dirty), - degrouted: Path.Map.union((_, (), ()) => Some(), l.dirty, r.dirty), + degrouted: + Path.Map.union((_, (), ()) => Some(), l.degrouted, r.degrouted), }; let union_all = List.fold_left(union, empty); From bc0161a08eeaf4ce4b64f82dbece008a90817d13 Mon Sep 17 00:00:00 2001 From: David Moon Date: Fri, 8 Nov 2024 14:28:57 -0500 Subject: [PATCH 2/5] logs --- src/core/editor/Modify.re | 29 ++++++++++++++++++++++----- src/core/material/Walker.re | 2 +- src/core/parser/Grouter.re | 30 ++++++++++++++++++++++++++-- src/core/parser/Melder.re | 39 +++++++++++++++++++++++++------------ src/core/parser/Molder.re | 15 ++++++++------ src/core/structure/Cell.re | 8 ++------ 6 files changed, 91 insertions(+), 32 deletions(-) diff --git a/src/core/editor/Modify.re b/src/core/editor/Modify.re index 8f959ab3..77ec3512 100644 --- a/src/core/editor/Modify.re +++ b/src/core/editor/Modify.re @@ -96,8 +96,15 @@ let mold = (ctx: Ctx.t, ~fill=Cell.dirty, tok: Token.Unmolded.t): option(Ctx.t) => { open Options.Syntax; let ((l, r), rest) = Ctx.unlink_stacks(ctx); + // Grouter.dbg := true; let+ (tok, grouted, l) = Molder.mold(l, ~fill, tok); + // Grouter.dbg := false; + // P.log("--- Molder.mold/success"); + // P.show("tok", Token.show(tok)); + // P.show("grouted", Grouted.show(grouted)); + // P.show("stack", Stack.show(l)); let connected = Stack.connect(tok, grouted, l); + // P.show("connected", Stack.show(connected)); connected.bound == l.bound ? Ctx.link_stacks((connected, r), rest) : Ctx.map_hd( @@ -107,7 +114,7 @@ let mold = }; let rec remold = (~fill=Cell.dirty, ctx: Ctx.t): (Cell.t, Ctx.t) => { - // P.log("--- remold"); + // P.log("--- Modify.remold"); // P.show("fill", Cell.show(fill)); // P.show("ctx", Ctx.show(ctx)); let ((l, r), tl) = Ctx.unlink_stacks(ctx); @@ -120,8 +127,14 @@ let rec remold = (~fill=Cell.dirty, ctx: Ctx.t): (Cell.t, Ctx.t) => { |> Ctx.map_hd(Frame.Open.cat(Stack.(to_slope(l'), to_slope(r')))) |> remold(~fill) | Ok((dn, fill)) => + // P.log("--- Modify.remold/done"); + // P.show("dn", Slope.Dn.show(dn)); + // P.show("fill", Cell.show(fill)); let bounds = (l.bound, r.bound); + // Melder.dbg := true; let cell = Melder.complete_bounded(~bounds, ~onto=L, dn, ~fill); + // Melder.dbg := false; + // P.show("completed", Cell.show(cell)); let hd = ({...l, slope: []}, {...r, slope: []}); let ctx = Ctx.link_stacks(hd, tl); (cell, ctx); @@ -317,9 +330,14 @@ let insert_toks = toks |> Chain.fold_left( fill => (ctx, fill), - ((ctx, fill), tok, next_fill) => + ((ctx, fill), tok, next_fill) => { + // P.log("--- insert_toks/tok"); + // P.show("ctx", Ctx.show(ctx)); + // P.show("fill", Cell.show(fill)); + // P.show("tok", Token.Unmolded.show(tok)); switch (mold(ctx, ~fill, tok)) { | Some(ctx) => + // P.show("molded tok", Ctx.show(ctx)); let (face, rest) = Ctx.pull(~from=L, ctx); switch (face, next_fill.marks.cursor) { // if molded token is longer than original, then move cursor out of @@ -343,7 +361,8 @@ let insert_toks = Option.is_some(fill.marks.cursor) ? Cell.mark_ends_dirty(fill) : next_fill; (ctx, next_fill); - }, + } + }, ); }; @@ -413,12 +432,12 @@ let insert = (s: string, z: Zipper.t) => { open Options.Syntax; let z = delete_sel(L, z); - // P.log("--- insert"); + // P.log("--- Modify.insert"); let- () = try_expand(s, z); let- () = try_move(s, z); let- () = try_extend(s, z); - // P.log("--- insert/molding"); + // P.log("--- Modify.insert/molding"); // P.show("z.ctx", Ctx.show(z.ctx)); let (toks, ctx) = relabel(s, z.ctx); // P.show("toks", Chain.show(Cell.pp, Token.Unmolded.pp, toks)); diff --git a/src/core/material/Walker.re b/src/core/material/Walker.re index 2a189c49..33fbcba7 100644 --- a/src/core/material/Walker.re +++ b/src/core/material/Walker.re @@ -1,7 +1,7 @@ open Stds; open Walk; -let debug = ref(false); +let dbg = ref(false); let mtrlize_tile = ((l, r, s, from)) => Grammar.v diff --git a/src/core/parser/Grouter.re b/src/core/parser/Grouter.re index d27c979a..c90a20ea 100644 --- a/src/core/parser/Grouter.re +++ b/src/core/parser/Grouter.re @@ -2,6 +2,8 @@ open Sexplib.Std; open Ppx_yojson_conv_lib.Yojson_conv.Primitives; open Stds; +let dbg = ref(false); + let rec split_cell_padding = (~side: Dir.t, c: Cell.t) => switch (Cell.get(c)) { | None => Cell.(empty, c) @@ -147,6 +149,12 @@ let fill_default = // assumes cs already squashed sans padding let fill_swing = (cs: Cells.t, sw: Walk.Swing.t, ~from: Dir.t) => { let cs = Dir.pick(from, (List.rev, Fun.id), cs); + // if (dbg^) { + // P.log("--- Grouter.fill_swing"); + // P.show("from", Dir.show(from)); + // P.show("sw", Walk.Swing.show(sw)); + // P.show("cs", Cells.show(cs)); + // }; let (bot, top) = Walk.Swing.(bot(sw), top(sw)); switch (bot) { | Space(nt) => @@ -195,10 +203,16 @@ let fill_swing = (cs: Cells.t, sw: Walk.Swing.t, ~from: Dir.t) => { }; let fill_swings = - (~repair, ~from, cells: list(Cell.t), swings: list(Walk.Swing.t)) => + (~repair, ~from, cells: list(Cell.t), swings: list(Walk.Swing.t)) => { + // if (dbg^) { + // P.log("--- Grouter.fill_swings"); + // P.show("from", Dir.show(from)); + // P.show("cells", Cells.show(Dir.pick(from, (List.rev, Fun.id), cells))); + // }; cells |> Dir.pick(from, (List.rev, Fun.id)) |> (repair ? List.concat_map(degrout) : Fun.id) + // |> (dbg^ ? P.oshow("degrouted", Cells.show) : Fun.id) |> Dir.pick(from, (List.rev, Fun.id)) |> Lists.split_bins(List.length(swings)) |> Oblig.Delta.minimize(~to_zero=!repair, c_bins => @@ -210,6 +224,7 @@ let fill_swings = }) |> Options.for_all ); +}; let fill = (~repair, ~from, cs, (swings, stances): Walk.t) => { open Options.Syntax; @@ -223,5 +238,16 @@ let fill = (~repair, ~from, cs, (swings, stances): Walk.t) => { // obligation delta. the given cells are expected to be oriented the same way as the // given walks according to from. let pick = (~repair=false, ~from: Dir.t, cs: list(Cell.t), ws: list(Walk.t)) => { - Oblig.Delta.minimize(~to_zero=!repair, fill(~repair, ~from, cs), ws); + // if (dbg^) { + // P.log("--- Grouter.pick"); + // P.show("from", Dir.show(from)); + // P.show("cs", Cells.show(cs)); + // P.log("ws"); + // ws |> List.iter(w => P.show("w", Walk.show(w))); + // }; + Oblig.Delta.minimize( + ~to_zero=!repair, + fill(~repair, ~from, cs), + ws, + ); }; diff --git a/src/core/parser/Melder.re b/src/core/parser/Melder.re index 9778291b..9079d88e 100644 --- a/src/core/parser/Melder.re +++ b/src/core/parser/Melder.re @@ -2,26 +2,22 @@ open Stds; exception Bug__failed_to_push_space; -let debug = ref(true); +let dbg = ref(true); // assumes w is already oriented toward side. // used to complete zigg top when it takes precedence over pushed wald. let complete_wald = (~side: Dir.t, ~fill=Cell.empty, w: Wald.t): Terr.t => { let from = Dir.toggle(side); let exited = Walker.exit(~from, Node(Wald.face(w).mtrl)); - let baked = Grouter.pick(~repair=true, ~from, [fill], exited); - // exited |> Oblig.Delta.minimize(Baker.bake(~from, ~fill=Fill.unit(fill))); - switch (baked) { - | Some(baked) => Grouted.complete_wald(baked, w) + let grouted = Grouter.pick(~repair=true, ~from, [fill], exited); + switch (grouted) { + | Some(grouted) => Grouted.complete_wald(grouted, w) | None => assert(!Cell.is_empty(fill)); print_endline("warning: dropping fill " ++ Cell.show(fill)); let baked = Grouter.pick(~repair=true, ~from, [], exited) - |> Options.get_fail("bug: expected bake to succeed sans fill"); - // walker bug if no exits - // let exited = List.hd(exited); - // let baked = Baker.bake_sans_fill(~from, exited); + |> Options.get_fail("bug: expected grouter to succeed sans fill"); Grouted.complete_wald(baked, w); }; }; @@ -30,10 +26,25 @@ let complete_terr = (~onto: Dir.t, ~fill=Cell.empty, terr: Terr.t): Cell.t => { let orient = Dir.pick(onto, (Meld.rev, Fun.id)); let exited = Walker.exit(~from=onto, Node(Terr.face(terr).mtrl)); let grouted = Grouter.pick(~repair=true, ~from=onto, [fill], exited); - // exited - // |> Oblig.Delta.minimize(Baker.bake(~from=onto, ~fill=Fill.unit(fill))); + // if (dbg^) { + // P.log("--- Melder.complete_terr"); + // P.show("onto", Dir.show(onto)); + // P.show("fill", Cell.show(fill)); + // P.show("terr", Terr.show(terr)); + // }; switch (grouted) { - | Some(grouted) => Cell.put(orient(Grouted.complete_terr(grouted, terr))) + | Some(grouted) => + let m = Grouted.complete_terr(grouted, terr); + // if (dbg^) { + // P.log("--- Melder.complete_terr/grouted"); + // P.show("grouted", Grouted.show(grouted)); + // P.show("completed meld", Meld.show(m)); + // P.show("oriented meld", Meld.show(orient(m))); + // Cell.dbg := true; + // P.show("oriented cell", Cell.show(Cell.put(orient(m)))); + // Cell.dbg := false; + // }; + Cell.put(orient(m)); | None => assert(!Cell.is_empty(fill)); print_endline("warning: dropping fill " ++ Cell.show(fill)); @@ -55,6 +66,10 @@ let complete_bounded = let fill = complete_slope(~onto, ~fill, slope); let fc_onto = bd_onto |> Bound.map(t => Terr.face(t).mtrl); let fc_from = bd_from |> Bound.map(t => Terr.face(t).mtrl); + // if (dbg^) { + // P.log("--- Melder.complete_bounded"); + // P.show("completed slope", Cell.show(fill)); + // }; Walker.walk_eq(~from=onto, fc_onto, fc_from) |> Grouter.pick(~repair=true, [fill], ~from=onto) |> Option.map(grouted => snd(Chain.hd(grouted))) diff --git a/src/core/parser/Molder.re b/src/core/parser/Molder.re index cfe85b3a..ef4a18c5 100644 --- a/src/core/parser/Molder.re +++ b/src/core/parser/Molder.re @@ -81,8 +81,12 @@ let rec mold = |> Option.map(((grouted, stack)) => (tok, grouted, stack)) ) ) { - // pushed token was empty ghost connected via neq-relation - | Some((tok, grouted, _) as molded) => + | Some((tok, grouted, _stack) as molded) => + // remove empty ghost connected via neq-relation + // P.log("--- Molder.mold/success"); + // P.show("tok", Token.show(tok)); + // P.show("grouted", Grouted.show(grouted)); + // P.show("stack", Stack.show(stack)); Mtrl.is_tile(tok.mtrl) && tok.text == "" && Grouted.is_neq(grouted) ? None : Some(molded) | None => @@ -102,19 +106,18 @@ let rec mold = and remold = (~fill, (l, r): Stack.Frame.t) : Result.t((Slope.Dn.t, Cell.t), (Cell.t, Stack.Frame.t)) => { - // open Result.Syntax; - // P.log("--- remold"); + // P.log("--- Molder.remold"); // P.show("fill", Cell.show(fill)); // P.show("(l, r)", Stack.Frame.show((l, r))); let bounds = (l.bound, r.bound); switch (r.slope) { | [] => - // P.log("--- remold/done"); + // P.log("--- Molder.remold/done"); // P.show("l", Stack.show(l)); // P.show("fill", Cell.show(fill)); Ok((l.slope, fill)) | [hd, ...tl] => - // P.log("--- remold/continue"); + // P.log("--- Molder.remold/continue"); // P.show("hd", Terr.show(hd)); // insert any pending ghosts if next terr has newlines let (l, fill) = diff --git a/src/core/structure/Cell.re b/src/core/structure/Cell.re index 52b20989..c06c5399 100644 --- a/src/core/structure/Cell.re +++ b/src/core/structure/Cell.re @@ -2,6 +2,8 @@ open Sexplib.Std; open Ppx_yojson_conv_lib.Yojson_conv.Primitives; open Stds; +let dbg = ref(false); + module Wald = { [@deriving (sexp, yojson)] type t('tok, 'cell) = @@ -93,12 +95,6 @@ let rec pp = (out, {marks, meld}: t) => { }; let show = Fmt.to_to_string(pp); -// module Wald = Meld.Wald; - -// include Meld.Cell; -// [@deriving (show({with_path: false}), sexp, yojson)] -// type t = Meld.Cell.t(Meld.t); -// let empty = mk(); let is_empty = (~require_unmarked=false, c: t) => Option.is_none(c.meld) && (!require_unmarked || Marks.is_empty(c.marks)); From 8d66284f8ccdb37b13ea00732bc3229cdd60689f Mon Sep 17 00:00:00 2001 From: David Moon Date: Fri, 8 Nov 2024 14:32:03 -0500 Subject: [PATCH 3/5] being extra careful about fill propagation but not sure if strictly necessary --- src/core/editor/Modify.re | 14 +++++++------- src/core/parser/Molder.re | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core/editor/Modify.re b/src/core/editor/Modify.re index 77ec3512..670b39be 100644 --- a/src/core/editor/Modify.re +++ b/src/core/editor/Modify.re @@ -93,8 +93,9 @@ let relabel = // None means token was removed. Some(ctx) means token was molded (or deferred and // tagged as an unmolded space), ctx includes the molded token. let mold = - (ctx: Ctx.t, ~fill=Cell.dirty, tok: Token.Unmolded.t): option(Ctx.t) => { - open Options.Syntax; + (ctx: Ctx.t, ~fill=Cell.dirty, tok: Token.Unmolded.t) + : Result.t(Ctx.t, Cell.t) => { + open Result.Syntax; let ((l, r), rest) = Ctx.unlink_stacks(ctx); // Grouter.dbg := true; let+ (tok, grouted, l) = Molder.mold(l, ~fill, tok); @@ -240,7 +241,7 @@ let try_expand = (s: string, z: Zipper.t): option(Zipper.t) => { // if expandable, consider all expandable const labels let* expanded = expand(tok); let ((l, r), tl) = Ctx.unlink_stacks(rest); - let* (t, grouted, rest) = Molder.mold(l, expanded); + let* (t, grouted, rest) = Result.to_option(Molder.mold(l, expanded)); if (t.mtrl == Space(Unmolded) || t.mtrl == tok.mtrl) { None; } else { @@ -336,7 +337,7 @@ let insert_toks = // P.show("fill", Cell.show(fill)); // P.show("tok", Token.Unmolded.show(tok)); switch (mold(ctx, ~fill, tok)) { - | Some(ctx) => + | Ok(ctx) => // P.show("molded tok", Ctx.show(ctx)); let (face, rest) = Ctx.pull(~from=L, ctx); switch (face, next_fill.marks.cursor) { @@ -355,11 +356,10 @@ let insert_toks = (ctx, next_fill); | _ => (ctx, next_fill) }; - | None => + | Error(fill) => // removed empty token let next_fill = - Option.is_some(fill.marks.cursor) - ? Cell.mark_ends_dirty(fill) : next_fill; + Cell.mark_ends_dirty(Cell.Space.merge(fill, next_fill)); (ctx, next_fill); } }, diff --git a/src/core/parser/Molder.re b/src/core/parser/Molder.re index ef4a18c5..fcba520c 100644 --- a/src/core/parser/Molder.re +++ b/src/core/parser/Molder.re @@ -73,7 +73,7 @@ let complete_pending_ghosts = (~bounds, l: Stack.t, ~fill) => { // returns None if input token is empty let rec mold = (stack: Stack.t, ~fill=Cell.empty, t: Token.Unmolded.t) - : option((Token.t, Grouted.t, Stack.t)) => + : Result.t((Token.t, Grouted.t, Stack.t), Cell.t) => switch ( candidates(t) |> Oblig.Delta.minimize(tok => @@ -88,12 +88,12 @@ let rec mold = // P.show("grouted", Grouted.show(grouted)); // P.show("stack", Stack.show(stack)); Mtrl.is_tile(tok.mtrl) && tok.text == "" && Grouted.is_neq(grouted) - ? None : Some(molded) + ? Error(Cell.mark_degrouted(fill, ~side=R)) : Ok(molded) | None => let deferred = Token.Unmolded.defer(t); Token.is_empty(deferred) - ? None - : Some( + ? Error(Cell.mark_degrouted(fill, ~side=R)) + : Ok( { let (fill, slope) = Slope.Dn.unroll(fill); let stack = Stack.cat(slope, stack); @@ -137,11 +137,11 @@ and remold = }) |> Option.value(~default=Slope.Up.unroll(hd.cell)); switch (mold(l, ~fill, Token.unmold(hd_w))) { - | None => + | Error(fill) => let (c, up) = unroll_tl_w_hd_cell(); let fill = fill |> Cell.pad(~r=c) |> Cell.mark_ends_dirty; (l, r_tl) |> Stack.Frame.cat(([], up)) |> remold(~fill); - | Some((t, grouted, rest)) when t.mtrl == hd_w.mtrl => + | Ok((t, grouted, rest)) when t.mtrl == hd_w.mtrl => // fast path for when hd_w retains original meld let connected = Stack.connect(t, grouted, rest) |> Stack.extend(tl_w); if (connected.bound == l.bound) { @@ -149,7 +149,7 @@ and remold = } else { Error((hd.cell, (connected, r_tl))); }; - | Some((t, grouted, rest)) => + | Ok((t, grouted, rest)) => let connected = Stack.connect(t, grouted, rest); // check if connection changed the stack bound if (connected.bound == l.bound) { From 8fe2efd8816c9ab8e5953e26284cb23cc9530846 Mon Sep 17 00:00:00 2001 From: David Moon Date: Fri, 8 Nov 2024 14:33:46 -0500 Subject: [PATCH 4/5] mv --- src/core/structure/Cell.re | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/structure/Cell.re b/src/core/structure/Cell.re index c06c5399..f9f813c0 100644 --- a/src/core/structure/Cell.re +++ b/src/core/structure/Cell.re @@ -134,6 +134,10 @@ let rec mark_degrouted = (~side: Dir.t, c: t) => {...c, meld: Some(M(l, w, r))}; } }; +let unmark_degrouted = (c: t) => { + let marks = {...c.marks, degrouted: Path.Map.empty}; + {...c, marks}; +}; let rec end_path = (~sans_padding=false, ~side: Dir.t, c: t) => switch (c.meld) { @@ -257,11 +261,6 @@ module Space = { }; let is_space = c => Option.is_some(get(c)); - let unmark_degrouted = (c: t) => { - let marks = {...c.marks, degrouted: Path.Map.empty}; - {...c, marks}; - }; - let mk = (cs: list(t), ts: list(Token.t)) => switch (cs, ts) { | ([c], []) => c From 3bd4f9a8f7b3f16c04ed2a8c7a572e7dcedc104f Mon Sep 17 00:00:00 2001 From: David Moon Date: Fri, 8 Nov 2024 14:39:39 -0500 Subject: [PATCH 5/5] rm spurious degrouted marking --- src/core/editor/Modify.re | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/editor/Modify.re b/src/core/editor/Modify.re index 670b39be..2aa54328 100644 --- a/src/core/editor/Modify.re +++ b/src/core/editor/Modify.re @@ -318,7 +318,6 @@ let delete_toks = ); }, ) - |> Chain.map_loop(Cell.mark_degrouted(~side=L)) // finally, unmold the tokens (only relabeling the last token) |> Chain.mapi_link(i => Token.unmold(~relabel=i - 1 / 2 == n - 1)); };