2. CodeDog language reference¶
2.1. CodeDog Files¶
A CodeDog program is defined in a text file. By convention filenames have a .dog extension.
2.1.2. #include files¶
CodeDog files may directly include the contents of other files by with an include directive.
#include colorDefinitions.dog
#include includeFiles/extraData.dog
CodeDog tries to find included files in three places, in order, before giving up. They are:
The current working directory
The directory where codeDog is located
The directory where the current source file is
2.2. The Three Sections of a CodeDog Program¶
Starting from the top of the dog file to the bottom there are three sections to every CodeDog program. They are called the buildSpecs section, the tags section and the class definition section.
2.2.1. Define your builds with buildspecs¶
The first section, called “BuildSpecs” is where the different builds requested for this program are defined.
Example:
LinuxBuild: Platform='Linux' CPU='amd64' Lang='CPP' optimize='speed';
SwingBuild: Platform='Java' CPU='JavaVM' Lang='Java' optimize='speed';
AndroidBuild: Platform='Android' CPU='JavaVM' Lang='Java' optimize='power';
iPhoneBuild: Platform='IOS' CPU='amd64' Lang='Swift' optimize='speed';
MacBuild: Platform='MacOS' Lang='Swift' optimize='speed';
This code specifies to build five variations of the program. The identifier at the beginning of each line is the name of a folder to place the build in. Next, after the colon, is a list of tags which define the build. Tags will be discussed in the next section, But they can give information to CodeDog such as which platform to build for.
Note
Unless there are specific libraries and classes that can use tags such as ‘optimize’ or ‘CPU’, these tags do not do anything. At the current time many libraries and classes can be selected by Platform and Lang tags. When such classes are written tags that specify the CPU or the size of the L2 cache, for example, could cause those optimized classes to be selected in the right situation.
Note
Leaving out the buildSpecs causes CodeDog to build for the current platform.
If you wish to separate the specifications of which builds to do from your code, place the buildSpecs in a separate file and #include it near the top of the dog file.
2.2.3. Write your classes and apply any patterns¶
The third section of a CodeDog program is where classes for the program are defined. CodeDog’s concept of classes is somewhat different than the typical object oriented class. The concepts, syntax and usage are described in the next chapter.
2.3. Classes¶
A recurring theme regarding CodeDog’s class definitions, and data types in general, is that there is a wealth of information about what your program is supposed to do or not do captured in the definition of a type. Languages that try to infer too much about the type of your data miss out on the opportunity to help in the creation of a program. Looking at a class definition you almost have enough information to figure out how to read or write that class to a file, or to a database. If it is known that a particular class is supposed to be filled in by a user, there is almost enough information to let a programmer create a GUI for the task. Almost. Classes in CodeDog are slightly different in several ways that allow them to be used for such tasks.
A common way of thinking about classes in programming languages is that they are a way to package data into a single ‘chunk’ together with the ‘methods’ or action that can be performed on that data. This perspective is not wrong, but there is a more useful way of looking at classes that explains several of the unusual aspects of CodeDog classes.
Think of classes as a ‘map’ of a chunk of information. If we are given a chunk of information, what data items are inside it and where are they located? In addition, what actions can be performed on that data?
2.3.1. Kinds of classes¶
The term “class” in CodeDog is not actually a keyword in the language. Instead, there are three ways to define a map of information. The keywords are ‘model’, ‘struct’, and ‘string’; all three are called classes.
2.3.1.2. Models¶
For all but the simplest situation, classes have some fields that are considered an integral part of the class and others that are merely implementation details. Typically, languages let the programmer distinguish the ‘essential’ fields from the implemetation details fields by marking them public or private. The idea is that public fields are those an outside programmer may use and private fields are those used internally by the class. This is not the best way of making the distinction. There may be some public fields that are needed by the programmer but which are not a part of the ‘essence’ of the class. For example, a function to dump the contents of a class may be public but it is not part of the essence of the class. This becomes important in CodeDog when the language needs to know which fields are the important ones. In addition, all the markings of public and private can really become complicated and clutter a program. To handle various cases languages add “friend” classes which are partly public and partly private. And the complexity grows.
Models vs structs: The first difference is that instead of public and private fields, CodeDog considers fields defined as a model to be the essential fields for the class and fields defined as a struct are considered non-essential.
Entending classes: The second difference is that classes defined under the same name are extensions of the same class. For example, if you define a model named “personRec’ with fields ‘name’ and ‘age’, you could later define a struct named personRec with fields ‘calculateAge()’ and ‘dumpStatus()’. The resulting class would have all the fields. If later you want to make a parser for this class, CodeDog can help. But it will not try to parse the struct fields; just the model fields.
Models are abstract: A third difference is that models are considered ‘abstract’ classes. They define what fields are in a class but not where they are. Therefore, if a class is defined only as a model it cannot be instantiated.
To be able to instantiate a model make a struct form of it.
model shape {
me Color: color
me mode[circle, square, rectangle, oval]: shapeType
}
// By itself, shape cannot be instantiated. We must add the following line:
struct shape{}
// We can also add more fields:
struct shape{
me bool: isPreferredShape
}
2.3.1.3. Structs¶
Classes denoted *struct*s are similar to classes of other languages and can be implemented.
2.3.1.4. Strings¶
Classes denoted *struct*s are sequences of bytes. They typically have a well defined size that does not change. Furthermore, they are typically considered to reside in memory.
Classes declared with string are maps of sequences of bytes which can vary in size. They are maps of strings. Moving data from a string class to a struct class requires a parser. These can often be generated automatically by CodeDog. String classes are described in detail in the section Generating Parsers.
2.3.1.5. A struct named GLOBAL¶
CodeDog uses the special struct named GLOBAL to represent fields that should have a global scope. To make a variable with global scope, simply place it inside the GLOBAL struct. The following example declares a global integer named globalCount and on program start it is initialized to 1.
struct GLOBAL{
me int: globalCount <- 1
}
In languages (Java) that are unable to support global variables, a commonly used work-around is used to get the same effect as global scope.
2.4. Fields¶
2.4.1. Data types and ownership¶
When declaring variables in CodeDog, both an ownership type and data type are specified. Data Types
2.4.1.1. Ownership¶
In CodeDog, variables are assigned an ownership type. The ownership type of an object tells CodeDog where to allocate memory and when to release the memory. For strictly Python users, this may be a foreign concept. For C++ users, they translate to objects, pointers, shared pointers, etc. This is a part of CodeDog because it translates your program to languages that have very different approaches to ownership, without you needing to know the details of each language.
An analogy for considering ownership types for the uninitiated is to think of a person as a class, we’ll call her Susie. Susie has integral components, such as an arm or head, and these are represented in CodeDog ownership as type “me”. It doesn’t make sense to reference them as separate from Susie, they’re an integral part of her. Those are “me”s.
Susie’s class may reference a belonging, such as a bike, that belongs to her but isn’t an integral part of her (unless she commutes on it, in which case it may feel like an appendage!) This is represented in CodeDog ownership by “my”, because it belongs to her but it isn’t an integral part. It could be lent to someone else to make use of it but it isn’t theirs to destroy, that right remains with Susie. She is the one that releases it from memory when it reaches the end of its life.
Susie’s class may reference something that she shares with many different people, possibly at the same time. Like a bus, Susie uses the bus, but so do others. In CodeDog this ownership is “our”. None of the users of “our” items have to destroy the “our” objects. The system will destroy them when the time is right.
Finally Susie’s class may also contain a reference to someone else’s stuff, like Dad’s car. Susie can use it but it’s not hers to destroy. This is represented in CodeDog ownership as “their”. It points to a reference in memory allocated somewhere else that Susie isn’t responsible for releasing.
In CodeDog memory releasing is done for you, so you won’t be manually releasing memory. But the analogy can be useful for wrapping your head around when memory is being freed.
Common ownership types
me, our, their
me - a part of its class or function, this is the object itself as a local or global variable or an argument in a function. As soon as the object it is in is erased from memory or the program exits the scope it’s declared in, any “me” elements are released from memory. If no ownership type is specified, CodeDog will default to “me”.
our - the “our” element is a reference to a variable that other classes or functions may also have a reference to. When the program exits the scope of an “our” element, the “our” element on the stack is freed, but if other things are still referencing the same variable, the variable itself will not be freed. Only when all pointers to a location are freed will the referenced variable be freed. This is how most variables in Java work and in C++ codeDog implements and manages them as a shared pointer.
their - the “their” element is a small piece of memory that points to a location in memory on the heap that is managed by another class. In CodeDog “their” is less commonly used than “me” and “our”, it is used primarily for pointers received from an API or another class. Analogous to an unmanaged pointer in C++. Advanced ownership types
The ownership types of “my”, “we” and “itr” are intended for more advanced use cases and will be explained later.
2.4.1.2. Basic Data Types¶
The basic data types built into CodeDog, each followed by an example that declares & assigns a variable of that type. Booleans
A boolean with possible values of “true” and “false”
me bool: isOn <- true
2.4.1.3. Text¶
char
A “char”, limited to a string literal of length one.
string
A “string” of text characters of arbitrary length.
me char: firstInitial <- "M"
me string: firstName <- "Matt"
User Strings
Some strings are specifically meant to be messages presented to users. These are enclosed in single quotes. User strings undergo extra processing so do not use them unless the extra functionality is warrented.
They can be interpolated
In the future they will be used when internationalizing a program to provide program wide translations to other languages.
2.4.1.4. Numeric values¶
C++ style numeric data types are built in, supporting signed and unsigned integers of 32 and 64 bit, as well as a double.
me int: age <- 27
me int32:
me int64:
me uint32:
me uint64:
me double: pi <- 3.1415926535897
Ranges
Flags
Modes
A range of integers can be specified. Currently this defaults to int. In the future this will be adjusted to choose an optimal data type and restrict values to the specified range.
me 0..23: hour <- 17
2.4.2. Declaring Fields¶
Name and field and optionally give an initial value
2.5. Functions¶
Fields that are declared with an argument list in the name are considered functions.
me int: addOne(me int: num) <- {
return(num + 1)
}
2.6. Expressions and Operators¶
The following operators are supported in CodeDog: Basic Arithmetic:
12 + 3 // addition, evaluates to 15
11 - 4 // subtraction, evaluates to 7
2 * 6 // multiplication, evaluates to 12
4 / 2 // division, evaluates to 2
5 % 2 // modulus, evaluates to 1
Equality:
myInt == 5 // test for equality, this evaluates to true if myInt is 5
counter != 10 // test for inequality, this evaluates to true if counter is not 10
ourObj1 === ourObj2 // test for pointer equality, this evaluates to true if both point to the same object
There are also the standard <, <=, > and >= operators for comparison.
Logical Operators:
!
and
or
Bitwise Operators:
| // bitwise OR
& // bitwise AND
^ // bitwise XOR
In CodeDog, operator precedence is the same as in C++ and most C-like languages.
2.7. Actions¶
2.7.1. New variable declarations and assignment Actions¶
Declare variables inside functions the same way they are declared as fields in classes.
me int: A <- 5
Compound Assignments
Compound assignments can be made by inserting an operator in the middle of an assignment operator. For example, inserting the sum operator between the leading “<” and the closing “-” of a standard assignment operator, “<+-“.:
counter <+- 1 // shorthand for counter <- counter + 1
counter <-- 1 // shorthand for counter <- counter - 1
multiplier <*- 2 // shorthand for multiplier <- multiplier * 2
multiplier </- 2 // shorthand for multiplier <- multiplier / 2
<%-
<|-
<&-
<^-
<deep- // copy the contents even if there is a pointer
2.7.2. Function call Actions¶
Call a function in the normal way:
a <- sqrt(9)
2.7.3. Conditional Actions¶
2.7.3.1. if / else¶
if(day=="Saturday"){
print("It's the weekend.")
}else if(day=="Monday"){
print("Coffee Please.")
}else{
print("Another day another dollar.")
}
Notice that after an else curly braces are not needed if the next statement is an if.
2.7.3.2. switch¶
switch(day){
case SAT:{print("It's the weekend.")}
case MON:{print("Coffee Please.")}
case default:{print("Another day another dollar.")}
}
Switch cases do not need to have a break at the end.
2.7.4. Repetition Actions¶
2.7.4.1. withEach¶
The withEach action is CodeDog’s primary construct for repetition over collections, ranges, and other iterable sources.
It expresses intent rather than a specific loop structure, allowing CodeDog to choose an appropriate implementation for the
target language and data structure.
At a high level, withEach has three parts:
Binding – what variable(s) you want to bind on each iteration
Source – what you are iterating over
Optional modifiers – conditions that affect loop behavior
2.7.4.1.1. Basic container traversal¶
The simplest form iterates over all values in a container.
me List<me int>: data {5, 7, 9}
withEach num in data{
print(num)
}
In this form:
numis bound to each element in the containerThe iteration order depends on the container and target language
CodeDog selects an efficient iteration strategy automatically
2.7.4.1.2. Traversal direction¶
Some containers support reverse traversal. You may request this explicitly:
withEach num in data Backward{
print(num)
}
If the underlying container does not support backward traversal, CodeDog will issue an error.
2.7.4.1.3. Binding modes¶
The variable bound by withEach can represent different aspects of the container element.
This is controlled by an optional binding axis specifier placed before the variable name.
The general form is:
withEach [axis] name in source { ... }
If no axis is specified, CodeDog chooses a default based on the container type.
2.7.4.1.3.1. Value binding (default)¶
The value axis binds the loop variable to the element value.
For list-like containers, this is the default.
withEach value num in data{
print(num)
}
This is equivalent to omitting the axis:
withEach num in data{
print(num)
}
2.7.4.1.3.2. Entry binding (associative containers)¶
The entry axis binds the loop variable to the container’s entry object
(key/value pair for maps and multimaps).
withEach entry e in myMap{
print(e)
}
The exact type of e depends on the container and target language.
2.7.4.1.3.3. Tuple binding (key, value)¶
For associative containers, entries can be destructured directly.
Tuple binding always implies the entry axis.
withEach (key, value) in myMap{
print(key)
print(value)
}
Tuple binding is only valid for containers that yield key/value entries.
2.7.4.1.3.4. Key binding¶
The key axis binds the loop variable to the key of each entry.
withEach key k in myMap{
print(k)
}
This form is only valid for associative containers.
2.7.4.1.3.5. Iterator binding¶
The iter axis binds the loop variable to the iterator itself.
withEach iter it in data{
it.eraseCurrent()
}
This form is intended for advanced use cases such as modifying a container during traversal. Whether it is allowed depends on the container’s capabilities.
2.7.4.1.3.6. Notes on binding defaults¶
If no axis is specified: * List-like containers default to
value* Associative containers default toentryTuple binding always implies
entryInvalid axis/container combinations result in a compile-time error
2.7.4.1.4. Numeric range traversal¶
You can iterate over an integer range using in range: syntax.
withEach i in range: 1..10{
print(i)
}
This iterates from 1 up to but not including 10.
2.7.4.1.4.1. Inclusive ranges¶
Use ..= to include the end value:
withEach i in range: 1..=10{
print(i)
}
Numeric ranges require:
A single binding variable
Both start and end expressions
2.7.4.1.5. Container sub-ranges¶
For some containers, you may specify a sub-range of iteration. The available range modes depend on container capabilities.
2.7.4.1.5.1. Iterator ranges¶
Iterate between two iterators:
withEach value in data iters: startItr..endItr{
print(value)
}
Iterator ranges are exclusive of the end iterator.
Inclusive (..=) iterator ranges are not allowed.
2.7.4.1.5.2. Key ranges (ordered associative containers)¶
For ordered associative containers, you may iterate over a key range:
withEach (k, v) in myMap keys: 10..=20{
print(k)
print(v)
}
This iterates over entries whose keys fall within the specified range.
2.7.4.1.6. Loop modifiers¶
Modifiers refine the behavior of a withEach loop.
2.7.4.1.6.1. where¶
Skip iterations that do not satisfy a condition:
withEach num in data where(num > 5){
print(num)
}
2.7.4.1.6.2. until¶
Terminate the loop when a condition becomes true:
withEach num in data until(num == 9){
print(num)
}
where and until conditions are evaluated after binding on each iteration.
2.7.4.1.6.3. Notes on semantics¶
withEachexpresses what you want to iterate over, not howCodeDog selects iteration strategies based on: * Container capabilities * Target language * Performance tags and constraints
Some combinations (e.g., backward traversal, tuple binding, iterator mutation) are only valid for certain containers
When an unsupported combination is used, CodeDog reports a clear error
2.7.4.2. while¶
The while action provides a traditional condition-controlled loop.
me bool: done <- false
while(! done){
done <- getStatus()
}
Use while when iteration does not naturally correspond to a collection or range.
2.1.1. Comments¶
CodeDog supports line and block comments. Line comments are from // to the end of the line. Block comments are between /* and */
To make comments that show up in the generated code place an asterisk after the comment symbol. For example, //* or /**…*/. These comments can only appear in certain places. The very first //* comment in a file is associated with the file. The comments before a class definition are associated with the class and can be used to autodoc. Comments before class field definitions or comments before lines of code are associated with the item after them.
//~ or /*~ are used to temporally comment out code for debugging.
//? or /*? are used to comment out code that should be saved “just in case”.
//! or /*! are used to comment out ‘in progress code’ that should be commented out until it works.
Comments such as //TODO:, //MUST: or //FIX: (or any of the form “//XXX:”) can be automatically writtem to a file to keep track of development tasks or requirements.