Repository navigation
Expression cards in the Logic Programmer, editable as text - #1766
Open
rubensworks wants to merge 28 commits into
Open
rubensworks wants to merge 28 commits into
rubensworks wants to merge 28 commits into
Conversation
An expression variable stores a tree of operator applications, references to other variables, inline constants and parameters. Without parameters, it evaluates to the value of its root, using cached lazy expressions per node. With parameters, it evaluates to an expression operator that can be passed to apply, map, filter, reduce, pipe, flip and other operator consumers. Includes type inference with structured errors per slot, and a pretty-printer used in tooltips. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Operator elements now edit an expression tree. A slot accepts a variable card, a typed value, an item, a parameter, or a nested operator picked from the list while the slot is selected, which filters the list by the slot type. Nested operators open in the same config area with a breadcrumb to go back up. A side panel shows parameters, the pretty-printed expression with errors highlighted, the inferred type and what is still missing. Type errors outline the failing slot. A single operator with only variable cards is still written as a classic operator variable, so existing cards and add-ons are unaffected. Suffix operators with word-like symbols are now printed in prefix notation, so item NBT size prints as size(nbt(item)). Closes #194 Closes #546 Closes #860 Closes #1112 Closes #1417 Closes #1495 Closes #367 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Covers an expression with references and inline constants on a Display Panel, equivalence with a chain of classic cards, an expression operator used by a filter over an Inventory Reader list, apply with references, persistence of the card data, and how the Logic Programmer writes and loads plain operators and expressions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
- Draw chips with short operator names at the largest scale that fits, and mark unfilled nested operators with a badge. - Show what to do next in the expression panel, depending on the selection and the state of the expression, instead of small hints in the config area and long slot tooltips. - Open a nested operator right after picking it. - Let selected parameters cycle through value types. - Make the expression panel collapsible, like the recipe book, so the Logic Programmer keeps its old width for players who don't need it. - Escape and clicking the empty config area cancel a selection instead of closing the gui. - Outline chips whose nested operators contain a type error, and name the operator in the error status. - Draw the breadcrumb as tabs, allow empty string values, and use readable colors for the type signature. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Loading cards into the Logic Programmer: - Store the output types of referenced cards in expression cards, so type checking works after loading a card back into the LP. - Show the actual referenced cards when they are in the player's inventory, otherwise a reference placeholder card with the id and type, instead of value cards with default values. - Remember cards placed in slots while navigating through nested operators. Before, navigating back replaced them by placeholders whose default type could corrupt the known reference type. - Infer missing reference types from concrete operator input types. Expression editing: - In Operator slots, list all operators. Operators that cannot give an operator are inserted as values, printed as (+). Shift-click inserts any operator as a value. - Print complete choices as c ? a : b, and don't show choice as "?". - Infer parameter types from Number slots as well. - Show errors of an operator as a whole in the hint, place the hint below the status, and clarify the hint texts. Tests for complex expressions: three parameters with partial application, higher-order parameters, expression operators as values and refs, nested materialization, deep parameter chains, expressions composed from expression cards in a network, live updates of references, reference cycles, and reference types surviving an LP write and load. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Right-clicking a Display Panel or Dynamic Light Panel with a Wrench always rotated the panel, so a configuration copied with #1721 could not be pasted onto a panel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Copy expression cards with and without parameters in the crafting grid, and paste one with the Wrench. The copies get a new id, keep their expression and reference types, and evaluate like the original. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Print operators by their scoped interact name, such as length or nbt,
and fall back to their global interact name, such as stringLength,
when the scoped name is shared by operators that the type of the first
argument does not tell apart. Only + - * / % == != < <= > >= && || and
! keep a symbol, so names no longer contain characters such as
NBT{}.get_integer or =NBT=, and every printed name leads back to one
operator. Chips, breadcrumb tabs and status messages use the same names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The expression panel gets Edit, Copy and Undo buttons. Edit turns the expression into text in the format of Integrated Scripting, which is parsed and type checked on the client while typing. Applying sends the resulting tree to the server once, as a single set action that the server validates, and makes the element of the new root operator active when it changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Drop the hint below the status and the "Ready to write" line, which the check mark next to the operator name already shows. Errors of an operator as a whole, which have no slot to hover, now show their message as the status. The type is drawn below the expression on its dark background, in the colors of the value types as in card tooltips. The button that adds a parameter is a regular button like the others in the Logic Programmer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Brings in the simplified expression panel. Edit, Copy, Undo, Apply and Cancel are now regular buttons in a row at the bottom of the panel, like the other buttons of the Logic Programmer, instead of drawn boxes in the title. The hint below the text box is gone with the other hints, and the type of the typed text is shown in the colors of its types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The dark box of the expression now reaches the bottom of the panel, with a footer that shows the type, or the first type error instead, so there is no empty area left for messages that may come. Hovering an error shows its full message. The expression and the footer are drawn at full size, since text scaled to 75% gets uneven strokes at most GUI scales. The empty slot count and depth note are dropped, empty slots already show as ? in the expression. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The text box ends right above the footer of the panel, which shows the type of the typed text, or the parse error with its line and column. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Parameter chips past the second row were not drawn, so with long types or names some parameters could not be selected, renamed or removed. Chips now show only the name, at full size and at most half the panel wide, and wrap onto as many rows as needed, with the expression below them. A chip's tooltip names its type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Besides clicking a parameter and then a slot, a parameter chip can now be dragged onto a slot. While dragging, the chip follows the cursor and the slot it would go in is outlined. Dropping it anywhere else cancels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The text of the expression in the panel can now be edited directly, instead of switching to a separate text box with an Edit button. It keeps the colors of the read-only view, and clicking a part places the caret and opens that operator in the slots. Typing makes a draft: Enter applies it, Escape reverts it, and slots wait until then. Applying keeps the open operator if it is still there. Undo and Redo go back and forth through all changes to the expression, from slots, parameters and text alike, and are reset when another expression is opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The font has no undo, redo or copy symbols, so the icons are drawn pixel by pixel, in the same colors and with the same shadow as the text of text buttons. Copying briefly shows a check mark instead of the "Copied" text. The hint when hovering the expression is removed, as the button tooltips and the book already explain the editor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
With icons, Undo, Redo and Copy fit right of the title, so the row of buttons below the expression is removed and the expression gets that height instead. Revert and Apply become a cross and a check there, with tooltips naming Esc and Enter. The add parameter button moves down a pixel so its plus is in line with the Parameters label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The label sat right under the title like a second heading, while parameters are optional. The chips now sit under the title on their own, followed by a plus to add another, which reads "+ Parameter" while there are none. Chips are as tall as the button, so they line up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The main texture covers the left of the panel, so its content had a margin of 2 pixels on the left and 3 on the top and right. It now starts one pixel further right. The footer shows only the type, recognizable by its colors, and names it as the type in its tooltip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The type is recognizable by its colors, so the tooltip only got in the way when moving the mouse over the footer. Errors and text that does not fit still show a tooltip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The whole text turned plain as soon as a character was typed, which hid the open operator and the empty slots. The text before and after what was typed now keeps its colors. Errors are left out while typing, as the footer then shows the errors of the draft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
The tooltip of an expression card inferred its type as if every referenced card could be of any type, so a -> a + #5 showed Integer -> Any, while the Logic Programmer showed Integer -> Integer. The card already stores the types of the cards it references, and the tooltip now uses them, like the expression line above it. The type that networks validate against stays as it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One variable card can now hold a whole expression built in the existing Logic Programmer, for example
instead of 6 to 8 cards in a Variable Store glued together with
operator,apply,pipeandflip. The expression can be built with slots, or typed and copied as text, in the same view.This PR now also contains what were the stacked PRs #1767 (text) and #1768 (editing in place), which are closed.
Screenshots of the current Logic Programmer: https://claude.ai/artifact/Fwyz9rB9VqPMmZY6FYasuF
What was built
Data model (
api/evaluate/expression/tree,core/evaluate/expression/tree)Expression(root, parameters)with sealedExpressionNode:Operator(op, children),Ref(variableId),Const(value),Param(index), plusEmptyfor slots that are still being edited.referenceTypes). The LP uses them when the card is loaded again, and the card's tooltip uses them for its type, so it matches what the LP showed. Networks validate against the type inferred without them, as they check the actual variables.Evaluation
ExpressionVariableFacade.getVariablebuilds nestedLazyExpressions. The root is cached in the network under the card id (same asOperatorVariableFacade, soLazyExpression-based advancement predicates still see it). Inner nodes use a cache local to the tree instead of derived ids, so they can never collide with other variable ids. Invalidation propagates through the normal listener chain;lastNetworkHashhandling mirrorsOperatorVariableFacade.ValueOperatorwrapping the newExpressionOperator(extendsOperatorBase). Inputs are the parameter types, output is inferred. Referenced cards are bound as variables when the card is built, likeCurriedOperatordoes, and a wrapper variable forwards their invalidation. Each call evaluates nodes lazily and at most once, without registering listeners, sochoiceonly evaluates the branch it needs.materialize()replaces refs by their current materialized values and keeps parameters. Materializing apipethat contains an expression operator also materializes it (reuses the Fix crash when displaying piped operators #1705 pattern inCombinedOperator).ExpressionOperator.Serializerwrites refs as their current values, like the curry serializer.Compatibility rule: a single operator whose inputs are all cards and that has no parameters is written as a plain
OperatorVariableFacade, exactly as before. Only trees that need the new features produce the newintegrateddynamics:expressionfacade. Old cards load and evaluate unchanged; loading an old card into the LP yields a one-level tree.Type inference (
ExpressionTypeChecker): walks the tree withgetInputTypes,validateTypesandgetConditionalOutputType, returning structured errors(node path, slot, expected, actual, message)and the number of empty slots.Names (
ExpressionPrinter,OperatorNames): operators are named the way Integrated Scripting names them. A call uses the operator's scoped interact name (length,nbt,getInteger), which IS uses for methods on values, when the type of its first argument tells it apart from other operators with that name; otherwise it uses the global interact name (stringLength), which IS uses foridContext.ops. 248 of the 267 scoped names belong to one operator, and the other 19 are always told apart by a concrete first argument type, so the fallback mostly shows up for empty slots, untyped parameters and operator values ((stringLength)). Only+ - * / % == != < <= > >= && ||are infix and!prefix, each meaning exactly one operator (strings useconcat), and complete choices print asc ? a : b, so names never contain LP symbols such asNBT{}.get_integeror=NBT=.OperatorNames.resolve(name, firstArgumentType)is the inverse, and a unit test checks for every registered operator that its printed name is an identifier that resolves back to it. Used in the card tooltip (Expression:andType:lines) and in the LP, where the text, chips, breadcrumb tabs and status messages use the same names.Logic Programmer (upgraded
OperatorLPElement, no new element)map,filter,reduce,apply, ...) every operator is listed. Picking one that cannot give an operator puts it in as a value, somap((stringLength), l)needs no extra card. Operators that may give an operator (flip,pipe,apply) are nested as calls, since that is what you usually want there; shift-click puts those in as a value too.size,nbt,>) or the value at the largest scale that fits, with a badge while a nested operator has empty inputs. A chip is outlined red when it or anything inside it has a type error, and hovering it highlights its part of the expression.+that reads+ Parameterwhile there are none. Drag a chip onto a slot to use it there, or click it and then the slot; while selected, type to rename it and use the arrows to set its type; right-click to remove it. Chips show only the name, with the type in their tooltip and in the type of the expression, and wrap onto as many rows as needed at most half the panel wide each, so all 8 parameters always show.?, highlights the part of a hovered slot, and can be clicked and edited (see Text).2 type error(s), the first in input 2 of *, or the message of an error of an operator as a whole (such aschoicebranches of different types), since that has no slot to hover. Hovering an error shows its full message. Whether the expression can be written is shown by the existing check mark next to the operator name.LogicProgrammerExpressionActionPacketcarrying small actions (const,op,param,clear,nav, parameter add/remove/rename/type), handled by the side-independentExpressionEditor. The server only accepts slot edits for the node being edited and ignores malformed actions.Text (
ExpressionParser,ExpressionPrinter.printExact)Line 1, column 24: The text ends too early, a value is missing), which is also underlined in the text. Enter applies the draft, Escape reverts it, and Shift+Enter adds a line. While there is a draft, slots, parameters and tabs ignore clicks, so the draft and the slots cannot both change the expression. Hiding the panel reverts the draft.setaction in the same packet. The server deserializes it with the existing limits (depth 64, 8 parameters, known operators and types) and also checksExpressionEditor.isEditable: the root is an operator, at most 1024 nodes, parameter names that the text form can read back, parameter types from the LP's list, and only operators that have a slot for each input. Anything else is ignored.(a + b) * 2while in>), the element of that operator becomes active, the same way loading a card does. The known ref types and the actual cards placed in slots are carried over first, since deactivating the old element forgets them. Otherwise, applying keeps the open operator in the slots if it still exists.length(s)for strings,length(l)for lists), and otherwise the global one (stringLength). Both are accepted when parsing.+ - * / % == != < <= > >= && ||with C precedence, prefix!, andc ? a : b.?is an empty slot, and(name)is an operator as a value, such as(+)or(stringLength).x -> ..., or(x: integer, y) -> ...with optional types.true/false,1,5L,1.5/1e3/2d/NaN/Infinity, and quoted strings with\" \\ \n \t \r \uXXXXescapes.@type{snbt}, so the printer is exact for every value, not only the common ones.#42refers to a card by ID.#limitor#"my label"refers to it by label, and only resolves when exactly one card has that label. Copy prints#labelonly in that case, and#idotherwise.ExpressionPrinter.printExactis the inverse ofExpressionParser.parse, and the tests check this both ways.ExpressionParseris a recursive descent parser with a small lexer, for at most 8192 characters, with 28 translatable error messages inExpressionParseException.ExpressionTextField(client only) uses vanillaMultilineTextFieldas the text model, and draws it with a color and background per character, a selection, a blinking caret, an underline for parse errors and a scrollbar, since vanilla's edit boxes can only draw a single color. It is kept in the container, so it survives the panel being recreated when navigating.ExpressionPrinter.tokenizeExactgives the text as tokens with the path of their node, which maps each character to its node for colors and for navigating from the caret.ExpressionHistoryrecords changes by observing the expression while rendering, so no action needs to know about it.ContainerScreenLogicProgrammerBaseforwards scrolling, dragging and releasing to the panel, since CyclopsCore sub-GUIs only get clicks and keys.true,NaNor1x.Book: new "Expressions" page after "Logic Programming" in the manual, covering slots, nesting, operator values, the panel, parameters and editing as text. Tutorial advancements and their criteria are untouched.
Also fixed: pasting onto panels with the Wrench
Not specific to expressions, found while testing card copies: since #1721, right-clicking a Display Panel or Dynamic Light Panel with a Wrench in a config mode rotated the panel instead of pasting the configuration, because
PartTypePanelVariableDriven.onPartActivatedhandled every wrench before the generic part code. A Wrench in a config mode now pastes; in the other modes it still rotates. Covered byGameTestsWrenchConfig.testWrenchConfigPasteOntoPanel, which fails without the fix. This is a separate commit, so it can be split out into its own PR if you prefer.Found, not fixed here
OperatorBuilders.FLUIDSTACK_3is built fromITEMSTACK, so the fluidwith_dataoperator has the unique nameitemstack_withdataof the item one, and both share a translation key. Everything that looks operators up by unique name (operator values, the card codec, LP elements, expression trees) gets the fluid operator, so an itemwith_dataoperator turns into the fluid one after a save and load. It is the only duplicate unique name. Fixing it changes which operator existingitemstack_withdatadata loads as, so I left it for a separate change.OperatorNamesuses global names to recognize registered operators, so it does not depend on this.Deviations from the plan, and why
logic_programmer.pngis untouched.#limit, not"limit". Quotes are used for string constants, so a quoted label would be ambiguous.Behavior changes players will notice
Things to decide on
#123for any ID. Evaluation still needs that variable in the same network, so I don't think this gives access to anything new, but you should decide on it explicitly. Requiring the card in the inventory for every#idon the server would be easy to add, but it would also reject loading and re-applying a card whose referenced cards are elsewhere.Known limitations
map(x -> x * 2, l)cannot be written as such. It needs a second card, or partial application inside the slot, as inmap(apply((*), 2), l).#5 + 1counts as Any there, and type errors involving it only show in the network.API additions for add-on authors
org.cyclops.integrateddynamics.api.evaluate.expression.tree:Expression,ExpressionNode(sealed records),ExpressionParameter.IExpressionVariableFacade(getExpression(),getReferenceTypes()with a default), handlerExpressionVariableFacadeHandlerwith type keyintegrateddynamics:expression.ReferencePlaceholderVariableFacadewith handlerintegrateddynamics:reference_placeholder, only used in LP slots (phantom, cannot be taken out). In a network it resolves to the card with the same id.ExpressionOperator(newIOperatorimplementation) with operator serializerintegrateddynamics:expression.ExpressionTrees(serialization, paths, queries),ExpressionTypeChecker(including the inferred type of every node),ExpressionPrinter(includingprintExactandtokenizeExact),ExpressionParserwithExpressionParseException,OperatorNames,ExpressionEvaluator.ExpressionEditor(including thesetaction andisEditable),OperatorLPElement.getEditor()/applyExpressionAction(...)/getCurrentOperator()/findReferencedCards(Player)/setExpression(...),ContainerLogicProgrammerBase.applyExpressionAction,refreshElementInventory,setExpressionSlotFilter, layout constantsEXPRESSION_PANEL_*,ContainerScreenLogicProgrammerBase.isExpressionPanelOpen(), andRenderPattern.getRenderSymbol().LabelsWorldStorage.getVariableIds(label).IOperatorVariableFacadeis unchanged and expression facades do not implement it, so add-ons reading it see exactly the cards they saw before.Testing
./gradlew build: passes, 983 unit tests. New:TestExpressionTrees(55 tests: serialization round trip, 60-level tree and its binary size, too-deep and invalid-index rejection, paths, compatibility rule, evaluation equivalence with aLazyExpressionchain, invalidation, parameter binding,apply/filter/map/reduce/pipe/flip/currying, lazychoice, operator serialization, materialization including insidepipe, type inference and error positions, the card's type with and without stored reference types, printer output including shared names told apart by type and former LP symbols written as calls; and more complex cases: three parameters with partial application,apply_2andflip, higher-order parameters, expression operators as inline values and as refs,map(apply((*), 2), l)as an inline doubler, nested materialization with live and frozen refs, a 30-level parameter chain, a parameter used in several subtrees, and an evaluation error deep in the tree).TestExpressionEditor(11 tests: actions, parameter type inference including Number, picking operators as values, cycling, value parsing, rejection of bad actions, the set action and what cannot be edited).TestOperatorNames(4 tests, including that every registered operator gets an identifier name that resolves back to it, with and without a known first argument type).TestExpressionParser(12 tests: every editable operator, more than 250, prints and parses back to itself; the display form also parses; values, including NBT and a curried operator, and refs and labels print and parse back; precedence, parameters, type-dependent names, literals and errors with positions; depth and node limits). Breaking theLsuffix on purpose makes two of them fail.TestExpressionHistory(4 tests) andTestExpressionTextField(5 tests, for the colors kept around typed text)../gradlew runGameTestServer: all 1054 pass. NewGameTestsExpressions: ref + inline constants on a Display Panel, equality with the equivalent classic card chain,item -> size(nbt(item)) > 1used by a classicfiltercard over an Inventory Reader list,applyof an expression operator with a ref, an expression operator on a Display Panel (serialized and evaluated), card data save/load, a missing reference, the LP compatibility rule and loading old/new cards, a type error position in the LP,l -> reduce(#add, map(#double, l), 0)where#addand#doubleare expression cards in the same network, three parameters partially applied throughapply, a Display Panel following a changed referenced card, two expression cards referencing each other and one referencing itself (validation error, no crash), reference types surviving an LP write and load, copying expression cards with and without parameters in a crafting grid, pasting one onto another Display Panel with the Wrench (the copies get a new id, keep the expression and reference types, and evaluate like the original), applying(#id + 3) * 2as text to an LP on>(Arithmetic Multiplication becomes active, a const root is ignored, and the written card shows 36 on a Display Panel), and applying text keeping the open operator. I confirmed the new class actually runs by breaking one assertion once and seeing it fail.item -> size(nbt(item)) > 3from scratch, changed a parameter type to cause a nested type error, cancelled with Escape, entered an invalid value, wrote the card, and hid and reopened the panel. Built(x, y, z) -> x > y ? x * z : y - z,l -> reduce((+), map((stringLength), l), 0)(checked the written card's tooltip:List -> Integer) andx -> x > 1 ? x * 2 : x(x inferred as Number), and caused a branch type mismatch inchoiceto check that its message is shown. Built#greeting == (#greeting + "!")with a String card, navigated in and out (the actual card stays), wrote it, and loaded it back with the card in the inventory (actual card shown) and with the card moved to a chest (placeholder with id and type shown). On an earlier UI revision (the network side is unchanged since): builtfilter(#bignbt, #items)(written as a classic card) andlength(filter(#bignbt, #1))in a real network with an Inventory Reader, Variable Store and two Display Panels, reloaded the world (both panels kept[Sword]and1), used the Labeller, made an item constant in the Portable LP, and checked that the List and Recipe elements are unchanged. Builtx -> getInteger(nbt(x), "key"), which showsitemstackNbtwhile the nested slot is empty andnbtoncexis in it. Copied the(x, y, z)card in a crafting table (the card left in the grid gets a new id, both keep the expression), and copied a Display Panel holding it onto another one with the Wrench (pasted with a new id, after the panel fix above).Integer -> String -> String), a wrapped type, a slot error, and the full message on hoverenot closing it either(a: integer) -> a + #5, which switched to Arithmetic Addition with the real#5card in its slot and rewrote the carda -> a + #5asInteger -> Integer(it saidInteger -> Anybefore the reference types were used), and#5 + 1asIntegerlength,concat) and string literalsScreen.hasControlDown()andScreen.hasShiftDown(), so I used the buttons instead.item -> length(name(item)) > #thresholdin an Item Importer's Import Items Predicate aspect, with the threshold an Integer card in a Variable Store. With single items, Diamond, Cobblestone and Oak Planks were imported and Apple, Stick and Egg stayed. With the threshold card taken out of the network, the importer showed "Variable with id 5 could not be found within the current network" and an added Emerald stayed; after putting the card back, the Emerald was imported. With stacks, note that ID'snameof an item stack includes the count ("Apple (10)"), which is existing behavior. Screenshots, taken before the panel changes: https://claude.ai/artifact/WZMMY6VcHoVUWMW4d27N42 (a private page owned by @rubensworks; GitHub's API does not accept image uploads, so share or copy them from there).Issues
Closes #194, closes #546, closes #860, closes #1112, closes #1417, closes #1495, closes #367 (these are also in the commit message). Partially addresses #193, #216, #527, #804, #930, #1293 (type shown before writing, errors pointing at the slot) and #43, #906, #1326, #525, #1130 (fewer intermediate cards).
🤖 Generated with Claude Code
https://claude.ai/code/session_01MHtucHo4Qy2pTfBMgeE54y