-- texecole-prefetch.lua — balayage précoce : soumet les calculs SymPy du -- document avant le chargement des paquets LaTeX (calcul pendant que TeX -- charge fontspec/tcolorbox/pgfplots) et détecte les paquets réellement -- utilisés. Optimisation seule : tout échec retombe sur le comportement -- normal (chargement complet, calcul à la demande). local M = {} local function trim(s) return (s:gsub("^%s+", ""):gsub("%s+$", "")) end -- : l'expression s'arrête au premier -- attribut, comme dans sl.fn_parse. local function extraire_fonctions(texte) local fns = {} for nom, var, def in texte:gmatch( "]*)>") do local expr = def local p = expr:find("%f[%S][%a_][%w_]*:") if p then expr = expr:sub(1, p - 1) end fns[nom] = { var = var, expr = trim(expr) } end return fns end -- Chaque gestionnaire reproduit à l'octet près les arguments que le -- bloc réel passera à CALC_SYMPY, pour toucher le même cache. local function soumettre_calculs(texte, S) local fns = extraire_fonctions(texte) for nom in texte:gmatch("") do local d = fns[nom] if d then pcall(S.variations, d.expr, d.var) end end for nom in texte:gmatch("") do local d = fns[nom] if d then local okA, A = pcall(require, "texecole-affine") local affine = okA and A and A.parse_product and (A.parse_product(d.expr, d.var)) or nil if not affine then pcall(S.signes, d.expr, d.var) end end end local blocs = { sympyderiv = function(m, c) local name, order, var = m:match("^(%S+)%s+(%d+)%s+(%S+)$") if not name then name, var = m:match("^(%S+)%s+(%S+)$"); order = "1" end S.derivative(c, var or "x", tonumber(order) or 1) end, sympyzeros = function(m, c) S.zeros(c, m:match("^%S+%s+(%S+)$") or "x") end, sympyprim = function(m, c) S.primitive(c, m:match("^%S+%s+(%S+)$") or "x") end, sympyexpand = function(m, c) S.expand(c) end, sympyapart = function(m, c) S.apart(c, "x") end, sympycanon = function(m, c) S.canonical(c, "x") end, sympyfactor = function(m, c) S.factor(c) end, sympysimplify = function(m, c) S.simplify_expr(c) end, sympylimit = function(m, c) local _, var, pt, dir = m:match("^(%S+)%s+(%S+)%s+(%S+)%s*(%S*)$") if var then S.limite(c, var, pt, dir ~= "" and dir or nil) end end, sympydl = function(m, c) local _, var, a, n = m:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%d+)$") if var then S.dl(c, var, a, tonumber(n)) end end, sympynint = function(m, c) local _, var, a, b = m:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)$") if var then S.nintegrate(c, var, a, b) end end, sympysolveeq = function(m, c) local lhs, rhs = c:match("^(.-)=(.+)$") if lhs then S.solve_eq(lhs, rhs, "x", m:match("^(%S+)")) end end, sympysum = function(m, c) local var, a, b = m:match("^(%S+)%s+(%S+)%s+(%S+)$") if var then S.closed_sum("sum", c, var, a, b) end end, sympyprod = function(m, c) local var, a, b = m:match("^(%S+)%s+(%S+)%s+(%S+)$") if var then S.closed_sum("prod", c, var, a, b) end end, } for tag, mots, contenu in texte:gmatch("<(sympy%a+)%s*([^>]*)>%s*{(.-)}") do local h = blocs[tag] if h then pcall(h, trim(mots), trim(contenu)) end end end -- "1" (charger) est la valeur sûre ; "0" seulement si le source est lu, -- traduit, et sans trace de la fonctionnalité. local function detecter_besoins(source, texte) local besoins = { plots = "1", tab = "1", poster = "1", tableau = "1" } if not texte then return besoins end besoins.plots = (texte:find(" » : le tag des tableaux, sans -- confondre « » (table des matières, pas de tblr). besoins.tableau = (texte:find("]") or source:find("begin{tblr}", 1, true)) and "1" or "0" return besoins end -- Sans deuxième cœur, soumettre tôt ne fait que disputer le processeur. local function machine_multicoeur() local f = io.open("/proc/cpuinfo", "r") if not f then return true end local n = 0 for ligne in f:lines() do if ligne:match("^processor%s*:") then n = n + 1 end end f:close() return n ~= 1 end -- Point d'entrée, appelé depuis texecole.cls. Ne lève jamais d'erreur. function M.lancer() local source, texte pcall(function() local f = io.open(tex.jobname .. ".tex", "r") if not f then return end source = f:read("*a") f:close() local corps = source:match("\\begin%s*{document}(.*)\\end%s*{document}") if not corps then source = nil; return end local L = require("texecole-lecteur") texte = L.traduire(corps) end) if source then local besoins = detecter_besoins(source, texte) pcall(token.set_macro, "texecole@besoinplots", besoins.plots) pcall(token.set_macro, "texecole@besointab", besoins.tab) pcall(token.set_macro, "texecole@besoinposter", besoins.poster) pcall(token.set_macro, "texecole@besointableau", besoins.tableau) end if texte and machine_multicoeur() and (texte:find("