Changelog for clafer-0.5.8
Clafer Version 0.5.8, unreleased
- Builds with GHC 9.12.4 (Stackage nightly-2026-11-11) and still with GHC 9.6.7. The lexer is generated with alex 3.5: the lexer generated in 2018 did not compile with GHC 9.12 (
Int#instead ofInt16#). - No longer depends on
data-stringmap, which is not maintained and does not build with GHC 9.6 or later from Hackage: the maps of UIDs areData.Mapmaps. The outputs are the same (the positive test models and the case studies), and compiling is as fast. - No longer depends on
json-builder, which is not maintained and does not build from Hackage with GHC 8.4 or later: the.cfr-mapand.cfr-scopefiles are written withaeson's encoding, field by field in the same order. The files of the positive test models (Alloy and Choco modes) are the same. - Depends only on the packages it uses:
cond,executable-path,mtl-compat,parsecandtransformers-compatare no longer dependencies of the library, andHUnit,QuickCheckandlens-aesonno longer of the test suites. - The initializer
:=is the initial value of a reference (kbak/clafer-semantics#164,doc/initial-value.md, errata T14). It compiled to an assertion in every output (#116), sox -> integer := 10with[ x = 5 ]had instances and a counterexample; it is a hard constraint now, and has no instance there.- In a static model,
:=is the same as=:[ some x => x.dref = 10 ]in the parent's context. The outputs are the same as with=, and an initializer does not make a model temporal. - In a temporal model, every lifetime of
xstarts with the value, which may change afterwards:[ initially this.dref = 10 ]in the context ofx. The value is resolved in the parent's context, where it is written:thisis the parent, and a nameyis the siblingyofxeven ifxhas a childy. - A set initializer, e.g.,
xs -> integer * := 1, 2, stays in the parent's context:[ initially (some xs => xs.dref = 1, 2) ]. The instances created later are not initialized. - A
finalorfinalrefreference keeps the value it starts with: withfinalref x -> integer := y,xtakes the value ofywhen its lifetime starts, and keeps it whenychanges, which= ywould contradict. - Inheritance is pure strengthening (reading A in kbak/clafer-semantics#164): a subclafer's own
:=or=is added to the inherited initializer and never replaces it, so= 2under:= 2fixes the value in every state, and:= 3has no instance. - New test models:
initialValueStatic(the same instances as with=in claferIG and chocosolver),tmp_initialValue01(an optionallevelcreated later),tmp_initialValue02(a mandatoryxwhose value changes),tmp_initialValue03(a parent that is replaced),tmp_initialValue04(finalrefandfinal, also with a value that changes),tmp_initialValue05(set initializers),tmp_initialValue06(the name resolution and an abstract clafer), and the test caseInitialValue.
- In a static model,
let v = e in bodycompiles (#114): it isbodywitheforv. It crashed the compiler in every mode ("pattern expressions should have been transformed previously"). The bound expression may be any expression abovein, e.g.,let s = a + b in s > 2orlet w = A.v in w = 3; it could only be a name. A quantifier or aletinbodythat bindsvagain hides it, and one that binds a name used ineis renamed (x_1), so thatekeeps its meaning. New test model:let_binding.- An ambiguous name of a super, e.g.,
x : TwithA.TandB.T, is an error that the name is not unique, with the paths of the candidates (A.A__T,B.B__T), as for a reference (#115). It was reported as a duplicate definition, with the UIDs. Two supers with the same name in the same namespace are still a duplicate definition. New negative test modelambiguous_super_nameand the test caseAmbiguousSuperName. - A group cardinality interval at the start of a line, e.g.,
2..3 bafter a claferawithout a cardinality, is the group cardinality ofb(#117). It silently became the cardinality ofa, and the children ofbbecame mandatory. The layout resolver separates the lines of siblings, so an element ends at the end of its line: an expression of a clafer, e.g., a reference target, no longer continues on the next line at the same indentation (a ->and thenbon the next line wasa -> b; it is a syntax error now). New test modelgroup_cardinality_after_claferand the test caseGroupCardinalityOnNextLine. - The Choco output declares the supers of all clafers, then the references, the reference of a super before those of its subclafers, and then the constraints (#118). The statements were in the order of the model, so chocosolver refused a reference refined to a clafer declared further down, e.g.,
E1 : Edge -> N2underabstract Edge -> Node ?beforeN2 : Node("E1 cannot refine reference from abstract Node to N2"), as in the wiki modelgraphReachability. New test model:refine_reference_to_later_clafer. - The graph outputs quote the labels of the reference edges (
-m graph) and the node names (-m cvlGraph), so that dot draws a model with a clafer named after a keyword of DOT, e.g.,abstract Edge -> Node ?inabstract Node; dot refusedlabel=Edge("syntax error near 'Edge'"). New test model:graph_dot_keywords.
Clafer Version 0.5.7, Nov 8, 2026
- The UID of a clafer is its least-partially-qualified name, with
__between the names:sizeif no other clafer is namedsize,x__sizefor thesizeinxotherwise, androot__sizefor a top-levelsizethat is the end of another clafer's path. It wasc0_size,c1_size, ..., numbered in the order of the model, for every clafer, so a UID changed when a clafer with the same name was added before it, and tools cut the prefix off to show the name (which also cutabc_deftodef). The UIDs are the names in all outputs: the Alloy signatures, the Choco model, the explicit model, the JSON IR, the HTML anchors and the scope files. An apostrophe is_prime(a'isa_prime; it wasc0_a__prime__), and a UID that would be the same as one before it in the model gets__2,__3, ... (e.g.,a_primenext toa', or a copy of an inherited clafer with-i).- A UID is also the least-partially-qualified name in the
.cfr-mapand the scope files; the fully qualified name isroot__x__size(it was::x::size). A lookup by name takes a UID or a qualified name with__, and the older form with::. - The outputs write the UIDs in the form their language allows: the Alloy output renames a clafer whose UID is an Alloy keyword (e.g.,
sig), a function ofutil/integer(e.g.,max) or a name the generators write (e.g.,show), or whose relation or reference names clash with another clafer's (r_xnext tox), to the first freesig_1,sig_2, ...; the variables of the Choco output are$and the UID ($x__size = $x.addChild("x__size")), and those of the locals of quantifiers$_and the name, so that a clafer named, e.g.,joinorglobal, or a local with the name of a clafer, does not replace them. - A name with
__is a warning in a full model, with its position, since__separates the names of a UID; in an explicit model (-f,--explicit) it is expected. - The HTML and graph outputs take the names of the clafers from the model, not from their UIDs.
- A UID is also the least-partially-qualified name in the
- A reference to a clafer that extends the referencing abstract clafer, e.g.,
abstract Person -> BobwithBob : Person, no longer makes the compiler loop with-f(since the names became the UIDs with-fin this release): the walk through the supers and references ends at a clafer that is already on its path. - A reference to a union of targets with a nested clafer, e.g.,
u -> Hub.p ++ fast, has valid Alloy output: the field is declared over the union of their signatures (p + fast), as it is for one target. Alloy refused the path in the declaration (The name "@r_p" cannot be found), in static and temporal models; the explicit model keeps the targets as a constraint ([this.dref in (Hub.p ++ fast)]). - Temporal models: the obligations of a constraint (
X,F,G,U) last while the parent of its context instance exists, not only while the context instance exists (#107). A transition nested in its source state, e.g.,[ req=down --> movingDown ]in the statemovingUp, as in the power window of Juodisius et al. (2019), takes the machine tomovingDown; it only forced leavingmovingUp. The pending obligations of nested states still lapse when their enclosing state is left, and[ X no this ]makes an instance last one step. For a top-level clafer, whose parent is the root, they last for the whole trace. - The transition of a clafer, e.g.,
on --> off, is a constraint of the clafer, with the same meaning as before. For-->>, the source lasts until the target holds, while its parent exists. - Temporal models: a negated obligation lapses with the parent too (#110). The negations are pushed inside the temporal operators first, with the dualities of LTL:
!F aisG !a,!X aisX !a, and!(a U b)is!b W (!a && !b). So[ !(F error) ]lets the parent disappear, like[ G !error ]; it required the parent to exist forever. A formula and its negation may both hold, on a lifetime that ends before their obligations are due. This also applies to the negated parts of the patterns, e.g.,F rin the scopebetween q and rholds only whenroccurs while the parent exists.finallyis an operator of its own, with the same meaning. - Temporal models: a top-level constraint or assertion holds for
root, without a variable that Alloy warns about (all self_ : root | ...leftself_unused: "This variable is unused", which claferIG shows as a relevance warning, e.g., for the CoffeeMachine example). No temporal test model or example has an Alloy warning; before, 27 of the test models had one. - The CheatSheet and the comments of the temporal generator say that a scope bounds the instances of a clafer over the whole trace, not in each state, and that an instance keeps its parent across its absences; a second way out of a state is a constraint in the state, which has the lifetime of the state's own transition (#111). New test models: a transition in a final machine and in a replaceable one, negated obligations, the identities in a scope, and the parent of a reappearing instance.
- The names after an explicit
drefare the children of the reference's target (#108):this.dref.speed,dref.speedandall t : this.dref | t.speed = 3compile, and mean the target'sspeed, also when the reference clafer has a childspeedof its own. They did not compile, since at least 0.5.2 ("Cannot deref from type", or an ambiguous name when the target was a reference too): the name was resolved through the reference a second time. - A comparison (
=,!=,in,not in), an intersection (**) or a difference (--) of two navigations to reference clafers is of their targets (#87):this.a = parent.b.aisthis.a.dref = parent.b.a.dref. It compared the instances ofa, which are never the same under different parents, so such a constraint made a model inconsistent, or held trivially when negated. An operand that isthis, a variable or ends withdrefis taken as written, e.g.,this in b.ainsel -> b.a. parentof an instance of a top-level clafer is the syntheticroot(#112): in a top-level claferb,parent.aandthis.parent.aareroot.a, i.e.,a. They compiled to Alloy that refers to the missing relation@r_c0_b, and failed in chocosolver with "Cannot join c0_b . parent". The root has no instance in the outputs:some root,one rootandlone roothold,no rootdoes not,#rootis 1, and other uses ofrootas a value are refused;[ some root ]compiled to an undefinedroot. The parent of a set of instances, e.g.,a.parentfora ?, is the root when an instance exists and empty otherwise:some a.parentissome a,#a.parentis 1 or 0, anda.parent.ciscwhenaexists.- New negative test models for the refused uses:
root_in_union,variable_over_root,root_as_child,compare_root_with_claferandparent_of_optional_top_level.doc/root.mdrecords why the static outputs have no root while the temporal output has a root signature, and what either alternative would cost.
- New negative test models for the refused uses:
- In a top-level abstract clafer whose subclafers are at the top level and nested, a constraint on the parent of
thisis split onthis: for the instances of the top-level subclafers, the parent is the root, and for each nested subclafer, the parent of its instances. Withabstract Feat [ some parent ],f1 : Featandgwithf2 : Feat,f1had no parent in Alloy, so the model had no instances, and chocosolver refused the model; both give the instance now. The parent of a variable of such a clafer is refused. Temporal models already gave the top-level subclafers the root as their parent. root.ain a temporal model is the top-level clafera; it was the relation from the root toa, e.g., inroot.a.x = 2.- Temporal models:
final,finalrefandfinaltargeton a clafer that inherits its reference fix the reference (#109), e.g.,final motor : engineunderabstract engine -> Engine. They fixed only a reference that the clafer declares, so the motor could point to another engine later in the trace;finalrefandfinaltargetwithout a reference of the clafer's own were ignored. A reference modifier after another modifier, e.g.,initial final x -> T, is no longer ignored either. - Temporal models:
finaltargetfixes the set of targets of a clafer's instances while their parent exists, so the instances may change and swap their targets, but a new instance points to one of the targets;finalreffixes the reference of each instance while it exists, andfinalboth (kbak/clafer-semantics#127).finaltargetfixed the reference of each instance, asfinalrefdoes. A clafer keeps all its reference modifiers, sofinalref finaltarget x -> Tmeans both, in either order; only the first one was kept. - Temporal models:
s must follow p between q and rrequires ansbeforerfor eachpup tor; it checked only apin the state ofq, since the brackets of Dwyer's formula were misplaced.s must precede p after qstarts at the firstq; Dwyer's formula could start at a laterq, so q, p, q, s held. At the top level, where a pattern holds in every state, eachqstarts a scope (kbak/clafer-semantics#127). - The test model
tmp_TLSHandshakeProtocol_distributed.cfr, the TLS 1.2 handshake, has traces; it had none since 0.5.0. It mixed tabs and spaces, so most states were nested in the wrong clafers; its versions were compared as instances (#87), and a client and a server agree on a version when they have one in common; the transitions that leave a phase are constraints of the channel, and a phase is left only from its last states; a fatal alert ends the connection; and the server's Finished message no longer contradicts its phase. Its scenarios: a full handshake of 10 states (12 with client authentication), no data before the server's Finished, and a fatal alert when the versions do not overlap. - Temporal models: a name that resolves through a reference, e.g.,
this.speedinl -> Port, compiles to valid Alloy; it joined the target with the set of all existingspeeds. make testfails when a test suite fails, or when an output of the positive test models differs from its regression, has no regression, or a regression has no output; the outputs become the new regressions, to be reviewed and committed. It passed regardless, so the regressions of 0.5.6 were outdated.make testcompiles and validates the positive test models in parallel, one model per core (make test JOBS=4for 4 at a time); the messages of each model are printed together. The positive suite takes 75 seconds on 32 threads.- The outputs are compared with their regressions exactly; whitespace and blank lines were ignored, so, e.g., a change of the spacing in the explicit models passed unnoticed.
make testalso fails when a positive test model does not compile or one of its outputs does not validate, and names those models after compiling all of them. It printed the errors and passed, e.g., with the Alloy output ofexplicit_dref_navigation, which Alloy refused.clafer -vexits with 1 when a tool rejects an output: Alloy, chocosolver, dot, or clafer itself, for the explicit model, which must compile to itself. A counterexample is not a failure. It printed the error and exited with 0, and did not check the explicit model.
- Temporal models:
parentof an instance of a top-level abstract clafer, and its constraints' obligations, use the parents of its subclafers' instances;parentwas empty for the instances of a nested subclafer, so[ some this.parent ]inabstract Portexcluded them. - An abstract clafer that no concrete clafer inherits from, directly or through other abstract clafers, and the clafers in it have the scope 0. With
--keep-unused, as in claferIG, Alloy gave such a clafer atoms of its own, which its constraints were translated for, and which references could point to; e.g., the power window with three abstract variants of its controller took 223 s for its first trace, and takes 2 s.emptyAbstractsinScopeAnalysisgives these clafers. - The simple scope analysis no longer adds the instances of a clafer that has none, e.g., such an abstract clafer, to the scope of its super: every subclafer added at least 1. The scopes of the supers are smaller, e.g.,
WaitingLine2 instead of 4 andparticipants4 instead of 8 in the tutorial, and theElementof the UML class diagram model 4 instead of 25. - Temporal models leave out the abstract clafers that the optimizer removed, as static models do; the temporal generator took the clafers of the model before the optimization.
- The unused JavaCC grammar
src/Language/Clafer/Front/Clafer.jjis removed. It dated from 2012, was never part of the compiler, and did not follow the syntax since 0.4; the grammar issrc/clafer.cf. - Scope files (
.cfr-scope, claferIG'sloadScopes): entries other than{"lpqName": name, "scope": n}, e.g., chocosolver's{"int": [-8, 7]}or{"defaultScope": 1}, are skipped; they stopped claferIG with an error. A name may also be a clafer's UID, e.g.,Component__Cfeatures, as chocosolver writes them. - Explicit models: a model with everything written out, e.g., as chocosolver's
AstPrinterprints one, is a Clafer model that compiles to itself withclafer -f -k -l --ss=none: all 131 positive test models with a Choco output, printed that way, compile to the same model and give the same instances.-f(--skip-resolver) means that the names of the clafers are their UIDs: they are not renamed, and they are still resolved and checked, so a misspelled name is an error. It was ignored for models with inheritance, which were renamed (c0_Abecamec0_c0_A), and without inheritance it skipped the name resolution and its checks. A name that cannot be a UID, of several clafers or with an apostrophe, is an error with-f;-fwas ignored then too.- The Choco output of a nested assertion is quantified over the instances of its clafer, e.g.,
assert [ age >= 0 ]inabstract Pisall this_ : P | this_.age >= 0. It was an assertion overthiswithout a context, which chocosolver's validation (-v) refused with a type error. - A navigation from an intersection or a difference of sets, e.g.,
(this ** g.f2).parent, resolves: from the right operand of**and the left of--, which contain the set. The split constraints of #112 are of this form. -m claferprints the explicit model of a static model: what chocosolver solves, with every name a UID, every card and group card (inherited ones too),this,parentanddrefwritten out, nested assertions quantified over their clafer, nesting in braces, and the objectives and assertions at the end. With--meta-data, its scope file (model.des.cfr-scope) has every scope: each clafer's by its UID, the default scope and the ranges of integers, strings and decimals. It printed the desugared model, which was not Clafer: elements ran together on one line (assert [...]assert [...]abstract ...). The JavaScript and the explicit model are printed from one walk over the model, so they are the same model: for all 132 positive test models with a Choco output, the explicit model is the one chocosolver prints for the.js(for 18, up to the order of abstract and concrete siblings that the solver does not see), with the same scope file, it compiles to itself, and chocosolver gives the same instances for it as for the.js. Models with reals have no explicit model yet:-m claferprints nothing for them (it printed their desugared model).-m claferprints the explicit model of a temporal model too, e.g., in ClaferIDE for the temporal CoffeeMachine example, where it showed nothing. It has the temporal modifiers (initial,final,finalref,finaltarget) on the clafers, the temporal operators (F,G,X,U,W,initially,finally), the patterns and the transitions as the formulas they stand for (a --> binais[((#this) >= 1) => (X ((#this.parent.b) >= 1))]), and the trace length among the options (//# OPTIONS --explicit --tl=10). chocosolver does not solve it, and Alloy does: all 64 temporal positive test models compile to themselves, and with the original's scopes, Alloy finds as many traces (up to 20) and counterexamples for the explicit model as for the original.- A nested assertion is quantified over the instances of its clafer by the path from its top-level clafer, as clafer resolves the name, e.g.,
all this_ : machine . ctx | ...for an assertion inctxinmachine: withctx, the explicit model did not compile to itself. --explicitmarks an explicit model: a shorthand for-f -k -l --ss=none. An explicit model starts with//# OPTIONS --explicit, which-m claferwrites, so it needs no options and any file name; chocosolver reads such a model with its own parser, without the compiler.
--meta-datano longer hangs on a model with a single clafer: the search for its least-partially-qualified name did not end.noclafer, the empty set: no instance of any clafer (kbak/clafer-semantics#134). Its type is below every clafer type, soa ++ noclaferandif c then a else noclaferhave the type ofa. Likeroot, it is a reserved name and never declared. The root handling of #112 writes it for a navigation from the parent of an optional top-level clafer (if some extra then a else noclafer), where it wrotea -- a. Alloy getsnone; Choco gets an empty constant of the clafer of the set beside it, e.g.,constant($a), or of the model's first clafer. A navigation from it, e.g.,noclafer.dornoclafer.parent, isnoclafer, whatever it navigates to, with a warning. The test modelnoclafer.cfrasserts its laws, which Alloy 6.2 and chocosolver both check.- A comparison, a difference or an intersection of an expression with itself, e.g.,
[ a = a ]ornoclafer = noclafer, is a warning, with its position and what it means (always true, always false, always empty, or the expression itself); it was an error, so a correct model did not compile. The warnings are printed with the statistics, like those about large scopes. claferas a value: the set of all instances of all clafers, the root excluded, i.e., the union of the clafers with no super clafer, nested ones by their paths (Thing ++ c ++ c.d). Static models only; temporal models refuse it.
Clafer Version 0.5.6, Nov 1, 2026
- The Alloy outputs have the nested assertions too, for each instance of their clafer, and name the assertions by their line and column (
assertAt_3_5), so that a line can have several assertions. Nested assertions were ignored, silently in temporal models. - Temporal models (behavioural Clafer) compile to the temporal logic of Alloy 6: mutable relations (
var) and the operatorsalways,after,eventuallyanduntil. The temporal generator encoded traces explicitly for Alloy 4.2, which had no temporal operators, and its output did not compile with Alloy 6. The Alloy Analyzer 6.2 finds the traces of small temporal models and checks their assertions in seconds. - The semantics of temporal models follows Juodisius et al., Clafer: Lightweight Modeling of Structure, Behaviour, and Variability (2019), sections 5 and 6:
a --> bisa => X b,a -->> bisa => (this && a) U b, and a guard strengthens the precondition (a -[g]-> bisa && g => X b). It differs from the paper in these points:- An instance may disappear and reappear, which cyclic state machines need in the lasso traces of Alloy.
- The temporal obligations of a constraint (
X,F,G,U) hold while its context instance exists; a constraint placed higher in the hierarchy, or at the top level, lasts longer. - The transition of a clafer, e.g.,
on --> off, is a constraint of its parent, so the transition of a nested state disappears when its enclosing state is left. - A chain of transitions is a sequence:
a --> b --> cisa => X (b && X c)(it wasa => X (b => X c)), e.g., the scenario[ initially (green --> yellow --> red) ]. Its first state is a condition, so a scenario states it when the model does not force it.
- Unsat cores of temporal models: the constraints and the cardinalities of the children are facts of their own in the Alloy output, for which Alloy gives the unsat core, so that claferIG can show the constraints that contradict each other; Alloy gives the core of the temporal formulas of a signature fact for the whole fact. The constraint of a clafer transition spans the clafer and its transition, e.g.,
green --> yellow. - Enumerations are final in dynamic models: their literals do not change over time (#92). In static models, they are not final, so that they do not make the model dynamic.
- The parent of a quantified variable, e.g.,
[ all b1 : b | b1.parent = a ], is the parent of its clafer; it was taken from the context of the constraint, and the compiler failed with "Prelude.head: empty list". - Partially qualified names match whole names:
yellowmatchedyellow_onandyellow_offtoo, so it was ambiguous, and its least-partially-qualified name, e.g., in a.cfr-scopefile, was qualified more than needed (normal::yellow). - HTML and graph outputs: expressions are shown with the parentheses that the grammar requires, e.g.,
[ (1 + 2) * 3 = 9 ]andX (a || b), which were shown as[ 1 + 2 * 3 = 9 ]andX a || b.Xwas shown asF, and the guarded arrows-[g]->and-[g]->>as-[g]-->and-[g]-->>. - The temporal test models are fixed according to the paper and the above changes, the traffic light models are split into one file per model and its scenarios, which import it, and the test suite compiles each model with the models it imports.
Clafer Version 0.5.5, Oct 27, 2026
- Decimals: a primitive type
decimalof numbers with a fixed number of fractional digits, the decimal scale of the model, for the Choco backend (chocosolver 0.5.5). A plain decimal literal, e.g.,19.99, is a decimal (it was areal); the scale is the most fractional digits of the literals, as written, or--decimal-digits.decimal * decimaland divisions with a decimal round half away from zero; a division of integers stays an integer division. Integers are decimals, decimals and doubles are reals, and a decimal and a double do not mix.realanddoublestay unsupported by the backends, and the Alloy backend does not support decimals. In the Choco output:refTo(Decimal),decimal("19.99")anddecimalScale(2). clafer-mode.elhighlightsdecimalas a type.- Scopes: a reference with an unbounded multiplicity (
-> Feature *) gets the scope of its target per parent, as intended; the target's scope was read before it was computed, so it could be 1 or a partial sum of the target's subclafers. Models whose references needed more targets than that had no instances, e.g., ClaferCaseStudies'AADL_Complete.cfr. - Scopes: in the Alloy and Choco modes, the statistics warn about such references whose scope is above 100 and suggest an upper bound, e.g.,
Warning: model.cfr:16:5: the scope of Cfeatures -> Feature 0..* is 1672, since Cfeatures may point to all 76 Feature in each of its parents. If fewer suffice, give it an upper bound, e.g., Cfeatures -> Feature 0..10, to make the instance generation faster.
Clafer Version 0.5.4, Oct 25, 2026
- Models in several files:
--import=FILE, in//# OPTIONSor on the command line, compiles the model together with the imported files, transitively, each file once and before the files importing it (#100). The options of all files are combined. The model and its imports can be URLs; an import is relative to the importing file's folder or URL. - Errors give their position as
file.cfr:line:column, also in a model of one file (Parse failed at model.cfr:3:5..., previouslyline 3 column 5), and in a model with imports, the file in which they are. A model read from the standard input is<stdin>. - A syntax error's message no longer repeats the line (
syntax error before ? }), which in a model with imports was a line of the complete model; the position is given with the error. - Restricted imports, for models that may come from anyone (e.g., in a web tool):
--import-root=DIRallows local imports only inside the given folders, after resolving..and symbolic links;--public-importsallows URL imports only from public addresses (not loopback, private, or link-local ones), checked when connecting, also after a redirect;--import-base=URLresolves the imports of a model whose text was read from a URL. These options,-v, and--tooldircan only be given on the command line, not in//# OPTIONS. Downloads are limited to 16 MB. - An imported file must be a
.cfrfile: a URL's path must end with.cfr, also after a redirect, and a URL served as HTML, JSON or XML is refused, so that a model cannot read other files or web pages. - Models read from URLs:
https://works (it was read as a local file name), outputs are named after the URL's last segment, in the current directory, andftp://is no longer supported. The compiler uses http-client instead of the HTTP package. //# OPTIONS: an unknown option or a malformed directive is an error; the whole directive used to be ignored silently. When compiling with theclaferexecutable, a directive with-mor with an option's short or custom name (e.g.,-k,--check-afm,--nl) was ignored entirely, and--tland--tooldirin the directive were overridden by the defaults.- Clafer names with apostrophes (
same') work with Alloy and Choco: an apostrophe is not allowed in their identifiers (in Alloy 6, it is the next-state operator), so it becomes__prime__in the clafer's UID (c0_same__prime__); the name keeps it. Such models are not counted as able to skip the name resolver. optmakes the children optional again, as in the language's definition: a child without a multiplicity is optional (0..1) under a keyword group (xor,or,mux,opt). Since 0.3.3, the default depended only on the interval, andopt, being0..*like no group at all, made its children mandatory. Only the keywords set_isKeywordin the IR, no longer the ranges0..n. Children of other ranges are optional and children of no group or0..*are mandatory, as before.- A group cardinality whose lower bound the children cannot reach is a compile error, e.g.,
xorororover no children, or3..*over two optional children. The children include the inherited ones, and the group cardinality may be inherited or redefined (2..2 myGroup : SuperGroup). Such groups were not constrained in the Alloy and Choco output. An abstract clafer's group is not checked, since its subclafers may add children. - Layout: comments do not count in the indentation. A line which starts with a comment (
/*history*/ xor on) is indented as if the comment was not there, at the column where the comment starts. The first clafer after the comment was nested by its own column, which put the following lines at wrong levels. - JSON output: the IR has the source positions of the clafers (
_cinPos) and expressions (_inPos), which were alwaysnull:{"file": "model.cfr", "start": {"line": 2, "column": 3}, "end": {"line": 2, "column": 6}}, the file and the position in it, with lines and columns from 1 and an exclusive end. A tab advances the column to the next multiple of 8, plus 1, as in the compiler's error messages. The span of a clafer with children ends one or two columns after its last token, at the closing brace that the layout resolver inserts. Desugared expressions without a position in the model are stillnull. - HTML output: comments are escaped and shown as written (
--add-comments); markup in a comment was rendered, and--in a//#comment could end the generated HTML comment early. - HTML output: each file of the model starts with its name (
<div class="file">model.cfr</div>), also a model of one file, and the comments before a//# FRAGMENTdirective are shown before it. The HTML shown for a model with errors gives their positions asfile.cfr:line:column, and an error message with quotes no longer ends the tooltip. - Release
Clafer Version 0.5.3, Oct 15, 2026
Minor release: compiler fixes found with larger example models, the integer range of the Choco output, and a reorganized test suite.
- Type checker: no crash on arithmetic over
max/minof a union, or on if/then/else over unions and dereferenced references. - Alloy output:
a - bwas generated as-a;parentof a top-level abstract clafer is the union of the parent relations of its subclafers (#84). - References:
-> 1lost the constant (#89); redefinition of an inherited abstract clafer (#86); Choco references to set expressions were dropped; unused abstract clafers named by a reference or a constraint are kept. - Implicit
.drefin unions (BobPicks ++ 3,others ++ p1);sumandproductover a union. - Inside a reference clafer, an ancestor (
Machine,parent) is the enclosing instance, and the children of the target resolve through the reference. - Scope inference covers the lower bound of group cardinalities.
- Sibling clafers on one line (
b : B A 3) are a parse error. - Alloy 6.2.0 (
alloy6.2.0.jar, Java 17+) replaces Alloy 4.2; the generated Alloy models are unchanged.--validateruns all Alloy commands headlessly (alloy exec) and no longer opens the Alloy visualizer. - New options
--minintand--maxint(also in//# OPTIONS) set the integer range of the Choco output. - The example models and regression tests run in the
model-testssuite (flagmodel-tests, off by default) and are not part of the package. - Release
Clafer Version 0.5.2 released on Oct 9, 2026
Minor release, Haskell compatibility (GHC 9.6.7 and lts-22.44).
Clafer Version 0.5.1 released on Sep 12, 2023
Minor release, latest Haskell compatibility (GHC 9.4.6 and LTS-21.11).
Clafer Version 0.5.0 released on Jan 31, 2018
This major release adds behavioral modeling capability to Clafer.
Clafer Version 0.4.5 released on Mar 13, 2017
Clafer Version 0.4.4 released on Jun 23, 2016
Clafer Version 0.4.3 released on Dec 22, 2015
Clafer Version 0.4.2.1 released on Oct 19, 2015
- Fixed Haddock build, updated README, fixed a test case.
Clafer Version 0.4.2 released on Oct 16, 2015
Clafer Version 0.4.1 released on Sep 1, 2015
Clafer Version 0.4.0 released on Jul 28, 2015
Clafer Version 0.3.10 released on April 24, 2015
Clafer Version 0.3.9 released on March 06, 2015
Clafer Version 0.3.8 released on January 27, 2015
Clafer Version 0.3.7 released on October 23, 2014
Clafer Version 0.3.6.1 released on July 08, 2014
Clafer Version 0.3.6 released on May 23, 2014
Clafer Version 0.3.5 released on January 20, 2014
Clafer Version 0.3.4 released on September 20, 2013
Clafer Version 0.3.3 released on August 14, 2013
Clafer Version 0.3.2 released on April 11, 2013
Clafer Version 0.3.1 released on October 17, 2012
Clafer Version 0.3 released on July 17, 2012
This was the first release of Clafer and included all code since the beginning of the project.
Basic features - See the README.md.