IEC standard library as a learning reference with LLM auto-fix
What it’s about
PLC programmers face two very different learning paths even
today. Beginners want to understand how a timer actually
works (what is IN, what is PT, why do I have to declare
an instance?). Professionals want the exact spec conformance
(which operators does matiec accept? how does MOD behave on
negative operands?). And an LLM assistant wants to translate
a compiler error message into an executable fix without
guessing.
forgeiec.io now hosts an IEC 61131-3 reference that serves all three audiences in parallel.
What’s live in the first wave
As of 2026-05-14:
- Structured layout: 3 language families as top-level (Textual — ST + IL, Graphical — LD + FBD, Structured — SFC) plus Common Elements and Standard Library.
- ST control flow documented in full:
IF/ELSIF/ELSE,CASE. - Function & FB calls, two deep-dive pages: FB instances vs. function calls and Positional vs. named parameters.
- Standard library: first complete function block TON — timer-on-delay with pin layout, examples, semantics and a row of auto-fix signals.
- Literals: Integer & hexadecimal including the two matiec quirks that cost us the morning of 2026-05-14 in the Ackersteuerung project.
The auto-fix block schema
Every page carries an llm_signals[] block in its front-matter:
llm_signals:
- error_pattern: "<substring or regex from compiler stderr>"
where: FStCompiler | matiec | gcc | linker | runtime
diagnosis: "<why this fires>"
fix_strategy: "<what to do>"
fix_example: |
(* before *) ...
(* after *) ...
A consuming LLM (the ForgeIEC MCP forge.help_for tool, the
chat-tab assistant, or an external IDE plug-in) walks the
pages, collects the error_pattern strings, and matches them
against the live compiler output. On a match, the page’s
fix_strategy + fix_example is the actionable answer. That
turns “error XYZ in POU PLC_PRG, line 17” into a concrete
set_text_body call — without guessing.
The where field constrains the matcher to a compiler layer
(local ST front-end, matiec, g++ on the PLC, linker, runtime).
The same construct can therefore appear with different fix
strategies depending on which layer fired the error.
Real quirks from real projects
The first nine documented error_pattern entries all come
from the 2026-05-14 Ackersteuerung cleanup — none of them
are invented. Highlights:
Unexpected token: '01'for16#xxinCASElabels — matiec’s lexer parses16#as a type prefix and chokes on the hex digits. Workaround: decimal labels.'BYTE' has no member named 'BYTE'during g++ compile on the PLC — matiec emitsdata__->BYTEinstead of an integer literal cast forSHL(BYTE#1, …). Workaround: pre-compute the value or useINT_TO_BYTE(1).is a Function Block — declare instance first— the classic LLM trap of callingTON(IN:=x, PT:=t)directly. Fix: declare an instance, then call the instance.
What’s coming next
The reference sprint runs over multiple sessions. Roadmap:
- More standard FBs (CTU, R_TRIG, RS, TOF, TP, …) following the TON template.
- Operator tables (arithmetic / bitwise / comparison / assignment + precedence).
- Iteration (
FOR,WHILE,REPEAT). - Common Elements: data types + variable scopes including ForgeIEC’s Anvil/Bellows/GVL specifics.
- Variables & references: Anvil/Bellows namespacing, pool resolver rules.
If a construct you need is missing, say so and it jumps to the top of the list.