lout-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Equation display styles and context-sensitive rendering


From: Ludovic Courtès
Subject: Re: Equation display styles and context-sensitive rendering
Date: Thu, 14 Dec 2006 22:51:27 +0100
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

Hi,

address@hidden (Jeff Kingston) writes:

> Given that the elements of environments don't behave anything
> like other Lout symbols, I think you would be better off not
> declaring them as symbols, but just having 
>
>     @SetEnv key { string } value { object } object
>
> to set the value of an environment variable named string in
> the second object to the first object, and
>
>     @GetEnv string
>
> to retrieve the value of the environment variable whose name
> is string from the current environment and return it as result.

I implemented this approach, thanks to Jeff's advice.  (The reason for
implementing this instead of what I initially advocated is that all
invocations in Lout have to somehow evaluate to a word, paragraph, etc.,
which is not what the address@hidden' construct I proposed expected.)

Anyway, while trying to implement the equation styles described by
Knuth, I understood a potentially serious shortcoming related to the
interaction of lazy evaluation and "context-sensitive variables".
Basically, the style of, say, the numerator of a fraction is a function
of the current style.  So one ends up writing things like this:

  @SetContext key { "EqCurrStyle" }
              value { { @GetContext "EqCurrStyle" } @Case {
                         "display" @Yield "text"
                         ...
                      } } {
    ... body ...
  }

The issue is that the `value' argument is evaluated lazily, i.e., when a
address@hidden' returns it _in the body of address@hidden'_.  This is
problematic since we'd rather want it to be evaluated exactly where it
occurs, that is, when the "EqCurrStyle" context variable is still bound
to its former value.

Is there a way to achieve this behavior?

I'm attaching my current experimental-and-dirty patch in case it's of
any use.

Thanks,
Ludovic.


--- orig/externs.h
+++ mod/externs.h
@@ -635,6 +635,19 @@ typedef struct
 /*                                                                           */
 /*****************************************************************************/
 
+/* A key-value pair.  */
+typedef struct context_type
+{
+  struct context_type *oprevious;
+  union rec    *okey;                  /* name of a context variable        */
+  union rec    *ovalue;                /* associated value                  */
+} CONTEXT;
+
+#define        context_key(x)          (x).okey
+#define        context_value(x)        (x).ovalue
+#define        context_previous(x)     (x).oprevious
+
+
 typedef struct
 {
   GAP          oline_gap;              /* separation between lines          */
@@ -663,6 +676,7 @@ typedef struct
   BOOLEAN      ostrut          : 1;    /* strut char metrics                */
   BOOLEAN      oligatures      : 1;    /* use ligatures                     */
   BOOLEAN      omarginkerning  : 1;    /* perform margin kerning            */
+  CONTEXT       ocontext;              /* context stack                     */
 } STYLE;
 
 #define        line_gap(x)     (x).oline_gap
@@ -691,34 +705,36 @@ typedef struct
 #define        strut(x)        (x).ostrut
 #define        ligatures(x)    (x).oligatures
 #define        marginkerning(x)(x).omarginkerning
+#define        context(x)      (x).ocontext
 
-#define StyleCopy(x, y)                                                        
\
-( GapCopy(line_gap(x), line_gap(y)),                                   \
-  GapCopy(space_gap(x), space_gap(y)),                                 \
-  yunit(x) = yunit(y),                                                 \
-  zunit(x) = zunit(y),                                                 \
-  outdent_len(x) = outdent_len(y),                                     \
-  smallcaps_len(x) = smallcaps_len(y),                                 \
-  font(x) = font(y),                                                   \
-  colour(x) = colour(y),                                               \
-  texture(x) = texture(y),                                             \
-  blanklinescale(x) = blanklinescale(y),                               \
-  language(x) = language(y),                                           \
-  vadjust(x) = vadjust(y),                                             \
-  hadjust(x) = hadjust(y),                                             \
-  padjust(x) = padjust(y),                                             \
-  small_caps(x) = small_caps(y),                                       \
-  space_style(x) = space_style(y),                                     \
-  hyph_style(x) = hyph_style(y),                                       \
-  fill_style(x) = fill_style(y),                                       \
-  display_style(x) = display_style(y),                                 \
-  outline(x) = outline(y),                                             \
-  nobreakfirst(x) = nobreakfirst(y),                                   \
-  nobreaklast(x) = nobreaklast(y),                                     \
-  baselinemark(x) = baselinemark(y),                                   \
-  strut(x) = strut(y),                                                 \
-  ligatures(x) = ligatures(y),                                         \
-  marginkerning(x) = marginkerning(y)                                  \
+#define StyleCopy(x, y)                                \
+( GapCopy(line_gap(x), line_gap(y)),           \
+  GapCopy(space_gap(x), space_gap(y)),         \
+  yunit(x) = yunit(y),                         \
+  zunit(x) = zunit(y),                         \
+  outdent_len(x) = outdent_len(y),             \
+  smallcaps_len(x) = smallcaps_len(y),         \
+  font(x) = font(y),                           \
+  colour(x) = colour(y),                       \
+  texture(x) = texture(y),                     \
+  blanklinescale(x) = blanklinescale(y),       \
+  language(x) = language(y),                   \
+  vadjust(x) = vadjust(y),                     \
+  hadjust(x) = hadjust(y),                     \
+  padjust(x) = padjust(y),                     \
+  small_caps(x) = small_caps(y),               \
+  space_style(x) = space_style(y),             \
+  hyph_style(x) = hyph_style(y),               \
+  fill_style(x) = fill_style(y),               \
+  display_style(x) = display_style(y),         \
+  outline(x) = outline(y),                     \
+  nobreakfirst(x) = nobreakfirst(y),           \
+  nobreaklast(x) = nobreaklast(y),             \
+  baselinemark(x) = baselinemark(y),           \
+  strut(x) = strut(y),                         \
+  ligatures(x) = ligatures(y),                 \
+  marginkerning(x) = marginkerning(y),         \
+  context(x) = context(y)                      \
 )
 
 
@@ -2206,7 +2222,9 @@ typedef struct back_end_rec {
 #define        EXT_GALL           153          /*        an external galley    
     */
 #define        CR_LIST            154          /*        a list of cross 
references */
 #define        SCOPE_SNAPSHOT     155          /*        a scope snapshot      
     */
-#define        DISPOSED           156          /*        a disposed record     
     */
+#define        SET_CONTEXT        156
+#define        GET_CONTEXT        157
+#define        DISPOSED           158          /*        a disposed record     
     */
 
 #define is_indefinite(x)  ((x) >= CLOSURE && (x) <= HEAD)
 #define is_header(x)  ((x) >= BEGIN_HEADER && (x) <= CLEAR_HEADER)
@@ -2226,20 +2244,20 @@ typedef struct back_end_rec {
 /*****************************************************************************/
 
 /* sides of a mark */
-#define        BACK               157          /* means lies to left of mark   
     */
-#define        ON                 158          /* means lies on mark           
     */
-#define        FWD                159          /* means lies to right of mark  
     */
+#define        BACK               159          /* means lies to left of mark   
     */
+#define        ON                 160          /* means lies on mark           
     */
+#define        FWD                161          /* means lies to right of mark  
     */
 
 /* constraint statuses */
-#define        PROMOTE            160          /* this component may be 
promoted    */
-#define        CLOSE              161          /* must close dest before 
promoting  */
-#define        BLOCK              162          /* cannot promote this 
component     */
-#define        CLEAR              163          /* this constraint is now 
satisfied  */
+#define        PROMOTE            162          /* this component may be 
promoted    */
+#define        CLOSE              163          /* must close dest before 
promoting  */
+#define        BLOCK              164          /* cannot promote this 
component     */
+#define        CLEAR              165          /* this constraint is now 
satisfied  */
 
 /* gap increment types */
-#define        GAP_ABS            164          /* absolute,  e.g.  3p          
     */
-#define        GAP_INC            165          /* increment, e.g. +3p          
     */
-#define        GAP_DEC            166          /* decrement, e.g. -3p          
     */
+#define        GAP_ABS            166          /* absolute,  e.g.  3p          
     */
+#define        GAP_INC            167          /* increment, e.g. +3p          
     */
+#define        GAP_DEC            168          /* decrement, e.g. -3p          
     */
 
 /* gap modes occupying mode(x) */
 #define        NO_MODE              0          /* for error detection: no mode 
     */
@@ -2570,6 +2588,8 @@ typedef struct back_end_rec {
 #define        KW_WEEKDAY              AsciiToFull("@WeekDay")
 #define        KW_YEARDAY              AsciiToFull("@YearDay")
 #define        KW_DAYLIGHTSAVING       AsciiToFull("@DaylightSaving")
+#define        KW_SET_CONTEXT          AsciiToFull("@SetContext")
+#define        KW_GET_CONTEXT          AsciiToFull("@GetContext")
 
 /*@::GetMem(), New(), NewWord()@**********************************************/
 /*                                                                           */
@@ -3049,6 +3069,9 @@ extern    BOOLEAN   InMemoryDbIndexes;
 extern BOOLEAN   Kern;
 extern BOOLEAN   SafeExecution;
 extern BOOLEAN   AltErrorFormat;
+extern OBJECT    SetContextKeyTag;
+extern OBJECT    SetContextValueTag;
+
 extern int       TotalWordCount;
 extern BOOLEAN   InitializeAll;
 #if LOCALE_ON
@@ -3207,6 +3230,7 @@ extern    FULL_CHAR *EchoGap(GAP *xgap);
 
 /*****  z18.c    Galley Transfer       **************************************/
 extern STYLE     InitialStyle;
+extern CONTEXT   InitialContext;
 extern OBJECT    InitialEnvironment;
 extern void      TransferInit(OBJECT InitEnv);
 extern OBJECT    TransferBegin(OBJECT x);

--- orig/z01.c
+++ mod/z01.c
@@ -239,6 +239,9 @@ typedef enum {
   BE_PDF
 } BE_TYPE;
 
+/* Named parameters for address@hidden'.  */
+OBJECT SetContextKeyTag = nilobj, SetContextValueTag = nilobj;
+
 static void run(int argc, char *argv[], int run_num, int *runs_to_do,
   FULL_CHAR *lib)
 { int i, len;  FULL_CHAR *arg;
@@ -838,6 +841,7 @@ static void run(int argc, char *argv[], 
   load(KW_FORCE_CROSS,  FORCE_CROSS,    TRUE,   TRUE,   FALSE, CROSSOP_PREC);
   load(KW_NULL,         NULL_CLOS,      FALSE,  FALSE,  TRUE,  NO_PREC     );
   load(KW_PAGE_LABEL,   PAGE_LABEL,     FALSE,  TRUE,   TRUE,  DEFAULT_PREC);
+  load(KW_GET_CONTEXT, GET_CONTEXT,    FALSE,  TRUE,   FALSE, DEFAULT_PREC);
 
 #define setcat(s, mk, jn)  has_mark(s)=mk, has_join(s)=jn
 
@@ -852,6 +856,33 @@ static void run(int argc, char *argv[], 
   s=load(KW_ACAT_NJ, ACAT, TRUE, TRUE, FALSE, ACAT_PREC); setcat(s,FALSE,TRUE);
   s=load(KW_ACAT_MJ, ACAT, TRUE, TRUE, FALSE, ACAT_PREC); setcat(s,TRUE, TRUE);
 
+  /* initialize address@hidden'  */
+  { OBJECT setcontext_tag, rpar;
+    setcontext_tag = InsertSym( KW_SET_CONTEXT, LOCAL, no_fpos, DEFAULT_PREC,
+                               FALSE, FALSE, SET_CONTEXT,
+                               StartSym, nilobj);
+    /* right parameter */
+    rpar = InsertSym( AsciiToFull("pb"), RPAR, no_fpos, DEFAULT_PREC,
+                     FALSE, FALSE, 0, setcontext_tag, nilobj);
+    is_compulsory(rpar) = TRUE;
+
+    /* named parameters */
+    SetContextKeyTag = InsertSym( AsciiToFull("key"), NPAR, no_fpos,
+                                 DEFAULT_PREC, FALSE, FALSE, 0,
+                                 setcontext_tag, nilobj);
+    visible(SetContextKeyTag) = TRUE;
+    is_compulsory(SetContextKeyTag) = TRUE;
+
+    SetContextValueTag = InsertSym( AsciiToFull("value"), NPAR, no_fpos,
+                                   DEFAULT_PREC, FALSE, FALSE, 0,
+                                   setcontext_tag, nilobj);
+    visible(SetContextValueTag) = TRUE;
+    is_compulsory(SetContextValueTag) = TRUE;
+
+    has_par(s) = TRUE;
+    right_assoc(s) = TRUE;
+  }
+
   /* intialize fonts and load @FontDef symbol */
   FontInit();
 
--- orig/z04.c
+++ mod/z04.c
@@ -236,7 +236,9 @@ FULL_CHAR *EchoToken(OBJECT x)
     case LUSE:
     case LEO:
     case LVIS:
-    
+    case SET_CONTEXT:
+    case GET_CONTEXT:
+
       return actual(x) != nilobj ? SymName(actual(x)) : Image(type(x));
 
 
--- orig/z05.c
+++ mod/z05.c
@@ -303,6 +303,7 @@ static void ReadTokenList(OBJECT token, 
     case LINK_DEST_NULL:
     case LINK_URL:
     case NOT_REVEALED:
+    case GET_CONTEXT:
 
       NextToken(t, res);
       break;
@@ -374,6 +375,7 @@ static void ReadTokenList(OBJECT token, 
       return;
 
 
+    case SET_CONTEXT:
     case CLOSURE:
 
       xsym = actual(t);
--- orig/z06.c
+++ mod/z06.c
@@ -470,6 +470,7 @@ static BOOLEAN Reduce(void)
     case OPEN:
     case RAW_VERBATIM:
     case VERBATIM:
+    case GET_CONTEXT:
 
        if( has_rpar(actual(op)) )
        { s2 = PopObj();
@@ -482,7 +483,6 @@ static BOOLEAN Reduce(void)
        PushObj(op);
        break;
 
-
     case CASE:
 
        if( has_rpar(actual(op)) )
@@ -514,6 +514,7 @@ static BOOLEAN Reduce(void)
        break;
 
 
+    case SET_CONTEXT:
     case CLOSURE:
     
        if( has_rpar(actual(op)) )
@@ -1071,6 +1072,7 @@ BOOLEAN defs_allowed, BOOLEAN transfer_a
       case LINK_SOURCE:
       case LINK_DEST:
       case LINK_URL:
+      case GET_CONTEXT:
 
        /* clean up left context of t (these ops are all right associative) */
        Shift(t, precedence(t), RIGHT_ASSOC,
@@ -1313,6 +1315,7 @@ BOOLEAN defs_allowed, BOOLEAN transfer_a
        /* NB NO BREAK! */
 
 
+      case SET_CONTEXT: /* FIXME: Is this correct?  */
       case CLOSURE:
       
        x = t;  xsym = actual(x);
--- orig/z07.c
+++ mod/z07.c
@@ -313,7 +313,9 @@ OBJECT CopyObject(OBJECT x, FILE_POS *po
     case HCAT:
     case ACAT:
     case ENV_OBJ:
-    
+    case SET_CONTEXT:
+    case GET_CONTEXT:
+
       New(res, type(x));
       for( link = Down(x);  link != x;  link = NextDown(link) )
       {        Child(y, link);
--- orig/z08.c
+++ mod/z08.c
@@ -1588,6 +1588,107 @@ OBJECT *enclose, BOOLEAN fcr)
       break;
 
 
+    case GET_CONTEXT:
+      assert( Down(x) != x, "Manifest: GET_CONTEXT!" );
+      Child(y, Down(x));
+      y = ReplaceWithTidy(y, WORD_TIDY);
+      assert( is_word(type(y)), "Manifest: @GetContext: right parameter is not 
a word" );
+
+      { CONTEXT *ctx; OBJECT value; int found = 0;
+
+        printf ("getcontext %s\n", string(y));
+        for( ctx=&context(*style); ctx != NULL; ctx=ctx->oprevious)
+         {
+           printf( "  ctx: %p\n", ctx);
+           if( ctx->okey != nilobj && (StringEqual(string(ctx->okey), 
string(y))) )
+             {
+               value = ctx->ovalue, found = 1;
+               break;
+             }
+         }
+
+       if( !found )
+       { Error(8, 33, "no value for context variable `%s', using the empty 
string",
+           WARN, &fpos(x), string(y));
+         res = MakeWord(WORD, STR_EMPTY, &fpos(x));
+       }
+       else res = CopyObject(value, &fpos(value));
+
+       ReplaceNode(res, x);
+       DisposeObject(x);
+       x = Manifest(res, env, style, bthr, fthr, target, crs, ok, FALSE, 
enclose, fcr);
+      }
+      break;
+
+    case SET_CONTEXT:
+      printf ("setcontext\n");
+      {
+       OBJECT key = nilobj, value = nilobj, rpar = nilobj;
+
+       while (1)
+         {
+           printf ("  *kid*\n");
+
+           Child(y, Down(x));
+           if( type(y) == PAR )
+             {
+               printf ("  got par\n");
+
+               Child(z, Down(y));
+               if( actual(y) == SetContextKeyTag )
+                 {
+                   z = ReplaceWithTidy(z, WORD_TIDY);
+                   assert( is_word(type(z)), "@SetContext/key: not a word!" );
+
+                   printf ("  keykey: %s!\n", string(z));
+                   key = MakeWord(WORD, string(z), &fpos(z));
+                   DisposeChild(Down(x));
+                 }
+               else if( actual(y) == SetContextValueTag )
+                 {
+                   printf ("  value! (type: %s)\n", Image(type(z)));
+                   value = CopyObject(z, &fpos(z));
+                   DisposeChild(Down(x));
+                 }
+               else if( type(actual(y)) == RPAR )
+                 {
+                   assert( key != nilobj && value != nilobj,
+                           "@SetContext: no key & value");
+                   printf ("  got rpar!\n");
+                   rpar = y;
+                   break;
+                 }
+               else assert1( FALSE, "@SetContext: invalid argument" ,
+                             Image(type(y)) );
+
+             }
+           else if( y == x ) break;
+         }
+
+       /* right parameter (body) */
+       assert( rpar != nilobj, "@SetContext: no right parameter!" );
+       assert( key != nilobj && is_word(type(key)),
+               "@SetContext/key: not a word!" );
+
+       printf ("end of setcontext\n");
+
+       /* get the right parameter's value */
+       Child(z, Down(rpar));
+       if( is_word(type(z)) )
+         printf ("rpar is a word: `%s'\n", string(z));
+
+       StyleCopy(new_style, *style);
+       context_key(context(new_style)) = key;
+       context_value(context(new_style)) = value;
+       context_previous(context(new_style)) = &context(*style);
+
+       ReplaceNode(z, x);
+       DisposeObject(x);
+       x = Manifest(z, env, &new_style, bthr, fthr, target, crs, ok, FALSE, 
enclose, fcr);
+       printf ("end of manifest\n");
+      }
+      break;
+
     case CURR_LANG:
 
       if( language(*style) == 0 )
--- orig/z18.c
+++ mod/z18.c
@@ -36,6 +36,8 @@ static OBJECT         targets[MAX_DEPTH];     /* cu
 static CONSTRAINT      constraints[MAX_DEPTH]; /* their COLM constraints    */
 static int             itop;                   /* stack top                 */
 static CONSTRAINT      initial_constraint;     /* initial COLM constraint   */
+       CONTEXT         InitialContext =        /* initial context           */
+        { (CONTEXT *)0, nilobj, nilobj };
        STYLE           InitialStyle;           /* initial style             */
        OBJECT          InitialEnvironment;     /* initial environment       */
 
@@ -94,6 +96,7 @@ void TransferInit(OBJECT InitEnv)
   baselinemark(InitialStyle)   = FALSE;                /* i.e. not baseline */
   strut(InitialStyle)          = FALSE;                /* i.e. not strutted */
   ligatures(InitialStyle)      = TRUE;                 /* i.e. ligatures    */
+  context(InitialStyle)                = InitialContext;
 
   /* construct destination for root galley */
   New(up_hd, HEAD);
--- orig/z25.c
+++ mod/z25.c
@@ -763,6 +763,9 @@ static void echo(OBJECT x, unsigned oute
     case TAGGED:
     case ENV_OBJ:
 
+  case SET_CONTEXT: /* FIXME: The right place? */
+  case GET_CONTEXT:
+
     
        /* print enclosing left brace if needed */
        braces_needed = (DEFAULT_PREC <= outer_prec);
--- orig/z26.c
+++ mod/z26.c
@@ -219,6 +219,8 @@ FULL_CHAR *Image(unsigned int c)
     case CURR_FACE:            return  KW_CURR_FACE;
     case CURR_YUNIT:           return  KW_CURR_YUNIT;
     case CURR_ZUNIT:           return  KW_CURR_ZUNIT;
+  case SET_CONTEXT:            return KW_SET_CONTEXT;
+  case GET_CONTEXT:            return KW_GET_CONTEXT;
     case COMMON:               return  KW_COMMON;
     case RUMP:                 return  KW_RUMP;
     case MELD:                 return  KW_MELD;
--- orig/z31.c
+++ mod/z31.c
@@ -243,6 +243,8 @@ void MemInit(void)
   zz_lengths[ CURR_FACE    ] =
   zz_lengths[ CURR_YUNIT   ] =
   zz_lengths[ CURR_ZUNIT   ] =
+  zz_lengths[ GET_CONTEXT  ] =
+  zz_lengths[ SET_CONTEXT  ] =
   zz_lengths[ COMMON       ] =
   zz_lengths[ RUMP         ] =
   zz_lengths[ MELD         ] =
--- orig/z41.c
+++ mod/z41.c
@@ -555,6 +555,8 @@ static void WriteObject(OBJECT x, int ou
     case CURR_FACE:    name = KW_CURR_FACE;    goto SETC;
     case CURR_YUNIT:   name = KW_CURR_YUNIT;   goto SETC;
     case CURR_ZUNIT:   name = KW_CURR_ZUNIT;   goto SETC;
+    case SET_CONTEXT:  name = KW_SET_CONTEXT;  goto SETC;
+    case GET_CONTEXT:  name = KW_GET_CONTEXT;  goto SETC;
     case COMMON:       name = KW_COMMON;       goto SETC;
     case RUMP:         name = KW_RUMP;         goto SETC;
     case MELD:         name = KW_MELD;         goto SETC;


reply via email to

[Prev in Thread] Current Thread [Next in Thread]