Methods and code effects
Methods and code effects in SCCharts' own statement language: if, loops, local variables and its rule for semicolons, then print and random numbers.
Transitions and actions say when something happens. Some computations are easier to write as a few lines of code: a formula with several cases, a loop over an array. For these SCCharts has methods and code effects. Both are written in SCL, the Sequentially Constructive Language, SCCharts’ own small imperative language. It looks like C, KIELER compiles it together with the chart, and it runs wherever the chart runs, this site included.
This chapter covers methods and code effects, the statements they are made of and what KIELER
compiles them into, then print and random numbers, which belong to SCCharts too. Code written in C
or Java itself, host code, has a chapter of its own: Host code.
Methods
Section titled “Methods”A method is a small function that belongs to the chart. It can return a value, take parameters, and read and write the chart’s variables:
Diagram
Code
scchart Thermometer { input int reading output int shown = 0 output bool hot = false
method int percent(int v) { if (v < 0) { return 0 } if (v > 100) { return 100 } return v }
method bool tooHot(int v) { return v > 80 }
initial state Display do shown = percent(reading); hot = tooHot(reading) go to Display}| Tick | input int reading | output int shown | output bool hot | Active state of the chart |
|---|
Guided tour, 4 ticks:
- reading = 42Tick 1 enters
Display; the self-loop is delayed:shown= 0. - reading = 250
percent(250)returns 100 andtooHot(250)returns true, both within this tick:shown= 100,hot= true. - reading = -7
shown= 0,hot= false. - reading = 90
shown= 90,hot= true.
percent and tooHot run within the tick, as if their bodies were written into the transition.-
method int percent(int v) { ... }declares a method that takes anintand returns one. A method can take several parameters (method int clampTo(int v, int lo, int hi)); leave the return type out (or writevoid) for a method that returns nothing. Declare methods with the chart’s variables, before its states. -
A body holds local declarations, assignments, calls,
if/else,forandwhileloops andreturn; Statements below shows each of them. A call runs the whole body within the tick. -
Every path through a method with a return type must end in a
return. Without any, KIELER reportsThe method does not return any value!; a method that returns on some paths only compiles, but breaks KIELER’s simulation when it ends without one. Areturnin a method without a return type givesReturn statement is used but no return type is declared in method signature. -
Call methods in triggers (
if tooHot(reading) ...), in effects, in actions and in dataflow equations. -
Treat parameters as read-only. When a method needs to change a value it was given, copy the parameter into a local variable and change that; Don’t assign to a parameter below shows why.
-
Methods inside a class are written without the keyword
method.
Code effects
Section titled “Code effects”For a few statements that are used only once, a code effect saves declaring a method: a block in
braces in place of an effect, do { a++; b = a * 2 } go to S, or in an action, entry do { ... }. Its
statements run one after another, in the same tick, like the body of a method without parameters (only
return is not allowed: Return statements can only be used in method bodies.):
Diagram
Code
scchart Thermostat { input int temperature output bool heater = false
initial state Control do { if (temperature < 18) { heater = true } else { if (temperature > 22) { heater = false } } } go to Control}| Tick | input int temperature | output bool heater | Active state of the chart |
|---|
Guided tour, 6 ticks:
- temperature = 20Tick 1 enters
Control; the self-loop is delayed, so the code effect has not run yet:heateris false. - temperature = 1717 is below 18: the
ifbranch switchesheateron. - temperature = 2020 is neither below 18 nor above 22: the
elsebranch runs, its innerifdoes not, andheaterkeeps its value, true. - temperature = 23Above 22:
heatergoes off. - temperature = 2020 again, and
heaterstays off: between 18 and 22 the thermostat keeps what it last did. - temperature = 15On again.
heater, so the thermostat keeps what it did last (ticks 3 and 5).An if takes braces on both branches, and the condition is usually written in parentheses, as in C.
There is no else if: } else if (...) { gives missing '{' at 'if'. Nest the second if inside
else { ... }, as Thermostat does.
Statements
Section titled “Statements”Method bodies and code effects use the same statements. Besides assignments, calls and if, they
have for and while loops and local variables, and a rule for semicolons that is not C’s.
for loops
Section titled “for loops”Diagram
Code
scchart Average { input int samples[4] output int total = 0, mean = 0
method int sum() { int s = 0 for (int i = 0; i < 4; i++) { s += samples[i] } return s }
initial state Measure do total = sum(); mean = total / 4 go to Measure}| Tick | input int samples | output int total | output int mean | Active state of the chart |
|---|
Guided tour, 3 ticks:
- samples = 1,2,3,4Tick 1 enters
Measure; the self-loop is delayed, sosum()has not been called yet. - samples = 10,20,30,41
sum()adds the four elements in one call:totalis 101.meanis 101 / 4 = 25: integer division drops the fraction. - samples = 0,0,0,-8
totalis −8,mean−2.
sum() adds up the four elements of samples in one call: four passes of the loop, all within the tick.for (int i = 0; i < 4; i++) { ... } is C’s for loop: a counter declared in the loop’s head, a
condition tested before each pass, and a step. The braces are required, even for a single statement:
for (...) a++ gives missing '{' at 'a'. Declare the counter in the loop’s head:
for (i = 0; ...) with a variable i of the chart passes KIELER’s checks, but its C code does not
compile (‘i’ undeclared (first use in this function)). A loop is a way of computing a value within
one tick, not a way of waiting: all its passes happen in the tick the method is called. To spread work
over ticks, use states.
while loops
Section titled “while loops”Diagram
Code
scchart Digits { input int value output int digits = 1
method int countDigits(int v) { int rest int n = 1 rest = v; while (rest >= 10) { rest = rest / 10; n++ } return n }
initial state Count do digits = countDigits(value) go to Count}| Tick | input int value | output int digits | Active state of the chart |
|---|
Guided tour, 5 ticks:
- value = 7Tick 1 enters
Count; the self-loop is delayed.digitskeeps its initial value, 1. - value = 42The loop runs once (
restgoes from 42 to 4):digitsis 2. - value = 12345Four passes round the loop, all within this one tick: 5 digits.
- value = 99 is below 10, so the loop body never runs: 1.
- value = 1003 digits.
countDigits divides by 10 until one digit is left. 12345 takes four passes in tick 3.A while loop runs as long as its condition holds, again within the one tick, so make sure it ends:
the tick cannot finish before the loop does. countDigits copies its parameter v into the local
variable rest and divides that, since a method should not change its parameters (see Don’t assign
to a parameter below).
Local variables
Section titled “Local variables”A declaration at the start of a block declares a variable that exists only in that block. A block is
a method body, a code effect, a branch of an if, or the body of a loop:
Diagram
Code
scchart Swap { input bool swap output int a = 1, b = 2
initial state Hold if swap do { int t = a a = b; b = t } go to Hold}| Tick | input bool swap | output int a | output int b | Active state of the chart |
|---|
Guided tour, 4 ticks:
- swap = falseTick 1 enters
Hold:ais 1,bis 2. - swap = trueThe code effect declares
t, keepsain it, and swaps:ais 2,bis 1.texists only while the block runs, so it is not in the trace. - swap = falseNo swap.
- swap = trueSwapped back.
a in its own variable t while it swaps a and b.The diagram’s label reads int t = aa = b: KIELER’s diagrams print a declaration in a code effect
without anything between it and the next statement. The code is int t = a, then a = b;.
- Declarations come first. A declaration after a statement of the same block is a syntax error:
a = b; int t = agivesmismatched input 'int' expecting '}'in a code effect andmissing '}' at 'int'in a method body. If you need a variable halfway through, open a new block there: a block in braces is a statement too, with its own declarations at its top. - A local variable is not visible after its block: a
tmpdeclared in a branch of anifis unknown after the branch (Couldn't resolve reference to ValuedObject 'tmp'.). - A local may have the name of one of the chart’s variables. Inside the block it hides that variable: the chart’s variable keeps its value.
- Local variables are not part of the data pool, so they never appear in a trace.
Semicolons
Section titled “Semicolons”KIELER’s rule is short: an assignment that is followed by another statement in the same block ends
with ;. That includes x++ and x += 2, and an assignment just before return. A line break is
not enough:
Diagram
Code
scchart Swap { input bool swap output int a = 1, b = 2
initial state Hold if swap do { int t = a a = b b = t } go to Hold}KIELER rejects this chart
- Line 8, column 7Missing semicolonKIELER says:
Missing Semicolon
In a code block, an assignment that another statement follows needs a;after it:{ x = 1; y = 2 }. Calls, declarations and blocks need none.About this message
a = b on line 8 is followed by another statement, so it needs a semicolon.Nothing else needs one:
- declarations (
int t = a), calls (inc(),print("a")) andif,forandwhileblocks may be followed by another statement without;; - the last statement of a block needs none, though a
;there is allowed; returntakes none at all:return v;givesextraneous input ';' expecting '}'.
Swap above follows the rule exactly: none after the declaration, one after each assignment but the
last. If in doubt, end every assignment with ;; that is always accepted. Effects on a transition
follow their own rule: they are separated by ;, with none after the last one
(see Missing Semicolon).
What KIELER makes of a method
Section titled “What KIELER makes of a method”KIELER’s netlist compiler inlines a method that is short, has no loop and calls no other method:
shown = percent(reading) becomes the statements of percent, with reading in place of v, as if
you had written them into the transition. Any other method, and a code effect with a loop, it keeps as
a C function. For sum() in for loops above, the generated C code has
int Average_sum(TickData* d) { int s; s = 0; for (int i = 0; i < 4; i++) { s += d->samples[i]; } return s;}and the transition calls d->total = Average_sum(d);. Mostly you see the difference only in the
generated code, and in warnings such as
ValuedObject with name _fn0_percent_v is contained in the model but not in VariableStore.which KIELER lists for the parameters and local variables of methods, and for the names of extern
functions. They are harmless. Two things, though, depend on how a method is compiled.
No variable called d
Section titled “No variable called d”Inside such a C function, d is the pointer to the chart’s TickData, through which the generated
code reaches the chart’s variables (see From chart to C). So in a method
or code effect that becomes a C function, no parameter or local variable may be called d. gcc
rejects the generated code: a parameter gives conflicting types for ‘d’, a local variable
‘d’ redeclared as different kind of symbol (the KIELER VS Code extension shows only
Compiler did not return success (exit value != 0)). An inlined method is not affected, but a method
stops being inlined as soon as it gets a loop, so it is simplest never to call a variable d.
Don’t assign to a parameter
Section titled “Don’t assign to a parameter”The second concerns the parameters of inlined methods. Zap passes its output o to a method that
sets its parameter to 0 before it returns. Predict o and r after tick 2, check, then open the
explanation.
Diagram
Code
scchart Zap { output int o = 5 output int r = 0
method int zap(int v) { v = 0; return v + 1 }
initial state Run do r = zap(o) go to Run}In tick 2 the self-loop calls zap(o), and zap sets its parameter v to 0 before it returns.
Explanation
r is 1, and o is 0, not 5. KIELER inlines zap: the call becomes the method’s statements, with the
argument in place of the parameter. So v = 0 is carried out as o = 0, and the method has changed the
variable it was given. KIELER says nothing about it beyond the harmless VariableStore warning.
With a number or an expression as the argument, zap(3) or zap(o + 1), there is no variable to put
in place of v, and the compiler fails: SCCharts Lab reports
The compiler failed during compilation setup.,
and in the KIELER VS Code extension the compilation never finishes. A method with a loop is a C
function and gets a copy of its argument, so there the same assignment changes nothing outside.
So whether an assignment to a parameter changes the caller’s variable, breaks the compiler or does
nothing depends on how KIELER compiles the method. Don’t depend on any of it: when a method needs to
change a value it was given, copy the parameter into a local variable first, as countDigits does.
print writes text to the simulation’s console. It is part of SCCharts, not host code, so it works in
every simulation:
Diagram
Code
scchart Report { input int level output int reports = 0
initial state Watch do print("level is", level); reports++ go to Watch}| Tick | input int level | output int reports | Active state of the chart |
|---|
Guided tour, 3 ticks:
- level = 3Tick 1 enters
Watch. The self-loop is delayed: nothing is printed yet,reports= 0. - level = 4The self-loop prints
level is 4and counts:reports= 1. The printed text is not a variable, so it is not in the trace. - level = 9Prints
level is 9;reports= 2.
level is and the current level; the printed text is not in the trace.KIELER turns print("level is", level) into a C printf with one conversion per argument:
- the arguments are joined with single spaces, and no newline is added, so two prints in one
tick run together:
print("a", 1); print("b")printsa 1b; - an
intprints in decimal, aboolas1or0, afloatwith six decimals (1.500000), a string as it is; - a
\ninside a print string breaks the generated C (missing terminating " character).
print changes no variable, so its text never appears in the trace. This site’s simulator lists it in
its description of each tick. KIELER prints it to the console of the simulation process; the
language-server tools, KIELER’s VS Code extension and SCCharts Lab among them, do not show it.
Random numbers
Section titled “Random numbers”random() returns a float between 0 and 1:
Diagram
Code
scchart Dice { output float r = 0 output int face = 0
initial state Roll do r = random(); face = r * 6 + 1 go to Roll}| Tick | output float r | output int face | Active state of the chart |
|---|
Guided tour, 4 ticks:
- Tick 1 enters
Roll; the self-loop is delayed, sorandom()has not been called:r= 0. - The first number of C’s
rand():r≈ 0.840, soface= 6. r≈ 0.394,face= 3.r≈ 0.783,face= 5. Run it again and you get exactly the same numbers: the generator is never seeded.
face = r * 6 + 1 turns the number into a die roll; assigning a float to an int cuts off the fraction.KIELER’s C simulation computes random() with C’s rand(), which it never seeds. So every run produces
the same numbers, one for each call, in the order the calls happen. (A random() in a trigger is called
in every tick, even while its state is not active: KIELER’s C code computes every trigger in every
tick.) With the GNU C library, which Linux
uses, they are 0.840, 0.394, 0.783, …, and this site’s simulator produces the same sequence. Other
systems’ C libraries have their own rand(), so in SCCharts Lab on Windows or a Mac the numbers may
well differ (How this guide is checked). Either way, the sequence
makes simulations reproducible, which is useful for testing, but it is not randomness. In the KIELER VS Code extension (0.3.0) the chart above also needs
#hostcode "#include <stdlib.h>", or gcc fails; current KIELER and SCCharts Lab add that include
themselves when a chart calls random().
randomize() is meant to seed the generator from the time of day, so that runs differ. In what KIELER
runs unconditionally in tick 1 (an entry action of the chart or of an initial state, or an immediate
transition without a trigger taken then) it does not even compile: KIELER’s netlist compiler moves it
into the reset code and writes it out as null, and gcc reports
‘null’ undeclared (first use in this function). Anywhere else, even on a triggered transition in
tick 1 (immediate if start do randomize()), it seeds the generator, and the runs are no longer
reproducible, provided gcc knows srand: the automatic <stdlib.h> comes only with random(), and not
at all in the KIELER VS Code extension. Without it, gcc 14 and later reject the generated code with
implicit declaration of function ‘srand’; older versions only warn, and the chart runs.
Either way, #hostcode "#include <stdlib.h>" fixes it.
Try it
Section titled “Try it”A sign method
Write a method int sign(int v) that returns −1 for a negative v, 1 for a positive one and 0 for
zero, and use it to set s from value in every tick after the first. Remember KIELER’s rules for
return and else.
Show the starter chart
scchart Sign { input int value output int s = 0
initial state Watch do s = value go to Watch}Show a solution
scchart Sign { input int value output int s = 0
method int sign(int v) { if (v < 0) { return -1 } if (v > 0) { return 1 } return 0 }
initial state Watch do s = sign(value) go to Watch}Count the high samples
Write a method int countAbove(int t) that counts how many of the four samples are greater than t,
with a for loop, and use it to set high from threshold in every tick after the first.
Show the starter chart
scchart Above { input int samples[4] input int threshold output int high = 0
initial state Measure do high = 0 go to Measure}Show a solution
scchart Above { input int samples[4] input int threshold output int high = 0
method int countAbove(int t) { int n = 0 for (int i = 0; i < 4; i++) { if (samples[i] > t) { n++ } } return n }
initial state Measure do high = countAbove(threshold) go to Measure}Check your understanding
Section titled “Check your understanding”Show the answer
Answer A: ready 1 3
print joins its arguments with single spaces and adds no newline. A bool is printed as a number,
1 for true, so the text is ready 1 3.
Show the answer
Answer B: { a = 1; b = 2 }
An assignment followed by another statement needs a semicolon, so the first is rejected with
Missing Semicolon. The third declares t after a statement; declarations must come first in a
block. The fourth uses else if, which SCL does not have: nest the second if inside else { ... }.
Key ideas
- Methods and code effects are written in SCL, SCCharts’ own statement language. They run within the tick, wherever the chart runs, this site included.
- SCL is close to C, but an assignment followed by another statement needs
;,returntakes none, there is noelse if, and declarations come first in each block. forandwhileloops run all their passes within one tick: they compute, they never wait.- KIELER inlines short methods into the transitions that call them, so don’t assign to a parameter: copy it into a local variable.
printjoins its arguments with spaces and adds no newline;random()gives the same sequence in every run.
Next, Enums, structs and classes: names for the values of a mode, records of related variables, and data that comes with its own methods.