% !TeX program = lualatex % ===================================================================== % scholatex.tex -- user documentation for the scholatex package % % This is a STANDALONE document: it uses the standard article class and % compiles with any LuaLaTeX (or pdfLaTeX) distribution, independently % of the scholatex class it documents. It does NOT require any images. % % Compile: lualatex scholatex.tex (twice, for the table of contents) % ===================================================================== \documentclass[11pt,a4paper]{article} \usepackage[T1]{fontenc} \usepackage[margin=25mm]{geometry} \usepackage{xcolor} \usepackage{amsmath} \usepackage{amssymb} \usepackage{booktabs} \usepackage{longtable} \usepackage{array} \usepackage{enumitem} \usepackage{fancyvrb} \usepackage{titlesec} \usepackage[colorlinks=true,linkcolor=Navy,urlcolor=Navy]{hyperref} % \fint (average integral) built as an integral crossed by a short bar, % without pulling in esint/commath, so the documentation keeps compiling on % a bare amsmath + graphicx setup. \usepackage{graphicx} \providecommand{\fint}{\mathchoice {\rlap{\,\,\raisebox{0.32em}{\scalebox{0.7}{$-$}}}\!\int} {\rlap{\,\,\raisebox{0.30em}{\scalebox{0.6}{$-$}}}\!\int} {\rlap{\,\raisebox{0.28em}{\scalebox{0.5}{$-$}}}\!\int} {\rlap{\,\raisebox{0.28em}{\scalebox{0.5}{$-$}}}\!\int}} % The surface/volume integrals and the integer-interval brackets are shown in % the maths table below. Under unicode-math the package provides \oiint, % \iiint, \lBrack and \rBrack natively; here, on a bare amsmath setup, we % supply lightweight fallbacks so the documentation keeps compiling without % esint or stmaryrd. They illustrate the syntax, not the production glyph. \providecommand{\oiint}{\mathop{\vcenter{\hbox{$\oint\!\!\!\!\oint$}}}} \providecommand{\iiint}{\mathop{\vcenter{\hbox{$\int\!\!\!\int\!\!\!\int$}}}} \providecommand{\llbracket}{\mathopen{[\![}} \providecommand{\rrbracket}{\mathclose{]\!]}} \definecolor{Navy}{HTML}{1F3A5F} \definecolor{CodeBg}{HTML}{F4F5F7} \definecolor{CodeFrame}{HTML}{D7DBE0} \definecolor{Accent}{HTML}{8A1C1C} % --- code blocks ----------------------------------------------------- \DefineVerbatimEnvironment{slcode}{Verbatim}{% frame=single, framerule=0.4pt, rulecolor=\color{CodeFrame}, fontsize=\small, xleftmargin=4mm, xrightmargin=4mm, framesep=3mm, formatcom=\color{black!88}} \newcommand{\tg}[1]{\texttt{#1}} \newcommand{\opt}[1]{\texttt{\textcolor{Accent}{#1}}} % Three-level hierarchy: \part for the broad categories, \section and % \subsection beneath. \part is reformatted as a compact coloured banner % (no dedicated blank page), so the table of contents reads as a plan % without wasting a page per part. The leading \par in the format forces % vertical mode, so a \part may follow a paragraph of prose directly % without titlesec's "Entered in horizontal mode" error. \titleformat{\part}[block] {\bigskip\Huge\bfseries\color{Navy}} {\partname~\thepart\quad}{0pt}{} \titlespacing*{\part}{0pt}{0.4em}{0.8em} \titleformat{\section}{\Large\bfseries\color{Navy}}{\thesection}{0.6em}{} \titleformat{\subsection}{\large\bfseries\color{Navy!85!black}}{\thesubsection}{0.6em}{} \titlespacing*{\section}{0pt}{1.4em}{0.5em} \setcounter{tocdepth}{2} % Section numbering restarts at 1 in every part (each part is a fresh % chapter-like unit), instead of running 1..n across the whole document. \usepackage{chngcntr} \counterwithin*{section}{part} \setlength{\parindent}{0pt} \setlength{\parskip}{0.55em} \hypersetup{ pdftitle={scholatex --- user manual}, pdfauthor={Gerard Dubard}, } % ===================================================================== \begin{document} \begin{titlepage} \centering \vspace*{3.5cm} {\Huge\bfseries\color{Navy} scholatex\par} \vspace{0.8cm} {\Large Print-ready teaching worksheets, the easy way\par} \vspace{2.5cm} {\large A small tag-based language for handouts and exercises,\\[2pt] built on LuaLaTeX\par} \vspace{3cm} {\large Gérard Dubard\par} \vspace{0.4cm} {\large Version 2.5\par} \vfill {\small Released under the GNU General Public License v3.0\par} \end{titlepage} \tableofcontents \newpage % ===================================================================== \part{Getting started} % ===================================================================== \section{Introduction} % ===================================================================== \textbf{scholatex} is a small markup language for producing print-ready teaching materials --- worksheets, exercise sheets, handouts, short assessments --- without writing LaTeX. It is not a general-purpose replacement for LaTeX; it is a focused, consistent little language for the documents a teacher actually makes: a few framed exercises, a table of results, a couple of simple formulas, an image or two, laid out cleanly on a page meant to be printed and handed out. It compiles to LuaLaTeX, so the output has full LaTeX typesetting quality, but the syntax is meant to be read and edited in minutes by someone who does not know LaTeX: no \tg{\textbackslash begin\{tabular\}\{|c|c|\}}, no counting ampersands, no hunting for the package that draws a coloured box. A single tag syntax covers text, tables, images, simple maths, framed boxes, lists and full-page named-area layouts --- the building blocks of a worksheet. scholatex is a \emph{closed language}: the backslash is an ordinary character, so raw LaTeX commands in the body are typeset literally rather than executed. Everything a worksheet needs is expressed through tags, which keeps documents consistent and safe to share. It is built for the people LaTeX usually loses: teachers preparing classroom materials, and anyone who wants a clean printable sheet without climbing the LaTeX learning curve. You write a \tg{.tex} file with a tiny, readable syntax between \tg{\textbackslash begin\{document\}} and \tg{\textbackslash end\{document\}}. At compile time the \tg{scholatex} class reads that body, transpiles it to LaTeX, and typesets it. Nothing to install beyond a LuaLaTeX distribution. \begin{slcode} % !TeX program = lualatex \documentclass[margins=20, size=12, imgdir=IMG]{scholatex} \begin{document} My first scholatex document This is a normal paragraph. {Bold}, {italic}, {red}. { Boxes, tables, images and maths all use the same tag syntax. } \end{document} \end{slcode} % ===================================================================== \section{The one rule} % ===================================================================== Everything is a \textbf{tag}: \tg{} followed either by \tg{\{content\}} (inline) or by a line that ends in \tg{\{} (a block). Attribute words follow a single case convention: \begin{center} \begin{tabular}{@{}lll@{}} \toprule \textbf{Form} & \textbf{Meaning} & \textbf{Examples} \\ \midrule \tg{lowercase} & a short keyword (style, alignment, skip) & \tg{b}, \tg{i}, \tg{c}, \tg{2tab} \\ \tg{CamelCase} & a CSS colour (151 of them) & \tg{Navy}, \tg{SteelBlue}, \tg{Crimson} \\ \tg{UPPERCASE} & a font name & \tg{DEJAVU SANS} \\ \bottomrule \end{tabular} \end{center} The order of words inside a tag never matters --- emission is always normalised (page break, then vertical skips, then alignment, then tabs, then styles). % ===================================================================== \section{Class options} % ===================================================================== Set in \tg{\textbackslash documentclass[...]\{scholatex\}}: \begin{center} \small \begin{longtable}{@{}p{2.4cm}p{2.6cm}p{8.4cm}@{}} \toprule \textbf{Option} & \textbf{Default} & \textbf{Meaning} \\ \midrule \endhead \opt{margins} & \tg{20} & \tg{N} (all sides) or \tg{\{top,right,bottom,left\}} in mm \\ \opt{font} & \tg{Latin Modern Roman} & main text font \\ \opt{mathfont} & \tg{Latin Modern Math} & math font \\ \opt{size} & \tg{11} & base font size in pt \\ \opt{imgdir} & \tg{img} & folder(s) searched for bare image names; accepts a comma-separated list, e.g. \tg{\{IMG, IMAGES/PNG\}} \\ \opt{tabwidth} & \tg{8} & width of one tab, in mm \\ \opt{lineheight} & \tg{8} & height of one explicit line skip (\tg{}, \tg{}), in mm \\ \opt{linespread} & \tg{1.0} & overall line spacing (\tg{1.5} = one-and-a-half); tall maths never touches even at \tg{1.0} \\ \opt{scriptscale} & \tg{100} & scale (\%) of \tg{up}/\tg{down} scripts \\ \opt{padding} & \tg{2} & inner padding (mm) between a box/grid-area frame and its content \\ \opt{lang} & \tg{fr} & decimal separator: \tg{fr} (comma) or \tg{en} (point); affects both literal decimals in maths and interpolated values \\ \opt{untrusted} & \tg{false} & when \tg{true}, runs the document's Lua in a restricted sandbox (see \S\ref{sec:security}) \\ \bottomrule \end{longtable} \end{center} Headings carry no extra vertical space of their own, and no built-in colour: to style a heading (colour, weight, size, or a line skip before it), fold a heading keyword into an alias and use it (see \S\ref{sec:aliases}). % ===================================================================== \part{Text and structure} % ===================================================================== \section{Text attributes} % ===================================================================== \textbf{Inline styles} --- \tg{b} bold, \tg{i} italic, \tg{u} underline, \tg{emph}, \tg{sf} sans-serif, \tg{sc} small caps. \textbf{Colours} --- a colour is always one of the 151 CSS / svgnames colours, written in CamelCase: \tg{Navy}, \tg{Red}, \tg{Tomato}, \tg{SteelBlue}, \tg{ForestGreen}, \dots. There is a single rule --- no lowercase shortcuts --- so the everyday colours are simply capitalised: \tg{Red}, \tg{Blue}, \tg{Green}, \tg{Gray}. These are exactly the colours xcolor provides under \tg{svgnames}: the 147 CSS Color Module names plus the four X11 extras \tg{LightGoldenrod}, \tg{LightSlateBlue}, \tg{NavyBlue} and \tg{VioletRed}. Names are case-sensitive --- write \tg{Seashell}, not \tg{SeaShell}. The \texttt{README} groups all 151 by family for browsing. \textbf{Fonts and sizes} --- a font name in CAPITALS (\tg{\{\dots\}}); sizes as \tg{Npt} or \tg{Npx} (\tg{<14pt>\{\dots\}}). \textbf{Alignment} --- \tg{l} left, \tg{c} centre, \tg{r} right, \tg{j} justified (the same letters as table columns). \textbf{Tabs and skips} (the number is always a prefix) --- \tg{Ntab} indents the first line by N tabs; vertical skips follow singular/plural agreement: \tg{line} or \tg{1line} skips one line, \tg{2lines}, \tg{3lines}, \dots\ skip several. Bare \tg{tab} = \tg{1tab}. \textbf{Scripts} --- \tg{upN} raises text by N mm (superscript-like), \tg{downN} lowers it (subscript-like): \tg{x\{2\}}, \tg{H\{2\}O}. \textbf{Page break} --- \tg{nextpage}. \textbf{Section headings} --- \tg{section}, \tg{subsection}, \tg{subsubsection} give the three heading levels, numbered automatically: \begin{slcode}
First topic A detail A finer point \end{slcode} renders as `1 First topic', `1.1 A detail', `1.1.1 A finer point'. By default the numbers are hierarchical; a counter keyword on a heading sets the style of that level on its own, with no inherited prefix: \tg{num} (1, 2, 3), \tg{roman} (i, ii), \tg{ROMAN} (I, II), \tg{alpha} (a, b), \tg{ALPHA} (A, B). Thus \tg{
} with \tg{} gives \texttt{I}, \texttt{II} and, under each, \texttt{1}, \texttt{2} --- not \texttt{I.1}. A heading can also be written as a \textbf{block} carrying a \tg{title:} option, with its body in braces: \begin{slcode} { This body paragraph is indented on its first line. } \end{slcode} A \textbf{table of contents} is printed with \tg{}; give it a title in braces: \begin{slcode} {Table of contents} \end{slcode} Combine anything in one tag: \begin{slcode} {A new page, a two-line skip, a two-tab indent, justified, Navy, bold --- all before the first letter of the text.} \end{slcode} % ===================================================================== \section{Aliases and macros --- the factoring tool}\label{sec:aliases} % ===================================================================== This is what makes scholatex scale. Define a style \textbf{once}, at the top of the document, then name it everywhere instead of repeating its attributes. One edit at the definition restyles the whole document. \begin{slcode} let title = % style alias let h1 = % a heading style, reusable let p = % a standard indented paragraph My heading <h1>First topic <p>{ A paragraph, indented and justified, named not described. } let n = 7 % value, usable in #{...} Seven squared is #{n*n}. let greet{name} = Hello #name! % text macro with parameters \end{slcode} Change \tg{let h1 = <Navy b section>} to \tg{<ForestGreen b section>} once and \textbf{every} first-level heading follows --- the single point of control that keeps a long document consistent. % ===================================================================== \section{Lists} % ===================================================================== \tg{<list:STYLE>\{ \dots \}} makes a list; the style is required and comes right after the name. One item per non-empty line --- no item tag. A \tg{<list:\dots>} written under an item becomes its sub-list, nested as deep as you like. \begin{slcode} <list:disc>{ Fruits <list:circle>{ apples pears } Vegetables } \end{slcode} Styles --- bullets: \tg{none} \tg{disc} \tg{circle} \tg{square}; numbered: \tg{decimal} \tg{alpha} \tg{ALPHA} \tg{roman} \tg{ROMAN} (the case of the keyword sets the case of the letters); checkboxes: \tg{check}. % ===================================================================== \section{Escapes} % ===================================================================== To print a character that scholatex treats specially, double it: \tg{<<} \tg{>>} give a literal \tg{<} \tg{>}, \tg{\{\{} \tg{\}\}} a literal \tg{\{} \tg{\}}, and \tg{\#\#} a literal \tg{\#}. A backslash \tg{\textbackslash} is an ordinary character --- write it as itself, so a Windows path like \tg{C:\textbackslash Users} just works. The characters \tg{\_} \tg{\&} \tg{\%} \tg{\textasciitilde} \tg{\textasciicircum} are escaped automatically. Braces carry structure (they delimit blocks and groups), so a \emph{literal} brace must be \textbf{balanced}: write the pair \tg{\{\{\dots\}\}} to print \tg{\{\dots\}}. A lone, unmatched \tg{\{\{} or \tg{\}\}} is reported as an unbalanced brace naming its line, rather than silently swallowing the rest of the document --- so set-builder notation like \tg{\{\{ x : x > 0 \}\}} is written as a pair and prints as one. Angle brackets and hashes have no such constraint; only braces need to balance. A line break is the tag \tg{<nextline>}; it works in running text and inside table cells. In ordinary text a blank line already starts a new paragraph, so \tg{<nextline>} is only needed for a break \emph{without} a paragraph change. A line whose first non-space character is \tg{\%} is a \textbf{comment} and is dropped. A bare \tg{\#} not followed by a name or \tg{\{\dots\}} is a literal \tg{\#}: only \tg{\#name} and \tg{\#\{expr\}} interpolate. % ===================================================================== \part{Containers and layout} % ===================================================================== \section{Tables} % ===================================================================== Columns are declared in brackets, \textbf{one two-letter placement code per column}. The first letter is vertical (\tg{t} top, \tg{m} middle, \tg{b} bottom), the second horizontal (\tg{l}, \tg{c}, \tg{r}). So \tg{mc} is middle-centre, \tg{br} bottom-right. Columns share the page equally; \tg{N:} before the code fixes a width in mm (\tg{[40:tl, 30:mc]}). Cells are separated by \tg{|}, rows by line breaks, and \tg{<nextline>} breaks a line inside a cell. \begin{slcode} <table [mc, tl, br] borders header>{ Figure | Formula | Value <img 25>{cat.png} | $1/2 + 1/3$ | $5/6$ } \end{slcode} \tg{borders} draws rules; \tg{header} bolds the first row. \tg{gap:N} sets the horizontal spacing between columns, in mm. A cell may span several columns with \tg{colspan:N}, or several rows with \tg{rowspan:N} (\tg{N}~$\geq$~2). For a \tg{rowspan}, each cell it covers on the lines below is marked with a lone \tg{.}. \begin{slcode} <table [mc, ml, mc, mc] borders header gap:3>{ <colspan:4 mc>{Term report} Day | Subject | Mark | Coef. <rowspan:2 mc>{Monday} | Maths | 15 | 4 . | French | 12 | 3 } \end{slcode} Colour options act \textbf{inside} the table, on its cells and rules --- \tg{fill:} colours every body cell, \tg{line:} the rules, \tg{text:} the text colour, and \tg{headerfill:} / \tg{headertext:} the header row. These are not box options: a table has no \tg{radius:}, \tg{title:} or outer frame. To frame a table, wrap it in a \tg{<box>}. % ===================================================================== \section{Images} % ===================================================================== \begin{slcode} <img>{chat.png} % fills the available width (the cell or line) <img 25>{chat.png} % 25 mm wide <img 25x15>{chat.png} % fits a 25x15 mm box, ratio preserved \end{slcode} A bare name is searched in each folder of \tg{imgdir} in turn, then at the project root. An explicit path always works, with or without \tg{./}. % ===================================================================== \section{Boxes} % ===================================================================== \begin{slcode} <box line:Crimson fill:MistyRose radius:4 title:{A note}>{ Content here. } \end{slcode} Options: \tg{line:} frame colour, \tg{fill:} background, \tg{text:} text colour, \tg{radius:N} rounded corners (mm), \tg{width:N} or \tg{width:N\%}, \tg{boxrule:N}, \tg{sep:N} (inner padding, mm; \tg{boxsep:N} is a synonym), \tg{break:yes} (allow page breaks), \tg{title:\{\dots\}}, \tg{titlefill:}, \tg{titletext:}. A line containing only \tg{-{-}-} splits a box into an upper and a lower region. \tg{<row gap:N>\{ \dots \}} lays its child boxes side by side, with equal widths and equalised heights. A \tg{row} accepts only \tg{<box>} children. A box also takes a \textbf{two-letter placement code} (\tg{tl tc tr ml mc mr bl bc br}, default \tg{tl}) that positions its content; the vertical part needs a \tg{height:} to have room to act. % ===================================================================== \section{Grid (named-area layout)} % ===================================================================== For full-page layouts, \tg{<grid>} borrows CSS Grid's named-area idea. A \tg{template:[ \dots ]} of quoted rows draws the layout; each word names the cell at that position. A name repeated \textbf{horizontally} spans columns; repeated \textbf{vertically} it spans rows. A dot \tg{.} is an empty cell. Each name must form a solid rectangle. \begin{slcode} <grid template:[ "title title logo" "intro info logo" "body body body" ] gap:4>{ <area title>{ <Red b 16pt>Maths assessment } <area logo >{ <img>{blason.png} } <area intro>{ Instructions: no calculator. } <area info >{ Name: <nextline> First name: } <area body line:Navy fill:AliceBlue radius:2 title:{Exercise 1}>{ Solve the equation... } } \end{slcode} An area can be framed like a box by giving it the same options (\tg{line:}, \tg{fill:}, \tg{radius:}, \tg{title:}). An area with no options stays invisible (a bare cell). The grid itself takes \tg{width:} (a percentage of the text width or a millimetre value) and \tg{height:} (a millimetre value fixing the total height). % ===================================================================== \section{Drawing figures} % ===================================================================== \tg{<draw>} draws a plane geometric figure from a description alone: you name the shape and give its measurements, and the coordinates are computed trigonometrically and emitted as hard numbers. One drawing unit is one centimetre, so \tg{side:5} draws a 5\,cm side --- nothing is rescaled, and a figure wider than the page is simply the cue to adjust the measurements before printing. \begin{slcode} <draw>triangle ABC equilateral side:5 marks:on measures:cm \end{slcode} \tg{marks:on} codes the equal sides and right angles; \tg{measures:cm} or \tg{measures:mm} labels each side with its length, set parallel to the side. Both are off by default. The block form \tg{<draw>\{ \dots \}} holds several figures (see \emph{Composite figures} below); the inline form above is a single figure. \subsection*{The figures} \renewcommand{\arraystretch}{1.25} \begin{longtable}{@{}p{0.46\linewidth}p{0.5\linewidth}@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{triangle ABC equilateral side:s} & equilateral triangle \\ \tg{triangle ABC isosceles side:e base:b} & isosceles, equal sides \tg{e}, base \tg{b} \\ \tg{triangle ABC right sides:(p,q)} & right angle at the first point (or \tg{right:X}), legs \tg{p}, \tg{q} \\ \tg{triangle ABC sides:(a,b,c)} & three sides \\ \tg{triangle ABC AB:3 BC:4 CA:5} & three sides named one by one (same as \tg{sides:(3,4,5)}) \\ \tg{triangle ABC sides:(a,b) angle:t} & two sides and the included angle \\ \tg{triangle ABC angles:(A,B) side:c} & two angles and a side \\ \tg{square ABCD side:s} & square \\ \tg{rectangle ABCD sides:(w,h)} & rectangle \\ \tg{rhombus ABCD side:s angle:t} & rhombus \\ \tg{parallelogram ABCD sides:(a,b) angle:t} & parallelogram \\ \tg{trapezoid ABCD bases:(b1,b2) height:h} & trapezoid \\ \tg{kite ABCD sides:(a,b) angle:t} & kite: upper pair \tg{a}, lower pair \tg{b}, apex angle \tg{t} \\ \tg{pentagon}, \tg{hexagon}, \tg{octagon} & regular polygons by name \\ \tg{polygon ABCDE side:s} & regular polygon, any number of points \\ \tg{circle O radius:r} \quad \tg{circle O diameter:d} & circle by centre and length \\ \tg{circle O radius:AB} & radius given by a placed segment (compass span) \\ \tg{circle ABC} & circle through three placed points (circumscribed) \\ \tg{circle ABC inscribed} & incircle of the triangle \tg{ABC} \\ \bottomrule \end{longtable} A \tg{kite} is distinct from a \tg{rhombus}: it has two pairs of adjacent equal sides of \emph{different} lengths, the rhombus being the special case where both pairs are equal. When a figure has two pairs of equal sides of \emph{different} lengths --- a kite, a rectangle, a parallelogram --- \tg{marks:on} tells the pairs apart by the number of ticks: a single tick on the one pair, a double tick on the other. A kite therefore never reads as a rhombus, nor a rectangle as a square. On a composite figure a shared edge is ticked once, not once per figure, so the coding stays clean where two pieces meet. \subsection*{Composite figures: sharing an edge} Several figures in one \tg{<draw>\{ \dots \}} block share their points. When a figure repeats a point already placed, it is grafted onto it; when two figures share an \textbf{edge} (two points), the new figure deduces that edge's length from the existing one --- so only the first figure states its side --- and is flipped to the far side of the edge so the pieces abut instead of overlapping. \begin{slcode} <draw>{ rhombus ABEF side:4 angle:60 square ABCD triangle BCG equilateral triangle ADK right:D DK:6 measures:cm } \end{slcode} \subsection*{Point names, loops, rotation} Point names are single letters written glued together --- \tg{triangle ABC} names A, B and C. When a figure needs many points, or names with a digit, they are written as a parenthesised, comma-separated list: \tg{triangle (O, A0, B0)}. The rule is one of the first character: an opening parenthesis introduces the list form, anything else is the glued form. A draw block runs the same loops as the rest of the language, \tg{for VARIABLE in FROM..TO \{ \dots \}}. Inside a loop, \tg{\#k} inserts the loop variable and \tg{\#\{expr\}} evaluates an expression, so point names and measurements can be generated. The attribute \tg{rotate:$\theta$} turns a figure by $\theta$ degrees about its first point, and \tg{labels:off} hides the vertex names. On the tag itself, \tg{scale:F} multiplies every coordinate of the picture by \tg{F} while leaving the text size alone (default \tg{1}; it applies to every form of \tg{<draw>} --- figures, axes, trigcircle, solids). Twelve isosceles triangles sharing an apex, stepped $30^\circ$ apart, fan into a full turn: \begin{slcode} <draw>{ for k in 0..11 { triangle (O, A#k, B#k) isosceles side:4 base:2 rotate:#{k*30} labels:off } } \end{slcode} \subsection*{Points and segments by coordinate} Two low-level primitives sit below the named figures. A point is placed at explicit coordinates, a segment is drawn between two points. Coordinates are declared once with \tg{let NAME = \{x, y\}}, then referred to by name. A point's label is placed clear of any segment leaving it. \begin{slcode} let P0 = {-1.6, -2.3} let P1 = {-1, 1.5} let P2 = {2.7, 3.1} <draw>{ point(P0) point(P1) point(P2) line(P1, P2) } \end{slcode} An argument may also be a literal pair, \tg{line(\{0,0\}, \{3,2\})}, and a segment takes \tg{measures:} like a figure side. Because each coordinate is an ordinary expression, a loop can compute endpoints --- twelve rays around a disc make a sun: \begin{slcode} <draw>{ circle O radius:1.2 for k in 0..11 { line({1.2*math.cos(k*math.pi/6), 1.2*math.sin(k*math.pi/6)}, {2.4*math.cos(k*math.pi/6), 2.4*math.sin(k*math.pi/6)}) } } \end{slcode} \subsection*{In a coordinate frame: \texttt{axes:}} The figures above are \emph{constructed} --- you give measurements and the coordinates are computed. The opposite need, a graduated frame on which points are placed \emph{at} their coordinates, is the same block in analytic mode. \tg{<draw axes:\{xmin,xmax,ymin,ymax\}>} draws the axes and switches every line to the analytic vocabulary; without \tg{axes:} the block stays in the construction mode above. One unit is still one centimetre, and the drawing is clipped to the declared window. \begin{slcode} <draw axes:{-5,5,-5,5}>{ point A (2,3) point B (-1,1) vector u (3,1) line y = 2x-1 line through A B } \end{slcode} Each directive takes coordinates, never measurements. \tg{point NAME (x,y)} places and labels a point; a placed name is then available to later lines. \tg{vector u (x,y)} draws a free vector from the origin, \tg{vector AB} the bound vector between two placed points. \tg{line} accepts an equation (\tg{y = 2x-1}, \tg{x = -3}, or the general \tg{2x+3y = 6}) or \tg{line through A B}. A named vector is remembered and composes. \tg{vector s = u+v} (or \tg{u-v}) draws the sum or difference, labelled with the expression itself, written along the arrow. \tg{from:} moves any vector's tail --- to a placed point, a literal pair, or \tg{\textasciitilde{}u}, the \emph{tip} of a drawn vector --- and \tg{style:} restyles the copy (\tg{style:dashed-gray}, \tg{style:Blue}). The parallelogram and triangle rules then read on one frame: each side is copied dashed to the far tip, the sum closes the parallelogram, and the difference runs from the tip of \tg{v} to the tip of \tg{u}: \begin{slcode} <draw axes:{-5,5,-5,5}>{ vector u (3,1) vector v (1,3) vector v from:~u style:dashed-gray vector u from:~v style:dashed-gray vector s = u+v style:Blue vector d = u-v from:~v style:Red } \end{slcode} A circle carries a tangent at a point on it. \tg{circle} takes \tg{center:} (a placed point or a literal pair) with \tg{radius:}, or an equation \tg{x\^{}2+y\^{}2 = 4}; a named circle (\tg{circle C \dots}) then accepts a \tg{tangent to:C at:P}: \begin{slcode} <draw axes:{-5,5,-5,5}>{ point T (0,3) circle C center:(0,0) radius:3 tangent to:C at:T } \end{slcode} \tg{region} shades the half-plane of a linear inequality (\tg{<}, \tg{>}), the boundary drawn dashed: \begin{slcode} <draw axes:{-5,5,-5,5}>{ region y > x+1 } \end{slcode} \textbf{The trigonometric circle.} One synthetic line draws the circle, its axes, and the sixteen remarkable angles labelled by their principal measure in $\left(-\pi, \pi\right]$: \begin{slcode} <draw>trigcircle radius:4 <draw>trigcircle radius:4 values:on <draw>trigcircle radius:4 step:{pi/6} <draw>trigcircle radius:4 range:{0, pi/2} step:{pi/12} values:on \end{slcode} \tg{radius:} is in centimetres (default 4). \tg{step:} replaces the remarkable set by the multiples of a fraction of $\pi$; \tg{range:} restricts to an arc --- the first-quadrant close-up. \tg{values:on} adds the dashed cosine and sine projections of the drawn first-quadrant angles, exact ($\tfrac12$, $\tfrac{\sqrt2}2$, $\tfrac{\sqrt3}2$) at the remarkable ones, two decimals elsewhere. Labels too crowded to read are a compilation error suggesting a larger step. \textbf{Solids in cabinet projection.} \tg{solid} lines draw the space-geometry repertoire in the French \emph{perspective cavali\`ere}: receding axis at $45^{\circ}$, ratio $0.5$ (override with \tg{angle:} and \tg{ratio:}). Hidden edges are \emph{computed}, not declared: a face of a convex solid is visible exactly when the projection preserves its outward orientation, and an edge is dashed exactly when both of its faces are hidden. Several solids in one block sit side by side. \begin{slcode} <draw>{ solid cube ABCDEFGH edge:4 solid pyramid SABCD base:4 height:5 } <draw>{ solid cuboid sides:{5, 3, 2} solid cylinder radius:1.5 height:4 solid cone radius:2 height:4.5 solid sphere radius:2.5 } \end{slcode} Vertex names are optional on \tg{cube}, \tg{cuboid} (eight letters, base then top) and \tg{pyramid} (apex first). Plane sections are not in this release. % ===================================================================== \part{Maths} % ===================================================================== \section{Basics} % ===================================================================== Wrap maths in \Verb|$...$|. A small mini-language keeps it light: \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{*} & $\times$ \\ \tg{+-} & $\pm$ \\ \tg{<=} \tg{>=} \tg{!=} & $\leq$ \quad $\geq$ \quad $\neq$ \\ \tg{a/b} & fraction (chained \tg{a/b/c} reads as \tg{(a/b)/c}) \\ \tg{x\^{}2} \quad \tg{x\_i} & power / index (bind tighter than \tg{/}) \\ \tg{sqrt(2)} & $\sqrt{2}$ \\ \tg{sum(i=1, n) i} & $\sum$ with bounds, display style \\ \tg{prod(k=1, n) k} & $\prod$ with bounds, display style \\ \tg{int(x) f(x)} & $\int f(x)\,\mathrm{d}x$ (differential added) \\ \tg{int(x=a, b) f(x)} & $\int_{a}^{b} f(x)\,\mathrm{d}x$ \\ \tg{contourint(C) f(z)} & $\oint$ contour integral \\ \tg{pvint(x=a, b) f(x)} & $\mathrm{p.v.}\!\int$ principal value \\ \tg{meanint(x=a, b) f(x)} & $\fint$ average integral \\ \tg{dy/dx}, \tg{df/dx} & derivative, upright $\mathrm{d}$ (ISO) \\ \tg{sin(x)} \tg{cos(x)} \tg{ln(x)} & upright function names \\ \tg{abs(x)} & $|x|$ \\ \tg{norm(v)} & $\|v\|$ \\ \tg{vec(AB)} & $\overrightarrow{AB}$ \\ \tg{floor(x)} \tg{ceil(x)} & $\lfloor x\rfloor$ \quad $\lceil x\rceil$ \\ \tg{bar(z)}, \tg{conj(z)} & $\overline{z}$ (conjugate, mean, closure) \\ \tg{not(A cup B)} & $\overline{A\cup B}$ (logical negation) \\ \tg{lim(x->0) f(x)} & limit, target under the word \\ \tg{partial}, \tg{nabla} & $\partial$, $\nabla$ \\ \tg{pi}, \tg{alpha}, \dots & Greek letters \\ \tg{inf} & $\infty$ \\ \bottomrule \end{tabular} \end{center} The helpers nest, so the secondary-school staples come for free: \tg{norm(vec(AB))} is the norm of a vector, \tg{vec(AB) + vec(BC) = vec(AC)} is Chasles' relation. % ===================================================================== \subsection*{Sets, logic and relations} A worksheet from collège to the upper years needs more than arithmetic. The mini-language carries the standard notation as plain words and ASCII shortcuts, so a statement reads as one types it. \textbf{Number sets} --- a doubled capital gives the blackboard-bold set, the ASCIIMath convention. Doubling avoids any clash with a one-letter variable: \tg{NN} $\mathbb{N}$, \tg{ZZ} $\mathbb{Z}$, \tg{DD} $\mathbb{D}$, \tg{QQ} $\mathbb{Q}$, \tg{RR} $\mathbb{R}$, \tg{CC} $\mathbb{C}$, plus \tg{PP} \tg{KK} \tg{HH} \tg{FF} \tg{EE} \tg{UU} for the advanced ones. \textbf{Comparisons} --- inside a formula, \tg{<} \tg{>} \tg{<=} \tg{>=} \tg{!=} give $<\ >\ \leq\ \geq\ \neq$. (In running prose, double the chevrons --- \tg{<<=>>} prints a literal $\Leftrightarrow$.) \textbf{Quantifiers and logic} --- \tg{forall} $\forall$, \tg{exists} $\exists$; the connectives \tg{and} $\land$, \tg{or} $\lor$, \tg{lnot} $\lnot$, \tg{neg} $\neg$. Implication and equivalence come both as words and as arrows: \tg{=>} or \tg{implies} $\Rightarrow$, \tg{<<=>>} or \tg{iff} $\Leftrightarrow$. The longer arrow forms always win over the comparison they contain, so \tg{abs(x) <= 1 <=> -1 <= x <= 1} reads correctly inside a formula. \textbf{Negation} --- a single rule: \tg{!} in front of a relation or quantifier negates it, choosing the proper struck-through glyph. So \tg{!in} is $\notin$, \tg{!exists} is $\nexists$, \tg{!subset} is $\not\subset$, \tg{!subseteq} is $\nsubseteq$, \tg{!equiv} is $\not\equiv$. (\tg{!=} stays ``not equal'', $\neq$.) \textbf{Set relations} --- \tg{in} $\in$, \tg{subset} $\subset$, \tg{supset} $\supset$, \tg{subseteq} $\subseteq$, \tg{supseteq} $\supseteq$, \tg{cup} $\cup$, \tg{cap} $\cap$, \tg{setminus} $\setminus$, \tg{emptyset} $\varnothing$ (negate any of them with \tg{!}). \textbf{Arrows} --- \tg{->} or \tg{to} $\to$, \tg{mapsto} $\mapsto$. \begin{slcode} $forall epsilon > 0, exists eta > 0$ $x in RR setminus QQ$ $x !in QQ$ $(P and Q) => P$ \end{slcode} % ===================================================================== \subsection*{Brackets and accents} The integer part and a few one-argument wrappers follow the same \tg{name(...)} shape as \tg{abs} and \tg{vec}: \tg{floor(x)} $\lfloor x\rfloor$, \tg{ceil(x)} $\lceil x\rceil$, \tg{set(x : x > 0)} a brace set, \tg{abr(u, v)} $\langle u, v\rangle$ an inner product. An overline is named by intent, the same rule under two words: \tg{bar(...)} for the generic bar (complex conjugate, mean, closure of a set) and \tg{not(...)} for logical negation --- so \tg{bar(z)} is $\overline{z}$ and \tg{not(A cup B)} is $\overline{A\cup B}$. The usual accents complete the set: \tg{hat(f)} $\widehat{f}$, \tg{tilde(x)} $\widetilde{x}$. % ===================================================================== \subsection*{Arithmetic and sets} A few arithmetic helpers and the set-builder notations: \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{lcm(a, b)} \tg{sign(x)} \tg{card(A)} & $\operatorname{lcm}$, $\operatorname{sgn}$, $\operatorname{card}$ \\ \tg{powerset(A)} & $\mathcal{P}(A)$ \\ \tg{range(1, n)} & $\llbracket 1, n\rrbracket$ (integer interval) \\ \tg{euler(n)} \tg{mobius(n)} & $\varphi(n)$, \quad $\mu(n)$ \\ \tg{a equiv b mod n} & $a \equiv b \pmod{n}$ \\ \bottomrule \end{tabular} \end{center} % ===================================================================== \section{Algebra} % ===================================================================== Matrices and systems as blocks, then the named vocabulary of linear maps. % ===================================================================== \subsection*{Matrix blocks} Matrices and systems are blocks: \textbf{one line is one row}, and inside a matrix \tg{;} separates the entries. \begin{slcode} <matrix>{ 1 ; 2 ; 3 4 ; 5 ; 6 } \end{slcode} \tg{<matrix>} draws parentheses, \tg{<det>} the vertical bars of a determinant, \tg{<bmatrix>} square brackets. A single \tg{|} inside a row draws the separation bar of an \textbf{augmented matrix}, at the column where you type it; it is allowed on \tg{matrix} and \tg{bmatrix}, never on \tg{det}: \begin{slcode} <bmatrix>{ 2 ; 1 | 7 1 ; -1 | 1 } \end{slcode} A \tg{<system>} stacks equations under a brace and aligns them on the first relational operator: \begin{slcode} <system>{ 2x + 3y = 7 x - y = 1 } \end{slcode} % ===================================================================== \subsection*{Linear-algebra vocabulary} Named operators on top of the matrix blocks: \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{ker(f)} \tg{im(f)} \tg{rank(A)} & $\operatorname{Ker}(f)$, $\operatorname{Im}(f)$, $\operatorname{rg}(A)$ \\ \tg{span(u, v)} \tg{tr(A)} \tg{com(A)} & $\operatorname{Span}(u, v)$, $\operatorname{tr}(A)$, $\operatorname{com}(A)$ \\ \tg{transpose(A)} \tg{inv(A)} & $A^{\top}$ \quad $A^{-1}$ \\ \tg{eigen(A)} \tg{adj(A)} & $\operatorname{Sp}(A)$, \quad $\operatorname{adj}(A)$ \\ \bottomrule \end{tabular} \end{center} % ===================================================================== \subsection*{Counting} The binomial coefficient and arrangement take \textbf{two} arguments, so a one-argument \tg{C(t)} stays an ordinary function: \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{C(n, k)} \quad \tg{A(n, k)} & $\binom{n}{k}$ \quad $A_{n}^{k}$ \\ \tg{factorial(n)}, \tg{n!} & $n!$ \\ \bottomrule \end{tabular} \end{center} % ===================================================================== \section{Analysis} % ===================================================================== Limits, operators, functions, derivatives and integrals, the vector operators and transforms, and the full function studies. % ===================================================================== \subsection*{Limits with an arrow} \tg{lim(x->0) f(x)} already sets a limit with its target under the word. For the running phrase ``$u_n$ tends to $\ell$'' written over an arrow, use \tg{arrow(...)}: \tg{u\_n arrow(n to +inf) l} sets a long right arrow with the condition underneath. \tg{to} and \tg{->} are interchangeable inside it. \begin{slcode} $lim(n -> +inf) u_n = l$ $u_n arrow(n to +inf) l$ \end{slcode} Scripts bind tighter than division, as in ordinary reading: \tg{x\^{}2/y} is the fraction $\frac{x^{2}}{y}$ and \tg{1/i\^{}2} is $\frac{1}{i^{2}}$, not $\frac{1}{i}^{2}$. To divide by a base before its script, parenthesise. % ===================================================================== \subsection*{Operators with an index} \tg{sum}, \tg{prod} and \tg{lim} carry their index in \tg{(...)} and set their whole expression in display style, so a fraction in the body keeps full size and a limit's target sits \emph{under} the word, as on a blackboard. A \tg{sum}/\tg{prod}/\tg{lim} body runs to the end of the formula or to the first \tg{=}, so the right-hand side of an identity stays outside the operator: \begin{slcode} $sum(i=1, n) i = n(n+1)/2$ $lim(x->0) sin(x)/x = 1$ $lim(x->+inf) 1/x$ \end{slcode} % ===================================================================== \subsection*{Functions and trigonometry} Function names are set upright automatically --- no backslashes: \tg{sin cos tan cot sec csc}, the inverses \tg{arcsin arccos arctan}, the hyperbolics \tg{sinh cosh tanh coth}, and \tg{ln log exp det dim gcd deg ker arg max min sup}. A name glued to \tg{(...)} takes its argument as one atom, so fractions and powers behave: \begin{slcode} $sin(x)^2 + cos(x)^2 = 1$ $tan(x) = sin(x)/cos(x)$ \end{slcode} % ===================================================================== \subsection*{Derivatives} A Leibniz derivative is written as the fraction it is, and the differential \tg{d} is set upright (ISO 80000-2), matching the \tg{d} of the integrals. The romanisation triggers \emph{only} when both sides of the fraction carry the differential, so a variable named \tg{d} is never disturbed: \tg{d/2} stays the fraction d over 2. \begin{slcode} $dy/dx$ % upright d $(d^2 y)/(dx^2)$ % second derivative $dy/dx + y = 0$ % differential equation \end{slcode} Partial derivatives use \tg{partial} ($\partial$); parenthesise each side so the fraction groups correctly: \begin{slcode} $(partial f)/(partial x)$ $(partial u)/(partial t) = (partial^2 u)/(partial x^2)$ \end{slcode} Inject a computed value with \tg{\#\{expr\}} (or \tg{\#name}), including inside maths: \Verb|$#k^2$|. Decimal numbers follow the \tg{lang} option. % ===================================================================== \subsection*{Differential operators and transforms} The vector operators, the Landau notations (spelt out so \tg{o}/\tg{O} stay free), and the integral transforms: \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{grad(f)} \tg{div(F)} \tg{curl(F)} & $\operatorname{grad} f$, $\operatorname{div} F$, $\operatorname{rot} F$ \\ \tg{lap(f)} & $\Delta f$ (no parentheses) \\ \tg{dirderiv(f, u)} & $\nabla_{u} f$ \\ \tg{bigO(...)} \tg{litO(...)} & $O(\cdot)$, \quad $o(\cdot)$ \\ \tg{laplace(f)} \tg{fourier(f)} & $\mathcal{L}\{f\}$, \quad $\mathcal{F}\{f\}$ \\ \tg{surfint(S)} \tg{volint(V)} & $\oiint_{S}$, \quad $\iiint_{V}$ \\ \bottomrule \end{tabular} \end{center} % ===================================================================== \subsection*{Integrals} The integral family names its variable and bounds in the head \tg{(...)} and captures the integrand as its body; the differential is appended for you. No bounds gives a primitive, a comma-separated pair a definite integral: \begin{slcode} $int(x) f(x)$ % primitive $int(x=a, b) f(x)$ % definite \end{slcode} Separate several domains with \tg{;} for multiple integrals: the count of domains chooses $\int$, $\iint$ or $\iiint$, and the differentials come out in reverse order (the Fubini convention). A single named domain is a region integral: \begin{slcode} $int(x=a, b ; y=c, d) f(x,y)$ $int(D) f$ \end{slcode} Three named integrals complete the family: \tg{contourint(C) f(z)} a contour integral $\oint$, \tg{pvint(x=a, b) f(x)} a Cauchy principal value, and \tg{meanint(x=a, b) f(x)} the average (normalised) integral. % ===================================================================== \subsection*{Function studies} % ===================================================================== Three tags turn a function into a variation table and a curve. They share one source of truth: a \emph{function object} built once with \tg{<fn>}, then read by \tg{<vartab>} (the table) and \tg{<plot>} (the graph). \textbf{The function object.} The equation form registers itself under its left-hand name and reads as the board does; the variable is declared in situ, table attributes stay attributes after the expression: \begin{slcode} <fn f(x) = (x+2)(x-3)> <fn g(t) = t^2 - 2t x:{-inf | 1 | +inf} deriv:{- | +} var:{+inf \ -1 / +inf}> \end{slcode} A tabular-only object takes the head without \tg{=} --- \tg{<fn v(x) x:\{...\} var:\{...\}>}. Objects rebind sequentially: a later \tg{<fn f(x) = ...>} shadows the name for what follows, never for what precedes. The 2.4 \tg{name:}/\tg{expr:} attributes still parse but are deprecated; the \tg{let} binding remains available: \begin{slcode} let k = <fn name:{k(x)} expr:{(x^2+1)/(x-1)} x:{-inf | 1-sqrt(2) | 1 | 1+sqrt(2) | +inf} second:{- | - || + | +} deriv:{+ | - || - | +} var:{-inf / 2-2sqrt(2) \ -inf || +inf \ 2+2sqrt(2) / +inf}> \end{slcode} \tg{let NAME = <fn \dots>} stores the object under \texttt{NAME} (the assignment may span several lines, up to the closing \tg{>}). The fields are \tg{name:} (function and variable, fixing the row labels), \tg{expr:} (the formula, read only by \tg{<plot>}), \tg{x:} (remarkable abscissas separated by \tg{|}), \tg{deriv:} and \tg{second:} (sign of the first and second derivative, one per interval), and \tg{var:} (the variation line). \textbf{The table.} \tg{<vartab k>} reads the object; or write the data inline with \tg{<vartab x:\{\dots\} deriv:\{\dots\} var:\{\dots\}>}. Only \tg{x:} and \tg{var:} are required, so the table comes in four shapes: \begin{itemize} \item \tg{second:} + \tg{deriv:} --- full study: $x$, $f''(x)$, $f'(x)$, $f(x)$ \item \tg{deriv:} --- classic variation table: $x$, $f'(x)$, $f(x)$ \item \tg{second:} --- convexity table: $x$, $f''(x)$, $f(x)$ \item neither --- plain value table: $x$, $f(x)$ \end{itemize} Sign lines take one sign per interval (\tg{+}, \tg{-}, or empty); \tg{||} marks a forbidden value and crosses every row; zeros at sign changes are placed automatically. In \tg{var:}, a connector is \tg{/} (rising) or \tg{\textbackslash} (falling) \emph{surrounded by spaces} --- glued, as in \tg{2/3}, it is a fraction. A value between two arrows of the same direction is rejected: the table lists only bounds and extrema. \textbf{The sign table.} \tg{<signtab>} is \tg{<vartab>}'s companion for factored expressions. The automatic form \emph{computes} the rows from a product or quotient of affine factors --- rational zeros printed exactly, a forbidden value at each denominator zero, even multiplicities known not to change sign: \begin{slcode} <fn f(x) = (x+2)(x-3)> <signtab f> <fn g(x) = (x+1)/(x-2)> <signtab g> \end{slcode} The manual block form is the general one, for rows the affine engine cannot derive (a trigonometric factor, an irreducible quadratic): one row per factor, usually a conclusion row last. \begin{slcode} <signtab x:{-inf | -2 | 3 | +inf}>{ x+2 : - | 0 | + | + x-3 : - | - | 0 | + f(x) : + | 0 | - | 0 | + } \end{slcode} A cell is a sign per interval; a \tg{0} marks a zero at the boundary it follows, \tg{||} a forbidden value. The invariant is checked: a row that changes sign without a \tg{0} or a \tg{||} at the boundary is a compilation error, not a wrong table in print --- an expression cannot change sign without vanishing or ceasing to exist. \textbf{The graph.} \begin{slcode} <plot k samples:200 x:{-4, 6} y:{-10, 12}> \end{slcode} \tg{<plot k>} reads \tg{expr:} and \tg{name:} from the object. \tg{x:\{a, b\}} is the horizontal display window (defaults to the table's finite abscissas), \tg{y:\{c, d\}} the vertical window (needed when the function runs to infinity), \tg{samples:N} the number of points. The formula is translated to the plotter's syntax (variable renamed, trigonometry in degrees, implicit products made explicit) and poles are handled so no spurious line joins the branches. Supported in \tg{expr:}: \verb!+ - * / ^!, parentheses, \texttt{sin cos tan exp ln log sqrt abs}, and \texttt{pi}. \tg{<vartab>} is declarative, \tg{<plot>} is computed: keeping \tg{var:} consistent with \tg{expr:} is the author's responsibility. \textbf{Parametric and polar curves.} A curve that is not a function graph is the same \tg{<plot>} with \tg{kind:}. \tg{kind:parametric} reads \tg{expr:\{x(t), y(t)\}} --- two components in the parameter \texttt{t} --- over the window \tg{t:\{a, b\}}. \tg{kind:polar} reads \tg{expr:\{r(theta)\}} over \tg{theta:\{a, b\}} and is drawn as the parametric pair $(r\cos\theta, r\sin\theta)$. Bounds may use \texttt{pi} (\tg{t:\{0, 2pi\}}), and \tg{x:} / \tg{y:} set the display window as before. \begin{slcode} let ell = <fn expr:{3cos(t), 2sin(t)}> <plot ell kind:parametric t:{0, 2pi} x:{-4, 4} y:{-3, 3}> let cardio = <fn expr:{1+cos(theta)}> <plot cardio kind:polar theta:{0, 2pi}> \end{slcode} A conic is drawn through its parametrisation: an ellipse as \tg{\{a cos(t), b sin(t)\}}, a parabola as \tg{\{t, t\^{}2\}}, a branch of a hyperbola as \tg{\{a cosh(t), b sinh(t)\}} --- no separate conic directive, the parametric form already names them all. \textbf{Areas.} \tg{area:\{a, b\}} shades the region between the curve and the $x$-axis on $[a, b]$, dashed verticals at the bounds; \tg{between:g} shades between two curves instead, clipped to \tg{area:} when given: \begin{slcode} <plot f x:{-1, 4} area:{1, 3}> <plot f x:{-1, 4} between:g area:{0, 3}> \end{slcode} \textbf{Sequences: the cobweb.} \tg{cobweb:\{u0\}} (or \tg{cobweb:\{u0, n\}}, default $n = 8$) draws the bisector $y = x$ and the staircase of $u_{n+1} = f(u_n)$, the iterates computed at compile time and the first three labelled on the axis: \begin{slcode} let h = <fn name:{h(x)} expr:{sqrt(2x+3)}> <plot h x:{0, 4} y:{0, 4} cobweb:{0.2, 10}> \end{slcode} \textbf{Probability laws.} The two laws of the lyc\'ee need no \tg{<fn>} object; \tg{area:} highlights $P(a \leq X \leq b)$ on both --- shaded under the Gaussian, stronger bars on the binomial: \begin{slcode} <plot normal:{0, 1} area:{-1, 1.5}> <plot binomial:{12, 0.4} area:{3, 6}> \end{slcode} The normal window defaults to $\mu \pm 4\sigma$; the binomial masses are computed exactly. \textbf{The number line.} \tg{<numberline>} draws the graduated line with interval sets --- the figure of inequalities and absolute values: \begin{slcode} <numberline x:{-5, 5} set:{[-2, 3) union (4, inf)} points:{-4}> \end{slcode} The block form \emph{solves}: it takes one inequality (a tag's braces cannot carry \tg{<} and \tg{>}) and computes the set exactly --- an affine expression against a constant, a product or quotient of affine factors against 0, or \tg{abs(affine)} against a constant. Strictness decides open or closed; a denominator zero is always excluded; an isolated solution is a point. \begin{slcode} <numberline x:{-5, 5}>{ abs(x - 1/2) >= 5/2 } \end{slcode} Brackets follow the international convention (\tg{[ ]} included, \tg{( )} excluded); a bound may be \tg{inf}, the segment then runs to the arrow. \tg{points:} pins isolated values. One unit is one centimetre, halved automatically while the line exceeds the page. % ===================================================================== \section{Probability} % ===================================================================== Counting feeds probability; the blackboard operators are the doubled letters \tg{PP} and \tg{EE}, with the conditional bar written \tg{mid}. \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{You write} & \textbf{You get} \\ \midrule \tg{PP(A)}, \tg{PP(A mid B)} & $\mathbb{P}(A)$, \quad $\mathbb{P}(A \mid B)$ \\ \tg{EE(X)} & $\mathbb{E}(X)$ \\ \tg{var(X)} \tg{std(X)} \tg{cov(X, Y)} & $\operatorname{Var}(X)$, $\sigma(X)$, $\operatorname{Cov}(X, Y)$ \\ \tg{normal(mu, sigma)} & $\mathcal{N}(\mu, \sigma^{2})$ \\ \tg{poisson(lambda)} \tg{binomial(n, p)} & $\mathcal{P}(\lambda)$, \quad $\mathcal{B}(n, p)$ \\ \tg{repart(X, x)} \tg{densite(X, x)} & $F_{X}(x)$, \quad $f_{X}(x)$ \\ \bottomrule \end{tabular} \end{center} \textbf{The weighted tree.} \tg{<tree>} is the most-drawn object of premi\`ere and terminale. Each line reads \tg{LABEL PROBABILITY}, an optional brace opening the children; a leading \tg{!} is the complementary event, rendered with the overline. Probabilities may be numbers or symbols (\tg{p}, \tg{1-p}), both typeset through the math mini-language. A node with exactly one branch receives its complement automatically --- the only case that is mathematically determined; sibling probabilities that do not sum to 1 are a compilation error. \begin{slcode} <tree products:on>{ A 0.3 { B 0.6 } !A 0.7 { B 0.1 } } \end{slcode} \tg{products:on} writes $P(A \cap B) = 0.18$ at the end of each leaf: the exact product when every probability on the path is numeric, the symbolic chain otherwise. The layout is computed --- leaves evenly spaced, each node at the mean height of its children. No loops inside the block: a tree is written by hand, that is the point of a tree. % ===================================================================== \section{Statistics} % ===================================================================== Descriptive statistics is one tag, \tg{<stats>}, five \tg{kind:}s, every number computed at compile time: \begin{slcode} <stats kind:bars data:{1: 4 | 2: 7 | 3: 2}> <stats kind:bars data:{Walk: 5 | Bus: 3 | Bike: 2}> <stats kind:histogram bounds:{0, 5, 10, 20} counts:{3, 7, 2}> <stats kind:pie data:{Walk: 5 | Bus: 3 | Bike: 2}> <stats kind:boxplot data:{12, 15, 9, 21, 14, 15, 18}> <stats kind:scatter data:{(1, 2.1) (2, 2.6) (3, 3.4)} fit:on> \end{slcode} Three honesty guarantees, because a statistics figure teaches by its construction: the histogram draws \emph{density} (count over class width), so unequal classes stay truthful; the boxplot uses the French secondary-school quartiles ($Q_1$ the smallest value with at least a quarter of the data at or below it, $Q_3$ likewise at three quarters); \tg{fit:on} draws the least-squares line, computed, not eyeballed. The pie starts at twelve o'clock and runs clockwise, percentages appended to the labels. The dictionary \tg{key: value | ...} is the form of the mappings (bars --- numeric keys give a numeric axis, words give categories --- and pie); pairs \tg{(x, y)} remain the form of the clouds, where an abscissa may repeat. A Lua table declared with \tg{let} is passed by name --- the home of data reused across figures: \begin{slcode} let notes = { Mathematiques = 15.5, Francais = 12.0, Anglais = 18.0 } <stats kind:bars data:notes> <stats kind:pie data:notes> \end{slcode} A Lua table has no writing order: map keys come out \emph{sorted} (numbers numerically, words alphabetically); when the order matters, a list of pairs keeps it: \tg{let t = \{\{"T1", 11.5\}, \{"T2", 13\}\}}. Lists of numbers feed \tg{boxplot}, lists of pairs feed \tg{scatter}, two list variables feed \tg{histogram} through \tg{bounds:} and \tg{counts:}. \part{Programming the document} % ===================================================================== \section{Block aliases} % ===================================================================== Define a reusable component once; \tg{\#param} placeholders are filled at the call site, and the call-site body becomes the block content. \begin{slcode} let card{title, frame} = <box title:{#title} line:#frame radius:2> <card First, Crimson>{ Called with two arguments. No box options to repeat. } <card Second, Navy>{ Same component, different look. } \end{slcode} % ===================================================================== \section{Control flow} % ===================================================================== \begin{slcode} for n in 1..3 { % numeric range <c Navy b>Sheet #n } for f in [chat.png, chien.png] { % explicit list <img 16>{#f} } if score >= 10 { <Green>Passed. } else { <Red>Try again. } \end{slcode} Loops and conditions work in the document body, inside boxes, and inside table bodies. The loop variable interpolates everywhere via \tg{\#}. % ===================================================================== \part{Reference} % ===================================================================== \section{Security}\label{sec:security} % ===================================================================== scholatex evaluates \tg{let name = expr}, \tg{\#\{expr\}} and the conditions of \tg{for}/\tg{if}/\tg{while} as Lua at compile time, so by default a document can run arbitrary code --- exactly like \tg{\textbackslash directlua}. Setting \tg{untrusted=true} runs that Lua in a restricted environment: only pure, side-effect-free names are visible (the maths the document needs plus \tg{string}/\tg{table} helpers), while \tg{os}, \tg{io}, \tg{package}, \tg{require}, \tg{load}, \tg{debug}, \tg{setmetatable} and \tg{getmetatable} are absent. A blocked access stops the compile with a clear message; a runaway loop is aborted by an instruction-count ceiling. Because the step counter cannot interrupt work inside a single C call, \tg{string.rep} and \tg{string.format} are capped --- both through the global \tg{string} table and through method calls on a string value (\tg{("x"):rep(n)}), which a restricted string metatable routes to the same capped functions. \textbf{Scope.} \tg{untrusted} hardens the scholatex expression layer only. It does \emph{not} sandbox LuaLaTeX as a whole: a hostile \tg{.tex} can still call \tg{\textbackslash directlua}, \tg{\textbackslash write18} (with shell-escape), \tg{\textbackslash input}, and so on. To compile a whole \tg{.tex} you do not trust, run \tg{lualatex} \textbf{without} \tg{-{-}shell-escape}, ideally inside a container. % ===================================================================== \section{Examples} % ===================================================================== The \tg{examples/} folder contains seven self-contained, fully commented documents that together exercise every feature: \begin{center} \begin{tabular}{@{}ll@{}} \toprule \textbf{File} & \textbf{Covers} \\ \midrule \tg{text-style.tex} & case rule, styles, colours, fonts, sizes, alignment, escapes, aliases, TOC \\ \tg{containers.tex} & tables, boxes and the named-area grid \\ \tg{basics.tex} & mini-language, sets, logic, negation, integer part, the overline, arithmetic helpers \\ \tg{algebra.tex} & matrix, determinant, augmented matrix, systems, and the linear-algebra vocabulary \\ \tg{analysis.tex} & operators, limits, trigonometry, derivatives, the vector operators, integrals, transforms \\ \tg{statistics-probability.tex} & counting, probability, variance, distributions --- then trees, the two laws and the descriptive-statistics figures \\ \bottomrule \end{tabular} \end{center} Compile any of them with \tg{lualatex <file>.tex} from the \tg{examples/} folder. The image folders \tg{IMG/} and \tg{IMAGES/PNG/} ship alongside them; \tg{containers.tex} uses \tg{imgdir=\{IMG, IMAGES/PNG\}} to show that bare image names are resolved across several directories. % ===================================================================== \section{Diagnostics} % ===================================================================== Errors point at the source line, e.g. \tg{scholatex: line 12: unknown tag attribute: 'xyz'}. Defining an alias whose name is a built-in (\tg{let section = \dots}, \tg{let tab = \dots}) prints a warning: the built-in always wins, so the alias would be silently dead --- pick a different name. \vfill \begin{center}\small\color{black!55} scholatex --- Released under the GNU General Public License v3.0. \end{center} \end{document}