How to use `eval` to recover declared variable?

Hello everyone,

For a personal project i have automated imports of pages into main.typ using a json file with the following structure :

{
    "reglement": [],
    "officiel": [
        "Braban\u00e7onne",
        "WoltjeKreet",
        "Gaudeamus",
        "IoVivat",
        "marche_etudiants",
        "LiedVanGeenTaal",
        "WoltjeClubLied"
    ],
    "leekes": [],
    "groupe_folklorique": [],
    "andere": [],
    "index": [],
    "others": [
        "fulltext",
        "repetition"
    ]
}

Basically, the keys in the following json file represent section of the the future text and the array represent variables to include.

I retrieve the content of the json in a dict and i iterate over each “section” and “variable”. After that i use eval() to call and use my variables :

// in main.typ

#import "imports.typ" : * // for importing variables

// normal call of my variable previously defined
#sacre_page
#WoltjeClubLied
#CridesPres
#Gaudeamus
#Brabançonne  // Variable brabançonne here
#marche_etudiants
#IoVivat
#LiedVanGeenTaal

// using json, dictionnary and eval
#for key in dict.keys() {
  for songname in dict.at(key){
    "#" + str(songname) + linebreak() // used to debug -> correct names
    eval("#" + str(songname), mode : "markup") // unknown variable Brabançonne 
  }
}

I suppose it could be due to the scope of the evaluated code that is not the same as the code located on top. Unfortunately i don’t really know how to correctly define it.
Another supposition is to evaluate the code including the #import "imports.typ" : *

For more insight you can check the project here : Typst and main.typ should be the most useful file to check.
( :warning: It is quite big and partially automated via python script so not everything make sense. )
Thank you in advance for your help

The import hypothesis was correct so i implemented the following workaround :

#let dict = json("songs.json")

#for key in dict.keys() {
  
  for songname in dict.at(key){
    eval(str("import \"imports.typ\" : *\n")+str(songname), mode: "code")
  }
}

I still think there’s a better way to do the same since for each song of each new page we make the same import.
For the moment it will be marked as solution

The eval function has a scope parameter. Let’s say I have

// imports.typ
#let foo = "hello"

// main.typ
#import "imports.typ": *

#foo  // hello

then using the scope parameter I could also write

#import "imports.typ": *

#eval("foo", mode: "code", scope: (foo: foo))

This is not useful yet, since you’d have to write all the variable names. However you can use the dictionary constructor to convert a module (imports.typ) into a dictionary, and pass that as the scope:

#import "imports.typ" as imports

#eval("foo", mode: "code", scope: dictionary(imports))

(as an aside, I used mode: "code" to avoid having to add the # to the variable)

You may notice that you’re not doing a lot with eval, and you have a dictionary already. So you can further simplify this to

#import "imports.typ" as imports

#dictionary(imports).at("foo")
1 Like