The command window — the command engine for a user#
The command window is the text way into ICoreBlocks. One line typed at its
prompt can be a calculator expression over matrices and transfer functions
(y = x*[1 2; 3 4]), a recipe statement that creates, wires or configures a
block on the open diagram (g = block(Gain)), or a named command
(modelConfig, templates). The same evaluator runs a line
headlessly: ICoreBlocks --console "<line>" prints the result and exits 0 or 1.
This page explains the engine — how a line is read, where variables live, what
Tab and the arrow keys do, and how the console reaches the canvas. The roster of
what you can type is Command glossary — console commands, verbs, functions; the recipe language, .icore
files and templates are on Recipes and templates — recording, replaying and reusing diagrams; words such as handle,
recipe and subsystem are defined in User glossary — the vocabulary of using ICoreBlocks.
Where it is#
- In the editor: the left panel page Quick Code (menu Code Engine →
Scripting → Quick Code, or the panel strip). It is a transcript on top, a
status row (
A- 13pt A+text-size control,● ready/● busy— or● for … end (block open, 1 deep)while afor/if/while/switch/function/tryblock typed over several lines is waiting for itsend; the transcript echoes those held lines with...instead of>>>) and the prompt below, placeholder textEnter Command.Ctrl+C— orEsc— abandons whatever is open: the held lines are dropped without running, and the transcript says which of the three things went (an open block, a...continuation, or a%{ … %}comment). It is the only way out of a block typed wrong, because typingendto close one would run it. With nothing open it just clears the half-typed line;Ctrl+Cis left alone as Copy whenever the prompt has a selection, so the shortcut never costs you a copy. The transcript is also reachable as View → Loggers → Command History. - Headless:
ICoreBlocks --console "<line>". The line runs 1.5 s after startup through the very same function the panel calls (ICoreConsoleInterpreter::evaluateLine, wired insrc/ICoreBlocks/Shell/Application.cpp); output goes to stdout on success, stderr on failure, and the process exit code is 0/1 by the line's verdict. A bare--consolewith no line never exits. - The ICore Script IDE (Code Engine → Scripting → ICore Script IDE) feeds a whole
.icorefile to the same evaluator one line at a time — see below.
The rules the engine enforces#
- A line that starts with a block keyword is held, not run —
if,for,while,switch,functionandtryopen a block; the prompt (and the ICore Script IDE) buffers every line up to theendthat closes the outermost block, then hands the whole block over at once.endwith nothing open is an error. A one-line block (if 1, x = 2, end) is complete on its own and needs no buffering — it is the same block, and runs the same way. if,forandwhilerun. A condition holds when it is non-empty and every element is non-zero, which is MATLAB's rule and a rule about numbers rather than about a true/false type:if [1 1]runs its body andif [1 0]does not.for name = exprwalks the columns ofexpr, so a range gives one number per pass, a matrix gives one column per pass, and the loop variable keeps its last value when the loop ends.breakandcontinuebelong to the innermostfor/whilebody;returnends the running script without failing it, and does nothing at a prompt because there is nothing to end.- A
whileloop has a ceiling of 200,000 passes. MATLAB does not need one because you can interrupt it; this console runs your line on the one thread it draws with, so a runaway loop would take the window with it. On reaching the ceiling the loop stops and says so — and that stop is the one failuretrycannot catch, because atrythat swallowed it would hand the runaway straight back. - A loop saves the project once, not once per pass. Assigning a variable
tells the editor the variables space changed, and the editor answers by
autosaving — which is right for a line you typed and was the entire cost of a
loop when it happened on every pass: the whole project, recipe and sidecars
and every
.ini, written to disk twice per iteration. Since 2026-09-03 a block and a script hold that one notification and send it when they finish. Nothing you can observe moved — the value is in the store the instant it is assigned, the Variables panel still refreshes on every write, and a block that fails still saves the writes it made before it failed. What changed is the clock: see (g) below. switchruns.switch expr … case value … otherwise … endtakes the first arm that matches and does not fall through to the next one. The expression is a single number or a string — a matrix is refused, as MATLAB refuses it — and acasematches a number by equality and a string by its text.case {1, 2}is a list of alternatives and matches if any of them does; the braces are syntax here, not a value, because the console has no cell arrays. Abreakinside a switch belongs to the loop around it: the switch does not consume one.try … catch … endruns. The body's failure is caught, whatever it printed before failing is kept, and the statements after the failing one never run. Atrywith nocatcharm swallows the failure and carries on.catch errbinds the failure toerr, and because this console has no struct type an exception is three names rather than one object:erris the message,err.messageis the same text under MATLAB's own spelling, anderr.identifieris the identifier anerror(id, msg)carried (empty when the console itself raised the failure).- A script can define functions.
function y = twice(x) … endregisters a name for the session, and the name is then callable anywhere an expression is:twice(21),z = twice(4) + 1,[s, d] = pair(7, 2). Definitions are registered before the script runs, so a call may sit above the definition — which is the only order MATLAB allows in a script, and the reason a MATLAB script's shape works here unchanged.- A function has its own workspace. It sees the arguments it was passed
and the names it assigns, and nothing else: not the caller's variables,
not the prompt's. Assigning inside it changes nothing outside.
narginandnargoutsay how many arguments came in and how many values the caller asked for. - An output the body never assigned is an error naming that output; too many
arguments in or out is an error naming the count; and
returninside a function returns from the function, leaving the script running. - Two limits are this console's rather than MATLAB's. A call may nest 200 deep and then stops and says so — the same one-thread argument as the loop ceiling. And a function may not take the name of a console command, which is refused when you define it rather than leaving a name that can never be reached.
- A function has its own workspace. It sees the arguments it was passed
and the names it assigns, and nothing else: not the caller's variables,
not the prompt's. Assigning inside it changes nothing outside.
[]is the empty matrix, and there is more than one of them.[]itself is 0×0;zeros(0, 3)is 0×3 andones(3, 0)is 3×0, andsizeis how you tell them apart — every empty prints as[].isempty(x)asks the question. An empty drops out of a concatenation ([[], 5]is5), an element-wise operation with a scalar answers an empty, and MATLAB's own answers for a 0×0 do not agree with each other:sum([])is 0,prod([])is 1,mean([])is NaN,max([])is[],any([])is false andall([])is true.det([])is 1. Those are measured, not derived.- A condition that is empty is false — MATLAB's truth test is "non-empty
and every element non-zero", so
if []does not run its body. - An index that selects nothing answers an empty rather than failing, and
which empty depends on how you asked:
v([])is 0×0 whilev(v > 9)on a row vector is 1×0. A subscript of0is still an error, as it is in MATLAB — an empty subscript is how "select nothing" is written. A(...) = []deletes.A(:) = []leaves 0×0; deleting every column of a 2×2 leaves 2×0.
- A condition that is empty is false — MATLAB's truth test is "non-empty
and every element non-zero", so
error,warningandassertare statements.error("boom")fails the line with that message;error("Comp:id", "boom")also sets the identifier acatchcan read;warning(...)prints beside the statement and carries on;assert(cond)does nothing when the condition holds and fails withAssertion failed.when it does not, or with your own message as a second argument. The forms that substitute arguments into a format string (error("x is %d", n)) are refused by name until the console hassprintf— a message that printed its own format string would be the one answer that cannot be right.warning('off', id)is refused too: it switches a warning state this console does not keep.- Some MATLAB names are refused on purpose, each saying why, and ten of
them because of how this console runs.
pause,input,keyboardanddbstop— there is no interactive input, and the console runs your line on the thread it draws with.eval,evalin,evalcandassignin— code held in a string cannot be translated to MATLAB and back, which every console line must be.globalandpersistent— a script-defined function gets its own workspace and no way to share one. They are names and not reserved words:input = [7 8 9]defines a variable andinput(2)then indexes it, which is the same shadowing rule that lets a variable win over a function. - A line is split into statements on top-level
;and,— a separator inside(...),[...]or<...>belongs to the literal or the call, which is what lets[1 2; 3 4]stay one matrix literal andkron(A, B)stay one call. Statements run in order and stop at the first failure.,is the one exception to "a command name is never special": a line whose first word is a registered command keeps its commas, because a command's arguments are free text (fromMatlab a = 1, b = 2hands the importer MATLAB whose commas are the point).;splits a command line as it always has. - A statement ending in
;is silent when it succeeds; one ending in,is not (MATLAB's rule).a = 1, b = 2prints both,a = 1; b = 2prints one. Failures are always printed; silencing never hides an error. %starts a comment, to the end of the line — MATLAB's spelling, cut before the statement is read, sox = 5 % fiveassigns 5. The console's own#is whole-line only:#is already a value in console text — a#rrggbbcolour insetColor, a#12in a command's free-text argument — and a mid-line rule would eat both. A%inside"..."or inside brackets is not a comment either, sosetText(50% done)keeps its text....continues a statement onto the next line, and%{…%}comments out a run of them — both are rules about physical lines, so they work wherever lines arrive one at a time: the prompt (which shows...while the statement is unfinished), a.icore, and the ICore Script IDE. The two markers of a block comment must stand alone on their lines, as they do in MATLAB. A script that ends mid-continuation, or with a block comment still open, fails — it dropped a statement on the floor, and reporting success would hide that.[ ... ]holds expressions, and whitespace separates them. A bracket that is one of the three literal spellings the console accepts (MATLAB[1 2; 3 4], Python[[1, 2], [3, 4]], C++{{1, 2}, {3, 4}}) is still a literal and behaves exactly as it always did. Anything else is a concatenation: elements fold side by side,;and a newline stack rows, and an element may be any expression ([A, A],[sin(0) cos(0)],[x x+1]). Inside a bracket the whitespace rule is MATLAB's, and it is the one thing worth reading twice: a space before a unary sign with nothing after the sign starts a new element, a space on both sides keeps one going, and an element that ends in a binary operator is unfinished. So[1 -2]is two elements,[1 - 2]is one (and is −1),[1+ 2]is one (and is 3), and[1 -2 + 3]is two. An unclosed[holds the line the way...does, so a bracket may span lines with each line a row.- The operators are MATLAB's, and so is their precedence. Arithmetic
(
+ - * / \ ^and the element-wise.* ./ .\ .^), the six comparisons (== ~= < > <= >=), the logical operators (& | ~and the short-circuit&& ||), the rangea:b/a:s:b, and transpose (',.'). Loosest first:||,&&,|,&, the comparisons,:,+ -,* /, unary+ - ~, then^— so1:2+3is1:5,0 | 1 & 0is 0, and~1 + 1is 1. A comparison answers 1 or 0 per element ([1 2 3] >= 2is[0, 1, 1]),==is exact (0.1 + 0.2 == 0.3is 0), and&&/||take one number each and never evaluate their right side once the left has decided it. |and&are logical OR and AND — they used to be concatenation. Before 2026-09-02 this console spelled "side by side"A | Band "stacked"A & B. Those spellings now mean what they mean in MATLAB, and the bracket is the only concatenation: write[A, B]and[A; B]. A script written against the old meaning keeps running and silently computes a logical, so it is worth grepping an old.icorefor|and&.A(i),A(i, j),A(:, j),A(v),A(mask)andA(:)index a variable, andendis the last position inside a subscript (v(end - 1),A(end, :),A(2:end, 1)). One subscript counts down the COLUMNS, soA(3)on a 2×2 is row 1 of column 2 andA(:)is every entry as a column — MATLAB's order, whatever the matrix stores internally. A variable shadows a function of the same name, sosum = [7 8 9]; sum(2)is- An index that selects nothing answers an empty matrix rather than
failing:
v = [3 4 5]; v(v > 9)is[].
- An index that selects nothing answers an empty matrix rather than
failing:
A(i, j) = xwrites into a variable,A(:, 2) = []deletes. A position past the end GROWS the array and fills the gap with zeros —x = [1 2 3]; x(5) = 7leaves[1, 2, 3, 0, 7], and a name that does not exist yet is built the same way. A scalar right-hand side fills every selected entry (A(A > 2) = 0), and one with several entries fills them in order. Deleting takes one non-colon subscript:A(:, 2) = []drops a column,A(1, :) = []a row.sum,mean,maxandminreduce down the COLUMNS, as they do in MATLAB:sum(A)on a matrix is a row of column sums, on a vector a single number.sum(A, 2)goes the other way,sum(A, "all")adds every element — which is whatsum(A)itself meant on this console before 2026-09-02 — andsum(A, "omitnan")skips a NaN where the default lets one poison its column.maxandmintake the same forms with MATLAB's placeholder (max(A, [], 2)), read a second array element-wise (max(A, B)), and hand back where the winner was as a second value ([m, i] = max(A)). They also IGNORE a NaN, wheresumandmeanpropagate one — that difference is MATLAB's, per function rather than per family. The console's own axis names (sumrows,sumcols,meanrows, …) still work and still mean what they say.ansholds the last unassigned result. A bare expression sets it, a suppressed one included (6 * 7;setsansto 42 and prints nothing); an assignment does not, because it named where its value goes, and neither does reading a variable. It is an ordinary entry in the variables space: it shows in the Variables panel andans = 9overwrites it. Reading a constant by name (pi) does set it, because that is a call rather than a variable read.- Each statement is classified in this order, first match wins:
- a recipe statement —
block(...),subsystem(...),connect(...),area/image/textbox(...),plot/semilogx/semilogy/loglog(...)and the other chart kinds (bar,stem,scatter, …), the figure verbs (figure,hold on,xlabel(...),subplot(...), …) in either of MATLAB's spellings,selectionModel(),clearDiagram(...),handle.method(...), or a bare handle name; - an indexed assignment
name(subscripts) = rhs, including thename(subscripts) = []deletion; - an assignment
name = rhs— the=that assigns, never the one inside== <= >= ~=; - a bare name that is a defined variable (echoes it), or — failing that — a name the math grammar itself knows, which today means the seven constants below;
- an expression — a number, a
[...]literal, or anything carrying an operator (+ - * / \ ^ < > = ~ : | & ' ( )); - a registered command: first word is the command, the rest are its
arguments;
name()is accepted asname. Nothing matched →unknown command: <first word>and the line fails.
- a recipe statement —
- Seven constants are readable by name:
pi,Inf(alsoinf),NaN(alsonan),eps,realmax,realminandflintmax.InfandNaNalso take a size —Inf(3)is 3×3,Inf(2, 3)is 2×3 — andeps(x)is the distance fromxto the next double, soeps(100)is1.42e-14whileepsalone is2.22e-16. They are looked up last, after the command engine and the variables space, sopi = 3shadows the constant until you clear it — MATLAB's own rule. - A command name is never shadowed. A variable named like a command is not
echoed by its bare name, and an expression whose first word is a command
(
reverse a+b,clear()) is dispatched as that command. - A block handle beats a variable of the same name in
name.method(...); a name that is only a variable falls through to the math grammar (h.values(),G.step(10)), which is how time series and transfer functions get their members. - Results are annotated with their type —
7 # Integer,0.333333 # Double,[[3, 6], [9, 12]] # Matrix of Double, and likewise# Logical,# Matrix of Logical,# Polynomial,# Transfer Function,# State Space,# Zero Pole Gain,# Struct,# Time Series,# Sym,# Curve Fit,# String. The annotation is the variable system's real type name; a scalar whose value is whole prints asInteger. A logical prints its numbers and says its class in the tag, which is how1 < 2(a logical 1) is told apart from1(a double). InfandNaNare spelled MATLAB's way, in the echo, inside a matrix and in the variables panel alike.- Only one line runs at a time. While a line runs the prompt is disabled and
the status shows
● busy; a second line cannot slip in underneath.
The variables space#
- Create:
x = 3,A = [1 2; 3 4],label = hello,G = tf([1],[1,2,1]). The right-hand side is either a literal (stored as typed), a bare name (copies an existing variable, else a constant such aspi, else stores the word as a string), or an expression (evaluated, then stored). The reply echoes the stored value:x = 3 # Integer. - Types are derived from the stored text:
Integer,Double,Matrix of Double,Logical,Matrix of Logical,String,Transfer Function,State Space,Zero Pole Gain,Polynomial,Struct,Time Series,Sym,Curve Fit. ASymis a symbolic expression — whatsyms xdeclares and whatx^2 + 1then is: the expression itself, carried rather than evaluated, so it can be differentiated, integrated, factored and substituted into. It becomes a number only when you ask (double(f),vpa(f)), and everything it answers is on Symbolic math in the command window. It survives the variables space as its own text inside asym(...)call, which is what tells it apart from a string holding the same characters. AStructis MATLAB's scalar struct as this console admits it: a fixed, ordered, named list of numbers, read asr.fieldname— MATLAB's own spelling, and the one member access here that takes no parentheses. It is what[x, fval, exitflag, output] = fminunc(...)answers in its fourth place. There is no field assignment and no nesting: a record is read, not built. A field MATLAB carries as TEXT —output.algorithm,output.message— refuses by name and says so, rather than being quietly absent;output.exitflagis the same verdict as a number. ACurve Fitis whatfit(x, y, model)answers: the fitted model itself, printed the way MATLAB prints acfit— the model's name, its formula, and each coefficient with its confidence bounds. It is CALLABLE, sof(3)evaluates the fit at 3, andcoeffvalues(f),confint(f)andf.p1read it apart. Like the other model kinds it survives the variables space as its own text; Curve fitting in the command window is the page for what it can do. Matrices print in Python list form; the stored form is MATLAB syntax. A logical is stored as the WORDS —true,[true false], which is whatmat2strwrites for one — and that is what carries its class from one line to the next: a stored[1 0]would read back as an ordinary double row. format longshows more digits,format short(the default) fewer. It is display only: the value never changes, sopi == 3.141592653589793is true in both.- Read: the bare name echoes it; use it in any expression.
- List:
whoprints the names in the store, andwhosadds each one's size, byte count and class. The left panel page Variables is the table view of the same store. - Clear:
clear(orclearvars) removes variables —clear x yonly those two — andclearVariablesSpaceempties the store;clearAllalso wipes the logs and the recipe handles. - One store, project-wide. There is exactly one variables space; blocks in
every subsystem see it. Its variables are saved with the project: the
.iprojfile is recipe text and its first lines arename = value;declarations (generateRecipeshows exactly what will be written; see the run below). A headless--consoleprocess starts with an empty store and an unsaved project, so nothing carries from one invocation to the next. - A block can read a console variable. Type a variable's name into a
block parameter (
Gain Value=K). When the run starts the block's configuration is loaded and every parameter whose text is not itself a number or matrix is looked up by name in the variables space; a numeric hit supplies the matrix, a miss leaves the text as a string (ICoreBlockSolverEnvironment::loadBlockConfig,src/ICoreBlocks/ICoreModel/SolverEnvironments/). The lookup happens at run start, so changeKand re-run; the block does not track it live. v = handle.get(prop)/v = handle.getConfig(name)store a numeric snapshot as an ordinary variable — later block edits do not updatev, and text properties refuse to bind.
true and false#
The console has MATLAB's logical class, and it is a class rather than a different number: a logical array holds ones and zeros like any other, and what differs is what it reports and what the operations that read a class do with it.
- What answers a logical: every comparison (
1 < 2), every connective (&,|,~,xor),any/all, everyis*question (isnan,isempty,isequal,ischar, …), thestrcmpfamily,contains,startsWith,endsWith, and the constantstrueandfalse— including their sized formstrue(n),true(m, n),false(m, n). - What leaves it behind: arithmetic.
true + 1is the double 2,-trueis -1, andsum([true true])is 2 — MATLAB does not carry the class through a sum, and neither does this console. - A bracket stays logical only when every element is one:
[true false]is a logical row,[true 1]a double one. logical(A)converts — every non-zero entry becomes 1 — and refuses a NaN rather than converting it, which is MATLAB's own rule and its own sentence.double(A)is the way back.islogical(x)asks, andclass(x)answerslogical.- MATLAB does not count a logical as numeric:
isnumeric(true)is 0, and so isisa(true, "numeric").
Strings#
The console has one string kind and it is a MATLAB char array — not MATLAB's
newer string object, and not a free-text blob. Everything below was checked
against MATLAB R2026a rather than reasoned about.
- Both literal spellings work:
'hello'and"hello". Inside a'...'literal,''is one quote ('it''s'), and there are no backslash escapes —sprintfis what turns\tinto a tab, exactly as in MATLAB. - A
'transposes only when it continues a value with no space before it.a',[1 2]',f(x)'are transposes;'abc',f('abc'),x = 'abc'are literals. That is MATLAB's own rule, and it is why['a' 'b']isab(two literals) while[a' b']is two transposes. MATLAB reads[a ']as an unterminated character vector, and so does this console. - Arithmetic on a string is arithmetic on its code points:
'a' + 1is 98,double('abc')is[97 98 99],-'a'is -97. To join text use[s1 s2]orstrcat(...)— and they differ:strcatdrops each argument's trailing whitespace (strcat('a ', 'b')isab) while the bracket keeps it (['a ' 'b']isa b). A number in a bracket beside a string becomes a character:['ab' 66]isabB. - Size:
length,numelandstrlengthall answer the character count,size('abc')is[1 3], andisempty('')is 1.
What is here, by name:
| format | sprintf(fmt, ...) |
| number ↔ text | num2str int2str mat2str str2num str2double string char double |
| join and compare | strcat · strcmp strcmpi strncmp strncmpi |
| case and trimming | upper lower strtrim deblank blanks newline |
| search and replace | strfind strrep replace contains startsWith endsWith extractBefore extractAfter |
| size | length numel strlength isempty size |
| patterns | regexp regexpi regexprep |
| printing | disp display fprintf |
Three behaviours are worth knowing before they surprise you, because each is MATLAB's and each is the opposite of the obvious rule:
sprintf's format cycles until the arguments run out and then stops at the conversion it cannot fill, keeping the text before it:sprintf('%d-%d,', [1 2 3])is1-2,3-. An integer conversion handed a non-integer switches to%e:sprintf('%d', 1.5)is1.500000e+00.strrepandreplacedisagree on overlapping matches, and neither is wrong:strrep('aaaa','aa','b')isbbb(it fires at every positionstrfindreports),replace('aaaa','aa','b')isbb(it scans past what it matched).num2strpicks its field width from the data, sonum2str([1 2.5])is1followed by nine spaces and2.5. Usemat2strwhen you want text that reads back as the value — its default is 15 significant digits wherenum2str's is five.
Regular expressions use ECMAScript syntax, not MATLAB's own. The common
subset — classes, quantifiers, anchors, groups, alternation, \d \w \s, and
$1 in a replacement — means the same thing in both; $0 (the whole match)
is translated for you. A pattern this engine cannot read is refused with its
reason rather than matched differently. regexp(s, pat) gives the start of
every match, "end" the ends, "once" the first, and "once", "match" the
matched text; the modes that answer a list of every match need a cell array,
which this console does not have.
fprintf prints exactly what sprintf builds — the same format, the same
cycling — so anything true of one is true of the other; the difference is that
sprintf hands you the text and fprintf writes it. fprintf(1, ...) and
fprintf(2, ...) are MATLAB's stdout and stderr and are one stream here. In
MATLAB fprintf also returns a byte count; here it returns nothing, so
n = fprintf(...) is refused — sprintf is what answers.
Not here, and each refuses by name rather than approximating: strsplit,
split, strjoin and join (they need a cell array or a string array, and
this console has neither); regexp's "match" and "tokens" modes without
"once", for the same reason; num2str, char or double of something that
would be a char matrix; and the date family.
Things that surprise users#
(More symptoms, beyond the console, are collected on Troubleshooting — the messages you will meet and what to do.)
unknown command: xfor a variable you just made — each--consoleprocess is fresh, andclearVariablesSpace/clearAllempty the store; a bare name that is not a variable, a handle, a constant or a command falls all the way through to the command dispatcher, whose message this is. Same cause for a typo in a variable name.- A variable named
pireally does hide the constant — the seven constants below are looked up after the command engine and the variables space, sopi = 3makespithree until you clear it. That is MATLAB's rule, not an accident. 2 ^ 3 ^ 2is 64, not 512 —^is left-associative here, as it is in MATLAB and unlike most languages, so it reads(2^3)^2. In the same family:-2^2is-(2^2)= −4, because unary minus binds looser than^, while the exponent may still carry its own sign (2^-1is 0.5). Bounds and precision of the evaluator: Numerics — what the solver will and will not do.A^0.5andA^Bare refused on matrices — MATLAB answers those with a matrix function (sqrtm,funm); the console says so rather than approximating. Writesqrtm(A).A^nfor a squareAand an integern— including a negative one, which goes through the inverse — is fine.- A chained line printed only its last result — every statement you closed
with
;was silent by design; only the unterminated tail speaks. - A failing statement stops the rest of the line — statements after the
failing one do not run; the exit code is 1. Dividing by zero is not one of
those failures:
1/0isInf,0/0isNaN, exactly as in MATLAB. - A variable named like a command will not echo —
clear,help,echoand every other registered name are dispatched as commands first; such a variable is reachable only inside an expression. Pick another name. - Tab completed nothing — completion only offers at a statement head, right
after
=, or right after a.(ICoreCompletion::tokenStart); mid-argument it stays quiet, and it completes names from the glossary only (commands, matrix functions, recipe verbs) — not your variables, not block types. - The suggestion popup ate my Enter — with the popup open, Enter on an entry you arrowed onto takes the entry; Enter with nothing highlighted closes the list and submits the line exactly as typed.
- Up arrow recalls last week's commands — history is on disk (below).
- The
.iprojgrew aK = 2.5;line — that is the variables space being saved with the project. It is intended.
Tab, the popup, and history#
- Tab completes the current token from the glossary (case-insensitive prefix match): the longest common prefix is filled in, and if more than one name is still open the popup lists them (at most 8 visible, scrolling). If an entry is already highlighted, Tab takes it exactly as Enter does.
- The popup appears as you type at a completable position and filters by prefix. Up/Down move inside it; Enter takes the highlighted entry; clicking an entry does the same.
- Up/Down with the popup closed walk the submitted-line history (newest first); the half-typed draft is kept and comes back at the bottom.
- History is persisted in
commandHistory.txtinside the app's data folder (on macOS~/Library/Application Support/ICore Blocks/), appended on every submit — a crash loses nothing. Settings → Editor → Command Console → Command History Kept sets how many lines Up can reach (default 500, max 5000; 0 stops recording without erasing).clearCommandHistorydeletes the file and empties the in-memory list. glossaryprints the full catalog the popup draws from;helplists the registered commands only.help <name>describes one name — command, function, keyword or recipe verb — and, for a function MATLAB keeps in one of its toolboxes, says which toolbox the name comes from. That line is worth reading before you copy a call into MATLAB: it is what tells you the other side needs a licence for it. When it ends inextra, the name is this console's own rather than MATLAB's and keeps its own arguments, so a call written for MATLAB is not the same call:``` > help pole pole - function pole(G|sys) Poles of a transfer function or state-space model toolbox: Control System Toolbox
> help butterlp butterlp - function butterlp(order,cutoffHz,fsHz) Discrete Butterworth lowpass transfer function toolbox: Signal Processing Toolbox (extra: the console's own name, not MATLAB's -- it keeps its own signature, so a call written for MATLAB is not this one) ```
Most names have no toolbox line at all: they are core MATLAB, which needs no licence beyond MATLAB itself.
- Ctrl+wheel / Ctrl+plus/minus / Ctrl+0 zoom the transcript;
clsorclcwipe it;clearAllLogswipes every output log.
Scripts#
A script is a plain-text file with the extension .icore, one console line
per line; blank lines and comment lines (# or %) are skipped, ...
continues a statement onto the next line, and %{ … %} comments out a run of
lines. It may contain anything the prompt accepts — recipe statements,
assignments, expressions, commands. Scripts live in <project folder>/scripts and are run from the
ICore Script IDE panel (Run for the open script, Run All for every script
in the folder, name order); a script stops at its first failing line and the
panel reports which line. A block spanning several lines runs as one unit when
its end arrives, and a failure inside it names the statement that failed, not
the line that closed the block. A script that ends inside a block fails at the
line that opened it, with 'for' block opened on line N is still open at the
end of the script (missing 'end'). The recorded form of a diagram is itself an .icore
(Import Diagram, generateRecipe, saveTemplate/useTemplate) — the whole
story is on Recipes and templates — recording, replaying and reusing diagrams. From the prompt or from another script,
run <name> runs <project>/scripts/<name>.icore in the same workspace (a script in a subfolder is run tools/fit); a
failure inside it reads <name>.icore:<line>: …, one prefix per script it
passed through, and a script that runs itself — directly or round a loop — is
refused by name.
The ICore Script IDE is also where scripts are written and fixed: an editor that lays out blocks, pairs brackets, underlines problems before a run, completes the script's own names and goes to definitions, with Problems and Variables panes and Run Line. It has its own page, The ICore Script IDE — writing, running, debugging and fixing .icore scripts.
Bringing a MATLAB .m in#
The ICore Script IDE's Import MATLAB button (and the console's
matlabImportScript <absolute .m path> [script name] [--overwrite]) translates
a MATLAB file's math statements into a new .icore. It is the mirror of
matlabExportScript, and it runs the same dictionary backwards, so what crosses
is exactly what the console can say: assignments, matrix literals, operators,
and the functions the bridge's catalog names. Two rules make the result
trustworthy rather than convenient:
- Nothing is guessed. A statement crosses only when the mapping is a catalog
row or a composition of catalog rows. Anything else is refused with a reason
and kept as a
# ICORE-NOT-IMPORTED(reason): <the MATLAB line>comment, so the script you land in accounts for the whole.m. The refusals you will meet first are control flow (if,for,while,switch,function: the console runs them, but the import reads one statement at a time and a block spans several), and a call to a name that is neither in the catalog nor assigned earlier in the file —q = A(1, 2)with noA = …above it reads as a call to a functionA. - Nothing is overwritten silently. The target is
<project>/scripts/<the .m file's stem>.icore; if it already exists the panel asks before replacing it and the command refuses without--overwrite.
Session noise with no console form — format long, for one — is dropped rather
than refused, and counted in the summary so the drop is never silent. clc,
clear, close all, figure and xlabel have console forms, and cross as
themselves.
Importing the ten-statement example below, run against the build named under Real runs:
>>> matlabImportScript /tmp/mbimp/example.m
10 of 11 statement(s) translated to <project>/scripts/example.icore
line 'if det(A) > 0, disp(x), end' skipped: 'if' -- the console runs control flow, but this import reads one statement at a time and a block spans several
exit=0
the .m | the .icore it wrote |
|---|---|
clc; clear all; | clc · clear all |
A = [4 -2; 1 3]; | A = [4, -2; 1, 3]; |
b = [1; 2]; | b = [1; 2]; |
x = A \ b; | x = solve(A, b); |
[Q, R] = qr(A); | [Q, R] = qr(A); |
s = sum(A); % column sums | s = sum(A); · # column sums |
t = 0:0.25:1; | t = 0:0.25:1; |
Z = zeros(2, 3); | Z = zeros(2, 3); |
r = A(1, :); | r = A(1, :); |
if det(A) > 0, disp(x), end | # ICORE-NOT-IMPORTED('if' -- the console runs control flow, but this import reads one statement at a time and a block spans several): if det(A) > 0, disp(x), end |
Four of those lines crossed as something else earlier in 2026 and cross as
themselves now, which is the direction this dictionary keeps moving in: the
factorization statement, the range, the index and sum all mean on this
console exactly what they mean in MATLAB, so there is nothing left to
translate in them — zeros was the last of the nine to move, and it moved on
2026-09-02: it named transmission zeros on this console, so an imported
zeros(2, 3) had to be spelled repmat(0, 2, 3) to keep it away from that
meaning. Transmission zeros are zero(G) now, which is what MATLAB calls
them.
The dictionary still exists for the entries that have not converged, and its
job is exactly this: where a name means two things, the import writes the
console function that means what the MATLAB line meant, rather than the one
that merely looks like it. fromMatlab <one MATLAB statement> prints a single
line's translation without writing a file. Paste the statement without its
trailing ;: the console reads ; before any command does, so
fromMatlab x = 5; is a silenced command that prints nothing, and
fromMatlab a = 1; b = 2 translates the first statement and runs the second.
A whole .m file, semicolons and all, is what matlabImportScript and the
Import MATLAB button are for.
The console and the canvas#
Recipe statements act on the open project: block(Type, Parent) creates a
block in a subsystem (parent defaults to Home), connect(a<1>, b<0>) links
output port 1 of a to input port 0 of b, .setConfig(name, value),
.move, .rename, .delete edit it, .info and .listConfig report it, and
generateRecipe [path] prints the recipe that reproduces a whole level.
Handles (g, s) are names for live objects and are listed in the Objects
Tracker panel; .delete deletes outright rather than moving to the Trash.
Every statement that edits the diagram is one step of the editor's Undo, so
Ctrl+Z in the editor takes back a console edit. Headlessly "the open diagram"
is the empty, unsaved project the app starts with — the run below creates a
block at ICore Blocks/Home/Gain — so a --console recipe is a way to test a
line, not a way to edit a saved project (open it in the editor for that, or use
the ICore Script IDE). Verbs are listed under Recipe verbs in Command glossary — console commands, verbs, functions.
Real runs#
Binary ICoreBlocks.app built 2026-09-25 23:58 (source at approximately commit
1dac24c5), re-run 2026-09-26. Every run below is one process,
HOME=<scratch> ICoreBlocks --console "<line>", with the startup lines removed;
exit= is the process exit code.
(a) Arithmetic and variables
>>> x = 3; y = x*[1 2; 3 4]; y
y = [[3, 6], [9, 12]] # Matrix of Double
exit=0
>>> A = [1 2; 3 4]; [A', A]
[[1, 3, 1, 2], [2, 4, 3, 4]] # Matrix of Double
exit=0
>>> A = [1 2; 3 4]; A' | A
[[1, 1], [1, 1]] # Matrix of Logical
exit=0
>>> label = hello; label
label = 'hello' # String
exit=0
>>> x = 3; clearVariablesSpace; x
unknown command: x
exit=1
(' is transpose. The two lines are the same characters read two ways: the
bracket concatenates, and | is logical OR — every entry of A and A' is
non-zero, so the OR is all ones. A' | A was the concatenation until
2026-09-02; the grammar is in
src/ICoreBlocks/ICoreMath/ICoreExpressionEvaluator.cpp.)
(b) An unknown command
>>> frobnicate 3
unknown command: frobnicate
exit=1
(c) Recipe verbs on the headless diagram
>>> K = 2.5; g = block(Gain); g.setConfig(Gain Value, K); g.listConfig; generateRecipe
K = 2.5;
Gain = block(Control_Systems/Base_Blocks/Gain);
Gain.rename(Gain);
Gain.move(0, 0);
Gain.resize(70, 70);
Gain.setConfig(Sampling Time (s), -1);
Gain.setConfig(Gain Value, K);
Gain.setConfig(Multiplication Type, Element-wise (K.*u)%~%Matrix (K*u)%~%Matrix (u*K)%~%Matrix (K*u) (u vector)~~Element-wise (K.*u));
subsystemTimes(ICore Blocks/Home, 1790448230285, 1790448232624);
exit=0
>>> g = block(Gain); s = block(Step); connect(s<0>, g<0>); g.getConfig(Gain Value)
Gain Value = 1
exit=0
g.info on the same block printed the full property sheet (name, type,
fullType: Control_Systems/Base_Blocks/Gain, path: ICore Blocks/Home/Gain,
size, rotation, ports and the block's description) — 75 lines, not repeated
here. Note the first line of the recipe: the console variable K is part of
what the project saves.
(d) A statement chain with and without the trailing ;
>>> x = 3; y = x*2
y = 6 # Integer
exit=0
>>> x = 3; y = x*2;
exit=0
>>> x = 3; x; x*2; 1/3
0.333333 # Double
exit=0
>>> x = 3; y = inv([1 2 3]); y
error: inv() needs a square matrix
exit=1
>>> x = 3; y = x/0; y
y = Inf # Double
exit=0
(e) The operators MATLAB users reach for first
>>> [-2 ^ 2, 2 ^ 3 ^ 2, 2 ^ -1]
[[-4, 64, 0.5]] # Matrix of Double
exit=0
>>> [1 2; 3 4] \ [1; 2]
[[4.96507e-16], [0.5]] # Matrix of Double
exit=0
(f) Comparing, testing, ranging and indexing
>>> [1 2 3] >= 2
[[0, 1, 1]] # Matrix of Logical
exit=0
>>> 0.1 + 0.2 == 0.3
0 # Logical
exit=0
>>> (3 > 2) && (2 > 1)
1 # Logical
exit=0
>>> 0:0.25:1
[[0, 0.25, 0.5, 0.75, 1]] # Matrix of Double
exit=0
>>> v = [3 4 5]; v(end - 1)
4 # Integer
exit=0
>>> v = [3 4 5]; v(v > 3)
[[4, 5]] # Matrix of Double
exit=0
>>> A = [1 2; 3 4]; A(:)
[[1], [3], [2], [4]] # Matrix of Double
exit=0
>>> x = [1 2 3]; x(5) = 7; x
x = [[1, 2, 3, 0, 7]] # Matrix of Double
exit=0
>>> A = [1 2; 3 4]; A(:, 2) = []; A
A = [[1], [3]] # Matrix of Double
exit=0
Blocks (a) to (f) are all from the run named at the top of this section. \ is least squares when the matrix is taller than it is
wide, so the residual noise in (e)'s second line is the QR's, not a wrong
answer. 0.1 + 0.2 == 0.3 really is 0 — == compares exactly, and those two
doubles are not that double.
(g) A 100,000-iteration loop, before and after the block hold
Both binaries built from commit b94aeadfa in the same build tree, on the same
machine, differing only by the change described above; each line is one
--console process timed whole, and loop is that total minus the startup
floor measured the same way (1+1).
process loop
1+1 2.199s — (startup floor, before)
1+1 1.794s — (startup floor, after)
s = 0; for i = 1:100000, s = s + i; end; s
before 123.886s 121.69s 1.22 ms per iteration
after 8.633s 6.84s 68 us per iteration
17.8x, and the answer is the same on both sides (s = 5.00005e+09). The
121.69 s was never arithmetic: it was 100,000 iterations, two variable writes
each, and one whole-project save per write. A loop is still not the fast way to
do arithmetic here — 68 us a pass is the cost of reading each line as a
statement, so prefer a whole-matrix expression (sum(1:100000)) when one says
what you mean — but a loop of this size is now something you can run at the
prompt and wait for.
For contributors#
The evaluator's order of resolution is ICoreConsoleInterpreter::evaluateLine
(src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/; the window in
src/ICoreBlocks/ICoreStudio/StudioObjects/Panels/CommandWindow/ forwards to it
and feeds an interpreter instance so a block is held until its end), and the
console-facing invariants — result handlers, main-thread only, the ;
terminator the serializer emits, --console exit codes — are stated on the
contributor page for the command system (icorecoder), with the expression
grammar on icoremath and the --console hook on shell.