[Top][All Lists]
[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;