diff --git a/README.md b/README.md index bee99e3..f108dca 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ window.__glpythonMount = (args, send) => { const embedOrigin = "https://glpython-edu.github.io" const embedUrl = `${embedOrigin}/glpython-editor/index.html` const [sessionId, mode, ...optionArguments] = args - // Optional per block, separated by spaces in any order: "box" or - // "substitution" presets the visualizer's mode, and URLs of shared Python + // Optional per block, separated by spaces in any order: "box", + // "substitution" or "tree" presets the visualizer's mode ("tree" only with + // the feature "tree"), and URLs of shared Python // files to load as libraries, e.g. @GLPython.eval(box https://…/parser.py). // LiaScript splits macro arguments at commas, so "features=run,repl" arrives // as several arguments: they are joined back together here. Arguments that @@ -37,17 +38,17 @@ window.__glpythonMount = (args, send) => { .join(",") .split(/\s+/) .filter(Boolean) - const visualizerMode = options.find((option) => option === "box" || option === "substitution") ?? null + const visualizerMode = options.find((option) => option === "box" || option === "substitution" || option === "tree") ?? null const libraryUrls = options.filter((option) => /^https?:\/\//.test(option)) // "features=run,repl,…" shows only these parts (the course builds its // profiles from it); without it, everything shows - except "coverage" - // (after Tests, the lines no example executed) and "listtree" (lists such - // as ["+", 2, 3] also drawn as a tree), which a course switches on - // explicitly. "large" enlarges the code and the visualizer, e.g. for a demo + // (after Tests, the lines no example executed), "listtree" (lists such + // as ["+", 2, 3] also drawn as a tree) and "tree" (the visualizer's mode + // «Baum»), which a course switches on explicitly. "large" enlarges the code and the visualizer, e.g. for a demo // on the projector. - const featureNames = ["run", "tests", "visualize", "reset", "repl", "export", "ide", "coverage", "listtree"] + const featureNames = ["run", "tests", "visualize", "reset", "repl", "export", "ide", "coverage", "listtree", "tree"] const featuresOption = options.find((option) => option.startsWith("features=")) - const requestedFeatures = featuresOption ? featuresOption.slice("features=".length).split(",").map((name) => name.trim()).filter(Boolean) : featureNames.filter((name) => name !== "coverage" && name !== "listtree") + const requestedFeatures = featuresOption ? featuresOption.slice("features=".length).split(",").map((name) => name.trim()).filter(Boolean) : featureNames.filter((name) => name !== "coverage" && name !== "listtree" && name !== "tree") const unknownFeatures = requestedFeatures.filter((name) => !featureNames.includes(name)) const hasFeature = (name) => requestedFeatures.includes(name) const large = options.includes("large") @@ -1039,7 +1040,8 @@ def quadrat(x): The visualizer shows a call to a function whose body is a single `return` either as a box or replaced by its return expression (substitution, the default), and learners can switch with the toggle in its toolbar. To preset the mode for one -block, pass `box` or `substitution` to `@GLPython.eval` or `@GLPython.visual`: +block, pass `box` or `substitution` (or, with the feature `tree`, `tree`) to +`@GLPython.eval` or `@GLPython.visual`: ```python def quadrat(x): @@ -1141,6 +1143,18 @@ window, and in the console, where such a value gets a «Baum» button next to «Visualisieren». The visualizer's ⋮ menu can switch the trees off («Listen als Baum zeigen»). Each tree is described by the list's own text for screen readers. + +`tree` isn't part of «everything» either. It gives the visualizer a third +mode, **Baum**, next to «Kasten» and «Ersetzung»: an expression is shown as +its tree while Python evaluates it. One step replaces the expression by its +tree, then each step of «Ersetzung» turns a subtree into its value, and one +step puts the value back in the line - `x = 2 + 3 * 4` takes 5 steps instead +of 3. A function with a single `return ` is replaced by that +expression's tree; any other call keeps its box below the tree, which says why +(«`notenschnitt` hat lokale Variablen – der Aufruf wird als Kasten gezeigt.», +also in «Ersetzung»). Labels are Python's (`+`, `quadrat`, `[ ]`), with no +parentheses. Without `tree` there's no «Baum» button. To start a block in +«Baum», pass `tree` too: `@GLPython.visual(tree features=visualize,tree)`. `large` makes the code 20px (line height 1.6) and the visualizer at least 60% of the window, e.g. for the projector in presentation mode. diff --git a/examples/profiles.md b/examples/profiles.md index d334dbe..e502aee 100644 --- a/examples/profiles.md +++ b/examples/profiles.md @@ -20,6 +20,8 @@ import: https://raw.githubusercontent.com/glpython-edu/glpython-liascript/main @Code.baum: @GLPython.eval(features=run,repl,visualize,ide,listtree @0) +@Code.ausdruck: @GLPython.eval(features=run,repl,visualize,ide,tree,listtree @0) + @Code.demo: @GLPython.visual(large @0) --> @@ -36,6 +38,7 @@ Jede Folie zeigt ein Profil des Kurses. Die Profile sind Makros des Kurses, die | pruefung | `features=run` | | voll | (alles) | | baum | `features=run,repl,visualize,ide,listtree` | +| ausdruck | `features=run,repl,visualize,ide,tree,listtree` | | demo | `@GLPython.visual(large)` | ## erkunden @@ -216,6 +219,28 @@ print(ausdruck) ``` @Code.baum +## ausdruck + +Der Visualisierer mit dem dritten Modus «Baum»: ein Ausdruck als Baum, Schritt +für Schritt ausgewertet. `notenschnitt` bleibt ein Kasten, mit dem Hinweis +warum (auch in «Ersetzung»). + +```python +def quadrat(x): + return x * x + +def notenschnitt(noten): + summe = sum(noten) + anzahl = len(noten) + return summe / anzahl + +x = (2 + 3) * 4 +y = quadrat(2 + 1) +z = notenschnitt([4, 5, 4.5]) +baum = ["+", 2, ["*", 3, 4]] +``` +@Code.ausdruck + ## demo Nur der Visualisierer, gross - für den Projektor.