jgm / typst-hs

Haskell library for parsing and evaluating typst
Other
44 stars 5 forks source link

Support `context` #53

Open KaiAragaki opened 1 month ago

KaiAragaki commented 1 month ago

Hello!

I'm rather new to Typst, so please forgive me if what I'm saying doesn't make sense or is otherwise insane!

I was having issues where Typst would compile a document fine, but Pandoc would not. It appeared to be an issue with library code in CeTZ

This appears to reproduce the issue:

#let fun() = context {
    _ = "hi"
}

However, both of these are fine:

#let fun() = {
    _ = "hi"
}
#let fun() = context {
    x = "hi"
}

Additionally, this compiles fine, leading me to believe that _ might be misinterpreted as the beginning of an emphasis marker:

#let fun() = context {
    __ = "hi"
}
jgm commented 1 month ago

Actually the second example isn't parsed correctly at all:

ld: warning: -U option is redundant when using -undefined dynamic_lookup
#let fun() = context {
    x = "hi"
}
--- parse tree ---
[ Code
    "stdin"
    ( line 1 , column 2 )
    (LetFunc (Identifier "fun") [] (Ident (Identifier "context")))
, Space
, Text "{"
, SoftBreak
, Text "x"
, Space
, Text "="
, Space
, Quote '"'
, Text "hi"
, Quote '"'
, SoftBreak
, Text "}"
, ParBreak
]

The issue is that we don't currently support context.

jgm commented 1 month ago

I'm not sure I completely understand what context does; but I suppose as a first measure we should at least support the syntax so we don't get these crashes. That should not be hard.

jgm commented 1 month ago

If I understand correctly, context can occur before any expression and it makes that expression into a special "contextualized" thing that is evaluated only in context.

knuesel commented 3 weeks ago

That's right, the context keyword turns an expression into an opaque "content" value that will be evaluated only after it's inserted in the document (so that its context is known). The same content can be inserted in several places, and produce different values.

From the documentation:

#let value = context text.lang
#value

#set text(lang: "de")
#value

Here the content returned by context text.lang evaluates to en when it's inserted at the start of the document. Later in the document the lang is changed to "de", so when the same content is inserted further in the document, it will evaluate to "de".

Now looking at the examples in this issue, the following function returns none (like any assignment in Typst):

#let fun() = {
    _ = "hi"
}

but the following returns a "content" value:

#let fun() = context {
    _ = "hi"
}

In this case we know that the content is just a wrapper for none, but in the program it's still an opaque value.

To see this, call these functions with #repr(fun()). In the first case we get none, in the second case context().

The other two examples (x = "hi" and __ = "hi") are not valid Typst code. They'll produce an error if you try to insert the return value in the document, because x and __ are not defined (you'd need to write let x = "hi" for example).