On the Impact of ‘Fork and Modify’
Customizations on Source Code Metrics
in X++
Thesis
in partial fulfilment of the requirements for the degree of
Master of Science in Software Engineering
at the University of Amsterdam
to be defended publicly
in room L120 at the Centrum Wiskunde & Informatica
on October 14, 2016 at 10:15
by Olav Christiaan Trauschke,
approved on October 12, 2016
by supervisor dr. T. van der Storm,
tutored by dr. M.R.H. Kooistra
from the host organization Avanade Netherlands B.v.
Publication status: Published
Graduation committee:
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
ii
Table of Contents
Abstract ... 1
Preface ... 2
Chapter 1 Introduction ... 3
Chapter 2 Background ... 4
Chapter 3 Problem analysis ... 5
Chapter 4 Experiment setup... 7
4.1 Global setup... 7
4.2 Pre-processing ... 7
4.3 Parsing X++ code ... 9
4.4 Metrics ... 10
Chapter 5 Results... 14
Chapter 6 Analysis ... 15
Chapter 7 Discussion ... 18
Chapter 8 Conclusion ... 18
Bibliography ... 19
Appendix A: SIG Maintainability Model ... 21
Appendix B: xpo-files ... 23
Appendix C: X++ grammar ... 25
Appendix D: eXPODigger ... 41
Abstract
Different metrics have been developed to measure source code quality. Software vendors who want
to improve the quality of their products are interested in the impact of changes on the quality of
software. Companies customizing software that allows customizations in different ways are
interested in differences between these ways with respect to the quality of their software.
The impact of customizations by overlayering on source code quality are researched here, by
measurements of volume, complexity, coupling, duplication, unit size, depth in tree and number of
children. Overlayering can be thought of as customization by ‘forking and modifying’. The
aforementioned measurements are performed on customized code of the enterprise resource
planning system Microsoft Dynamics AX. Here we show why we were unable to base a valid
conclusion on the described research. We also show that we developed a Rascal parser and analysis
tools for X++ code that may be of use to future research.
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
2
Preface
After receiving a bachelor’s degree in medical information sciences and completing the six other
courses of the Master of Science in Software Engineering at the University of Amsterdam, the time
had come for me to show what I have learned in my Masterproject. I started researching the
relevance of different sub-characteristics of maintainability for the implementation of a fail-safety
mechanism in an enterprise resource planning (ERP) system to be used in operating rooms. My
supervisor, however, discouraged me to apply the method I had found for this project and finally I
even found an error in a key publication about this method that was confirmed to be an error by one
of the authors. Therefore, I was forced to adapt this research plan.
After the aforementioned change of plans, the result of my Masterproject and therefore the result of
many years of education, is the text you are currently reading: my master’s thesis On the Impact of
‘Fork and Modify’ Customizations on Source Code Metrics in X++. The Masterproject that resulted in
this thesis, provided a way for me to not only show what I have learned, but to start doing what I
have wanted to for the past years: working with software to improve healthcare. I sincerely hope the
results described in this thesis will be of use for the development of AX4Health and possibly even
other (healthcare-related or not) implementations of large systems that require customization with
code.
Before continuing this thesis with what it is all about, I would like to take this opportunity to thank
everyone who helped me to complete this thesis. Trying to list everyone who helped me to even get
to the moment I was allowed to start working on my Masterproject, would take far too long and I
would certainly forget to mention people if I would try to do that. Therefore, without being any less
grateful for their help, I will not try to list all these people here and will instead focus on the people
who helped me complete this project from the moment I started working on it.
First of all, I would like to thank the wonderful people at Avanade Netherlands, for providing me with
an idea for my Masterproject and their help when I had to change this plan, for a great environment
to work in and for many good advices when I needed them. Specifically, I would like to thank Gertjan
van der Linden for allowing me to do my Masterproject at Avanade Netherlands, for keeping track of
my progress and providing me with great advice whenever I needed it, whether I asked for it or not.
Furthermore, I would like to thank Nicole Holla for ensuring I kept track of the progress of my project
and for providing me with useful trainings and feedback. The last, but certainly not the least person
from Avanade I would like to thank specifically, is my tutor Matthijs Kooistra. Matthijs has been the
first person to read many versions of different deliverables of my project and always invested his
time to provide me with feedback.
Besides Avanade employees, I would like to thank Tijs van der Storm, my supervisor from the
University of Amsterdam. Tijs was the person who received drafts for parts of this thesis and other
deliverables for review after feedback I had processed any feedback from Matthijs. Despite knowing I
would not always immediately express gratitude for his efforts when they made me realize I should
have done something completely differently, Tijs remained critical and helped me understand his
feedback when I was too focused on my own opinion to understand what he meant immediately.
Finally, I would like to thank my parents. They supported me mentally, provided me with a place to
rest, enjoy a rare day off or work out of office hours. Now, without further ado, on with what you are
probably reading this for: a thesis On the Impact of ‘Fork and Modify’ Customizations on Source Code
Metrics in X++.
Chapter 1 Introduction
Source code quality
Source code quality has different characteristics: functional suitability, performance efficiency,
compatibility, usability, reliability, security, maintainability and portability. [2]. Different metrics have
been developed to measure these characteristics [3-5, 9, 11, 16, 20, 35, 36, 38]. In the research
described in this thesis, the metrics number of lines of code, cyclomatic complexity, coupling
between objects, depth in tree, number of children, duplicated blocks over six lines and unit size are
applied to measure the impact of overlayering on the quality of the software. These metrics are
selected because they can be measured by static code analysis and are considered important to the
domain the research described here was executed in, as described in Chapter 2.
Cyclomatic complexity is a metric for the number of ways a program can be executed in depending
on the circumstances (input in any possible way). As the other metrics, this metric is described in
more detail in Chapter 4. Coupling between objects measures the number of other objects an object
depends on. Depth in tree is the number of objects above an object in an inheritance tree, which is a
tree in which any object is a child of other objects it extends. Number of children is the number of
children an object has in the inheritance tree. Duplicated blocks of six lines are blocks of six lines of
code appearing more than once within the analyzed domain. Unit size, finally, is the volume of a unit
within the analyzed domain. Metrics that are measured per object or per unit are aggregated to
obtain a result for the whole domain of an analysis.
Customization
Overlayering is a specific way of making changes to software. When overlayering a “developer must
create a new model that overlays the model they want to customize” in “order to customize
metadata and source code of a model” [18]. In this context, a model is a collection of elements (such
as classes, macros and tables) that are compiled into one assembly [17]. Overlayering can be thought
of as customization by ‘forking and modifying’. When overlayering, programmers make changes just
like they are changing the original code, while in fact only the changes they make are stored. This
allows programmers to customize a basic system in different ways for usage in different settings.
The difference between overlayering and another way of making changes to software called
extension, is that when a programmer changes software by extension, he does not make changes to
the original source code, but writes his own source code that is somehow integrated in the
execution, e.g. by means of some event-driven mechanism in which the original system throws
events, which can be caught from other code to execute a modification [18].
Methods and conclusion
In this research the X++ code of the classes in Microsoft Dynamics AX as it is produced by Microsoft
(the default version) and in the customized version of this product called AX4Health is exported,
pre-processed and parsed and the aforementioned metrics of this software are measured. AX4Health is a
product built on Microsoft Dynamics AX to serve as an enterprise resource planning (ERP) system for
hospitals. AX4Health was created by overlayering the source code of Microsoft Dynamics AX, which is
written in Microsoft’s domain specific language X++ [24]. The pre-processing, parsing and measuring
steps are performed by tools created during this research. We find that the validity of the research
here is threatened and therefore no conclusion can be based on the results.
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
4
Chapter 2 Background
ERP “allows companies to integrate various departmental information. (…) The essence of it is the
fundamental premise that the whole is greater than the sum of its parts. (…) Supply-chain capabilities
of ERP increase efficiency and productivity of their users” ([8], pp. 114-115) Jenkins and Christenson
reported that due “to significant differences between manufacturing and patient care, ERP-based
systems do not easily translate to the healthcare setting” ([10], p. 48).
They also noted that – despite this disadvantage – vendors – such as Avanade – developed ERP
systems “that offer administrative and logical solutions for large healthcare systems” and concluded
that “an ERP-based system can help a healthcare organization integrate many functions, including
patient scheduling, human resources management, workload forecasting, and management of
workflow, that are not directly dependent on clinical decision making” ([10], p. 48).
Given the difficult position of systems such as AX4Health, the manufacturer of this system – Avanade
– is interested in the quality of this software, to improve sales and client satisfaction. This topic is
specifically interesting to Avanade at this time, because Microsoft recently allowed extension as “a
new way of customizing an application by using extensions” in Dynamics AX, as opposed
Chapter 3 Problem analysis
Product quality
The product quality model of ISO standard 25010 distinguishes eight characteristics of systems that
influence their quality, which are further divided into sub-characteristics as shown in Table 1 below
[2]. This table shows which characteristics the product quality model of ISO standard 25010
distinguishes and how they are divided into 31 unique sub-characteristics. E.g. functional suitability is
divided into functional completeness, functional correctness and functional appropriateness.
Table 1 Characteristics and sub-characteristics in the product quality model in ISO standard 25010 ([2], p. 10)
Characteristic
Sub-characteristics
Functional
suitability
Functional completeness
Functional correctness
Functional appropriateness
Performance
efficiency
Time behavior
Resource utilization
Capacity
Compatibility
Co-existence
Interoperability
Usability
Appropriateness recognizability
Learnability
Operability
User error protection
User interface aesthetics
Accessibility
Reliability
Maturity
Availability
Fault tolerance
Recoverability
Security
Confidentiality
Integrity
Non-repudiation
Accountability
Authenticity
Maintainability Modularity
Reusability
Analyzability
Modifiability
Testability
Portability
Adaptability
Installability
Replaceability
The research described in this thesis, focuses on characteristics from Table 1
-
that are expected to be differ between functionality implemented using overlayering and the
same functionality implemented using an extension;
-
that are considered important for ERP systems;
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
6
For the aforementioned reasons, the research described here does not focus on functional suitability
(how much as system provides needed functions), compatibility (how much as system or component
can share information and hard- and software environments), usability (how much a system can be
used effectively, efficiently and satisfying), security (how much a system protects information and
data so that others have the degree of access appropriate for them) or portability (how effectively
and efficiently a system or component can be brought to another environment) [2].
No complete model is found to measure the reliability of software (“degree to which a system,
product or component performs specified functions under specified conditions for a specified period
of time” [2]), but separate metrics for reliability that meet the above criteria and are used in the
research described here are number of lines of code, cyclomatic complexity [11], coupling between
objects, depth in tree and number of children [35]. For measuring maintainability (how effectively
and efficiently a system can be modified by its maintainers [2]), metrics used in the SIG
Maintainability Model are applied. This model contains the following metrics that were not
mentioned before and met the criteria described above: duplicated “blocks over six lines” and unit
size [9, 38].
Aim
The research described in this thesis, aims to gather insight in what characteristics of source code
quality suffer from customizations implemented by ‘forking and modifying’ (stronger than or even
opposed to other characteristics). This insight is expected to raise awareness of the impact of this
technique among programmers applying it and this is expected to help them take appropriate
countermeasures (such as refactoring and application of evolutionary algorithms [19]) before the
effects of the customizations they make become a serious problem for the quality of the software.
More concretely, the research described in this thesis aims to answer the following question: How
does customizing a system by overlayering affect the quality of the system’s source code with respect
to reliability and maintainability?
Hypotheses
Since customizations can specify things more, they are expected to increase the number of lines of
code. Because customizations by ‘forking and modifying’ are changes to existing code, they are not
expected to influence the class hierarchy in a system significantly, as programmers who make
changes to an existing system are not expected to change more than they directly need to reach
their goal in most cases. Customizations by ‘forking and modifying’ are therefore not expected to
make a relevant difference with respect to the coupling between classes and the depth in tree and
number of children of classes.
Customizations by ‘forking and modifying’ are expected to increase duplication, because different
parts of a system can be modified separately in similar ways. Furthermore, customizations by ‘forking
and modifying’ are expected to increase the complexity of source code, because they involve
changes to existing code. For the same reason, these customizations are expected to increase the
unit size in the source code.
Characteristics
The default version of the analyzed ERP system Microsoft Dynamics AX contains 16,218 classes.
AH4Health, the implementation of Microsoft Dynamics AX customized for hospitals, is bigger and
contains 16,600 classes.
Chapter 4 Experiment setup
4.1 Global setup
As described in Chapter 3 our experiment focuses on metrics of the version of the code of Microsoft
Dynamics AX as it is delivered and the version used in AX4Health that are found to be used for
measuring maintainability and reliability. How these metrics are applied and why they are
implemented the way they are, is described in detail in Section 4.4.
Notably, the volume is measured as the number of statements instead of the number of lines of code
as this is expected to be the easiest to measure out of twelve volume metrics that were previously
found to be internally consistent. The unit size is interpreted as the volume of a method and in this
metric statements are again used as unit of volume. Because the metric used for measuring
duplication (duplicated blocks over six lines) is a based on a number of lines, actual lines (and not
statements) were used as unit of volume in the calculation of this metric. Table 2 below summarizes
the metrics obtained from the literature and the corresponding metric measured in the research
described here. Reasons for differences the differences are given in Section 4.4.
Table 2 Metrics obtained from literature and corresponding metrics measured in the research described here
Metric from literature
Measured metric
Number of lines of code
Statement count
Cyclomatic complexity
Cyclomatic complexity
Coupling between objects
Coupling between objects (classes in this context)
Depth in tree
Depth in tree
Number of children
Number of children
Duplicated blocks over six lines
Percentage “of all code that occurs more than once
in equal blocks of at least six lines” [9]
Unit size
Statement count per unit (methods in this context)
The files exported from Microsoft Dynamics AX (and AX4Health) to represent classes cannot be
parsed immediately, because they do not contain just X++ code. Therefore, we developed
eXPODigger, a tool to process the exported files to get input for our X++ parser. This
pre-processing is described in section 4.2 Pre-pre-processing. The parsing itself is described in section 4.3
Parsing X++ code. Finally, section 4.4 Metrics describes how another tool we developed – eXPyMeter
is used to measure the quality of the source code of classes in the default Microsoft Dynamics AX and
in AX4Health.
4.2 Pre-processing
Need for pre-processing
No way is found to export source code from Microsoft Dynamics AX 2012 other than as xpo-files,
including not just the code, but also metadata about the class and not including all code as an
uninterrupted text. The structure of xpo-files is described in more detail in Appendix B: xpo-files. In
addition, no way is found to export the customized versions of classes, but only a way to export the
original classes and the customizations in separate files.
Exporting customizations using the built in export functionality of Microsoft Dynamics AX resulted in
xpo-files containing only methods that were changed as part of the customization and the
declaration of the class itself was also included in these files only if it was changed. When
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
8
and classes that were added as part of the customizations are exported in the same way the
unmodified versions of classes were exported.
Pre-processing tool: eXPODigger
eXPODigger is a tool that we developed to reconstruct source code based on xpo-files. The source
code of eXPODigger is presented in Appendix D: eXPODigger. In its simplest mode, eXPODigger
creates one X++ source code file (xpp-file) per xpo-file it is presented. When eXPODigger is presented
a folder with customizations and a folder with the uncustomized version of the same code, it does
not just create one xpp-file for each xpo-file it processes. Instead, in case eXPODigger finds a
representation of (customizations to) a class in both folders, it integrates the code it extracts from
the file representing the customization into the file representing the uncustomized version of the
class.
For this integration, eXPODigger first collects the methods and – if present – the class declaration
from the file with customizations. It then adds any methods from the uncustomized version of the
class that were not found before, as well as the class declaration if it was not found in the file with
customizations. eXPODigger compares methods in the same way as Microsoft Dynamics AX 2012
differentiates between overloading and overriding: by method signature (name, return type and
parameter types). [22] eXPODigger recognizes the elements of a method’s signature by their
positions at the start of the declaration of the method, ignoring any comments and calls to macros
that may precede them.
Standardization of marks signaling the start of blocks of never executed code in eXPODigger
Before writing the reconstructed code to a results file, eXPODigger performs one last operation: it
standardized marks that signal the start of blocks of code that are never executed (and can thus be
dealt with in the same way as is dealt with comments). These marks consist of the string “#if.never”,
but allow arbitrary numbers of whitespace characters between words and are not case sensitive, so
e.g. “#if . NeVer” is also valid. This standardization was performed, because no way is found to deal
with the possible whitespace characters in these marks during parsing.
Example of the use of eXPODigger
The following example demonstrates the use of eXPODigger. eXPODigger is presented a folder “AX”
with two files: A.xpo and B.xpo called A and B respectively. It is also presented a folder “AX4Health”
with another two files, one of which is also called B.xpo and the other C.xpo. A.xpo and C.xpo are
both unique in one of the folders. Therefore, the files representing them both have to contain the
complete code of the classes they contain. This code is extracted from these files and written to files
A.xpp and C.xpp in an automatically created results folder.
Both folders contain a file representing a class B. eXPODigger is told that the folder “AX” contains the
original source code, while the folder “AX4Health” contains customizations. It therefore first collects
the methods and the class definition it finds in B.xpo in “AX4Health”. It then adds any methods to
this collection that it finds in B.xpo in “AX” that have signatures that differ from the methods found
in B.xpo in “AX4Health”. This does not include the class definition, because that was already found in
B.xpo in “AX4Health”. Finally, the whole collection of methods and the class definition is written to a
file B.xpp in the results folder.
4.3 Parsing X++ code
Applied tooling: Rascal syntax definition and parser generation
The Rascal Metaprogramming Language (Rascal) integrates source code analysis, transformation and
generation primitives “on the language level” [6]. Rascal is used for the measurements of software
metrics, because this was specifically developed for metaprogramming. Syntax “definitions make it
possible to define new and existing languages and to write tools for them” in this language ([13], p.
5). Furthermore, Rascal offers the functionality needed for source code analysis and transformation
combined in one language. Also, it facilitates encapsulation and reuse [14].
The syntax definition that is created as described below and is used for the analyses that are part of
the research described here, is given in Listing 2 in Appendix C: X++ grammar. Readers may want to
consult this listing while reading the rest of this section for a better understanding of how concepts
were defined in the created Rascal syntax definition for X++ code. X++ is an object-oriented
programming language comparable to C#. A difference in the syntax of X++ as compared to C# is that
X++ contains keywords like SQL statements (such as “select”), which can be used for accessing
relational databases directly from X++ code, without having to call a separate database query written
in SQL [24].
Defining the syntax of X++ code
To parse X++ code with Rascal to perform analyses, a grammar of X++ code has to be defined in a
Rascal syntax definition [15]. A grammar describes how constructs of a language (such as words in
English) can be used to construct other language constructs (such as English sentences). To create
this syntax definition, the X++ grammar used by the X++ compiler (found at [7]) is first translated to a
Rascal syntax definition.
An important step in this translation is the definition of terminals (structures of a language that
cannot be split into smaller structures further, such as letters in English), which are not included in
[7]. This includes the definition of identifiers and accompanying reservation of keywords. Keyword
reservation has to be defined separately for different kinds of identifiers, because keywords that
have to be restricted in one place to avoid ambiguity, are found to be valid identifiers in another
place. The definitions of identifiers are redefined iteratively until all files that were analyzed in the
research described in this thesis can be parsed without errors or ambiguities.
Keywords in X++
X++ threats keywords in a case insensitive way. Rascal developer Jurgen Vinju explained case
insensitive disambiguations are not implemented in Rascal (yet). Keywords are therefore defined in
lowercase, uppercase and camel case manually in this syntax definition and other alternative
notations are added when they are found in the analyzed files.
Empty non-terminals
Furthermore, empty non-terminals (structures of a language that can be split into smaller structures,
such as words in English which can be split into letters), which – as X++ developer Peter Villadsen told
the author – are used to communicate status information between the lexer and the parser in the
original X++ compiler, are removed, because they would not be of use to an automatically generated
lexer and parser as is created with Rascal for the research described here. By consequence, no way is
found to distinguish between two concepts the original X++ compiler distinguishes in the syntax
definition used for the research described here: functions and intrinsic functions (see [26] for more
information about intrinsic functions in X++).
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
10
Literals
Formats for literals of different types are also added to the syntax definition, because these were not
defined in [7]. No distinction is made between integers of different lengths. These definitions are
again refined iteratively until all files analyzed for the research described here can be parsed
unambiguously.
Layout
Besides whitespace characters, single- and multi-line comments blocks of never executed code are
included in the definition of layout in the syntax definition used for the research described here, to
stop the parser generated from this definition from attempting to parse them as code. Blocks of
never executed code are if-directives – which stop compilation if and only if a specified macro is
undefined – with the name “never”, that is by convention never used to define a macro. The
non-breaking space character is also included in the definition of layout, because it is present in some of
the export files with code that are analyzed at unpredictable places and for no apparent reason.
Macros
Finally, macros have to be included in the syntax definition, because these are not considered in the
grammar provided in [7], because this is the grammar used by the X++ compiler, while macros are
normally handled “before the text reaches the compiler” [7, 37]. Macros in X++ are constant strings
of characters that can be referred to by identifiers preceded by a hashtag [29]. Macros cannot be
replaced by the code they represent like is done by the pre-processor, because extraction of code for
macros from xpo-files was not included in the research described here, due to time constraints.
Constructs related to macros are allowed to occur wherever they are found in the code that is
analyzed for the research described here, if and only if this is found to be possible without
introducing ambiguities (which happens if the macro calls replace too many keywords or other
structuring elements). Files with calls to macros that cannot be parsed without introducing
ambiguities, are excluded from the analyses. This resulted in 16,174 out of 16,218 classes of
uncustomized code and 16,554 out of 16,600 classes of customized code being parsed successfully
and being included in the metrics.
4.4 Metrics
The number of lines of code, cyclomatic complexity, coupling between objects, depth in tree, number
of children, duplicated “blocks over six lines” and unit size of the original source code of Microsoft
Dynamics AX and a version that was modified for usage in AX4Health applying overlayering are
measured. Due to time constraints and the way code objects can be exported from Microsoft
Dynamics AX 2012, the research described here focusses on classes and does not include other
constructs such as macros, enumerations, extended data types or tables. Unless stated otherwise,
these metrics are calculated by iterating over the parse trees resulting from the parsing described in
the previous section. This is done by another tool we developed for the research described here (in
Rascal): eXPyMeter. The source code of eXPyMeter is presented in Appendix E: eXPyMeter.
Volume
Li and Cheung found twelve internally consistent volume metrics: statement count, line count
excluding comments, unique operand count, operator count, operand count and seven metrics
calculated from the (unique) operand and operator counts [16]. Of these metrics, statement count is
expected to be the easiest to calculate based on parse trees and is therefore used to measure the
volume of the analyzed software, because eXPyMeter measures the statement count by counting
statements while visiting the parse tree resulting from parsing the analyzed code. This metric is
expected to be the easiest to calculate based on a parse tree because it does not require handling of
metadata resulting from the parsing such as the source code location of an element from the parse
tree and less different kinds of statements are expected to exist than kinds of operators or operands.
Cyclomatic complexity
The cyclomatic complexity is measured per unit – i.e. per method – and based on these results, the
relative volume of code with any complexity is calculated, as was done by Heitlager, Kuipers and
Visser [9]. Unlike they did, complexities are not aggregated in the research described here, to allow
for more fine-grained comparisons. Also, Heitlager, Kuipers and Visser used lines of code as metric of
the (relative) volume and – as stated above – the number of statements is used in the research
described here instead.
Each metric that does not measure a system as a whole but measures smaller units of code, is
calculated in a similar way [9]. The relative volume with any complexity is calculated by visiting each
method body in the parse tree and counting the statements in it and measuring the complexity of it.
It is important to note only (both volume and complexity of) contents of method bodies are thus
considered in this metric.
McCabe defined cyclomatic complexity as 𝑣 = 𝑒 − 𝑛 + 2𝑝, where 𝑒 is the number of arcs, 𝑛 is the
number of nodes and 𝑝 is the number of connected components in the program’s control flow graph.
McCabe also showed this can be simplified as the number of conditions plus one [20].
Based on the above, the cyclomatic complexity of the body of a method in X++ is defined as:
-
the sum of the number of conditions in the predicates determining the control flow in
while-statements, for-while-statements, do while-statements and (inline) if-statements;
-
plus the number of search statements, because they are effectively for-each statements
(thus for statements with a single condition) combined with database queries to obtain the
data to iterate over;
-
plus the number of occurrences of the CaseHeader construct, because that equals the
number of cases in switch statements, excluding default cases, which do not create an extra
path in the control flow graph, because they are executed in the same path as any code
following the switch statement they are part of;
-
plus one.
The above definition does not include catch, retry or transaction abort statements, because they only
define what happens along a specific path (when an exception has been thrown, when an exception
should lead to retrying something and when a transaction should be aborted respectively) and the
extra paths they may seem to imply are actually created elsewhere (when an exception is thrown in a
specific case or when a program determines whether or not something should be retried after an
exception has been thrown or a transaction should be aborted). eXPyMeter measures this metric by
counting (conditions in) the aforementioned constructs while visiting the parse tree.
Coupling between objects
Coupling between objects is defined as “a count of the number of other classes to which a class is
coupled. It is measured by counting the number of distinct non-inheritance related class hierarchies
on which a class depends” [5]. eXPyMeter calculates this metric by visiting each class’s body in the
code and counting the total number of statements and the number of unique
-
DeclTypeIds (identifiers for types of declared variables);
-
ClrTypes (qualified identifiers for common language runtime types of declared variables),
without ClrArraySpecs (pairs of brackets used to define an array of a type) [21, 30];
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
12
-
StdIds after the keywords “as” and “is” in expressions;
-
StdIds in constants, preceding DblColonSyms (a symbol consisting of two colons) that are
used to separate types form elements within them (such as fields)
-
and StdIds and EvalClrTypes in EvalNames (names of things that are evaluated, mainly
method calls) following the keyword “new”
in it.
Depth in tree
Chidamber and Kemerer define the depth in tree of a class as its height in the inheritance tree of the
system it is part of ([4], p. 201). eXPyMeter calculates the depth in tree by filling a map from each
class to the class it extends, based on the extends clause in the class declaration and counting the
number of statements in the body of each class, while visiting the parse tree. Then, it builds an
inheritance tree based on the resulting map, with a single root class at the top and it calculates the
depth in tree for each class using this inheritance tree. Finally, eXPyMeter calculates the relative
volume of code with any depth in tree based on the collected data.
Number of children
Chidamber and Kemerer defined the number of children of a class as its number of immediate
descendants in the inheritance tree of the system it is part of ([4], p. 201). The relative volume of
code with any number of children is calculated for the research described here. To calculate this
metric, eXPyMeter uses the same inheritance tree and relative volume as used to calculate the
relative volume of code with any depth in tree. This time it counts the number of children of each
class in the tree instead of the height in the tree.
Unit size
Heitlager, Kuipers and Visser defined the unit size as the volume of a unit and stated a unit is a
method in Java or C# [9]. As X++ has similarities to C#, a unit is defined as a method for X++ as well.
The unit size is therefore calculated as the number of statements per method. How this metric is
calculated, was described in the description of how the relative volume of code with any complexity
is calculated above. As units of code with a larger unit size have a larger volume by definition, unit
sizes are aggregated differently and the number of methods with any unit size is calculated.
Duplication
The metric used to measure duplication (duplicated “blocks over six lines”), is a line based metric; it
is defined as “the percentage of all code that occurs more than once in equal code blocks of at least 6
lines” [9]. Therefore, lines are used as a unit for volume (instead of statements) to calculate this
metric. To avoid converting back and forth between parsed structure and lines, type-1 duplication is
measured and calculated based on the (unparsed) source code. Type-1 duplication defines clones as
parts of code that are identical “except for variations in whitespace, layout and comments” ([36], p.
472). The relative number of lines of code that is part of a block of at least six lines of duplicated code
is calculated.
eXPyMeter reads all lines from input files, excluding blank lines and comments. It then iterates over
all but the last five lines of each file and stores the block of six consecutive lines – including their
locations in the source files – starting at this line in a duplication class, i.e. a collection of all (already
categorized) occurrences of a specific block of six lines of code. It then iterates over the duplication
classes and creates a set of locations of lines that are part of a duplication class containing more than
one block of code. To calculate the relative number of lines of code that is part of a block, the
number of lines in the generated set is finally divided by the total number of lines that are processed.
The component of eXPyMeter responsible for this functionality was based on [32].
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
14
Chapter 5 Results
The analyzed uncustomized code is found to consist of 1,467,725 statements. The analyzed
customized code is found to consist of 1,504,616 statements. In the uncustomized code the relative
number of lines of code that is part of a block of at least six lines of duplicated code, finally, is
28.0991%. In the customized code this is 28.4807%. Table 3 presents the results of the metrics that
are measured for smaller units than complete systems. Because the data were found not to be
distributed normally (see Chapter 6 Analysis), the medians and interquartile ranges (IQLs) are given.
Means are also provided to show the direction of a difference when the medians are equal.
Table 3 shows the complexity has a median of four with an interquartile range of nine in both the
uncustomized and the customized code. The mean complexity is, however, 9.846 in the
uncustomized code and 9.891 in the customized code, showing the average complexity is 0.045
higher in the customized code. The coupling has a median of 109 related classes in the uncustomized
code and 110 related classes in the customized code, both with an interquartile range of 82 related
classes. As the medians, the means show an increase in coupling from the uncustomized code to the
customized code.
Like for the complexity, Table 3 shows no difference in mean or interquartile range is found with
respect to the unit size, depth in tree and number of children. However, the means show a decrease
in unit size and an increase in depth in tree and number of children from the uncustomized code to
the customized code.
Table 3 Results for metrics not measured per system
Metric
Uncustomized code
Customized code
Median
IQL
Mean
Median
IQL
Mean
Complexity
4
9
9.846
4
9
9.891
Coupling
(related
classes)
109
82
39.18
110
82
39.32
Unit size
(statements)
3
5
6.636
3
5
6.629
Depth in
tree
1
1
1.636
1
1
1.638
Number of
children
0
0
1.642
0
0
1.646
This table shows the median, interquartile range and mean for both the uncustomized and customized code for the metrics that were not measured for the complete systems and therefore had to be aggregated to obtain system-wide results
.
Chapter 6 Analysis
We analyze the results presented above using IBM SPSS Statistics 24. We retract the null hypothesis
of a statistical test (that a result was found by coincidence) when the chance it is true (p) was found
to less than 0.05. A chi-square test is performed to calculate the significance of the difference in
volume between the uncustomized and the customized code. The result of this test was p<0.000,
meaning the increment in volume caused by the customizations is found to be significant. This result
was expected as customizations can be used for adding functionality and adding functionality to a
program obviously requires code to be added to the system (at least in some cases).
Visual inspection of the scatter plots below shows the complexity, coupling, unit size, depth in tree
and number of children are not normally distributed for both the uncustomized and the customized
code. For the sake of completeness all used scatter plots are presented, although plots for the same
metric for both versions of the code may look similar to humans due to the small size of the
customizations as compared to the total size of the analyzed code.
We therefore perform a (non-parametric) Mann-Whitney U-test to test the significance of
differences between the uncustomized and the customized code. The differences in complexity
(p=0.081), unit size (p=0.418) and depth in tree (p=0.451) are found to be insignificant. The
differences in coupling (p=0.013) and number of children (p=0.005) are found to be significant.
Figure 1 Scatter plot of complexity in uncustomized code Figure 2 Scatter plot of complexity in customized code
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
16
Figure 5 Scatter plot of unit size in uncustomized code Figure 6 Scatter plot of unit size in customized codeFigure 7 Scatter plot of depth in tree in uncustomized code Figure 8 Scatter plot of depth in tree in customized code
Figure 9 Scatter plot of number of children in Figure 10 Scatter plot of number of children in
uncustomized code customized code
The significance of the difference in the relative number of lines of code that is part of a block of at
least six lines of duplicated code is tested by performing a chi-square test. The result of this test is
p<0.000, meaning the increment with respect to this metric caused by the customizations is found to
be significant.
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
18
Chapter 7 Discussion
The analysis described above shows significant impact was found on the volume, coupling, number of
children and duplication. This suggests ‘fork and modify’ customizations have (more) impact on these
metrics (than on complexity, unit size and depth in tree). However, the research described here is
subject to threats to the validity of this possible conclusion.
First and foremost, the customizations the difference between the compared versions of the
analyzed code consist of, may partially be a direct consequence of the addition of functionalities. The
results may therefore be a consequence of characteristics of these functionalities instead of the way
of customizing the code. Also, macros are excluded from the analysis, due to which some code is
unparseable and is excluded from the analysis. Code that programmers put in macros, may differ
from other code, making this a potential bias.
Another reason the comparison between the uncustomized and the customized version of the code
may be invalid is that the observed differences may be caused by different characteristics between
the uncustomized code and the code written as part of the customizations. Although no evidence
was found to support this claim, this chance is expected to be increased by the fact the
customizations are made by another party (thus likely other developers) than the uncustomized
code.
Furthermore, units size and cyclomatic complexity are measured per method, causing code that is
not part of the body of a method to be ignored for the calculation of these metrics. Also, the
generalizability of a conclusion based on the research described here would be questionable,
because the analyzed customizations are made by one specific group of programmers, that may have
specific characteristics which are not accounted for.
Finally, due to time constraints, we have not been able to validate eXPODigger and eXPyMeter
against other tools and it thus remains unproven these tools provide valid measurements of metrics
in X++ code. Especially after validation, these tools may however be of use for other research for
which X++ code is analyzed.
Chapter 8 Conclusion
In the research described in this thesis, metrics regarding the maintainability and reliability of X++
code are measured to compare code of Microsoft Dynamics AX as it is delivered by Microsoft to a
customized version of the code used in the product AX4Health to measure the impact customizations
on these aspects of source code quality. Tools are developed to pre-process and parse the code and
to measure the number of statements, cyclomatic complexity, coupling between objects, depth in
tree, number of children, percentage of the code that is part of duplicated blocks over six lines and
unit size.
The research described here is subject to multiple threats to the validity, that would make any
conclusion based on the results untrustworthy. Therefore, even although the results are conclusive,
the author is not able to present a conclusion that answers the research question described in the
introduction to this thesis. However, the tools that are created for the performed analyses –
eXPODigger and eXPyMeter – may be of use to any future research projects regarding X++ code.
Bibliography
Besides research papers, sources about Microsoft Dynamics AX and the used tools are referenced.
For clarity, references to research papers and other evidence based sources are highlighted in this
bibliography.
[1] Bilous B. What's new in X++ for AX 7, 2016. Available at
https://axforce.wordpress.com/2016/01/18/whats-new-in-x-for-ax-7/. Accessed 9/26/2016.
[2] British Standards Institution Systems and software engineering — Systems and software Quality
Requirements and Evaluation (SQuaRE) — System and software quality models, BS ISO/IEC
25010:2011, (2011).
[3] Brooks A. MSc Software Maintenance, Fyrirlestur 42, Maintainability Index Revisited, 2016.
Available at http://staff.unak.is/andy/MScMaintenance0809/Lectures/SMLec42.ppt. Accessed
5/10/2016.
[4] Chidamber S.R. & Kemerer C.F. Towards a metrics suite for object oriented design, Proc ACM Conf
OOPSLA 6 (1991) 197-211.
[5] Cho E.S., Kim M.S. & Kim S.D. Component metrics to measure component quality, Aspec 8 (2001)
419-426.
[6] CWI. Rascal Metaprogramming Language, 2014. Available at http://www.rascal-mpl.org/.
Accessed 5/23/2016.
[7] Dynamic AX Developer Reference. X++ Syntax, 2016. Available at
https://ax.help.dynamics.com/en/wiki/x-syntax/. Accessed 7/11/2016.
[8] Gupta A. Enterprise resource planning: the emerging organizational value systems, Industrial
Management & Data Systems 100 (3) (2000) 114-118.
[9] Heitlager I., Kuipers T. & Visser J. A practical model for measuring maintainability, QUATIC 6
(2007) 30-39.
[10] Jenkins E.K. & Christenson E. ERP (enterprise resource planning) systems can streamline
healthcare business functions, Healthc Financ Manage 55 (5) (2001) 48-52.
[11] Kaur G. & Bahl K. Software Reliability, Metrics, Reliability Improvement Using Agile Process,
IJISET 1 (3) (2014) 143-147.
[12] Khan S. Working with dates in X++, 2015. Available at
https://dynamicsaxed.wordpress.com/2015/06/04/working-with-dates-in-x/. Accessed 9/26/2016.
[13] Klint P., Van Der Storm T. & Vinju J. EASY Meta-programming with Rascal, Springer, 2009,
222-289.
[14] Klint P., Van Der Storm T. & Vinju J. Rascal: A domain specific language for source code analysis
and manipulation, (2009) 168-177.
[15] Klint P. & Vinju J. Syntax Definition, 2016. Available at
http://tutor.rascal-mpl.org/Rascal/Declarations/SyntaxDefinition/SyntaxDefinition.html. Accessed 7/11/2016.
[16] Li H.F. & Cheung W.K. An Empirical Study of Software Metrics, IEEE Trans Software Eng 13 (6)
(1987) 697-708.
[17] Maertens M. Model split, 2016. Available at
https://ax.help.dynamics.com/en/wiki/understanding-the-model-split/. Accessed 6/27/2016.
[18] Maertens M. Customization: Overlayering and extensions, 2015. Available at
https://ax.help.dynamics.com/en/wiki/customization-overlayering-and-extensions/. Accessed
6/10/2016.
[19] Martens A. et al. Automatically improve software architecture models for performance,
reliability, and cost using evolutionary algorithms, (2010) 105-116.
[20] McCabe T.J. A complexity measure, IEEE Trans Software Eng 2 (4) (1976) 308-320.
[21] Microsoft. How to: Use X++ Syntax for CLR Arrays [AX 2012], 2012. Available at
https://msdn.microsoft.com/en-us/library/cc557456.aspx. Accessed 7/13/2016.
[22] Microsoft. Overriding a Method [AX 2012], 2012. Available at
https://msdn.microsoft.com/en-us/library/aa880278.aspx. Accessed 7/11/2016.
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
20
[23] Microsoft. update_recordset [AX 2012], 2012. Available at
https://msdn.microsoft.com/en-us/library/aa674382.aspx. Accessed 9/26/2016.
[24] Microsoft. X++ Language Programming Guide [AX 2012], 2012. Available at
https://msdn.microsoft.com/nl-nl/library/aa867122.aspx. Accessed 5/23/2016.
[25] Microsoft. Best Practices for Indexes [AX 2012], 2011. Available at
https://msdn.microsoft.com/en-us/library/aa617587.aspx. Accessed 9/26/2016.
[26] Microsoft. Intrinsic Functions [AX 2012], 2011. Available at
https://msdn.microsoft.com/en-us/library/aa626893.aspx. Accessed 9/11/2016.
[27] Microsoft. utcdatetime [AX 2012], 2011. Available at
https://msdn.microsoft.com/nl-nl/library/cc597805.aspx. Accessed 9/26/2016.
[28] Microsoft. GUID's, 2009. Available at https://msdn.microsoft.com/en-us/library/cc967363.aspx.
Accessed 9/26/2016.
[29] Microsoft. How to: Use #define and #if to Test a Macro [AX 2012, 2009. Available at
https://msdn.microsoft.com/en-us/library/cc197117.aspx. Accessed 10/11/2016.
[30] Microsoft. Common Language Runtime (CLR). Available at
https://msdn.microsoft.com/en-us/library/8bs2ecf4(v=vs.110).aspx. Accessed 7/13/2016.
[31] Parnas D.L. On the criteria to be used in decomposing systems into modules, Commun ACM 15
(12) (1972) 1053-1058.
[32] Poot E. & Trauschke O.C. software-evolution-assignment-1, 2015. Available at
https://github.com/edwardmp/software-evolution-assignment-1. Accessed 8/15/2016.
[33] Reddy S. Get underlying SQL query using getSQLStatement [Dynamics AX 2012], 2012. Available
at
https://dynamicsaxgyan.wordpress.com/2012/05/11/get-underlying-sql-query-using-getsqlstatement-dynamics-ax-2012/. Accessed 9/26/2016.
[34] Reddy S. Unchecked keyword in Dynamics AX [unchecked(Uncheck::TableSecurityPermission)]
using X++, 2011. Available at
https://dynamicsaxgyan.wordpress.com/2011/04/05/unchecked-keyword-in-dynamics-ax-uncheckedunchecktablesecuritypermission-using-x/. Accessed 9/26/2016.
[35] Rosenberg L., Hammer T. & Shaw J. Software metrics and reliability, 9th ISSRE (1998).
[36] Roy C.K., Cordy J.R. & Koschke R. Comparison and evaluation of code clone detection techniques
and tools: A qualitative approach, Sci Comput Program 74 (7) (2009) 470-495.
[37] Villadsen P. Macros - definitions and ptifalls, 2007. Available at
https://blogs.msdn.microsoft.com/x/2007/11/12/macros-definitions-and-pitfalls/. Accessed
7/11/2016.
[38] Visser J. SIG/TÜViT Evaluation Criteria Trusted Product Maintainability, version 6.1, (2014).
Appendix A: SIG Maintainability Model
The SIG Maintainability model is intended for standardized evaluation of the internal technical
quality of source code with respect to maintainability and its sub-characteristics (as listed in Table 1
on page 5) ([38], p.6). Maintainability and its sub-characteristics are qualified based on properties of
the analyzed source code: volume, duplication, unit complexity, unit size, unit interfacing, module
coupling, component balance and component independence ([38], p. 9). Below, these properties are
defined.
Volume is the size of the source code, expressed as the number of lines of code in it. Duplication
expresses (the part of the source code involved in) occurrence of identical fragments on multiple
places in the source code.
An earlier publication about the SIG Maintainability Model defines a unit as “the smallest piece of
code that can be executed and tested individually. In Java or C# a unit is a method, in C a unit is a
procedure” ([9], p. 33). Unit complexity expresses how hard an individual unit is to understand. Unit
size is the volume of a unit. Unit interfacing expresses the size of a unit’s interfaces in terms of the
number of parameters in the interface. Module coupling is the number of other modules a module
depends on [9]. The notion of module corresponds to a grouping of related units” ([38], p. 9). The
statement that “a module in Java or C# is a class” ([3], p. 12). Corresponds to the definition that
decomposition into modules can be based on information hiding [31].
“Component balance is the product of the system break-down, which is a rating for the number of
top-level components in the system, and the component size uniformity, which is a rating for the size
distribution of those top-level components. The notion of top-level components corresponds to the
first subdivision of the source code modules of a system into components, where a component is a
grouping of source code modules. (…) Component independence is a rating for the percentage of
code in modules that have no incoming dependencies form modules in other top-level components.
(…) The determination of these software product properties is based on objective, repeatable, and
accurate measurements at the level of source code lines, units, modules and components” ([38], pp.
9-10).
Evaluation
“The evaluation leads to an assignment of evaluation results as a number of stars between one and
five, where more stars represent better performance. The assignment is done for every product
property, for every quality sub-characteristic, and for the main characteristic of maintainability”
([38], p. 11). Table 4 shows which product property influences which sub-characteristic of
maintainability.
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
22
Table 4 Influence on sub-characteristics of maintainability per product property, ([38], p. 12)Maintainability
sub-characteristic
P
ro
d
u
ct
p
ro
p
ert
y
Vo
lu
m
e
D
u
p
licatio
n
Un
it size
Un
it
co
m
p
lexit
y
Un
it
in
te
rfaci
n
g
M
o
d
u
le
co
u
p
lin
g
Co
m
p
o
n
ent
b
ala
n
ce
Co
m
p
o
n
ent
in
d
epend
ence
Analyzability
x
x
x
x
Modifiability
x
x
x
Testability
X
x
X
Modularity
x
x
X
Reusability
x
X
A cross (X) indicates that a product property contributes to the maintainability sub—characteristic. All five
sub-characteristics contribute to the main quality characteristic of maintainability. The weights of the different contribution are discussed in the following chapter.
Appendix B: xpo-files
Objects exported from Microsoft Dynamics AX 2012 are saved in xpo-files. For classes, these files are
structured as follows: A file starts with some metadata about the export and the exported class (such
as version numbers). This is followed by a mark signaling the start of the contents of a class, of the
form CLASS #[class name]. At the end of the file, just before a final mark signaling the end of the
export (“***Element: END”), is a matching “ENDCLASS” mark. In between these two marks are two
other blocks. The first block starts with “PROPERTIES” and ends with “ENDPROPERTIES” and contains
metadata about the class, such as its name and location and which class it extends (if any). Names
and parents, are however also mentioned in class declarations. Therefore, we did not use this
metadata at all for our analyses.
The second block found between the marks signaling the start and end of the contents of classes
starts with a “METHODS” mark and ends with an “ENDMETHODS” mark. Between these marks, one
block can be found for each method in the export, as well as one for the class declaration, if present
in the export. These blocks all start with a mark of the form SOURCE #[method name] and end with
an “ENDSOURCE” mark. In case of a class declaration, “classDeclaration” is used as method name.
Between these marks, the contents of a method or class declaration can be found, with each line
preceded by a number of whitespace characters and a hash. Listing 1 below gives a graphical
overview of the contents of xpo-files representing classes.
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
24
Listing 1 xpo-file formatSquare brackets indicate places in the file structure where concrete values are, that can differ per
file. Ellipses indicate preceding items can be present zero or more times.
[Metadata about export and exported class]
CLASS #[Class name]
PROPERTIES
[Property] [Value]
…
ENDPROPERTIES
METHODS
SOURCE #[method name]
#[source code line]
…
ENDSOURCE
…
SOURCE #classDeclaration
#[source code line]
…
ENDSOURCE
SOURCE #[method name]
#[source code line]
…
ENDSOURCE
…
ENDMETHODS
ENDCLASS
***Element: END
Appendix C: X++ grammar
When concepts were found in the grammar used by the X++ parser without any definition,
definitions were searched via Google and validated by searching the analyzed source code for the
found definitions.
Listing 2 X++ grammar
/*When empty directives were removed, Function and Intrinsics became special cases of Eval and were therefore removed*/
keyword Reserved = TrueSym | FalseSym | NullSym | SuperSym;
keyword FuncReserved = EventhandlerSym | SuperSym | ReturnSym | ThrowSym;
keyword TypeReserved = StrTypeSym | IntTypeSym | Int64TypeSym | DblTypeSym | DateTypeSym | DatetimeTypeSym | GuidTypeSym | QueueTypeSym | VoidTypeSym | AnyTypeSym
| ThrowSym | ReturnSym | SelectSym | DeleteSym | NextSym | FlushSym | PrintSym;
layout Layout = LayoutOccurence* !>> Whitespace !>> "//" !>> "/*" !>> "#if.never";
lexical LayoutOccurence = Whitespace | SingleLineComment | MultiLineComment
| NeverExecutedCode; //Code containns non-breaking space characters (U+00A0)
keyword Whitespace = [\ \t\n\r\u00A0];
lexical SingleLineComment = BeforeCommentStart
<< ActualSingleLineComment;
lexical BeforeCommentStart = ^NoIncompleteStrings;
lexical NoIncompleteStrings = NoIncompleteString*;
lexical NoIncompleteString = NoQuotes | StrSym;
lexical NoQuotes = ![\'\"];
lexical ActualSingleLineComment = "//" NotNewLine* "\n";
lexical NotNewLine = ![\n];
lexical MultiLineComment = BeforeCommentStart << ActualMultiLineComment;
lexical ActualMultiLineComment = "/*" NotMultiLineCommentEnd* "*/";
lexical NotMultiLineCommentEnd = NotStar | StarNotFollowedBySlash;
lexical NotStar = ![*];
lexical StarNotFollowedBySlash = "*" !>> "/";
syntax NeverExecutedCode = MacroStart IfNever EndIfClause | MacroStart IfNeverClause;
syntax IfNever = "if.never";
lexical IfNeverClause = IfNever Whitespace+ NotEndIfWords Whitespace+ EndIfClause | IfNever Whitespace* SemicolonSym
Whitespace+ (NotEndIfWords Whitespace+)? EndIfClause;
lexical NotEndIfWords = (NotEndIfWords Whitespace+)? NotEndIfWord;
lexical NotEndIfWord = Word \ EndIfSym;
lexical Word = ![\ \t\n\r\u00A0]+ !>> ![\ \t\n\r\u00A0];
On the Impact of ‘Fork and Modify’ Customizations on Source Code Metrics in X++
26
keyword EndIfSym = "#endif" | "#Endif" | "#endIf" | "#EndIf" | "#ENDIF" | "#endif;"| "#Endif;" | "#endIf;" | "#EndIf;" | "#ENDIF;";
//Body
start syntax ClassDecl = MacrosBeforeClass ClassHeader LeftbrSym DclList? FuncDecls? RightbrSym;
syntax MacrosBeforeClass = (CallToMacro | MacroDefinition)*;
syntax ClassHeader = AttributeDef? ClassMods? Classorinterface StdId ExtendsClause?
ImplementsClause?;
syntax AttributeDef = LeftBrktSym AttributeInit? AttributeList RghtBrktSym;
lexical AttributeInit = ".";
syntax AttributeList = (AttributeList ListSepSym)? Attribute;
syntax Attribute = StdId
| AttributeWithArgsBegins AttributeWithArgsEnds | CallToMacro Attribute?;
syntax AttributeWithArgsBegins = StdId LeftParSym;
syntax AttributeWithArgsEnds = AttributeArgs? RghtParSym;
syntax AttributeArgs = (AttributeArgs ListSepSym)? AttributeConstant;
syntax AttributeConstant = IntSym | DblSym | StrSym | DateSym | DatetimeSym | StdId DblcolonSym StdId | True | False | AttributeIntrinsic | CallToMacro;
syntax AttributeIntrinsic = StdId LeftParSym IArgs? RghtParSym;
syntax IArgs = StrSym | StdId (ListSepSym StdId)?;
syntax ClassMods = ClassMods? ClassModifier;
syntax ClassModifier = Public | Final | Static | Abstract | Private;
syntax Classorinterface = Class | Interface;
syntax ExtendsClause = Extends StdId;
syntax ImplementsClause = Implements Implementlist;
syntax Implementlist = (Implementlist ListSepSym)? StdId;
syntax FuncDecls = FuncDecls? (FuncDecl
| MacroInsteadOfFuncDecl);
syntax MacroInsteadOfFuncDecl = CallToMacro | MacroDefinition | LocalOrGlobalMacroClause;
syntax FuncDecl = FuncHead Body;
syntax FuncHead = AttributeDef? Funcname ParmDclList;
syntax Funcname = Functype FuncId;
syntax Functype = FuncMods? DeclType;
lexical FuncId = ([_@a-zA-Z][_@a-zA-Z0-9]*) !>> [_@a-zA-Z0-9] \ FuncReserved;
syntax FuncMods = FuncMods? FuncModifier | FuncMods CallToMacro;
syntax FuncModifier = Public | Private | Protected | Final
| Static | Abstract | Display | Edit | Server | Client | Delegate;