├── .editorconfig ├── .gitignore ├── README.md ├── old ├── anonymous-functions.md ├── associated-functions.md ├── async.md ├── book.md ├── borrow-checker.md ├── casting.md ├── composite-initializers.md ├── contracts-assertions.md ├── destructors.md ├── effects.md ├── enums.md ├── error-handling.md ├── exceptions.md ├── extensions.md ├── fields.md ├── functions.md ├── generators.md ├── generics.md ├── ideas-considering.md ├── ideas-rejected.md ├── introduction.md ├── lifetimes.md ├── object-literals.md ├── out-of-memory.md ├── ownership-summary.md ├── ownership.md ├── partial-classes.md ├── patterns.md ├── pseudo-references.md ├── reference-types.md ├── result.md ├── target.md ├── value-types.md ├── variable-bindings.md ├── variable-references.md └── version-numbers.md └── src ├── bitwise-operations.md ├── book.md ├── boolean-expression.md ├── choice-expressions.md ├── class-constructors.md ├── class-value-types.md ├── comments.md ├── conventions.md ├── conversions.md ├── documentation-comments.md ├── empty-types.md ├── enumerations.md ├── execution-order.md ├── expression-blocks.md ├── expressions.md ├── external.md ├── functions.md ├── glossary.md ├── grammars.md ├── ideas.md ├── identifiers.md ├── implementers-notes.md ├── influences.md ├── interpolated-strings.md ├── keywords.md ├── lexical-analysis.md ├── literals.md ├── localization.md ├── loop-expressions.md ├── member-access.md ├── namespaces.md ├── newlines.md ├── operator-overloading.md ├── operators-and-punctuators.md ├── optional-types.md ├── packages.md ├── planned-aliases.md ├── planned-ctfe.md ├── planned-expressions.md ├── planned-features.md ├── planned-lop.md ├── planned-operators.md ├── planned-qualifier.md ├── planned-types.md ├── pointer-types.md ├── pointers.md ├── ref-types.md ├── reference-types.md ├── reserved-words.md ├── simple-types.md ├── statements.md ├── std-lib-global-namespace.md ├── struct-constructors.md ├── struct-initializers.md ├── struct-types.md ├── structs.md ├── syntactic-analysis.md ├── syntax-reference.md ├── system.collections.md ├── system.collections.specialized.md ├── system.io.md ├── system.math.md ├── system.md ├── system.memory.md ├── system.text.md ├── tokens.md ├── traits.md ├── tuple-types.md ├── type-expressions.md ├── types.md ├── unsafe.md └── whitespace.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 4 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | indent_style = space 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Adamant Language Reference 2 | 3 | Will be a complete, though informal, reference to the entire Adamant language. 4 | 5 | Browse the [Table of Contents](src/book.md). 6 | 7 | Note: The [Old Version](old/book.md) is still available while its sections are being moved over. If something is missing from the new version, it may be in the old version. 8 | 9 | ## Influences 10 | 11 | The structure and content of this reference has been influenced by the following sources: 12 | 13 | * *C# 6.0 draft language specification*: [source](https://github.com/dotnet/csharplang/tree/master/spec), [rendered](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/) (table of contents, structure, grammars, and tone) 14 | * *The Swift Programming Language* Language Guide: [rendered](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html) (table of contents, structure, and tone) 15 | * *The Rust Programming Language* (2018 Edition): [source](https://github.com/rust-lang/book), [rendered](https://doc.rust-lang.org/book/2018-edition/) (markdown file structure and small chapters) 16 | -------------------------------------------------------------------------------- /old/anonymous-functions.md: -------------------------------------------------------------------------------- 1 | # Anonymous Functions 2 | 3 | Anonymous functions allow the creation of unnamed functions inside another function. 4 | 5 | ```adamant 6 | let add_two = fn(x) => x + 2; // addTwo: (int) -> int 7 | ``` 8 | 9 | The parameter types can be inferred. However, sometimes it is necessary or clearer to directly state them. 10 | 11 | ```adamant 12 | let say_hello = fn(name: string) => "Hello " + name + "."; 13 | ``` 14 | 15 | The above examples are simple functions that only require a single expression to compute. However, an anonymous function can have a complete function body by using a block. 16 | 17 | ```adamant 18 | let repeat_n = fn(n, func) 19 | { 20 | foreach _ in 0.. (int) -> int 33 | { 34 | return fn x => x + n; 35 | } 36 | ``` 37 | 38 | **TODO:** explain the complexities of closures in a language with a borrow checker 39 | -------------------------------------------------------------------------------- /old/associated-functions.md: -------------------------------------------------------------------------------- 1 | # Associated Functions 2 | 3 | Like Rust, any function in a class that does not take a self argument is an associated function. -------------------------------------------------------------------------------- /old/book.md: -------------------------------------------------------------------------------- 1 | # The Adamant Programming Language Reference 2 | 3 | Note: This is the old version of the reference. Sections are being moved from the old version to the new version. If something is missing, it may be documented in the [new version](../src/book.md). 4 | 5 | * [Introduction](introduction.md) 6 | * [Target](target.md) 7 | * [Variable Bindings](variable-bindings.md) 8 | * [Functions](functions.md) 9 | * [Anonymous Functions](anonymous-functions.md) 10 | * Standard Types 11 | * [Composite Initializers](composite-initializers.md) 12 | * [Ownership](ownership.md) 13 | * [Reference Types and Borrowing](reference-types.md) 14 | * [Value Types and Copy](value-types.md) 15 | * [Variable References](variable-references.md) 16 | * [Lifetimes](lifetimes.md) 17 | * [Pseudo References](pseudo-references.md) 18 | * [Summary of Ownership](ownership-summary.md) 19 | * [Formal Description](borrow-checker.md) 20 | * [Patterns](patterns.md) 21 | * Classes 22 | * [Fields](fields.md) 23 | * Methods (including `open`) 24 | * [Associated Functions](associated-functions.md) 25 | * [Destructors](destructors.md) 26 | * [Object Literals](object-literals.md) 27 | * [Extensions](extensions.md) 28 | * [Partial Classes](partial-classes.md) 29 | * [Enums](enums.md) 30 | * [Generators](generators.md) 31 | * [Async and Await](async.md) 32 | * [Error Handling](error-handling.md) 33 | * [Contracts and Assertions](contracts-assertions.md) 34 | * [Exceptions](exceptions.md) 35 | * [Results](result.md) 36 | * [Out of Memory and Stack Overflow](out-of-memory.md) 37 | * [Effects](effects.md) 38 | * Error Model Summary 39 | * [Generics](generics.md) 40 | * Const and Static 41 | * Params and List Initializers 42 | * [Casting with `as`](casting.md) 43 | * Testing 44 | * Standard Library 45 | * Reflection 46 | * [Version Number Scheme](version-numbers.md) 47 | * Feature Ideas 48 | * [Considering](ideas-considering.md) 49 | * [Rejected](ideas-rejected.md) 50 | -------------------------------------------------------------------------------- /old/casting.md: -------------------------------------------------------------------------------- 1 | # Casting with `as` 2 | 3 | `as!` will cast a value to a given type and panic if the cast fails. `as?` will cast a value to the optional of the given type, returning error `Result` if the cast fails. `as` will perform a statically safe cast. That is a cast up or widening conversion 4 | 5 | NOTE: downcasting is never allowed. 6 | -------------------------------------------------------------------------------- /old/composite-initializers.md: -------------------------------------------------------------------------------- 1 | # Composite Initializers 2 | 3 | Adamant supports three different forms of composite initializers. Similar user defined literals, these initializers do not determine the type being constructed. The type constructed is inferred. These composite initializers are named after the type they are generally used to construct, but they actually could be used to construct any type. All of them are prefixed with a pound sign that is followed by a comma separated list of values, the difference it simply in what those values are bracketed with. The three types of initializers are: 4 | 5 | | Initializer | Syntax | 6 | | ----------- | --------- | 7 | | Tuple | `#(x, y)` | 8 | | List | `#[x, y]` | 9 | | Set | `#{x, y}` | 10 | 11 | ## Construction 12 | 13 | Composite initializers invoke special constructors whose names mirror the syntax. There arguments can be one of a number of forms. 14 | 15 | ```adamant 16 | public class Example 17 | { 18 | // Tuple Constructor with Regular parameters 19 | public implicit new #() (x: int, y: int) 20 | { 21 | // construct 22 | } 23 | 24 | // List Constructor with params constructor 25 | public implicit new #[] (params values: Array[int]) 26 | { 27 | // construct 28 | } 29 | 30 | // Set Constructor with params constructor 31 | public implicit new #{} (params values:) 32 | } 33 | ``` 34 | 35 | ## Typed Construction Syntax 36 | 37 | If needed or desired for clarity, the type of a composite initializer can be specified as `new Example #{x, y}` (or one of the other two initializer types). If the constructor is declared implicit, it can be called without specifying the type. If it is not implicit, the type must be stated. 38 | -------------------------------------------------------------------------------- /old/contracts-assertions.md: -------------------------------------------------------------------------------- 1 | # Contracts and Assertions 2 | 3 | ## Abandonment 4 | 5 | Abandonment conditions are allowed to report their error early. The compiler doesn't need to preserve there order relative to each other or other code. 6 | 7 | ## Contracts 8 | 9 | Contracts are a special type of attribute. Like all attributes they are introduced by the at sign, but are followed by an expression instead of arguments. There are three kinds of contracts: 10 | 11 | * `@requires` - enforces preconditions. Has immutable access to all parameter values and the public interface of the current class. 12 | * `@ensures` - enforces postconditions. Has access to the original value of arguments, new and old state of the current class and return value. Can access the return value with the name `return` 13 | * `@invariant` - must hold before and after every method of a class. I assume this is only public methods, but perhaps there needs to be a way to declare invariants that apply to private methods as well. 14 | 15 | Not sure how contracts being attributes interacts with the fact that they need to apply to subtyping. 16 | 17 | ## Enforcement 18 | * By default, all contracts are checked at runtime. 19 | * The compiler is free to prove contracts false, and issue compile-time errors. 20 | * The compiler is free to prove contracts true, and remove these runtime checks. 21 | 22 | Which one happens varies by call site so that at one call site the compiler might prove a precondition satisfied while at another one there must be a runtime check. 23 | 24 | ## Accessing 25 | 26 | Midori used `old` keyword as a way to reference the previous state of `this` and then `old()` as a function of an argument to access the previous value of an argument. One concern about that is whether it might require copying the value. 27 | 28 | # Assertions 29 | 30 | Debug.Assert(condition); 31 | Release.Assert(condition); 32 | 33 | Both of these will panic if the condition is not true. Debug assertions are only checked when compiling for debug while release assertions are checked even in release builds. 34 | -------------------------------------------------------------------------------- /old/destructors.md: -------------------------------------------------------------------------------- 1 | # Destructors 2 | 3 | Destructors have an implicit `mut self` parameter. They do not take ownership of self so that they can't resurrect the instance. All types have an implicit destructor that calls delete on any owned objects. Destructors are executed most derived class first moving toward the base class. 4 | 5 | **TODO:** while it makes sense to allow destructors to call member functions like C++. Obviously, they shouldn't call `open` functions. But what if they call a method that calls an open function? 6 | 7 | ## Exceptions in Destructors 8 | 9 | All destructors implicitly are `no throw`. This is because destructors are called while exceptions are unwinding the stack. 10 | 11 | Alternatives: 12 | 13 | 1. Allow destructors to throw, but panic if this happens during stack unwinding 14 | 2. Allow destructors to throw, but give them a way to tell if it is during stack unwinding (i.e. std::uncaught_exception()) and choose what happens. This seems to be discouraged even in C++ 15 | 3. Allow destructors to throw, but enforce something about try catch blocks so that types with destructors that throw can never be destructed by stack unwinding. This seems very restrictive 16 | -------------------------------------------------------------------------------- /old/effects.md: -------------------------------------------------------------------------------- 1 | # Effects 2 | 3 | Generics over effects. For example (Code from Midori, syntax may differ): 4 | 5 | Map(T[] ts, Func func) -> U[] E 6 | { 7 | U[] us = new U[ts.Length]; 8 | for (int i = 0; i < ts.Length; i++) { 9 | us[i] = effect(E) func(ts[i]); 10 | } 11 | return us; 12 | } 13 | 14 | ## Other Effects 15 | 16 | Effects are written after the return type. Any given effect is marked as `may` or `no`. Omitted effects are inferred. Throws clauses are effects. The effects supported are: 17 | 18 | * unsafe - whether unsafe blocks can be executed 19 | * abandon - whether a function can cause abandonment, note that since contracts and assertions could cause abandonment, this requires that the compiler be able to prove that the contracts are satisfied. (What if you could distinguish different kinds of abandonment? Like contracts vs arithmetic overflow?) 20 | * allocate? - whether memory can be allocated (besides the stack?) 21 | * io? - separate for network, file and console? other graphics? 22 | * init? - prevent constructors from calling other methods? see [No Leaky Abstractions](http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/) 23 | * pure? 24 | * out of memory and stack overflow 25 | 26 | --- 27 | 28 | Notes from 2017-10-15 29 | 30 | Just as with inferred throws, it makes sense to have all effect types inferred except at public API boundaries where they have to be explicit. How do you support generic kinds of effects. How do you infer that an effect doesn't propagate. The syntax for effects could be `may` or `no`. So after the return type of a function could say `may effect` or `no effect`. Effects would be listed comma separated for example `may abandon, throw(exception1, exception2)`. In theory `throw exception1, throw exception2` are the same. 31 | 32 | Effects: 33 | * throw 34 | * abandon - preconditions and post conditions could cause abandonment. So I need to prove that they hold to be able to say something won't abandon 35 | * out of memory - separate because it seems more pervasive 36 | * arithmetic overflow cause abandonment 37 | * unsafe - even functions marked safe that call unsafe code would be `may unsafe` 38 | * is trusted an effect? 39 | 40 | Do effects have a hierarchy? How fine grained are effects. For example, a method that panics could do so because of overflow or precondition. Are there effects for IO? Is there a different effect for file and network IO. Given pervasive async/await, it seems like blocking shouldn't be an effect. However, a long running computation could kind of be like blocking. 41 | 42 | Perhaps it is valid to mark a function with a precondition as not panicking because the check of the precondition is effectively in the calling code not in the function itself? (At call site because some call sites prove the precondition is satisfied). 43 | -------------------------------------------------------------------------------- /old/enums.md: -------------------------------------------------------------------------------- 1 | # Enums 2 | 3 | An enum in Adamant is a type that can have one of a number of possible variants. Each variant can optionally have data associated with it. Both classes and structs can be enums. This is indicated by placing the keyword `enum` before the `class` or `struct` keyword. Enums are sometimes referred to as discriminated unions because they union together different types safely with a discriminator value. 4 | 5 | public enum class Result 6 | { 7 | Ok(T), 8 | Error 9 | } 10 | 11 | ## Enum Structs 12 | 13 | All enums are distinguished by a discriminator field created by the compiler. Enum structs can specify the exact type of this discriminator and the value of it for variants without data. The type of the discriminator follows the `enum` keyword with a colon and the discriminator type. 14 | 15 | public enum:int struct Day_Of_Week 16 | { 17 | Sunday = 0, // The value for Sunday is 0 18 | Monday = 1, // The value for Monday is 1 19 | Tuesday, 20 | Wednesday, 21 | Thursday, 22 | Friday, 23 | Saturday, 24 | } 25 | 26 | When a discriminator value is specified for a variant, each variant after without a discriminator value is inferred to have the discriminator one greater than the previous variant. 27 | 28 | ## Enum Forms 29 | 30 | There are a number of forms that an enum variant can take. 31 | 32 | ```adamant 33 | public enum struct Example 34 | { 35 | // TODO what are the naming conventions for enum values? 36 | InferredDiscriminator, // discriminator value unknown 37 | ExplicitDiscriminator = 1, // discriminator value is 10 38 | ImplicitDiscriminator, // follows explicit discriminator 1 so discriminator is 2 39 | Constructed = new(45), // call constructor, don't need to name the class, can call named constructor 40 | SubclassA 41 | { 42 | // new members 43 | }, 44 | SubclassB <: Interface // subclass implementing interface, again base class implied 45 | { 46 | // new members 47 | }, 48 | SubclassC: OtherBase <: Example // inherits another class, implements the interface of Example, not allowed for structs 49 | { 50 | // new members 51 | }, 52 | PatternType(type1, type2) // Allow names? auto create stuff? Combine with class members? 53 | 54 | // Members 55 | public let value: int?; 56 | 57 | public new() 58 | { 59 | self.value = none; 60 | } 61 | 62 | public new(value: int) 63 | { 64 | self.value = value; 65 | } 66 | } 67 | ``` 68 | 69 | ## Custom Matching 70 | 71 | Any type can be pattern matched. To control matching implement the `is` operator (TODO something better? scala uses unapply). This is modeled on C#. 72 | 73 | ```adamant 74 | public operator is(self, x: ref int, y: ref int) 75 | { 76 | x = self.X; 77 | y = self.Y; 78 | } 79 | ``` 80 | 81 | --------- 82 | ## Old Notes 83 | 84 | From http://tratt.net/laurie/blog/entries/another_non_argument_in_type_systems 85 | 86 | "The basic idea is that sum types are a super type that ranges over other types. Whereas in OO languages a sub-class explicitly chooses its super-class(es), sum types (the super class equivalent) choose the types they range over (the subclass equivalent) — the latter have no idea which sum types they are ranged over by." 87 | 88 | This might be a hint at how to define sum types. Perhaps you can literally say a type is the sum of existing types: 89 | 90 | // Possible ways to declare enum values 91 | public enum Example 92 | { 93 | Name, // A unit typed value, inherits Example 94 | Constructor(3.14), // A unit typed value calling the constructor of Example with 3.14 95 | Assignment = new() // A singleton like value, 96 | { 97 | }, 98 | AssignmentPlus = new :Something:Foo() // A singleton that doesn't actually have the type Example 99 | { 100 | }, 101 | Typed: Type, // Like a field that has that type, i.e. discriminated union 102 | Tuple: (int, int), // Same as before, except the type is a tuple, can also use structs? 103 | Struct: // An anonymous type that inherits Example 104 | { 105 | 106 | }, 107 | Class: class // basically an alternate to the syntax just above 108 | { 109 | }, 110 | } 111 | 112 | public enum DayOfWeek : int 113 | { 114 | Sunday = 0, 115 | Monday, 116 | Tuesday, 117 | Wednesday, 118 | Thursday, 119 | Friday, 120 | Saturday, 121 | } 122 | 123 | public enum Maybe 124 | { 125 | null, 126 | Value: T, // not sure right syntax here 127 | } 128 | 129 | public enum Planet 130 | { 131 | // TODO use correct case, I just copied a Java example 132 | MERCURY (3.303e+23, 2.4397e6), 133 | VENUS (4.869e+24, 6.0518e6), 134 | EARTH (5.976e+24, 6.37814e6), 135 | MARS (6.421e+23, 3.3972e6), 136 | JUPITER (1.9e+27, 7.1492e7), 137 | SATURN (5.688e+26, 6.0268e7), 138 | URANUS (8.686e+25, 2.5559e7), 139 | NEPTUNE (1.024e+26, 2.4746e7); 140 | 141 | public let Mass: double; 142 | public let Radius: double; 143 | 144 | private new (double mass, double radius) 145 | { 146 | Mass = mass; 147 | Radius = radius; 148 | } 149 | 150 | // universal gravitational constant (m3 kg-1 s-2) 151 | public let G: double = 6.67300E-11; 152 | 153 | public SurfaceGravity() => double 154 | { 155 | return G * Mass / (Radius * Radius); 156 | } 157 | public SurfaceWeight(double otherMass) => double 158 | { 159 | return otherMass * SurfaceGravity(); 160 | } 161 | } 162 | 163 | 164 | public enum Currency: Runnable 165 | { 166 | PENNY(1) 167 | { 168 | @Override 169 | public String color() 170 | { 171 | return "copper"; 172 | } 173 | }, 174 | NICKLE(5) 175 | { 176 | @Override 177 | public String color() 178 | { 179 | return "bronze"; 180 | } 181 | }, DIME(10) 182 | { 183 | @Override 184 | public String color() 185 | { 186 | return "silver"; 187 | } 188 | }, QUARTER(25) 189 | { 190 | @Override 191 | public String color() 192 | { 193 | return "silver"; 194 | } 195 | }; 196 | private int value; 197 | 198 | public abstract String color(); 199 | 200 | private Currency(int value) { 201 | this.value = value; 202 | } 203 | .............. 204 | } 205 | 206 | -------------------------------------------------------------------------------- /old/error-handling.md: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | 3 | Adamant's error handling model is built around a nuanced view of the different kinds of errors that can occur and how they should be handled. It handles errors in a variety of ways. 4 | 5 | ## Prevent Errors 6 | 7 | The first and best strategy for error handling is to prevent them or make them compile errors. Any time you write code that could error at runtime by any mechanism, ask yourself whether it could be redesigned to prevent that error at compile time. 8 | 9 | The Adamant language and standard library are built around this error handling approach because by greatly reducing the number of errors that can occur at runtime the correctness, performance and readability of the program is improved. 10 | 11 | For example, optional types entirely eliminate null pointer errors that bedevil code written in other languages. Instead looping through an array by index, the preferred idiom which the language directs programmers to is iterators. Code using iterators will never cause an array index out of bounds exception. At the same time, the code is usually simpler and better conveys the intent of the code. 12 | 13 | When reading about the error handling mechanisms available, remember that a large percentage of Adamant code is error free by design. It doesn't have to concern itself with any of these. 14 | 15 | ## Recoverable vs. Unrecoverable Errors 16 | 17 | The error handling model is focused around a principled distinction between recoverable and unrecoverable errors. 18 | 19 | * A *recoverable error* is an anticipated state detected by some programmatic validation. That is, the code has examined the state of the world and determined it is not possible to make progress on the computation in some manner. Recoverable errors generally include: 20 | * Invalid user input 21 | * Invalid data input (files, network data etc.) 22 | * Transient network failure 23 | * Hardware Failure 24 | 25 | In these cases the program is expected to recover. The developer must plan for and handle these errors. Possible responses include signaling the situation to the user, retry, or abandoning the current task. Despite being called an error, it is a *predictable* and *planned* situation. 26 | * An *unrecoverable error* is any kind of error the programmer didn't expect. That is to say that unrecoverable errors are *bugs*. Input may not have been properly validated. Logic may be written wrong. Often these are not detected at the moment they occur, but propagate through the program state until they are detected because of some unexpected consequence. By that point, nothing can be assumed. All program state is suspect. 27 | 28 | Most languages do not make the distinction between recoverable and unrecoverable errors or try to handle them in substantially similar ways. 29 | 30 | Given that bugs are inherently unrecoverable, Adamant treats them that way by *abandonment*. 31 | 32 | For a detailed analysis of error handling options, read about [The Error Model](http://joeduffyblog.com/2016/02/07/the-error-model/) of Midori by Joe Duffy. 33 | 34 | ## Unrecoverable Errors 35 | 36 | All bugs are unrecoverable errors and cause abandonment. Abandonment terminates the process without running any destructors or cleaning up anything. These bugs are caught and abandonment triggered by [contracts and assertions](contracts-assertions.md). 37 | 38 | Additionally, [out of memory errors and stack overflow](out-of-memory.md) are an edge case that are semi-recoverable, but can lead to abandonment. 39 | 40 | **TODO:** Need to create a way of having abandonment scopes that can be recovered. Rust recovery doesn't seem to provide adequate isolation 41 | 42 | ## Recoverable Errors 43 | 44 | Recoverable errors are further divided into errors which can typically be handled locally where they occur versus ones that typically must be non-locally handled farther up the call stack. The distinction between these two is fuzzy, so the recoverable error handling mechanisms let you mix the two and convert an error from one kind to the other. 45 | 46 | ### Non-local Error Handling 47 | 48 | Non-local errors are usually those involving file or network IO errors. Often the best response is to back out of what is being done. They are dealt with through [exceptions](exceptions.md). 49 | 50 | ### Local Error Handling 51 | 52 | Local errors are usually those involving input or data validation. Often the best response is to record the error, either by logging or reporting it to the user and then substituted a default or error indicating value. These errors are dealt with through the [result type](result.md). 53 | 54 | ## Effects 55 | 56 | Exceptions are actually just one instance of a more general mechanism called effect typing which allows you to manage the side effects and attributes of your code. There are [effects](effects.md) for tracking many different things. 57 | -------------------------------------------------------------------------------- /old/extensions.md: -------------------------------------------------------------------------------- 1 | # Extensions 2 | 3 | A class can be extended with additional traits and methods using an extension. To use an extension, you must use the namespace it is declared in. 4 | 5 | ```adamant 6 | public extend class Class <: Trait 7 | { 8 | } 9 | ``` 10 | 11 | Note: extensions have access to protected members of their class 12 | 13 | ## Notes 14 | 15 | Swift protocols mix required methods which "dispatch dynamically" with extensions which "dispatch statically". That seems really confusing. This is connected to the idea in Rust that you don't arbitrarily extend structs, but rather, you implement traits for them. That provides structure to the idea of dispatch. You are adding that functionality only when you can see it has having that type. 16 | 17 | Another way to think of this is the different between interface methods in C# and extension methods. That makes it seem like the two should be clearly different syntaxes. Luckily, we have a ready made syntax for this. Consider the following function outside any class. 18 | 19 | ```adamant 20 | public fn my_method(self: Example) -> int 21 | { 22 | return self.field * 2; 23 | } 24 | ``` 25 | 26 | That clearly looks like an extension that is outside of the class and would be statically dispatched. It could be invoked using regular function syntax by qualifying it with the namespace. This even allows extension properties and operators! (Though operators should perhaps just be static functions instead) 27 | 28 | ```adamant 29 | public fn get property(self: Example) -> int 30 | { 31 | return self.field * 2; 32 | } 33 | 34 | public fn operator +(self: int, x: Example) -> int 35 | { 36 | // ... 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /old/fields.md: -------------------------------------------------------------------------------- 1 | # Fields 2 | 3 | Fields are basically the same as variable bindings. If public, fields can be overridden in subclasses using accessors. 4 | 5 | ## Lifetimes -------------------------------------------------------------------------------- /old/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Every Adamant program has at least one function, the main function: 4 | 5 | public Main() -> void 6 | { 7 | } 8 | 9 | This is the simplest possible function. It is public, meaning it is visible everywhere including outside of this package (see [Access Modifiers](access-modifiers.md). That is followed by the name of the function. The empty parentheses indicate the function takes no arguments. The `->` indicates this is a function declaration and is followed by the return type. This function doesn't return a value which is indicated with the special type `void`. 10 | 11 | So how to take arguments? Here is a main function that takes the console as an argument, so it can print hello world. 12 | 13 | public Main(console: mut Console) -> void 14 | { 15 | console.WriteLine("Hello World"); 16 | } 17 | 18 | Function parameters are declared like `let` bindings. In fact, they are completely equivalent to them except the type must always be fully annotated. No type inference is done on function parameters. Multiple arguments are separated by commas. In the rare circumstance where a parameter needs to be a mutable binding, preceded it with the var keyword. 19 | 20 | public WriteIt(console: mut Console, var x: int) -> void 21 | { 22 | console.WriteLine("x = {x}"); 23 | x -= 1; // we can assign a new value to `x` because we declared it with var 24 | console.WriteLine("x - 1 = {x}"); 25 | } 26 | 27 | ## Returning from a Function 28 | 29 | To return a value simply declare the correct return type and use a return statement. 30 | 31 | public AddTwo(x: int) -> int 32 | { 33 | return x + 2; 34 | } 35 | 36 | To return from a function whose return type is `void`, use a return statement without a value, as `return;`. 37 | 38 | ## Diverging Functions 39 | 40 | Adamant has special syntax for [diverging functions](https://en.wikipedia.org/wiki/Divergence_(computer_science)). Those are functions that never return by normal means. That could be because they always cause a panic (terminate the program), throw an exception or loop forever. 41 | 42 | public Diverges() -> never 43 | { 44 | Panic("Stop the program now"); 45 | } 46 | 47 | The function panic is a special function that crashes the program immediately, outputting the message passed to it. The `Diverges` function never returns, because it crashes the program. This is indicated with the special return type `never`. 48 | 49 | A diverging function can be used where an expression of any type is expected. Thus the type `never` is a subtype of all other types. In formal type theory, it is the [bottom type](https://en.wikipedia.org/wiki/Bottom_type). This can be useful in situations where two expressions are expected to have the same type, but you want one to be an error. For example, using an [`if` expression](choice.md#if_expression), we might write: 50 | 51 | let y: string = if condition => "We're good" else => Panic(); 52 | 53 | The `never` type is a first class type and can be used anywhere a type can be. However, there can never be a value of that type. 54 | 55 | ## Function References 56 | 57 | We can also create variable bindings that reference a function. 58 | 59 | let f: (int) -> int; 60 | 61 | `f` is a variable that references a function taking an `int` and returning an `int`. It can be assigned a function and called as a function. For example: 62 | 63 | // Without type inference 64 | let f: (int) -> int = AddTwo; 65 | 66 | // With type inference 67 | let f = AddTwo; // f: (int) -> int 68 | 69 | // Calling f 70 | let five = f(2); 71 | -------------------------------------------------------------------------------- /old/generators.md: -------------------------------------------------------------------------------- 1 | # Generators 2 | 3 | The `yield` keyword acts basically as `yield return` does in C#. 4 | 5 | Python uses `return` for the equivalent of `yield break`, however that causes an inconsistency where removing yield from a method changes the meaning of the return. Adamant should use either `yield break;` or `yield return;` or something else. 6 | 7 | Python has `yield from` for recursively yielding another iterator (Python already used `from` for other things). A paper proposed `yield foreach` for the same thing in C#. Adamant doesn't have a `foreach` or `from` keyword and isn't likely to get either. Options for this are `yield for`, `yield from`, and `yield all`. -------------------------------------------------------------------------------- /old/generics.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | For value type `V` and reference type `R`, the type parameter `T` can be assigned any of: 4 | 5 | * `V` 6 | * `mut V` 7 | * `ref V` 8 | * `ref mut V` 9 | * `ref var V` 10 | * `ref var mut V` 11 | * `R` 12 | * `mut R` 13 | * `ref var R` 14 | * `ref var mut R` 15 | 16 | // TODO what about own? 17 | 18 | Types can then be constructed from `T` in any of the following ways: 19 | 20 | * `T` 21 | * `ref T` 22 | * `ref var T` 23 | 24 | Note that you can't specify the mutability of `T`. That is inherent to the type parameter. 25 | 26 | To the code using type parameter `T`, it behaves as value type with move semantics and no copy function. This only changes if there are generic constraints. 27 | 28 | ## Constraints 29 | 30 | ```adamant 31 | where T: class // Then `ref T` becomes invalid 32 | where T: struct 33 | where T: move struct 34 | where T: copy struct 35 | where T: copy() // T has a copy method, explicit or implicit. must be used explicitly though 36 | where T <: S // subtype 37 | ``` 38 | 39 | ## Type Lists 40 | 41 | In some cases, it is useful to have generic parameters actually provide a list of types. The visitor pattern is one such situation. To use a type list, mark a type parameter as being of the type `type...`. This indicates it is a tuple of types. **Conceptually we have something like `class Foo[T: L] forSome L where L: Tuple`.** 42 | 43 | ```adamant 44 | public class Something_Visitor[TArgs: type..., TReturn, TThrows: type...] 45 | { 46 | public Visit(host: Something, args: TArgs...) -> TReturn 47 | throws TThrows... 48 | { 49 | } 50 | } 51 | ``` 52 | 53 | The `...` operator causes the args to be gathered into a tuple and passed as a single tuple value. 54 | 55 | Note: If `...` means to break apart the tuple into listed things, it seems like you should be able to call a function as `Func(tuple...)` and have it break apart the tuple. Note here, the generic types are passed like `new Something_Visitor[#[int,int],int,#[void]]()`. 56 | 57 | ## Variadic Generics 58 | 59 | If one wants a type that allows multiple type arguments to be passed inline then do: 60 | 61 | public class My_Tuple[T...] 62 | { 63 | } 64 | 65 | ## Valued Template Parameters 66 | 67 | Here we compute with type parameters. 68 | 69 | public Factorial[N: integer] -> integer 70 | { 71 | return N*Factorial(); 72 | } 73 | 74 | public Factorial[0] -> integer 75 | { 76 | return 1; 77 | } 78 | 79 | Then with inlining and constant folding `let x = Factorial[10];` could be evaluated at compile time. 80 | 81 | ## Computing Types 82 | 83 | Types can be computed at compile time. For example, given a type `Array[n: size, T]` one can declare a variable like so: 84 | 85 | ```adamant 86 | let x = new Array[5.power(2), int](); 87 | for i in 0..5.power(2) 88 | { 89 | x[i] = i; 90 | } 91 | ``` 92 | 93 | However, in such cases, the arguments need to be constant expressions. But, a generic argument should itself be considered to be known at compile time. So could it be used as well? 94 | 95 | ```adamant 96 | public fn matrix[n: size]() -> Array[n.power(2), int] 97 | { 98 | return new Array[n.power(2), int](); 99 | } 100 | ``` 101 | 102 | This seems like it could easily lead to issues. What if the two expressions differed in some inconsequential way? For example what if one used `3-1` in place of the power `2`? Even though only pure functions are allowed it isn't obvious that the same value is being used twice. I think it makes sense to restrict them to use a shared calculation. This could be done with a special from of where. 103 | 104 | ```adamant 105 | public fn matrix[n: size]() -> Array[n2, int] 106 | where let n2: size = n.power(2) 107 | { 108 | return new Array[n2, int](); 109 | } 110 | ``` 111 | 112 | It is now straight forward to type check this function without any code evaluation. Conceptually, the same thing could come up for non-generic functions. 113 | 114 | ```adamant 115 | public fn matrix() -> Array[n, int] 116 | where let n: size = 5.power(2) 117 | { 118 | return new Array[n, int](); 119 | } 120 | ``` 121 | 122 | The compiler would thus treat each constant expression in a type position as a separate type. If one wanted to use the same type in multiple places, one would need to use a `where let` clause. Of course, defining which kinds of expressions require this could be awkward. 123 | 124 | Compare https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md 125 | 126 | There is also the issue of what one can know about the result of a function evaluating to a type. 127 | 128 | ```adamant 129 | public fn make_array(n: size) -> Array[T] 130 | where T = compute_type() 131 | { 132 | return (1..size).select(fn i => T.default()).to_array(); 133 | } 134 | 135 | // TODO how to make promises about the type returned? 136 | public fn compute_type() -> type 137 | ensures return <: Default 138 | // or does it need some form of `where let`? 139 | { 140 | // ... 141 | } 142 | 143 | public trait Default 144 | { 145 | public fn default() -> Self; 146 | } 147 | ``` 148 | 149 | This same mechanism can be used to implement specialization. Assuming generics can only be overloaded on number of parameters then a `List[T]` could be specialized like so: 150 | 151 | ```adamant 152 | public class List[T] <: List[T] 153 | where let T = bool 154 | { 155 | // special implementation 156 | } 157 | ``` 158 | -------------------------------------------------------------------------------- /old/ideas-rejected.md: -------------------------------------------------------------------------------- 1 | # Rejected Feature Ideas 2 | 3 | ## `if not` and `while not` 4 | 5 | *Rejected Reason:* adoption of control flow requiring blocks but not parenthesis (Rust style) means that this feature doesn't really makes sense as it is already essentially supported because control flow statements don't have parenthesis. 6 | 7 | Languages like Ruby have inverted control flow with `until(condition)` for `while(not condition)` and `unless(condition)` for `if(not condition)`. In languages without that there is often a need for an extra set of parens which can be ugly and confusing (i.e. `if(not(a and b)) statement;`. What if the language allowed a not outside the required parens? So that could become `if not(a and b) statement;`. Of course, if we switch to control flow not requiring parens this becomes a non-issue (i.e. `if not(a and b) { statement; }`). Though there is some extra clarity when parens are required, because you know the not must apply to the whole condition, not just the first part of it. 8 | 9 | ## Alternatives for Escaped Identifiers 10 | 11 | *Rejected Reason:* The backslash syntax was adopted. 12 | 13 | Currently, backtick `` ` `` is used to escape identifiers and double backtick creates escaped string identifiers. 14 | 15 | | Font | Escaped Keyword | Escaped String | 16 | | ------------ | --------------------------------------------- | -------------- | 17 | | Monospace | `` `class`` | ``` ``A name with spaces`` ``` | 18 | | Proportional | \`class | \`\`A name with spaces\`\` | 19 | 20 | However, this conflicts with the use of markdown in comments when referring to code. While escaped identifiers are rare, it can still be confusing. Unfortunately, preventing this would mean not being able to use the backtick as a symbol in the language. Still, this might be worth it. Note in all these examples, that the single symbol form may be restricted to keywords and reserved words only. So it may not be legal to use `` `hello``. This can make the syntax less ambiguous. Possible syntaxes: 21 | 22 | * `^class` and `^^This is a test^^` (exponent would be `**`) 23 | * `^class^` and `^This is a test^` 24 | * At sign `@class`, but what about names with spaces? Confusing around attributes 25 | * Square brackets `[class]` and `[This is a test]` (this conflicts with tuple syntax, for example `let [x,y] = x` does that destructureor assign to the identifier `[x,y]`?) 26 | * `[[class]]` and `[[This is a test]]` 27 | * `*class` or `class*`, and `**This is a test**` (dereference could be caret `^` and exponent double caret `^^`) (Suffix asterisk makesit seem like the star of a footnote which I like) (Double star reads like bold or shouting though) 28 | * `--This is a Test--` 29 | * `~~This is a Test~~` 30 | * `'class` and `''This is a test''` vs string `"This is a test"` but in variable width font that is ''This is a test'' vs string "Thisis a test" 31 | * `'class'` and `'This is a test but don't use apostrophes'` 32 | * `#class` and `##This is a test##` 33 | 34 | For some of these syntaxes, the symbol could be taken to mean "literal" and used as the prefix for literal strings. However, it was suggested by Alex Burkhart that string escaped string identifiers could instead be created by prefixing strings with the same character. These strings would work like literal strings in terms of what characters they accept. Possible syntaxes for that are: 35 | 36 | * `#class` and `#"An identifier"` 37 | * `'class` and `'This is a test'` and `'"A literal string\"` 38 | * `\class` `\"This is a test"` (the blackslash is used for escape sequences, i.e. it escapes from the current mode. In this caseescaping from regular parsing of keywords and strings) The blackslash could possibly be combined with other characters to create otherescape like things, perhaps around macros. 39 | * `@class` `@"This is a test"` (but this is confusing with attributes) 40 | * `*class` `*"This is a test"` 41 | 42 | Finally, perhaps using backtick isn't so bad. It is unlikely that a code snippet in markdown begins with a backtick. If we could eliminate the double backtick by putting backtick in front of strings then code could always be surrounded by double backtick in markdown. 43 | 44 | | Font | Escaped Keyword | Escaped String | 45 | | ------------ | ----------------------------------------- | -------------- | 46 | | Monospace | `` `class`` | `` `"A name with spaces"`` | 47 | | Proportional | \`class | \`"A name with spaces" | 48 | 49 | However, this seems like a very easy to overlook syntax. 50 | 51 | ## Other Uses of Backtick 52 | 53 | *Note:* There was some thought of using backtick in other places when it was the escape character. Those are recorded here. 54 | 55 | Backtick may also be a convenient way to mark other literals. For example: 56 | 57 | * Date times - `` `2015-03-03`` 58 | * Intervals - `` `[3..6)`` or `` `[3,6)`` 59 | 60 | Perhaps backtick could be used for operator overloading? So `` `*`` would be the method name for overloading operator '`*`' However, that might be confusing. 61 | 62 | ## Don't Distinguish between Non-interpolated and Verbatim String Literals 63 | 64 | *Note:* When string interpolation was done using curly braces, and there were verbatim strings, there was some thoughts that verbatim strings may not be that important of a feature. 65 | 66 | It may not make sense to have non-interpolated string literals as distinct from verbatim ones. The only thing that lets one do is write curly braces without escaping them. That also opens up the idea of using a syntax that doesn't support distinguishing the two, like the number sign. 67 | -------------------------------------------------------------------------------- /old/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Adamant is a new language that is under development. It will be a general-purpose language with high performance, deterministic safe resource management, and guaranteed thread safety. Documentation and resources can be found at [adamant-lang.org](http://adamant-lang.org). Adamant should be a compelling alternative to other high level languages like C# and Java. It's focus on compile time safety aids in creating correct code. Other features of interest include: 4 | 5 | * object lifetimes 6 | * write once, compile anywhere 7 | * guaranteed optimizations 8 | * asynchronous everywhere 9 | * type inference 10 | * generics with partial specialization 11 | * class defined interfaces 12 | * optional exception specifications 13 | * minimal runtime 14 | 15 | ## Purpose 16 | 17 | This book serves is an informal reference to the Adamant language. It tries to be comprehensive by covering all features of the language. It can serve as an introduction to the language for programmers who are already familiar with another language like C#, Java or Rust. Features unique to Adamant are explained in more detail while features shared with other languages are only briefly covered with a focus on stating how they work and any differences with other languages. 18 | 19 | Those less familiar with programming would do better to read the [Introduction to Programming in Adamant](https://github.com/adamant/adamant.language.introduction/blob/master/book.md) book. If you are already very comfortable with computer programming and just want to see what sets Adamant apart from other languages, continue on to the next section for a brief summary of some of the unique features of Adamant. If you'd like to simply learn Adamant. Feel free to skip ahead to the section on the language, [Variable Bindings](variable-bindings.md). 20 | 21 | ## Unique Features 22 | 23 | This section introduces a few of Adamant's more unique features and links out to the relevant section on each feature. 24 | 25 | ### Ownership, Borrow Checking and Lifetimes 26 | 27 | Adamant takes a very different approach to memory management than other high-level languages today. Garbage collection has become the common approach to memory management. Instead, Adamant uses the approach pioneered by Rust of having the compiler statically check rules for memory management and determine when objects should be deleted. For more information see [Ownership](ownership.md) and its subsections. 28 | 29 | ### Inferred Throws Clauses 30 | 31 | Adamant has checked exceptions similar to Java or C++. However, unlike either of those languages, by default the exceptions thrown by a function are automatically inferred. Only on externally exposed APIs are exception specifications required. 32 | 33 | ```adamant 34 | // Throws clause is required because this functions is publicly exposed 35 | public Function1() -> void 36 | throws exception 37 | { 38 | Function2(); 39 | } 40 | 41 | // Throws clause is inferred because it is omitted and this is an internal function 42 | internal Function2() -> void 43 | { 44 | throw new exception(); 45 | } 46 | ``` 47 | 48 | For more information see [Exceptions](exceptions.md). 49 | 50 | ### Interfaces of Classes 51 | 52 | In many languages there is a distinction between an interface or trait that has no implementation and a class which can have implementation. Adamant doesn't have separate interfaces. Instead, any class can implement the implicitly defined interface of another class. In a class declaration, the base class appears after the first colon and classes whose interface is being implemented appear after the second colon. 53 | 54 | ```adamant 55 | public class MyClass: BaseClass <: ClassAsInterface1, ClassAsInterface2 56 | { 57 | } 58 | ``` 59 | 60 | This simplifies the type hierarchy and eliminates the practice often needed in C# and Java of defining a matching interface for most classes. The feature also necessitates several of Adamant's other unique features. Members declared private are accessible only from the same instance, not by other instances of the same class. Public fields can be overridden by properties in subclasses, so there is no need to declare properties for every field. For more information see [Interfaces](traits.md). 61 | 62 | ### Immutable by Default 63 | 64 | All variable declarations and types are immutable by default. Mutability has to be opted into. 65 | 66 | ### Guaranteed Optimizations 67 | 68 | Because of immutable by default and other features of the language, it becomes possible to safely execute some code at compile time. For example, if you initialize a constant to the square root of two, this computation will be performed during compilation and the constant will be directly initialized with the resulting value. 69 | 70 | Exactly how this feature will work is still being determined. 71 | 72 | ### Asynchronous Everywhere 73 | 74 | Adamant will have C# and JavaScript style async/await support. However, in those languages, async becomes contagious and must be passed up the call chain throughout your code. Because of pervasive support for async execution, that is not the case in Adamant. Instead, it is safe to await a task in synchronous code and it will block the current thread of execution without holding a thread. 75 | 76 | In C#, asynchronous code can easily lead to race conditions as two threads share data across an asynchronous boundary. In Adamant, the borrow checker protects the developer from any such possible race conditions so that asynchronous code is fully safe. 77 | 78 | ### Generics with Partial Specialization 79 | 80 | Generics as supported by C# and Java are much easier to reason about than C++ style templates. However, they can be more restrictive. The type system of Adamant allows for much greater flexibility in generic types. For example, it is possible to use the type `void` as a type parameter to a generic class or function. Additionally, it is possible to provide specialized implementations of a generic class or function for specific types for better performance. 81 | 82 | ### Open Methods 83 | 84 | The fragile base class problem occurs when a class overrides a base classes methods. This is avoided by specifically marking methods which the base class will call subclass implementations with the `open` keyword. 85 | 86 | ### Named Constructors 87 | 88 | Constructors can be given names to indicate their purpose and meaning. 89 | 90 | ### Accept Operator 91 | 92 | The `..` operator is the accept operator used to accept visitors following the visitor pattern or function pointers whose first argument is the value to the left of the accept operator. 93 | 94 | ## Uncommon Features 95 | 96 | This section lists features Adamant shares with other languages that are less common, but still contribute to the distinctive flavor of the Adamant language. 97 | 98 | * Type Inference on Local variable declarations 99 | * Diverging Functions 100 | * A Specific Infinite Loop Keyword 101 | * All for loops are iterator based 102 | * Iterator performance often optimizes down to a C style for loop 103 | * Operator Overloading 104 | * Object Literals - allows creation of single instance of anonymous type 105 | * Classes can be extended with additional methods in separate libraries 106 | * Partial Classes - supports code generation 107 | * Both reference and value types 108 | * C# style generators with `yield` keyword 109 | * `unsafe` code blocks and low level language features like raw pointers 110 | * C interop 111 | -------------------------------------------------------------------------------- /old/object-literals.md: -------------------------------------------------------------------------------- 1 | # Object Literals 2 | 3 | ```adamant 4 | var foo = new : <: () 5 | { 6 | // constructor 7 | // Fields 8 | // Methods 9 | } 10 | ``` 11 | -------------------------------------------------------------------------------- /old/out-of-memory.md: -------------------------------------------------------------------------------- 1 | # Out of Memory and Stack Overflow 2 | 3 | Out of memory errors are handled separately because they are so pervasive. Any creation of a reference type could lead to an out of memory situation and any function call could lead to a stack overflow situation. 4 | 5 | You can avoid causing an out of memory condition by using the `new?` construction which will return none if there was not enough memory to allocate the object. 6 | 7 | **TODO:** how to handle when an out of memory error occurs inside the constructor of the type being constructed with `new?`. 8 | -------------------------------------------------------------------------------- /old/ownership-summary.md: -------------------------------------------------------------------------------- 1 | # Summary of Ownership 2 | 3 | ## Passing 4 | 5 | | Kind | Move | Copy† | Borrow | 6 | | ----------------------------------------- | -----: | -----: | -----: | 7 | | Move Value (`struct` w/o `implicit copy`) | x | copy x | ref x | 8 | | Copy Value (`struct` w/ `copy`) | move x | x | ref x | 9 | | Reference (`class`) | move x | copy x | x | 10 | 11 | † for Move Value and Reference types, a copy method must be implemented to do this 12 | 13 | ## Types 14 | 15 | | Kind | Move | Copy | Borrow | 16 | | ----------------------------------------- | ------: | ------: | -----: | 17 | | Move Value (`struct` w/o `implicit copy`) | T | T | ref T | 18 | | Copy Value (`struct` w/ `implicit copy`) | T | T | ref T | 19 | | Reference (`class`) | T$owned | T$owned | T | 20 | -------------------------------------------------------------------------------- /old/partial-classes.md: -------------------------------------------------------------------------------- 1 | # Partial Classes 2 | 3 | Partial classes work basically as they do in C#. Additionally, you can use `replace` to override a member declared in another copy of a partial class. This idea comes from proposals for C# 7. -------------------------------------------------------------------------------- /old/patterns.md: -------------------------------------------------------------------------------- 1 | # Patterns 2 | 3 | Match patterns do two things, they describe the shape of data that should match, and they destructure that data into variables to access specfic portions. 4 | 5 | ## Uses 6 | 7 | * `match` expression arms 8 | * `if let` expressions 9 | * `while let` expressions 10 | * `for` expressions 11 | * `let` and `var` statements 12 | * function parameters (should this be supported?) 13 | 14 | ## Refutability 15 | 16 | Some pattern matches can't fail, others can. Whether a pattern is refutable or not determines where it can be used. 17 | 18 | ## Pattern Syntax 19 | 20 | ### Literals 21 | 22 | ```adamant 23 | let x = 1; 24 | 25 | match x 26 | { 27 | 1 => console.write_line("one"), 28 | 2 => console.write_line("two"), 29 | 3 => console.write_line("three"), 30 | _ => console.write_line("anything"), 31 | } 32 | ``` 33 | 34 | `string` and `code_point` literals also work. 35 | 36 | ### Variable Names 37 | 38 | A variable name declares a variable and always matches. 39 | 40 | ### Matching Multiple Patterns 41 | 42 | ```adamant 43 | let x = 1; 44 | 45 | match x 46 | { 47 | 1 | 2 => console.write_line("one or two"), 48 | 3 => console.write_line("three"), 49 | _ => console.write_line("anything"), 50 | } 51 | ``` 52 | 53 | Note that alternatives can contain the same variable name as long as the type matches in each alternative. 54 | 55 | ### Ranges 56 | 57 | Ranges work exactly as they do in other places 58 | 59 | ```adamant 60 | let x = 5; 61 | 62 | match x { 63 | 1..5 => console.write_line("one through five"), // TODO but .. isn't inclusive? 64 | _ => console.write_line("something else"), 65 | } 66 | ``` 67 | 68 | How can this be overloaded to match other types? 69 | 70 | ### Tuples 71 | 72 | ```adamant 73 | let #(x, y) = #(1, 2) 74 | ``` 75 | 76 | ### Matching Based on Type 77 | 78 | ```adamant 79 | let x = new Foo(); 80 | 81 | match x 82 | { 83 | f: Foo => console.write_line("Foo"), 84 | _ => console.write_line("not Foo"), 85 | } 86 | ``` 87 | 88 | Any class type can be matched. If the type is not visible outside the package, then the match can be exhaustive because no subclasses can be added without rebuilding the package. This can also be achieved by marking the class "closed?" 89 | 90 | ### Optional Types 91 | 92 | ```adamant 93 | if let x? = value 94 | { 95 | // x is value 96 | } 97 | ``` 98 | 99 | If the `?` operator can be overloaded, then it should be possible to overload this to match for other types besides optional. 100 | 101 | ### Ignoring Values 102 | 103 | ```adamant 104 | foo(console: Console, _: int, y: int) -> void 105 | { 106 | console.write_line("This code only uses the y parameter: \(y)"); 107 | } 108 | ``` 109 | 110 | Can be used to ignore sub-parts. 111 | 112 | ### Unexpected Values 113 | 114 | There should be a syntax similar to `_` that serves as a catch all for match expressions, but generates a warning if the compiler detects something will match it. This would be useful for libraries that contain open types where you must have a `_` but don't want to accidentally match added types. But is this different than `_ => NOT_IMPLEMENTED!()"? 115 | 116 | Should you really be required to have a default case for matches on open types? If the library is changed, you should deal with it. 117 | 118 | ### Unused Variables 119 | 120 | Use `_x` to avoid the warning that variable `x` is unused. As with Rust, this has the affect of binding the value to the variable which has lifetime effects, whereas just `_` does not bind. 121 | 122 | ### Conditionals for Match Guards 123 | 124 | ```adamant 125 | let num: int? = 4; 126 | 127 | match num { 128 | x? if x < 5 => console.write_line("less than five: \(x)"), 129 | x => console.write_line(x), 130 | none => void, 131 | } 132 | ``` 133 | 134 | ### Object Properties 135 | 136 | ```adamant 137 | class Point 138 | { 139 | public let x: int; 140 | public let y: int; 141 | 142 | public new(.x, .y) { } 143 | } 144 | 145 | main() -> void 146 | { 147 | let p = new Point(0, 7); 148 | 149 | let Point { x, y } = p; 150 | assert(0 == x); 151 | assert(7 == y); 152 | 153 | // TODO is this the correct syntax? 154 | let Point { a=.x, b=.y } = p; 155 | assert(0 == a); 156 | assert(7 == b); 157 | } 158 | ``` 159 | 160 | ### Arrays/Lists? 161 | 162 | ```adamant 163 | let [x, y, ...] = list; 164 | ``` 165 | 166 | How do you expand this to work on other types? A special kind of match method. 167 | 168 | ```adamant 169 | public class Foo 170 | { 171 | match []() // creates array match method? 172 | { 173 | } 174 | } 175 | ``` 176 | 177 | ### Match Methods 178 | 179 | Classes can declare how they are matched with match methods. These are like Scala unapply functions. 180 | 181 | ```adamant 182 | class Foo 183 | { 184 | match(.x, .y) => true; // match the x and y properties 185 | match() => #(int, int)? // Form allowing more sophisticated logic 186 | { 187 | return #(x, y); 188 | } 189 | match named(.x, .y); // matches can be named 190 | new match (.x, .y) // declare both a constructor and a match method at once 191 | { 192 | } 193 | } 194 | ``` 195 | 196 | These are then matched as 197 | 198 | ```adamant 199 | let v = new Foo(); 200 | 201 | match v 202 | { 203 | T(x,y) => ..., 204 | T.name(x, y) => ..., 205 | .name(x, y) => ..., // type assumed to be that of `v` 206 | } 207 | ``` 208 | 209 | Should classes that are tuple like be supported as a shortcut for matching? 210 | 211 | ```adamant 212 | public class T(a: int, b: int) // declares fields `a` and `b`, constructor and match method all at once 213 | { 214 | } 215 | ``` 216 | 217 | ### Regular Expressions 218 | 219 | Is there a way to use a regular expression to match a string? 220 | -------------------------------------------------------------------------------- /old/pseudo-references.md: -------------------------------------------------------------------------------- 1 | # Pseudo References 2 | 3 | You've learned all about the ownership system in Adamant. About reference types, value types, variable references and lifetimes, but some types you use on a regular basis may not seem like they fit in that list. It is possible using the features of Adamant to create types that behave differently then one might expect. An important case of this are *pseudo references*. These are value types that appear as if they are actually reference types. Examples include `string`, `Array[T]`, and `Counted_Ref[T]`. Let's explore how pseudo references work. 4 | 5 | **TODO:** Pseudo references need to act like references, this means that you shouldn't be able to take pointers to them etc. 6 | 7 | ## Declaring 8 | 9 | **TODO:** alternate syntaxes `public ref Test` or `public struct Test$self` 10 | 11 | ```adamant 12 | public struct Test$t 13 | { 14 | private let x: string$t; 15 | 16 | // This delete will be called when the struct has the lifetime $owned when it goes out of scope 17 | public delete() 18 | // if the where clause is used, this should maybe be where $t == $owned 19 | { 20 | // ... 21 | } 22 | 23 | // This delete will be called when the struct does not have the lifetime $owned when it goes out of scope 24 | // The logic here is that regular delete has a special form of ownership, while this delete just gets a reference 25 | // However that is confusing. A better syntax is really needed. 26 | public delete(ref mut self) 27 | where $t =/= $owned // idea for another syntax 28 | { 29 | // ... 30 | } 31 | } 32 | ``` 33 | 34 | ## In the Standard Library 35 | 36 | ### `string` 37 | 38 | The predefined string type is defined as a `struct` for efficiency. At the same time, passing around a large string doesn't constantly copy all that data. Furthermore, if you've used methods like `substring()` you know that strings can have lifetimes as if they were references. 39 | 40 | ### `Counted_Ref[T]` 41 | 42 | The `Counted_Ref[T]` defined in the standard library is a true pseudo reference. It is designed to behave as much like a reference to an object of type `T` as possible. 43 | -------------------------------------------------------------------------------- /old/result.md: -------------------------------------------------------------------------------- 1 | # `Result` 2 | 3 | Can't be ignored when returned from a function. The error type is some kind of exception so it can be thrown. 4 | 5 | **TODO:** 6 | Perhaps you should have to use or explicitly ignore the return value of all functions. For example: 7 | 8 | ignore Foo(); 9 | -------------------------------------------------------------------------------- /old/target.md: -------------------------------------------------------------------------------- 1 | # Target 2 | 3 | No programming language can be the best language for everything. It is important for languages to be clear not only what they are meant for, but what they are *not* meant for. To that end, this section lists some assumptions Adamant makes about the target machine, environment, and usage. 4 | 5 | * 8 bit bytes 6 | * heap/dynamic allocation is available 7 | * not for very small embedded applications 8 | 9 | Adamant may be useful for some embedded applications. Larger platforms like Arduino have 8 bit bytes and support dynamic memory allocation. It would be fine for those. 10 | -------------------------------------------------------------------------------- /old/value-types.md: -------------------------------------------------------------------------------- 1 | # Value Types 2 | 3 | We have covered value types which are the majority of types you will deal with in Adamant. However, there are also value types which are important to understand as many of the predefined types in Adamant are value types. Again a value type is a type that is passed by value. So they always reside on the stack or directly in the object holding them. When they are passed between functions they are moved or copied. For both of those reasons, it is a good practice to limit the amount of memory used by a value type. In Adamant, any type declared as a `struct` is a value type. In general, you should reach for a `class` first and only use a `struct` when your particular scenario fits the few situations they are good for. It's important to note that, just because you pass a struct by value doesn't mean a copy of the object is happening. In many important cases, the compiler is able to optimize those copies out of existence. 4 | 5 | ## Move Semantics 6 | 7 | Simple structs have *move semantics*. That means rather than being borrowed like reference types, the value always moves. The equivalent for reference types would be if we used `move` whenever passing it. 8 | 9 | ```adamant 10 | public mut struct Channel_Receiver 11 | { 12 | public let buffer_size: int; 13 | 14 | public new(buffer_size: int) 15 | { 16 | self.buffer_size = buffer_size; 17 | } 18 | } 19 | 20 | // In Main 21 | let a = new Channel_Receiver(83); 22 | let b = a; // implicit move 23 | console.write_line("b.buffer_size = \(b.buffer_size)"); 24 | console.write_line("a.buffer_size = \(a.buffer_size)"); // complier error 25 | ``` 26 | 27 | The last line gives a compiler error stating that the value has been moved out of `a`. We see that when we assigned `b = a` the value was moved just as if we had used the `move` keyword. The same thing happens when passing a struct to a function. 28 | 29 | Structs with move semantics aren't used very often. The most common use is in [pseudo references](pseudo-references.md), where they are combined with other features to make something that feels more like a reference type. Instead, most value types have copy semantics. 30 | 31 | ## Copy Semantics 32 | 33 | Most value types have *copy semantics*. We're already familiar with a type that has copy semantics. The `int` type we've been using all along is an example of a struct with copy semantics. To see how to create our own types with copy semantics, let's make a simple complex number struct. 34 | 35 | ```adamant 36 | public struct complex 37 | { 38 | public let real: int; 39 | public let imaginary: int; 40 | 41 | public new(real: int, imaginary: int) 42 | { 43 | self.real = real; 44 | self.imaginary = imaginary; 45 | } 46 | 47 | public implicit new copy(from: ref complex) 48 | { 49 | } 50 | } 51 | 52 | // In Main 53 | let a = new complex(6, 4); 54 | let b = a; 55 | 56 | console.write_line("b = \(b.real)+\(b.imaginary)i"); 57 | console.write_line("a = \(a.real)+\(a.imaginary)i"); 58 | ``` 59 | 60 | We did the same thing we did with our `foo` struct, but this time it compiles and runs, outputting: 61 | 62 | ```console 63 | b = 6+4i 64 | a = 6+4i 65 | ``` 66 | 67 | We see that `b` became a copy of `a`. How did that happen? It's because of the special *copy constructor* we declared. Copy constructors are declared with `new copy`. The `implicit` keyword in front tells the compiler to go ahead and call the copy constructor whenever a copy of the struct is needed. That gives this struct copy semantics instead of move semantics. If we really wanted to, we could still explicitly move a value though. 68 | 69 | ```adamant 70 | let a = new complex(6, 4); 71 | let b = move a; // note the use of `move` 72 | 73 | console.write_line("b = \(b.real)+\(b.imaginary)i"); 74 | console.write_line("a = \(a.real)+\(a.imaginary)i"); // complier error 75 | ``` 76 | 77 | Now we get the same compiler error as before stating that we can't use `a` because the value has been moved out of it. The `move` keyword has just the same effect on copy types as it does on reference types. 78 | 79 | You're probably still wondering how the copy happened since we didn't write any code in the copy constructor. Since all the fields of our struct were themselves copyable, the compiler wrote the code to copy them for us. If any had not been implicitly copyable, we would have needed to write code to copy them. 80 | 81 | You may also be wondering about that `ref` in front of the type of the copy constructor parameter. Since copy types cause a copy when they are passed, but we are defining how to copy this structure, we need to avoid a copy being made. Otherwise, there would be an infinite loop. So we have to use a reference to the parameter. [Variable references](variable-references.md) are explained in detail later. 82 | 83 | Notice that we made our complex struct immutable rather than mutable like our Point class earlier. In general, copy types should be immutable. Mutable copy types can be very confusing to people using them because it is easy to accidentally make a copy and mutate the copy when you mean to mutate the value. 84 | 85 | ## Explicit Copy 86 | 87 | It doesn't make sense to give copy semantics to some structs, but we still want to be able to copy them when needed. For example, if we wanted to make our complex struct mutable, then it would make sense to give it move semantics, but be able to explicitly copy it. This can be done simply by declaring the copy constructor as `explicit` instead of `implicit`. 88 | 89 | ```adamant 90 | public mut copy struct Complex 91 | { 92 | public var real: int; 93 | public var imaginary: int; 94 | 95 | public new(real: int, imaginary: int) 96 | { 97 | self.real = real; 98 | self.imaginary = imaginary; 99 | } 100 | 101 | public explicit new copy(from: ref Complex) 102 | { 103 | } 104 | } 105 | 106 | // In Main 107 | let a = mut new Complex(6, 4); 108 | let b = new copy(ref a); // explicit copy 109 | a.X = 4; 110 | 111 | console.write_line("b = \(b.real)+\(b.imaginary)i"); 112 | console.write_line("a = \(a.real)+\(a.imaginary)i"); 113 | ``` 114 | 115 | This outputs: 116 | 117 | ```console 118 | b = 6+4i 119 | a = 4+4i 120 | ``` 121 | 122 | Notice that `b` is a copy of `a`. We were able to mutate `a` without affecting `b`. The copy didn't happen automatically though, we had to explicitly ask for a copy to be made. If we hadn't the value would have moved from `a` to `b` and we couldn't have used `a` anymore. Calling a copy constructor is different from calling other constructors. Where we would normally give the type we are constructing we use the `copy` keyword instead. The type we are constructing matches the type we are copying. Because the argument to the copy constructor is a reference, we have to take a reference to `a` using the `ref` keyword. 123 | 124 | If you're observant you may have noticed that we declared `a` as `let` and `mut`, just like it was a reference type. A mutable value type will let you change fields and call methods requiring a mutable value. Declaring a value type with `var` will let you assign a different variable to the var. 125 | 126 | Classes can have explicit copy constructors too. 127 | -------------------------------------------------------------------------------- /old/variable-bindings.md: -------------------------------------------------------------------------------- 1 | # Variable Bindings 2 | 3 | Almost all programs require variables. The standard form of variable binding is the `let` statement. 4 | 5 | public Main() -> void 6 | { 7 | let x = 5; 8 | } 9 | 10 | In the rest of this section we will leave off the declaration of the main function. It is possible to have global variables declared outside of any function, but the rules for those are more complicated. 11 | 12 | ## Type Annotations 13 | 14 | Adamant is a statically typed language, meaning that every expression and variable has a type determined at compile time. Our first example doesn't indicate the type of `x` though. That is because Adamant has type inference. It is able to figure out the type of something without you typing it out. 15 | 16 | If you want or need to spell out the type of a variable you can do so by placing it after a colon. 17 | 18 | let x: int = 5; 19 | 20 | You can read this as let x be an int with the value 5. The `int` type is one of the many [primitive types](primitive-types.md) in Adamant. It is a 32 bit signed integer. There are other types for the various standard integer sizes. 21 | 22 | In future examples, we many annotate the type of a variable that Adamant can infer using a comment to make sure the type is clear to you. That would look like: 23 | 24 | let x = 5; // x: int 25 | 26 | ## Mutability 27 | 28 | By default all values and bindings are *immutable*. Meaning they can't be changed. Since integers are inherently immutable (the number 2 is always the number 2), we'll use the `Point` class in our examples. This code won't compile: 29 | 30 | let p = new Point(4, 5); 31 | p.X = 6; 32 | 33 | It will give an error on the second line saying that it is immutable and can't be assigned to. If we want to be able to mutate it, we must declare the value as mutable. 34 | 35 | let p = mut new Point(4, 5); // p: mut Point 36 | p.X = 6; 37 | 38 | Now the point `p` is mutable and can be changed. But the variable `p` is still immutably bound to that particular point and can't be assigned a different point. 39 | 40 | let p = mut new Point(4, 5); // p: mut Point 41 | p.X = 6; 42 | p = new Point(10, 10); // compile error: can't assign to immutable binding 43 | 44 | If we want to be able to assign `p` a different point to reference we need the variable binding to be mutable. We declare a mutable binding using the `var` keyword. 45 | 46 | var p = mut new Point(4, 5) 47 | p.X = 6; 48 | p = new Point(10, 10); 49 | 50 | Now, we can both modify which object is referenced by `p` and the value of that object. Notice that the two are independent. We can declare a variable to an immutable object. 51 | 52 | var p = new Point(4, 5); 53 | p = new Point(10, 10); 54 | p.X = 12; // compile error: can't assign into immutable object 55 | 56 | For some types, the value is inherently immutable and we need to use `var` to be able to change it at all. A common example of this is `int`s. 57 | 58 | var x = 5; // x: int 59 | x = 7; 60 | // The next line does not compile. compile error: `int` is an immutable type and can't be declared mutable (`mut`). 61 | let y: mut = 6; // y: mut int 62 | 63 | There are many reasons things are immutable in Adamant by default. One of the primary reasons is safety. By making things immutable by default the compiler can check if you accidentally mutate something you didn't intend to. 64 | 65 | In general, you should prefer immutable data and avoid mutation when possible. Use `let` rather than `var` and don't declare things `mut` when possible. However, there are times when mutation is what is needed. 66 | 67 | ## Definite Assignment 68 | 69 | Another way variables in Adamant are different than in many other languages is that they must be assigned a value before you can use them. 70 | 71 | let x: int; 72 | let y = x + 5; // compile error: can't use x before it is assigned a value 73 | 74 | The compiler will check that all variables have definitely been assigned along all code paths before they are used. 75 | 76 | ## Rebinding 77 | 78 | A `let` binding can be redeclared in order to bind a name to a different value. 79 | 80 | let x = 6; // x: int 81 | let y = x + 6; // x is an int here 82 | let x = "Hello"; // x: string 83 | let s = x + " World"; // x is a string here 84 | 85 | Note this can only be done with `let` bindings, not with `var`. This is to avoid confusion between assigning a new value to a variable and redeclaring it. 86 | 87 | ## Scopes and Shadowing 88 | 89 | Variables declared in a scope are only usable inside that scope after they are declared. 90 | 91 | let x = 65; 92 | { 93 | let y = 3; 94 | let z = x + y; // both `x` and `y` are in scope 95 | } 96 | let p = x; // `x` is in scope 97 | let q = x + y; // compile error: `y` can't be accessed because it is out of scope 98 | 99 | When a variable hides another variable in an outer scope with the same name, that is called shadowing. Most languages allow shadowing everywhere. Adamant does not allow shadowing within functions. 100 | 101 | let x = 65; 102 | { 103 | let x = 2; // Not allowed, this `x` shadows the other `x` 104 | } 105 | let y = x; // `y` will be 65 106 | 107 | Shadowing is allowed between a variable and a class member or declaration outside the class. However, Adamant follows the tradition of C# in not allowing shadowing within the parameters or variables of a function. This avoids programmer confusion and prevents bugs while almost never causing inconvenience. 108 | 109 | ## Patterns? 110 | 111 | A section about destructuring patterns? 112 | -------------------------------------------------------------------------------- /old/version-numbers.md: -------------------------------------------------------------------------------- 1 | # Version Number Scheme 2 | 3 | Version numbering is adapted from [SemVer 2.0.0 (semver.org)](https://semver.org/). 4 | 5 | Version numbers consist of four parts in order. 6 | 7 | * Channel: The channel is a release channel and determines the interpretation of the subsequent version number. Channels are ASCII alphabetic `[a-zA-Z]`. Channel determines the interpretation of the rest of the version number. 8 | * Version: A dot separated list of identifiers. Each identifier is either numeric `[0-9]` or alphanumeric `[a-zA-Z0-9]`. Numeric identifiers must not have leading zeros. 9 | * Pre-release/Post-release: A dot separated list of identifiers. Identifiers MUST comprise only ASCII alphanumerics and hyphen `[0-9A-Za-z-]`. 10 | * Hash: A unique identifier for the version. The identifier MUST comprise only ASCII alphanumerics and hyphen `[0-9A-Za-z-]`. 11 | * Metadata: Metadata about the version 12 | 13 | Version Ordering: 14 | 15 | * No ordering is defined between different release channels. 16 | * A dash separates the channel and version. For the default channel, the channel name is dropped. 17 | * Versions are ordered by parts 18 | * Pre-release is separated from the version by `-`. Post-release by `+`. Both are unstable. Pre-release versions are before the main version. Post-release are after. 19 | * The hash is separated by `#` if a hash is present, two versions are equal only if there hashes are equal. Hashes imply no version ordering. 20 | * Metadata is separated by `!` and isn't considered as part of ordering. 21 | 22 | The channel determines the interpretation and allowed format of version numbers. The default channel is always SemVer. 23 | 24 | Channels: 25 | 26 | * nightly - date versioning 27 | * dev - hash versioning 28 | 29 | Schemes: 30 | 31 | * date: `...` 32 | * hash: only provide a hash 33 | 34 | Metadata is used for indicating things like what platform something is built for. Note: the version number should always distinguish x86 form x64 versions. 35 | -------------------------------------------------------------------------------- /src/bitwise-operations.md: -------------------------------------------------------------------------------- 1 | ## Bitwise Operations 2 | 3 | Bitwise operations are provided on all the integer numeric types including "`size`" and "`offset`". However, they are not operators. Instead they are methods. However, they will always be inlined and their use does not involve the overhead of a method call. 4 | 5 | | Operation | Meaning | 6 | | -------------------------- | --------------------------------------- | 7 | | `x.bit_complement()` | The bitwise complement of `x` | 8 | | `x.bit_and(y)` | The bitwise and of `x` and `y` | 9 | | `x.bit_or(y)` | The bitwise or of `x` and `y` | 10 | | `x.bit_xor(y)` | The bitwise xor of `x` and `y` | 11 | | `x.bit_shift_left_by(y)` | The value of `x` shifted left `y` bits | 12 | | `x.bit_shift_right_by(y)` | The value of `x` shifted right `y` bits | 13 | | `x.bit_rotate_left_by(y)` | The value of `x` rotated left `y` bits | 14 | | `x.bit_rotate_right_by(y)` | The value of `x` rotated right `y` bits | 15 | | `x.count_one_bits()` | The number of one bits in `x` | 16 | | `x.count_zero_bits()` | The number of zero bits in `x` | 17 | 18 | For the shift and rotate operations, the `y` value is unsigned. When shifting signed types right, the value is sign extended. When shifting unsigned types right, the value is zero filled. 19 | -------------------------------------------------------------------------------- /src/book.md: -------------------------------------------------------------------------------- 1 | # The Adamant Programming Language Reference 2 | 3 | Note: This is the new version of the reference. Sections are being moved from the old version to the new version. If something is missing, it may be documented in the [old version](../old/book.md). 4 | 5 | 1. Introduction 6 | 2. Lexical Structure 7 | * [Packages](packages.md) 8 | * [Grammars](grammars.md) 9 | * [Lexical Analysis](lexical-analysis.md) 10 | * [Newlines](newlines.md) 11 | * [Whitespace](whitespace.md) 12 | * [Comments](comments.md) 13 | * [Tokens](tokens.md) 14 | * [Identifiers](identifiers.md) 15 | * [Keywords](keywords.md) 16 | * [Reserved Words](reserved-words.md) 17 | * [Literals](literals.md) 18 | * [Operators and Punctuators](operators-and-punctuators.md) 19 | 3. Basic Concepts 20 | * [Syntactic Analysis](syntactic-analysis.md) 21 | * Declarations 22 | * [Member Access](member-access.md) 23 | * Signatures and Overloading 24 | * Scopes 25 | * [Execution Order](execution-order.md) 26 | 4. [Types](types.md) 27 | * [Empty Types](empty-types.md) 28 | * [Struct Types](struct-types.md) 29 | * [Simple Types](simple-types.md) 30 | * [Tuple Types](tuple-types.md) 31 | * [Class Value Types](class-value-types.md) 32 | * [Pointer Types](pointer-types.md) 33 | * [Reference Types](reference-types.md) 34 | * Type Parameters 35 | * [Optional Types](optional-types.md) 36 | * [Variable Reference Types](ref-types.md) 37 | * [Type Expressions](type-expressions.md) 38 | * Generic Types 39 | 5. [Conversions](conversions.md) 40 | 6. [Expressions](expressions.md) 41 | * [Expression Blocks](expression-blocks.md) 42 | * [Choice Expressions](choice-expressions.md) 43 | * [Loop Expressions](loop-expressions.md) 44 | * Operators 45 | * Logical Operators 46 | * [Bitwise Operations](bitwise-operations.md) 47 | * [Boolean Expression](boolean-expression.md) 48 | * [Interpolated Strings](interpolated-strings.md) 49 | 7. [Statements](statements.md) 50 | 8. [Namespaces](namespaces.md) 51 | 9. [Functions](functions.md) 52 | 10. Classes 53 | * Declarations 54 | * Members 55 | * Fields 56 | * [Constructors](class-constructors.md) 57 | * Destructors 58 | * Methods 59 | * Properties 60 | * [Operator Overloading](operator-overloading.md) 61 | 11. [Structs](structs.md) 62 | * [Struct Initializers](struct-initializers.md) 63 | * [Struct Constructors](struct-constructors.md) 64 | 12. [Traits](traits.md) 65 | 13. [Enumerations](enumerations.md) 66 | * Enumeration Structs 67 | * Enumeration Classes 68 | 14. Generics 69 | 15. Exceptions 70 | 16. Attributes 71 | 17. Patterns 72 | 18. [Unsafe Code](unsafe.md) 73 | * [Pointers](pointers.md) 74 | 19. [External Declarations](external.md) 75 | 20. [Documentation Comments](documentation-comments.md) 76 | 21. Standard Library 77 | * [Localization](localization.md) 78 | * [Global Namespace](std-lib-global-namespace.md) 79 | * [`system` Namespace](system.md) 80 | * [`system.collections` Namespace](system.collections.md) 81 | * [`system.collections.specialized` Namespace](system.collections.specialized.md) 82 | * [`system.io` Namespace](system.io.md) 83 | * [`system.math` Namespace](system.math.md) 84 | * [`system.memory` Namespace](system.memory.md) 85 | * [`system.text` Namespace](system.text.md) 86 | 22. [Conventions](conventions.md) 87 | 23. [Planned Features](planned-features.md) 88 | * [Global and Package Qualifiers](planned-qualifier.md) 89 | * [Additional Types](planned-types.md) 90 | * [Aliases](planned-aliases.md) 91 | * [Additional Expressions](planned-expressions.md) 92 | * [Operator Features](planned-operators.md) 93 | * [Compile-time Function Execution](planned-ctfe.md) 94 | * [Language-Oriented Programming](planned-lop.md) 95 | 96 | Appendices: 97 | 98 | * [Ideas](ideas.md) 99 | * [Glossary](glossary.md) 100 | * [Implementer's Notes](implementers-notes.md) 101 | * [Influences](influences.md) 102 | * [Syntax Reference](syntax-reference.md) 103 | -------------------------------------------------------------------------------- /src/boolean-expression.md: -------------------------------------------------------------------------------- 1 | ## Boolean Expression 2 | 3 | A boolean expression is an embedded expression whose type is "`bool`". 4 | 5 | ```adamant 6 | boolean_expression 7 | : embedded_expression 8 | ; 9 | ``` 10 | -------------------------------------------------------------------------------- /src/choice-expressions.md: -------------------------------------------------------------------------------- 1 | ## Choice Expressions 2 | 3 | There are two constructs which allow the choice of which code to execute. They are "`if`" and "`match`". 4 | 5 | ```grammar 6 | choice_expression 7 | : if_expression 8 | | match_expression 9 | ; 10 | ``` 11 | 12 | ## `if` Expression 13 | 14 | An if expression evaluates a condition. If that condition is true, the first expression block is executed. If that condition evaluates to false, then the else clause is evaluated if it is present. The type of an if expression with no else clause and expression block type "`T`" is "`T?`" unless "`T`" is "`void`" in which case the if expression has type "`void`". When the condition is true, the value of the expression is the value of the expression block. When the condition is false, the value is "`none`". The type of an if expression with an else clause is the type both the expression block type and else clause type can be assigned to. If the condition is true, the value of the if expression is the value of the expression block, otherwise it is the value of the else clause. 15 | 16 | ```grammar 17 | if_expression 18 | : "if" boolean_expression expression_block else_clause 19 | ; 20 | 21 | else_clause 22 | : "else" expression_block 23 | | "else" if_expression 24 | | // epsilon 25 | ; 26 | ``` 27 | 28 | The else clause is associated with the lexically closest if expression. Thus "`if x => if y => F() else => G()`" is equvalent to: 29 | 30 | ```adamant 31 | if x 32 | { 33 | => if y 34 | { 35 | => F(); 36 | } 37 | else 38 | { 39 | => G(); 40 | }; 41 | } 42 | ``` 43 | 44 | Examples: 45 | 46 | ```adamant 47 | if condition 48 | { 49 | // executed if condition is true 50 | } 51 | 52 | if condition 53 | { 54 | // executed if condition is true 55 | } 56 | else 57 | { 58 | // executed if condition is false 59 | } 60 | 61 | if condition1 62 | { 63 | // executed if condition1 is true 64 | } 65 | else if condition2 66 | { 67 | // executed if condition1 is false and condition2 is true 68 | } 69 | else // optional else 70 | { 71 | // executed if condition1 is false and condition2 is false 72 | } 73 | 74 | let x = if condition => "true" else => "false"; 75 | let y = (if condition => "value") ?? "default"; 76 | 77 | let x = if condition 78 | { 79 | DoSomething(); 80 | => "true"; 81 | } 82 | else 83 | { 84 | DoSomethingElse(); 85 | => "false"; 86 | }; // semi-colon ends let statement 87 | 88 | 89 | ``` 90 | 91 | ## `match` Expressions 92 | 93 | A match expression allows the selection of choices based on matching a value to patterns. 94 | 95 | ```adamant 96 | match v 97 | { 98 | [0, y] 99 | { 100 | Action(y); 101 | }, 102 | _ // Matches anything 103 | { 104 | SomethingElse(); 105 | }, // last comma optional 106 | } 107 | ``` 108 | 109 | The compiler checks that the conditions are exhaustive. 110 | 111 | Like "`if`", "`match`" is an expression of type "`void`" by default, but can be given an expression value using expression blocks. 112 | 113 | ```adamant 114 | let z = match v 115 | { 116 | [0, y] 117 | { 118 | Action(); 119 | => y; 120 | }, 121 | [x, 0] => x, 122 | [x, y] => x + y, // last comma optional 123 | }; 124 | ``` 125 | 126 | The type of the match expression is the type all the match arms are compatible with. 127 | 128 | ```grammar 129 | match_expression 130 | : "match" embedded_expression "{" match_arm{",", 1, *} ","? "}" 131 | ; 132 | 133 | match_arm 134 | : pattern expression_block 135 | ; 136 | ``` 137 | -------------------------------------------------------------------------------- /src/class-value-types.md: -------------------------------------------------------------------------------- 1 | ## Class Value Types 2 | 3 | A class value type is the value type for a specific class. Essentially, the class value type is the struct type for the data structure on the heap that is referenced. Given a class "`T`" the class value type is "`^T`". Think of this as unreferencing the reference type to give the underlying value type. Class value types are a way to force a reference type to be allocated on the stack or locally as if it were a struct type. They also enable the use of pointers to classes. 4 | 5 | Note that given two reference types "`S <: B`" there is no subtype relationship between "`^S`" and "`^B`". This is because they might be of different size and so assigning one to the other might lead to object slicing as can happen in C++. 6 | 7 | ```grammar 8 | class_value_type 9 | : "^" class_reference_type 10 | ; 11 | ``` 12 | 13 | Just as with struct types, a value of the type "`ref ^T`" can be converted to all of the traits implemented by "`T`". 14 | -------------------------------------------------------------------------------- /src/comments.md: -------------------------------------------------------------------------------- 1 | ## Comments 2 | 3 | Both single line and delimited comments are supported. Single-line comments start with the characters "`//`" and extend to the end of the source line. Delimited comments start with the characters "`/*`" and end with the characters "`*/`". Delimited comments may span multiple lines. Comments starting with "`///`" are used for documentation (see [Documentation Comments](documentation-comments.md)) 4 | 5 | ```grammar 6 | comment 7 | : single_line_comment 8 | | delimited_comment 9 | ; 10 | 11 | single_line_comment 12 | : "//" non_newline_character* 13 | ; 14 | 15 | input_character // every character that is not a newline character 16 | : [^] - newline 17 | ; 18 | 19 | delimited_comment 20 | : "/*" delimited_comment_section* "*"+ "/" 21 | ; 22 | 23 | delimited_comment_section 24 | : "/" 25 | | "*"* [^/*]+ // i.e. asterisks followed by characters that aren't slash or asterisk 26 | ; 27 | ``` 28 | 29 | Comments do not nest. The character sequences "`/*`" and "`*/`" have no special meaning within a single-line comment, and the character sequences "`//`" and "`/*`" have no special meaning within a delimited comment. 30 | 31 | Comments are not processed within string literals or user defined literals. 32 | 33 | An example including both single line and delimited comments is: 34 | 35 | ```adamant 36 | // A single line comment 37 | 38 | /* this is a block comment 39 | let x = 5; // it can contain code and other comments 40 | */ 41 | ``` 42 | -------------------------------------------------------------------------------- /src/conventions.md: -------------------------------------------------------------------------------- 1 | # Conventions 2 | 3 | ## Naming Conventions 4 | 5 | The following naming conventions are enforced by compiler warnings. 6 | 7 | | Item | Convention | 8 | | --------------------------- | -------------------------------------------------- | 9 | | Packages | `dotted.snake_case` | 10 | | Namespaces | plural `snake_case` | 11 | | Types (except copy structs) | `Pascal_Case_With_Underscores` | 12 | | Copy Structs | `snake_case` | 13 | | Type Parameters | concise `Pascal_Case_With_Underscores`1 | 14 | | Functions | `snake_case()` | 15 | | Divergent Functions | `ALL_CAPS()` | 16 | | Local variables | `snake_case` | 17 | | Fields | `snake_case` | 18 | | Constants | `PascalCase`2 | 19 | | Global variables | `ALL_CAPS` | 20 | 21 | 1 Type parameters are often single uppercase letters (i.e. "`T`").
22 | 2 Acronyms are capitalized and run together (i.e. "`ScreenDPIToPrint`"). 23 | 24 | ## Curly Brace Placement 25 | 26 | Curly braces should almost always be on their own line, never K&R style with the open brace on the same line. However, for control flow statements with a single statement, the braces and statement can appear on a single line indented. If the single statement is short it may appear on the same line as the control flow. For `break`, `continue` and `return` this should be an if expression. 27 | 28 | ```adamant 29 | for x in 1..10 30 | { 31 | if f(x) 32 | { 33 | // Multiple lines of work 34 | } 35 | if g(x) 36 | { doSomething(x); } 37 | if bar => break; 38 | } 39 | ``` 40 | 41 | ## Parameter placements 42 | 43 | Function parameters should all be on one line or each on a separate line. If on separate lines, the left parenthesis is on the line with the function and the first parameter goes on the line after. The close parenthesis goes on the line with the last parameter. The exception is the self parameter which stays on the line with the function unless it doesn't fit. When parameters are on separate lines the return type is on its own line starting with `->`. 44 | 45 | ```adamant 46 | 47 | public fn example(self, 48 | arg1: int, 49 | arg2: int, 50 | arg3: int, 51 | arg4: int) 52 | -> int 53 | { 54 | // ... 55 | } 56 | ``` 57 | 58 | ## Code Width 59 | 60 | Stay within 80 characters most of the time, never go beyond 100. This is not because of what fits on screen, but rather, what humans can easily read and which parts of text humans focus on. 61 | 62 | ## Other 63 | 64 | * Prefer named constructors to a method that makes an instance (for example making a child node, copy or converted value) 65 | * In constructors, always use self or implicit self to access fields and properties, in methods, use only when necessary. (There should be compiler warnings for this). 66 | * Use spaces instead of tabs (need compiler warning for this). 67 | -------------------------------------------------------------------------------- /src/conversions.md: -------------------------------------------------------------------------------- 1 | # Conversions 2 | 3 | A *conversion* is a transformation of values from one type to another that allows expressions of the first type to used as the second type. Conversions can be *implicit* or *explicit*. Explicit conversions require an explicit conversion expression. For example, the type "`int`" can be implicitly converted to "`int64`", so expressions of type "`int`" can implicitly be treated as "`int64`". The opposite conversion, from "`int64`" to "`int`, is explicit, so ane explicit conversion is required. 4 | 5 | ```adamant 6 | let a: int = 123; 7 | let b: int64 = a; // implicit conversion from int to int64 8 | let c: int = b as! int; // explicit conversion from int64 to int 9 | ``` 10 | 11 | Additionally, certian numeric conversions will not fail, but may lead to a loss of precision. These are described in [Explicit Numeric Conversions](#explicit-numeric-conversions) and can be performed using the "`as`" operator. 12 | 13 | Some conversions are defined by the language. Programs may also define their own conversions (see [User-defined Conversions](#user-defined-conversions)). 14 | 15 | ## Implicit conversions 16 | 17 | The following conversions are implicit conversions: 18 | 19 | * Implicit numeric conversions 20 | * Implicit optional conversions 21 | * Implicit constant expression conversions 22 | * User-defined implicit conversions 23 | 24 | The "`as`" operator can be used to explicitly cause an implicit conversion. 25 | 26 | ### Implicit Numeric Conversions 27 | 28 | The implicit numeric conversions are those conversions between simple numeric types that can be safely performed with no loss of precision. 29 | 30 | The implicit numeric conversions are: 31 | 32 | | From | To | 33 | | --------- | ------------------------------------------------------------------------- | 34 | | `int8` | `int16`, `int`, `int64`, `float32`, `float` | 35 | | `byte` | `int16`, `uint16`, `int`, `uint`, `int64`, `uint64`, `float32`, `float` | 36 | | `int16` | `int`, `int64`, `float32`, `float` | 37 | | `uint16` | `int`, `uint`, `int64`, `uint64`, `float32`, `float` | 38 | | `int` | `int64`, `float` | 39 | | `uint` | `int64`, `uint64`, `float` | 40 | | `float32` | `float` | 41 | 42 | ### Implicit Optional conversions 43 | 44 | Given a value type `S` and reference type `T` such that `S <: T`, there are implicit boxing conversions from "`S?`" to "`T?`" and from "`S`" to "`T?`". There is also an implicit conversion from `S` to `S?`. 45 | 46 | ### Implicit Constant Expression Conversions 47 | 48 | A constant expression of the types "`int8`", "`byte`", "`int16`", "`uint16`", "`int`", "`uint`", "`int64`", "`uint64`", "`size`", "`offset`", "`float32`", or "`float`" can be implicitly converted to any other type in the list if the value of the constant expression is within the range of the destination type and can be represented without loss of precision. Thus for conversion to "`float32`" and "`float`" the value must not have more significant bits than be represented by the type. 49 | 50 | ### User-defined Implicit Conversions 51 | 52 | **TODO** 53 | 54 | ## Explicit Conversions 55 | 56 | The following conversions are explicit conversions: 57 | 58 | * All implicit conversions 59 | * Explicit numeric conversions 60 | * Explicit reference conversions 61 | * User-defined explicit conversions 62 | 63 | Explicit conversions can be performed with the "`as!`" or "`as?`" operators. The former causes abandonment when the conversion fails at runtime, the latter produces the value "`none`". 64 | 65 | ### Explicit Numeric Conversions 66 | 67 | The explicit numeric conversions are all the conversions between numeric types for which an implicit conversion doesn't exist. An explicit numeric conversion fails if the value being converted is not in the range of the target type. 68 | 69 | Conversions from any integer numeric type including "`size`" and "`offset`" to "`float32`" or "`float`" will not fail but may cause a loss of precision. These can be performed using the "`as`" operator. 70 | 71 | ### Explicit Reference Conversions 72 | 73 | Given reference types "`S`" and "`T`", there is an explicit conversion from "`S`" to "`T`" unless it can be proven that there does not exist a type "`U`" where `U <: S` such that `U <: T`. 74 | 75 | ### User-defined Explicit Conversions 76 | 77 | **TODO** 78 | -------------------------------------------------------------------------------- /src/documentation-comments.md: -------------------------------------------------------------------------------- 1 | # Documentation Comments 2 | 3 | Adamant programs support documentation using documentation comments. 4 | 5 | **TODO:** Need a way to have package level comments (Not just namespace). 6 | 7 | ## Documentation Blocks 8 | 9 | A documentation block consists of one or more single line comments beginning with "`///`". The comment text is interpreted using a subset of Markdown. Documentation blocks must proceed a declaration they are documenting. 10 | 11 | ```grammar 12 | documentation_comment_block 13 | : documentation_comment_line+ 14 | ; 15 | 16 | documentation_comment_line 17 | : "///" 18 | ``` 19 | 20 | Example: 21 | 22 | ```adamant 23 | /// And this is a doc comment for the next declaration 24 | /// It can contain some markdown formatting. 25 | public func() -> void 26 | { 27 | } 28 | ``` 29 | 30 | ## Namespace Documentation 31 | 32 | Documentation for a namespace can be included by placing a file named "`README.md`" inside the directory for the namespace. The entire contents of the file are taken as the documentation for the namespace. 33 | 34 | ## Supported Markdown 35 | 36 | Documentation comments support a subset of [CommonMark](https://commonmark.org/), a strongly defined, highly compatible specification of Markdown. The following features of CommonMark are not supported: 37 | 38 | * Inline HTML 39 | * Trailing pound signs on ATX headings 40 | * Setext headings 41 | * Indented code blocks 42 | * List items with "`+`" 43 | * Whitespace at the end of a line does not cause a hard line break 44 | 45 | Additionally, numbered lists will be supported, but there interpretation may be modified. 46 | 47 | ## Conventions 48 | 49 | Use top level Markdown headers in documentation comments (i.e. "`# Header`"). These will be converted to the apropriate level of headers when documentation is generated. 50 | 51 | Document conditions outside of the preconditions that will cause abandonment with: 52 | 53 | ```adamant 54 | /// # Abandonment 55 | ``` 56 | 57 | If a function or type is unsafe, explain which invariants the caller is responsible for upholding. 58 | 59 | ```adamant 60 | /// # Safety 61 | ``` 62 | 63 | Examples can be included in an examples section. 64 | 65 | ```adamant 66 | /// # Examples 67 | ``` 68 | 69 | **TODO:** provide a way to document conditions that will cause exceptions to be thrown 70 | -------------------------------------------------------------------------------- /src/empty-types.md: -------------------------------------------------------------------------------- 1 | ## Empty Types 2 | 3 | The empty types are those types for which there is no value of that type. 4 | 5 | ```grammar 6 | empty_type 7 | : "never" 8 | | "void" 9 | ; 10 | ``` 11 | 12 | ### `never` Type 13 | 14 | The `never` type is the bottom type in Adamant. That is, it is a subtype of all other types. A function returning `never` or an expression of type `never` can't return a value. Instead it must either throw an exception, cause program abandonment, or never terminate. The never type is useful for functions which are known to never return. It is also the type of various expressions like `return`, `break`, `continue`, and `throw` which don't evaluate to a value. This allows for their use in boolean or coalescing expressions. 15 | 16 | The `never` type can be useful in cases where a type is expected but can't occur. For example, a function expecting a "`Result[T, Error]`" type which is either a value of type "`T`" or an error of type "`Error`", could be passed a value of type "`Result[T, never]`" if an error is not possible in this circumstance. The value "`none`" used with [optional types](optional-types.md) has the type "`never?`". 17 | 18 | ### `void` Type 19 | 20 | The `void` type functions similarly to `void` in languages like C, Java, and C#. However, those languages don't treat it as a proper type. In Adamant, `void` is a type that can be used as the argument to a generic parameter. Semantically, the void type behaves somewhat like a special unit type. However, rather than being a union type, it represents the absence of a type. Functions that return `void` don't return a value. Parameters of type `void` are dropped from the parameter list. It is illegal to directly use `void` in a number of ways a type normally can be simply because the result would be somewhat nonsensical. A variable or parameter can't be directly declared to have a void type. An expression of type void can't be assigned into anything. However, when void is used as the argument of a type parameter, it can cause variable and parameter declarations to be of type `void`. This is not an error. Parameters of type `void` are removed from the parameter list. Variables of type void are not allocated space. This can also create situations where an expression of a generic type is actually of type `void` and assigned into a variable of the same generic type. This is not an error. Effectively, the assignment is not performed. 21 | 22 | Functions declared without a return arrow (`->`) and return type are defaulted to returning `void`. Explicitly declaring a function as returning void generates a warning. 23 | 24 | For purposes of covariance and contravariance, "`void`" acts like a top type. For example, if a base class function returns "`void`" then it can be overridden with a function returning any type. 25 | -------------------------------------------------------------------------------- /src/enumerations.md: -------------------------------------------------------------------------------- 1 | # Enumerations 2 | 3 | Enumerations are types that can have a number of "variants." These variants can be a simple set of constant values, or they could be distinct data types. Thus they provide a [discriminated union](https://en.wikipedia.org/wiki/Tagged_union) type. In type theory this is known as a sum type. 4 | 5 | Both classes and structs can enumerations, and there are important differences in their behavior and use. 6 | -------------------------------------------------------------------------------- /src/execution-order.md: -------------------------------------------------------------------------------- 1 | ## Execution Order 2 | 3 | Expressions are evaluated left to right. Arguments to function calls are evaluated left to right. If an expression contains only pure functions/operations, then the compiler may execute them in any order for optimization purposes. The compiler may assume that functions marked pure are pure and can be evaluated in any order. 4 | -------------------------------------------------------------------------------- /src/expression-blocks.md: -------------------------------------------------------------------------------- 1 | ## Expression Blocks 2 | 3 | ```grammar 4 | expression_block 5 | : block 6 | | result_expression 7 | ; 8 | ``` 9 | 10 | ### Blocks 11 | 12 | A block is zero or more statements enclosed in curly braces. If the block contains result expressions, then the type of the expression is the type that all the result expression types are compatible with. If a block contains no result expressions, then the type of the expression is "`void`". 13 | 14 | ```grammar 15 | block 16 | : "{" statement* "}" 17 | ; 18 | ``` 19 | 20 | ### Result Expressions 21 | 22 | A result expression is used to provide the value a block evaluates to. Certian control flow expressions like "`if`" allow a result expression to be used directly as the body of the expression. In that case the block has the type of the expression and yields the expression's value. When a result expression is used inside a block in other contexts, then all control flow paths in the block must lead to a result expression. The type of each result expression is "`never`". The type of the block is determined by taking the types of the expressions of all result expressions in the block and finding a common type they can all be converted to. It is an error to have statements or expressions after the control flow is terminated by the result expression. 23 | 24 | ```grammar 25 | result_expression 26 | : "=>" expression 27 | ; 28 | ``` 29 | 30 | A result expression can't be used outside of a block. However, it can be used to create a block that can be used as expression with a value. 31 | 32 | ```adamant 33 | var x = => "Hello"; // ERROR 34 | x = { 35 | DoSomething(); 36 | => "Goodbye"; 37 | }; 38 | ``` 39 | -------------------------------------------------------------------------------- /src/expressions.md: -------------------------------------------------------------------------------- 1 | # Expressions 2 | 3 | Expressions can be expression blocks which are blocks or result expressions, however a number of places in the grammar do not allow expression blocks. For that purpose, an embedded expression is any expression that is not an expression block. 4 | 5 | ```grammar 6 | expression 7 | : expression_block 8 | | embedded_expression 9 | ; 10 | 11 | embedded_expression 12 | : choice_expression 13 | | loop_expression 14 | ; 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /src/external.md: -------------------------------------------------------------------------------- 1 | # External Declarations 2 | 3 | The `external` keyword declares blocks of functions, structs and global variables that are implemented externally. Use compiler attributes and compiler settings to control this. 4 | 5 | ## External Structs 6 | 7 | Structs declared in external block follow the layout rules of the target language. 8 | 9 | ## External Function Example 10 | 11 | All parameters and returns from C external functions must be one of: 12 | 13 | * A simple numeric type (i.e. `int`, `float` etc.) 14 | * An external struct 15 | * A pointer to one of the above 16 | 17 | ```adamant 18 | #link("libc") // Less generic attribute name 19 | external // by default, changes to 'C' ABI 20 | { 21 | public fn function() -> int; 22 | } 23 | ``` 24 | 25 | ## Exporting Example 26 | 27 | Exporting allows functions to be called from other languages. 28 | 29 | ```adamant 30 | #export // tells it to make this function visible externally, and not mangle the name, by default, changes to 'C' ABI 31 | publish fn something() -> void 32 | { 33 | // implementation 34 | } 35 | 36 | #export("NewName") // export as the given name 37 | #calling_convention(.Fast) // should take an enum value, can also be applied to external block 38 | publish fn something2() -> void 39 | { 40 | // implementation 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /src/functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | ## Params List 4 | 5 | The `params` keyword allows for the creation of variadic functions. That is function that can take a variable number of arguments. It also allows for the invocation of variadic functions using a list of arguments. Additionally, it can operate with tuples, not just lists. 6 | 7 | ### Declaring a Variadic Function 8 | 9 | A variadic function is declared by prefixing a parameter with the `params` keyword. That parameter should have a type that can be constructed with a list of values (see the next section). That is, the type of this argument is not limited to `Array[T]`. It may be borrow, borrow mut or owned. If the `params` parameter is the last parameter, this is always valid. If the parameter is not the last parameter, none of the following parameters can have default values. 10 | 11 | ```adamant 12 | public fn example(params items: List[int]) -> void 13 | { 14 | // ... 15 | } 16 | ``` 17 | 18 | ### Calling Variadic Functions 19 | 20 | A variadic function can be called as if it were a normal function except that a variable number of arguments can be passed in place of the `params` parameter. This could be zero arguments. 21 | 22 | ```adamant 23 | example(1, 2, 3, 4, 5); 24 | ``` 25 | 26 | ### Construction of the List 27 | 28 | When gathering the parameters into the collection type of the parameter, the compiler will generate code based on the available constructors and methods of the type. It will first look for a constructor taking `size, Raw_Array[T]` or `size, mut Raw_Array[T]`. If one of those exists, it will construct a raw array of the values and pass the number of values and the raw array to that constructor. Even if that constructor is `unsafe` (which it should be), no safety error will be generated if it is outside an unsafe context. If no such constructor is present, it will look for a constructor named `capacity` taking a single argument of `size`. If it exists, it will construct the collection using that constructor passing the number of values, then call `add()` repeatedly, passing each value. If no such constructor is present, it will look for a default constructor, use it and add the values. If none of these exists, an error will be generated. 29 | 30 | ### Calling Variadic Functions with a List 31 | 32 | Sometimes you wish to pass a pre-existing list of values in place of the variable number of arguments. This can be done by prefixing the argument in place of the variadic arguments with the params keyword. Normal type compatibility and conversion rules apply between the passed collection argument and the expected collection type. 33 | 34 | ```adamant 35 | let numbers = #[1, 2, 3, 4]; 36 | example(params numbers) 37 | ``` 38 | 39 | ## Params Tuple 40 | 41 | The `params` keyword can also be used with the tuple type. While this typically leads to functions with a fixed arity, variadic generics can be used to create variadic functions accepting a variable number of parameters of varying type. The `params` keyword works with the tuple type analogously to how it works with list. It can be used both for declaring a function and for invoking that function with an existing tuple. Additionally, it can be used with any function to take a tuple and expand it out so each element is passed as an argument. Thus the place where the params argument is passed is effectively replaced by an access to each element of the tuple for a number of arguments equal to the length of the tuple. 42 | -------------------------------------------------------------------------------- /src/glossary.md: -------------------------------------------------------------------------------- 1 | # Appendix: Glossary 2 | 3 | Reference Type 4 | : Any type that is implicitly passed by reference. All types declared using the `class` keyword are reference types. 5 | 6 | Value Type 7 | : Any type that is implicitly passed by value. All types declared using the `struct` keyword are value types. 8 | 9 | Copy Type 10 | : A value type that has an implicit copy constructor so that when it is passed by value, a copy is passed rather than moving the value. Any type declared using the `struct` keyword that also has a copy constructor declared `implicit` is a copy type. 11 | 12 | Move Type 13 | : A value type that does not have an implicit copy constructor so that when it is passed by value, the value is moved out of the original variable. Any type declared using the `struct` keyword that does *not* have a copy constructor declared `implicit` is a move type. 14 | 15 | Object Type 16 | : A type of an object. Types declared with `class` and `struct` are object types. Types that are not object types include function types and ref types (note: ref types are different than reference types). 17 | 18 | Ref Type 19 | : A ref type is the type of a variable reference. That is a reference to a variable rather than an object. 20 | -------------------------------------------------------------------------------- /src/grammars.md: -------------------------------------------------------------------------------- 1 | ## Grammars 2 | 3 | This reference presents the syntax of Adamant using two grammars. The *lexical grammar* defines how characters combine to define tokens. The *syntactic grammar* defines how the tokens resulting from the lexical grammar are combined to form Adamant programs. 4 | 5 | ### Grammar Notation 6 | 7 | The grammars in this reference use a variant of [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form). They are not formal and may be ambiguous or have a structure which is not supported by certain parsing algorithms. Ambiguity in the grammar may be resolved by additional restrictions stated alongside the grammar. The notation used is summarized in the table below. Lexical grammars operate over unicode scalar values, thus whitespace cannot occur between nonterminals. When matching strings, i.e. in lexical grammars, the longest match is taken. Syntactic grammars operate over tokens and nonterminals may be separated by whitespace. 8 | 9 | | Syntax | Meaning | 10 | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | 11 | | *name* | A nonterminal | 12 | | *name* `:` *expression* `;` | A derivation rule for the nonterminal *name* | 13 | | *expression* `\|` *expression* | Unordered choice | 14 | | *expression*`*` | Expression repeated zero or more times | 15 | | *expression*`+` | Expression repeated one or more times | 16 | | *expression*`?` | Expression is optional | 17 | | `(`*expression*`)` | Expression grouping, provides order of operations | 18 | | *expression*`{`(*separator* `,`)? *n*`}` | Repeat expression *n* times, separated by *separator* if provided | 19 | | *expression*`{`(*separator* `,`)? *n*`,`*m*`}` | Repeat expression between *n* and *m* times, separated by *separator* if provided | 20 | | *expression*`{`(*separator* `,`)? *n*`,*}` | Repeat expression *n* or more times, separated by *separator* if provided | 21 | | *expression* `-` *expression* | Match the first expression except if it matches the second expression | 22 | | `\u(`*hex digits*`)` | A single unicode code point | 23 | | `[`*character class*`]` | A regular expression style character class (`^` negates a class, `\` escapes, and `-` matches a range) | 24 | | `\p{`*unicode class*`}` | Match any single character in the given Unicode class | 25 | | `"`*literal*`"` | Matches a literal string (no escape sequences are supported) | 26 | | `?`*description*`?` | Matches a string according to the description | 27 | | `//` *comment* *newline* | A line comment | 28 | | `/*` *comment* `*/` | A multi-line comment | 29 | -------------------------------------------------------------------------------- /src/identifiers.md: -------------------------------------------------------------------------------- 1 | ## Identifiers 2 | 3 | Identifiers are used as the names of types, variables, functions etc. in Adamant programs. There are three kind of identifiers. *Simple identifiers* are identifiers whose names are unambiguous in the Adamant syntax. In many languages, these are the only kinds of identifiers. *Escaped identifiers* allow the use of keywords and numbers as identifiers. Finally, *identifier strings* allow arbitrary text to be used as an identifier. When determining what a name refers to, the kind of identifier is not used, only the value. For example, an escaped identifier and an identifier string with the same value are the same identifier. Additionally, identifier values are converted to Normalization Form C (NFC) before comparison. 4 | 5 | ```grammar 6 | identifier 7 | : simple_identifier 8 | | escaped_identifier 9 | | identifier_string 10 | ; 11 | ``` 12 | 13 | ### Simple Identifiers 14 | 15 | Simple identifiers start with a letter or underscore and may be followed by a letters, underscores, and digits. However, a reserved word or keyword may not be used as a simple identifier. Additionally, all identifiers containing double underscore characters are reserved for use by the implementation and may not be used as simple identifiers. Identifier strings can be used to create an identifier containing double underscore. 16 | 17 | ```grammar 18 | simple_identifier 19 | : identifier_or_keyword - keyword - reserved_word - reserved_identifier 20 | ; 21 | 22 | identifier_or_keyword 23 | : identifier_start_character identifier_part_character* 24 | ; 25 | 26 | reserved_identifier 27 | : identifier_part_character* "__" identifier_part_character* 28 | ; 29 | 30 | identifier_start_character 31 | : letter_character 32 | | '_' 33 | ; 34 | 35 | identifier_part_character 36 | : letter_character 37 | | decimal_digit_character 38 | | connecting_character 39 | | combining_character 40 | | formatting_character 41 | ; 42 | 43 | letter_character 44 | : \p{L} // Unicode Letter 45 | | \p{Nl} // Unicode Number, letter 46 | ; 47 | 48 | combining_character 49 | : \p{Mn} // Unicode Mark, nonspacing 50 | | \p{Mc} // Unicode Mark, spacing combining 51 | ; 52 | 53 | decimal_digit_character 54 | : \p{Nd} // Unicode Number, decimal digit 55 | ; 56 | 57 | connecting_character 58 | : \p{Pc} // Unicode Punctuation, connector 59 | ; 60 | 61 | formatting_character 62 | : \p{Cf} // Unicode Other, format 63 | ; 64 | ``` 65 | 66 | ### Escaped Identifiers 67 | 68 | A backslash can be used to escape keywords, reserved words, and names starting with digits for use as identifiers. For example "`\class`", allows one to use the "`class`" keyword as a variable name. Only keywords, reserved words and names starting with digits are allowed to be used as escaped identifers. For example, "`\hello`" is a nonfatal syntax error because "hello" is not a keyword. Escaped identifiers are used to access the fields of a tuple. For example, `t.\1` is the first field of the tuple `t`. 69 | 70 | ```grammar 71 | escaped_identifier 72 | : "\" keyword 73 | | "\" reserved_word 74 | | "\" decimal_digit_character identifier_part_character* 75 | ; 76 | ``` 77 | 78 | For example, this code declares a variable using an escaped identifer and uses it. 79 | 80 | ```adamant 81 | let \class = "Mammalia"; 82 | let order = "Carnivora"; 83 | let family = "Canidae"; 84 | let genius = "Canis"; 85 | let species = "lupus"; 86 | 87 | console.WriteLine("The Gray Wolf is \(\class) \(order) \(family) \(genius) \(species)"); 88 | ``` 89 | 90 | ### Identifier Strings 91 | 92 | For more flexibility in variable names, strings can be used as identifiers by escaping them with a backslash. Even delimited strings may be used as identifier strings. However, identifier strings may not contain [interpolated segments](literals.md#interpolated-strings). It is *not* an error to use an identifier string for an identifier that could be expressed as a simple or escaped identifier. However, it is highly discouraged except in specific cases. This enables a convention of consistently naming tests with identifier strings. 93 | 94 | ```grammar 95 | identifier_string 96 | : "\" string_literal 97 | ; 98 | ``` 99 | 100 | Identifier strings are particularly useful for naming tests. 101 | 102 | ```adamant 103 | #Test 104 | public \"`foo()` returns `true`"() -> void 105 | { 106 | assert(foo()); 107 | } 108 | ``` 109 | 110 | Identifier strings containing double quotes can be easily written using delimited strings. 111 | 112 | ```adamant 113 | let \#"He said "Hello" to me."# = true; 114 | ``` 115 | -------------------------------------------------------------------------------- /src/influences.md: -------------------------------------------------------------------------------- 1 | # Appendix: Influences 2 | 3 | This appendix lists various languages and papers that have influenced the design and implementation of the Adamant language. This is not an exhaustive list. 4 | 5 | * [Rust](https://www.rust-lang.org/) 6 | * Borrow Checking 7 | * Enum types 8 | * Traits 9 | * Bit size integer and floating point types 10 | * Never Type (based on RFC) 11 | * Control flow without parens 12 | * Match 13 | * Pattern Matching 14 | * `loop` 15 | * `foreach` 16 | * Documentation Comments are Markdown 17 | * `unsafe` 18 | * Comparison operator chaining 19 | * Type Inference 20 | * Project Midori 21 | * Effect Typing 22 | * Error Handling 23 | * C# 24 | * Value types (i.e. `struct`) 25 | * Declaration site reference vs value types 26 | * Preprocessor 27 | * Optional (i.e. Nullable) Types 28 | * Access Modifiers 29 | * Coversions 30 | * Namespaces 31 | * Using 32 | * Documentation Comments 33 | * `decimal` type 34 | * `unchecked` 35 | * Generics 36 | * Collection Initializers 37 | * Linq API 38 | * Partial Classes 39 | * Generator Functions 40 | * Async/Await 41 | * `params` 42 | * [Swift](https://swift.org/) 43 | * Interpolated String Syntax 44 | * Multi-line String Literals 45 | * Metatypes 46 | * [Agda](https://github.com/agda/agda) 47 | * Operand placeholders (`_`) for operator overloads 48 | * Scala 49 | * Case Classes 50 | * JAI 51 | * Symmetric Operators 52 | * Compile-time code execution 53 | * F# 54 | * Units of Measure 55 | -------------------------------------------------------------------------------- /src/interpolated-strings.md: -------------------------------------------------------------------------------- 1 | ## Interpolated Strings 2 | 3 | **TODO:** determine how conversion to string works for interpolated string. 4 | **TODO:** determine how formatting of the expression works. 5 | -------------------------------------------------------------------------------- /src/keywords.md: -------------------------------------------------------------------------------- 1 | ## Keywords 2 | 3 | A keyword is an identifier like sequence of characters that is reserved for use by the language and can't be used as an identifier except by escaping it. Note that the lexical set of keywords includes things that are not standard keywords from a syntactic perspective. For example, it includes the boolean operators `and` and `or`, types like `int`, and literal values such as `true` and `false`. 4 | 5 | ```grammar 6 | keyword 7 | : "abstract" 8 | | "and" 9 | | "Any" 10 | | "as" 11 | | "base" 12 | | "bool" 13 | | "break" 14 | | "byte" 15 | | "class" 16 | | "const" 17 | | "copy" 18 | | "delete" 19 | | "else" 20 | | "ensures" 21 | | "enum" 22 | | "explicit" 23 | | "external" 24 | | "false" 25 | | "float" 26 | | "float32" 27 | | "fn" 28 | | "foreach" 29 | | "forever" 30 | | "get" 31 | | "held" 32 | | "id" 33 | | "if" 34 | | "implicit" 35 | | "in" 36 | | "init" 37 | | "int" 38 | | "int8" 39 | | "int16" 40 | | "int64" 41 | | "invariant" 42 | | "iso" 43 | | "let" 44 | | "loop" 45 | | "match" 46 | | "may" 47 | | "Metatype" 48 | | "move" 49 | | "mut" 50 | | "namespace" 51 | | "new" 52 | | "never" 53 | | "next" 54 | | "no" 55 | | "none" 56 | | "not" 57 | | "offset" 58 | | "operator" 59 | | "or" 60 | | "override" 61 | | "owned" 62 | | "params" 63 | | "protected" 64 | | "public" 65 | | "published" 66 | | "ref" 67 | | "return" 68 | | "requires" 69 | | "safe" 70 | | "self" 71 | | "Self" 72 | | "set" 73 | | "size" 74 | | "struct" 75 | | "throw" 76 | | "true" 77 | | "Tuple" 78 | | "Type" 79 | | "uint" 80 | | "uint16" 81 | | "uint64" 82 | | "uninitialized" 83 | | "unsafe" 84 | | "using" 85 | | "var" 86 | | "void" 87 | | "where" 88 | | "while" 89 | ; 90 | ``` 91 | -------------------------------------------------------------------------------- /src/lexical-analysis.md: -------------------------------------------------------------------------------- 1 | ## Lexical Analysis 2 | 3 | Every Adamant source file is composed of code points from the Unicode standard encoded in UTF-8. The terminal symbols of the lexical grammar are the Unicode code points. The lexical grammar specifies how these code points are combined to form [newlines](line-terminators.md), [whitespace](whitespace.md), [comments](comments.md), and [tokens](tokens.md). 4 | 5 | Every source file in an Adamant program must match the `input` production of the lexical grammar. 6 | 7 | ```grammar 8 | input 9 | : input_element* 10 | ; 11 | 12 | input_element 13 | : newline 14 | | whitespace 15 | | comment 16 | | token 17 | ; 18 | ``` 19 | 20 | Lexical processing consists of breaking down the file into a sequence of tokens that are the input to the syntactic analysis. Newlines, whitespace, and comments can separate tokens, but have no impact on the syntactic structure of programs, except for [documentation comments](documentation-comments.md). 21 | 22 | When multiple lexical rules match, the processing always forms the longest match. 23 | 24 | Historically, languages were designed using only characters from the ASCII character set even if they used Unicode encoding and allows Unicode values in strings and character literals. Adamant intentionally makes use of Unicode characters in some places when they improve readability. However, due to the difficulty of using Unicode characters on standard keyboards, these are always an alternative to more traditional ASCII notations. 25 | 26 | Note: the current version of Adamant does not have a preprocessor. When one is added, it will be handled similarly to the C# preprocessor by including it in the lexical grammar. 27 | -------------------------------------------------------------------------------- /src/localization.md: -------------------------------------------------------------------------------- 1 | ## Localization 2 | 3 | The standard library relies on a concept of locals for all formatting and parsing. 4 | 5 | For localization, there are a number of locales that might be in play. They are: 6 | 7 | * Display Locale - The locale to use for all formatting/output a user may see. The default value is controlled by the target locale. 8 | * User Locale - The locale as configured by the current user. (For web servers etc. this is the server's locale settings) 9 | * Invariant Locale - A locale whose output is uniform across all systems and times. 10 | * Specific Locales - Locale objects can be created for specific locales with or without user overrides. 11 | 12 | Many applications are developed without regard for internalization. For these applications the concept of *target locale* exists. The target locale determines the default for the current locale. Target locale can be set to one of three options: 13 | 14 | * User Locale - The app is meant to be fully localized, current culture defaults to the user's culture 15 | * Specific Locale *with* User Overrides - The default locale is the specified locale with user settings overriding things like date formatting. 16 | * Specific Locale *without* User Overrides - The default locale is the specified locale without any user overrides. This then allows the setting of specific formatting etc. In this case, the locale is fully determined at compile time. 17 | 18 | **The target locale defaults to en-US *with* user overrides.** 19 | 20 | Note: .NET uses the term "culture", but "locale" seems to be both the more standard term and more precise. 21 | 22 | ### Debug Formatting 23 | 24 | When converting a value to a string for debug purposes the display locale is used by default. However, there should be a way to override this for a developer wishing to test the app in a separate locale from their testing. That probably means there is a debug display locale. 25 | 26 | ### Conventions 27 | 28 | The names below are used throughout the standard library for converting to strings. In these, the "`FormatEnum`" is specific to the type being formatted. Overloads with a "`FormatEnum`" may not be provided in cases where formatting can't be controlled. 29 | 30 | * `to_display_string()` 31 | * `to_display_string(format: FormatEnum)` 32 | * `to_debug_string()` 33 | * `to_debug_string(format: FormatEnum)` 34 | * `to_invariant_string()` 35 | * `to_invariant_string(format: FormatEnum)` 36 | * `to_string(locale: Locale)` 37 | * `to_string(locale: Locale, format: FormatEnum)` 38 | -------------------------------------------------------------------------------- /src/loop-expressions.md: -------------------------------------------------------------------------------- 1 | ## Loop Expressions 2 | 3 | Adamant has three loop constructs. They are "`loop`", "`while`" and "`foreach`". All loop bodies must be block statements. 4 | 5 | ```grammar 6 | loop_expression 7 | : simple_loop_expression 8 | | while_expression 9 | | foreach_expression 10 | ; 11 | ``` 12 | 13 | ### `loop` 14 | 15 | The simplest kind of loop is an infinite loop. Infinite loops are created with the "`loop`" keyword. 16 | 17 | ```grammar 18 | simple_loop_expression 19 | : loop_label? "loop" block 20 | ; 21 | ``` 22 | 23 | Example: 24 | 25 | ```adamant 26 | loop 27 | { 28 | console.write_line("Loop Forever"); 29 | } 30 | ``` 31 | 32 | ### `while` 33 | 34 | ```grammar 35 | while_expression 36 | : loop_label? "while" boolean_expression block 37 | ; 38 | ``` 39 | 40 | Example: 41 | 42 | ```adamant 43 | while condition 44 | { 45 | // do work 46 | } 47 | ``` 48 | 49 | You might be tempted to write an infinite loop as "`while true`" however "`loop`" is better for this because the compiler treats them differently for definite assignment. Namely, a variable can be definitely assigned in a "`loop`" but will not be considered definitely assigned by a "`while true`" loop. 50 | 51 | If you need something like a "`do {} while`" loop available in many other languages, the proper way to do that is: 52 | 53 | ```adamant 54 | loop 55 | { 56 | // do work 57 | if not condition => break; 58 | } 59 | ``` 60 | 61 | ### `foreach` 62 | 63 | Foreach loops allow something to be done a set number of times or for a set of values. 64 | 65 | ```grammar 66 | foreach_expression 67 | : loop_label "foreach" "var"? pattern "in" embedded_expression block 68 | ; 69 | ``` 70 | 71 | Example: 72 | 73 | ```adamant 74 | foreach x in 1..10 75 | { 76 | console.write_line("{x}"); 77 | } 78 | ``` 79 | 80 | The `foreach` loop can operate over any value that is iterable. 81 | 82 | Sometimes the actual value of the loop variable isn't needed. This can be denoted using the underscore. 83 | 84 | ```adamant 85 | foreach _ in 1..10 86 | { 87 | console.write_line("Hi"); 88 | } 89 | ``` 90 | 91 | **TODO:** show how to handle references to structs 92 | 93 | ### Controlling Loop Iteration 94 | 95 | You can use `break` and `next` expressions to control loop iteration. Both "`break`" and "`next`" expressions have the type "`never`". 96 | 97 | ```grammar 98 | break_expression 99 | : "break" loop_label? expression? 100 | ; 101 | 102 | next_expression 103 | : "next" loop_label? 104 | ; 105 | ``` 106 | 107 | ### `break` Values 108 | 109 | The "`break`" expression can have a value. This value then becomes the value of the loop expression. All break expressions must have values of compatible types. The type of a "`loop`" expression is the type of the values. 110 | 111 | Example: 112 | 113 | ```adamant 114 | let x = foreach x in items 115 | { 116 | do_something(); 117 | if condition 118 | { 119 | break get_value(); 120 | } 121 | } ?? alternate_value; 122 | ``` 123 | 124 | The "`while`" and "`foreach`" loop return an optional value to indicate the loop body never executed. In that situations, the value of the loop expression is "`none`". This allows you to use the coalesce operator to easily specify a value for that case. 125 | 126 | ```adamant 127 | let x = foreach y in items 128 | { 129 | if condition 130 | { 131 | break 234; 132 | } 133 | } ?? foo; 134 | ``` 135 | 136 | ### Loop Labels 137 | 138 | When using break or next it may be necessary to indicate which loop you are controlling. This can be done by labeling the loop. Loop labels are essentially lifetimes and are named as such. 139 | 140 | ```grammar 141 | loop_label 142 | : "$" identifer ":" 143 | ; 144 | ``` 145 | 146 | Example: 147 | 148 | ```adamant 149 | $outer: foreach x in 0..10 150 | { 151 | $inner: foreach y in 0..10 152 | { 153 | if x % 2 == 0 => next $outer; 154 | if y % 2 == 0 => next $inner; 155 | console.write_line("x = {x}, y = {y}"); 156 | } 157 | } 158 | ``` 159 | 160 | When loop labels are combined with break values, the label is placed before the value as `break $label value;`. 161 | -------------------------------------------------------------------------------- /src/member-access.md: -------------------------------------------------------------------------------- 1 | ## Member Access 2 | 3 | ### Declared Visibility 4 | 5 | In Adamant there are a number of levels of visibility an item can have. These are specified using the access modifiers "`protected`", "`public`", and "`published`". If there is no access modifier on an item, it is private. The access limitations imposed by these visibility levels are: 6 | 7 | * "`published`": Access is not limited. 8 | * "`published protected`": Access is limited to the containing declaration or subtypes of the containing type. 9 | * "`public`": Access is limited to the the current package. 10 | * "`protected`": Access is limited to the containing declaration or subtypes of the containing type within the current package. 11 | * private: Access is limited to the current instance of the containing declaration. For namespace declarations, visibility is limited to the current file. 12 | 13 | Note that private access is more restrictive than in other object oriented languages like C# or Java. In those languages, private visibility allows other instances of the same class to access those members. In Adamant, it does not. 14 | 15 | ```grammar 16 | access_modifier 17 | : "published" 18 | | "published" "protected" 19 | | "public" 20 | | "protected" 21 | | // private 22 | ; 23 | ``` 24 | -------------------------------------------------------------------------------- /src/newlines.md: -------------------------------------------------------------------------------- 1 | ## Newlines 2 | 3 | Newlines divide an Adamant program into lines. For the purposes of viewing a source file as a sequences of lines, the end of file terminates the last line. 4 | 5 | ```grammar 6 | newline 7 | : \u(000D) // Carriage return 8 | | \u(000A) // Line feed 9 | | \u(000D)\u(000A) // Carriage return, line feed 10 | | \u(0085) // Next line 11 | | \u(2028) // Line separator 12 | | \u(2029) // Paragraph separator 13 | ; 14 | ``` 15 | -------------------------------------------------------------------------------- /src/operators-and-punctuators.md: -------------------------------------------------------------------------------- 1 | ## Operators and Punctuators 2 | 3 | Operators and punctuators are symbols used in Adamant as operators and other symbolic connectors. Some symbols are used in both capacities. As a consequence, the division into operator and punctuator categories is somewhat arbitrary and is done for convenience only. Note that the lexical category of operator does not include everything that is semantically an operator. For example, the `and` and `or` keywords are operators. 4 | 5 | ```grammar 6 | operator_or_punctuator 7 | : operator 8 | | punctuator 9 | ; 10 | ``` 11 | 12 | ### Operators 13 | 14 | The following symbols are used as operators. 15 | 16 | ```grammar 17 | operator 18 | : "." 19 | | ".." 20 | | "<.." 21 | | "..<" 22 | | "<..<" 23 | | "@" 24 | | "^" 25 | | "^." 26 | | "+" 27 | | "-" 28 | | "*" 29 | | "/" 30 | | "=" 31 | | "==" 32 | | "=/=" 33 | | "≠" // U+2260 34 | | "<" 35 | | "<=" 36 | | "≤" // U+2264 37 | | "" 39 | | ">=" 40 | | "≥" // U+2265 41 | | ">/=" 42 | | "+=" 43 | | "-=" 44 | | "*=" 45 | | "/=" 46 | | "??" 47 | | "?." 48 | | "<:" 49 | | "=>" 50 | | "|" 51 | | "&" 52 | ; 53 | ``` 54 | 55 | ### Punctuators 56 | 57 | The following symbols are used as symbolic connectors. 58 | 59 | ```grammar 60 | punctuator 61 | : "$" // was used for lifetimes, will likely be removed 62 | | "::." 63 | | "?" 64 | | "{" 65 | | "}" 66 | | "(" 67 | | ")" 68 | | "[" 69 | | "]" 70 | | ";" 71 | | "," 72 | | "#(" 73 | | "#[" 74 | | "#{" 75 | | ":" 76 | | "->" 77 | ; 78 | ``` 79 | 80 | ### Other Symbols 81 | 82 | Any symbol sequences not defined as operators or punctuators are errors. Thus these sequences are not technically reserved, but are available for future language features. However, the following symbols are "reserved" for the below uses: 83 | 84 | | Symbol | Use | 85 | | ------- | ----------------------------------------------- | 86 | | `^` | Raise to power (when used as a binary operator) | 87 | | `` ` `` | Reserved for code expressions and blocks | 88 | | `##` | Preprocessor | 89 | -------------------------------------------------------------------------------- /src/optional-types.md: -------------------------------------------------------------------------------- 1 | ## Optional Types 2 | 3 | Optional types can have all values of an underlying type plus an additional value "`none`". The underlying type can be any type including value or reference types. 4 | 5 | ```grammar 6 | optional_type 7 | : type "?" 8 | ; 9 | ``` 10 | 11 | ### Subtyping 12 | 13 | For all reference types `T` and `U` where `T <: U`, `T <: U?`. However, given value type `S` and reference type `T`, the type `S` is not a subtype of `T?`. However, there is an implicit conversion. 14 | 15 | ### The "`none`" Value 16 | 17 | The special value "`none`" is used to represent when an optional type does not have a value. The value "`none`" has the type "`never?`" thus it can be assigned into any optional type. 18 | 19 | ### Conditioning on a Value 20 | 21 | To conditionally operate on an optional value, use an "`if let`" expression. Other ways of checking for "`none`" are possible but not preferred. 22 | 23 | ```adamant 24 | let x: int? = ...; 25 | 26 | // Idiomatic way of checking for `none` 27 | if let y? = x 28 | { 29 | // y is the value of x 30 | } 31 | 32 | // Not Recommend 33 | match x 34 | { 35 | y? => ..., 36 | none => ..., 37 | } 38 | 39 | // Not Recommend 40 | if x =/= none 41 | { 42 | // Can't directly use the value of `x` in this block 43 | } 44 | ``` 45 | 46 | ### Coalescing Operator 47 | 48 | The coalescing operator `??` allows for the replacement of `none` with another value. If the expression to the left of the operator evaluates to a value other than `none` then the result of the coalescing operator is that value and the right hand expression is not evaluated. Otherwise, the result is the result of evaluating the right hand expression. Note that this is a short circuiting evaluation. 49 | 50 | ### Conditional Access 51 | 52 | Members of optional values can be accessed using the conditional access operator `x?.y`. This operator evaluates the left hand side. If the left hand side evaluates to `none`, then the right hand side is not evaluated and the result of the expression is `none`. Otherwise, the member is accessed and evaluated. Note that this is a short circuiting evaluation so that `x?.y.z()` would prevent the method `z` from being called if `x` were `none`, rather than simply evaluating `x?.y` to `none` and then attempting to call `z()` on it. 53 | 54 | ### Operator Lifting 55 | 56 | Operators are lifted for optional types similar to how they are in C#. 57 | 58 | ### Optional Types Precedence 59 | 60 | The optional type is immutable so `mut T?` must mean `(mut T)?`. But what does `ref V?` mean? Is it a reference to a variable of type `V?` or an option of a `ref V`? Phrased another way, is it `ref (V?)` or `(ref V)?` ? 61 | 62 | ```adamant 63 | let x1: int? = none; 64 | let y1 = ref x; // y: ref (int?) 65 | var x2: int? = none; 66 | let y2 = ref x2; // y2: ref var (int?) 67 | 68 | foo(x: ref x?) -> void 69 | { 70 | // ... 71 | } 72 | 73 | foo(ref x1); // requires ref (int?) 74 | foo(none); // requires (ref int)? 75 | let z = 5; 76 | foo(ref z) // requires (ref int)? 77 | ``` 78 | 79 | Notice that certain implicit conversions are safe. 80 | 81 | | From | To | 82 | | ------------------ | -------------- | 83 | | `ref (V?)` | `(ref V)?` | 84 | | `ref (mut V?)` | `(ref mut V)?` | 85 | | `ref var (V?)` | not safe | 86 | | `ref var (mut V?)` | not safe | 87 | 88 | Given that, it makes sense to interpret optional types to be of the second form. On the other hand, it seems much more likely you will want a reference to a variable of type `T?` than an optional reference to a variable of type `T`. So, `mut` and `ref` have higher precedence than optional, but optional has higher precedence than `ref var`. Thus for value type `V` and reference type `R` the optional types have the following meanings. 89 | 90 | | Type | Meaning | 91 | | ---------------- | ------------------ | 92 | | `V?` | `V?` | 93 | | `mut V?` | `(mut V)?` | 94 | | `ref V?` | `(ref V)?` | 95 | | `ref mut V?` | `(ref mut V)?` | 96 | | `ref var V?` | `ref var (V?)` | 97 | | `ref var mut V?` | `ref var (mut V?)` | 98 | | `R?` | `R?` | 99 | | `mut R?` | `(mut R)?` | 100 | | `ref var R?` | `ref var (R?)` | 101 | | `ref var mut R?` | `ref var (mut R?)` | 102 | 103 | **TODO:** What would be the syntax for "`(ref var V)?`"? 104 | -------------------------------------------------------------------------------- /src/packages.md: -------------------------------------------------------------------------------- 1 | ## Packages 2 | 3 | The *package* is the fundamental unit of deployment in Adamant. A package may be an executable or library. A package is compiled from one or more *source files* and a *package configuration*. Source files are formally described as *compilation units*. A source file is a sequence of unicode scalar values encoded in UTF-8. It is a non-fatal compilation error for a source file to begin with a unicode byte order mark. Source files typically have a one-to-one correspondence with files in a file system, but this is not required. The location of the source files relative to a root directory for the package source files is the basis of namespaces within the package. The package configuration specifies configuration values needed to properly compile the package such as the root namespace, package name, referenced packages, and aliases for the referenced packages. 4 | 5 | Broadly speaking, an Adamant package is compiled in three steps: 6 | 7 | 1. Lexical analysis translates Unicode code points into a stream of tokens. 8 | 2. Syntactic analysis translates tokens into a structured representation of Adamant code. 9 | 3. Semantic analysis translates that structured representation into executable code. 10 | -------------------------------------------------------------------------------- /src/planned-aliases.md: -------------------------------------------------------------------------------- 1 | ## Aliases 2 | 3 | The keyword "`alias`" introduces an alias for a type name. It does not create a separate type. 4 | 5 | ```adamant 6 | public alias PromiseResult[T] = Promise[Result[T]] 7 | ``` 8 | 9 | Aliases can have any access modifier. It may be useful/desirable to have aliases for other things such as namespaces etc. For that reason, or just for increased clarity it might be preferable to use the keyword pair `type alias` for type aliases. 10 | -------------------------------------------------------------------------------- /src/planned-ctfe.md: -------------------------------------------------------------------------------- 1 | ## Compile-time Function Execution 2 | 3 | Compile-time function execution allows "arbitrary" functions to be executed at compile time. This can be useful for precalculating constants and initial values. It is also important to macro processing and language oriented programming. Generally, the thought is that generics would be reinterpreted as being the values that are known/computed at compile time. Thus generics would be similar to C++ templates but would be optimized into shared implementations when it made sense. This is consistent with the idea that all generic arguments must be known at compile time. 4 | 5 | ### Compile Time Limitations 6 | 7 | Not all code would be executable at compile time. Instead, only code that had no side effects would be executable at compile time (no IO or mutating external state). This would be supported by the effect typing system so that any pure function could be evaluated at compile time. For this purpose, functions the developer explicitly marked as "pure" would be assumed to be pure and executable at compile time. There may also be limitations on the number of iterations loops were allowed to execute to avoid infinite loops during compile time. 8 | 9 | ### Custom Data 10 | 11 | All data types should be computable at compile time and be able to be used either as constant values or as precomputed initial values. For example, it should be possible to precompute a hash table of values that was the initial value of a static variable but was then updated at runtime. This would allow user literals to be preconstructed from their string literal representation. It would also allow all user data types to be used as generic arguments. 12 | 13 | ### Compile Time Only Functions 14 | 15 | There should be a way to indicate that certain pure functions should only be used at compile time and not at runtime. This would be a modifier or attribute that told the compiler the function couldn't be used at runtime and would ensure that the function was never emitted as compiled code. One use case for this would be code that performed hashing or encryption of security features. Another use case would be code for optimizing web assets (i.e. image optimizers). 16 | 17 | ### Const Expressions 18 | 19 | There should be a way to clearly indicate that a given expression must be evaluated at compile time. Currently, the thought it that this would use the `const` keyword. It could be a simple expression as `const(`*expression*`)`. However, following the idea that all code in square brackets is evaluated/known at compile time, it could be `const[`*expression*`]`. 20 | 21 | ### Constant Propagation 22 | 23 | While there are explicit constant expressions, any expression which the compiler determined was fully executable at compile time would be a candidate for constant propagation so that it could be evaluated and replaced with the resulting value. 24 | 25 | ### Type Functions 26 | 27 | Compile time functions should be able to manipulate types using reflection in a way that interacts well with types. For example, it should be possible to use the result of a function as a type anywhere a type is expected. That could allow for complex behaviors like varying the return type of a function based on the generic parameters of that function in nontrivial ways. It isn't clear whether these would need to be meta functions as described in the next section or whether regular pure functions could be used. 28 | 29 | ### Meta Functions 30 | 31 | Conceptually, type constructors are functions from types and values to types. Meta functions would remove the special classness of type constructors by allowing other functions over generic parameters. A meta function is a function with no runtime parameters that would always be evaluated at compile time. These would be declared by omitting the parentheses (i.e. `fn foo[n: size] -> Type`). They would be called without parentheses (i.e. `foo[45]`). There are some potential issues with meta functions though. They can't be generic in the types the operate on. For example, it wouldn't be possible to create a function that evaluated something as a const expression by passing it as a parameter (i.e. `constant[some_function()]`). That would not be possible because the function can't be generic in the type of its parameter. It might seem that meta functions would be useful for evaluating types, but pure functions would probably serve that purpose. For example a pure function `fn tuple_of(params types: Array[Type]) -> Type` which constructs the tuple type for a list of types can already be written and (assuming it is pure) called in a type context. It would seem the only thing meta functions would allow one to do is create functions that appeared to be type constructors in the same way generic classes are. One question there is whether they would need to return a metatype for consistency with the way classes work. It isn't clear this would add much value when one can already use regular functions to construct types. 32 | 33 | If one curried meta functions or allowed direct declaration, then maybe they could be generic. For example, `fn constant[T] -> [v: T] -> T` would accept a generic type parameter, then return a meta function that would take a value of that type and return it. It isn't clear whether the inference system could handle that. Alternatively, one could allow them to be directly declared as `fn constant[T][v: T] -> T`. 34 | 35 | The examples in the previous paragraph also point out that there might need to be function types for meta functions. 36 | 37 | ### Implementer's Notes 38 | 39 | Executing code at compile time would most likely require some kind of interpreter or virtual machine so that code which contained pointer manipulation could be safely executed and so that data types containing pointers etc could be properly reified. 40 | -------------------------------------------------------------------------------- /src/planned-expressions.md: -------------------------------------------------------------------------------- 1 | ## Additional Expressions 2 | 3 | ### Multiline String Literals 4 | 5 | Multiline string literals start with three double quotes followed by only whitespace on the line. They continue until a line that begins with whitespace followed by three double quotes. The lines with the double quotes are not included. So the final line does not end with a line break. They can be indented. The indent of the first line is ignored, indentation beyond that is included in the string. Double quotes can be used in a multiline string. A line can be continued by ending it with a backslash. *Regardless of the line endings of the source file, lines in the string are terminated with '\n'.* Multiline string literals should also support string interpolation. (see [Swift Multiline String Literals](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html) for more information). 6 | 7 | ```adamant 8 | let s = """ 9 | Indent to the left of this is ignored 10 | This is indented one tab 11 | This line continues \ 12 | onto the next line. 13 | """; 14 | ``` 15 | 16 | ### Unchecked Expressions 17 | 18 | Inside an unchecked expression, the basic operators on integer types would be unchecked. Thus overflow on "`+`", "`-`", "`*`" and "`/`" would not be checked. Note that wrapping on division can occur for `MIN / -1` on signed types. Because the result is `-MIN` which is too large of a positive value. Also note that unary "`-`" can likewise overflow. 19 | 20 | Like unsafe, both expression and block forms would be allowed: 21 | 22 | ```adamant 23 | let x = unchecked(y + z); 24 | 25 | unchecked 26 | { 27 | let a = b * c; 28 | } 29 | ``` 30 | 31 | An unchecked expression is treated the same as an unsafe block. That is, you must now mark the function as "`safe`" or "`unsafe`". This supports the idea that unchecked math is used when you believe the wrapping won't occur but don't want checking to be done. When wrapping is expected, there are the "`wrapping_`*x*" functions. This is also why an unchecked expression makes more sense than individual unchecked operators. The wrapping methods should be used when wrapping is intended. Unchecked is used for performance. In that case, you don't want to accidentally miss an operator, you want a whole chunk of code to be unchecked. This is then easier to read. 32 | 33 | Additionally, it might be possible to allow operator and function overloading on unchecked by using "`unchecked`" as a modifier on the function. This would allow non-primitive types to participate in unchecked contexts. For clarity, a "`checked`" modifier could be used as well. Both keywords are reserved. 34 | 35 | One open question is what the spec should say about wrapping behavior in unchecked contexts. It could specify two's complement wrapping. However, there might be a performance benefit to leaving it more open. We wouldn't want the C style undefined behavior, but allowing different bit representations and segfaults might help for some hardware. 36 | -------------------------------------------------------------------------------- /src/planned-features.md: -------------------------------------------------------------------------------- 1 | # Planned Features 2 | 3 | To keep the first version of Adamant simple, a number of features have been omitted. However, to ensure that the language design will accommodate those features, they have been considered from the beginning. Those features are listed in this section. 4 | -------------------------------------------------------------------------------- /src/planned-lop.md: -------------------------------------------------------------------------------- 1 | ## Language-Oriented Programming 2 | 3 | [Language-oriented programming](https://en.wikipedia.org/wiki/Language-oriented_programming) (LOP) is a programming paradigm that focuses on constructing software out of not only software modules but out of languages. Thus developers create multiple domain specific languages (DSLs) for solving problems and then use those languages to solve them. Adamant's LOP is largely inspired by the Racket language and in particular the [brag library](https://docs.racket-lang.org/brag/) as used in the online book [Beau­tiful Racket](https://beautifulracket.com/) by Matthew Butt­erick. 4 | 5 | ### Code Expressions 6 | 7 | Code expressions are chunks of DSL code embedded in expressions for computing some or all of the expression's value. Code expressions are similar to code spans in Markdown. They start and end with a backtick. If the DSL code contains backticks, this can be handled by surrounding the code expression in multiple backticks. Any number of backticks is acceptable, but the start and end of the code expression must match. A trimming scheme allows code expressions that begin or end with backticks. Any code expression which begins (or ends) with whitespace followed by a backtick character will have the first space character trimmed from the end of the DSL code. (Note this is similar too but slightly different than the rule used in Markdown.) Type inference is used to determine the correct language to use for a code expression. Each language registers the type(s) it can create expressions for. The compiler selects the language that can fill the necessary type in the containing expression and is in scope. Languages are imported through namespaces the same as a class might be. There may be additional options for language selection. For example, languages may be able to provide a regular expression that must match the beginning or end of a code expression in order for that language to be considered. One possible use case for code expressions is embedding SQL statements in code safely. Another use case is regular expressions. 8 | 9 | ```adamant 10 | var query = `select * from User where PasswordHash=HashedPassword`; 11 | var results = db.run(query); 12 | ``` 13 | 14 | ```adamant 15 | var matches = str.match(`(a|b)+\|[^ ]*`); 16 | ``` 17 | 18 | ### Code Blocks 19 | 20 | Code blocks allow DSL code to be placed at the statement or declaration level. A code block is begun by a line that begins with whitespace followed by three or more consecutive backticks. The backticks are followed by the language parameters which determine which language is used in the code block and any options needed for compiling the language. The info string may not contain any backtick characters (this ensures a code block is distinct from a code expression). The code block is ended by a line beginning with the same whitespace as the block start followed by the same number of backticks as the start and a newline. The block end may optionally contain whitespace before the line end. A code block may also be terminated by the end of the file if it was not nested inside any other declarations. This allows a whole code file to easily be in another language by having the first line be "` ``` language `". One use case for code blocks would be a parser generator based on a BNF like notation. 21 | 22 | ````adamant 23 | ``` parser.BNF 24 | exp = a "+" b 25 | | a "-" b 26 | | a "*" b 27 | | a "/" b 28 | ; 29 | ``` 30 | ```` 31 | 32 | Code blocks may also allow for setting a default language so the language can be omitted with repeated blocks in a file. One way to do this would be with a `##lang` preprocessor directive. Alternatively, `##lang` could be an alternate way to specify a whole file as being in another language. That way code blocks could be required to be closed. 33 | 34 | ### Code Files 35 | 36 | It should be possible to register file extensions with the Adamant compilation process in a way that allows files with those extensions to be picked up and compiled as the matching language. 37 | 38 | ### Code Compilation 39 | 40 | The details of code expression and block compilation have not been worked out. It is expected that something similar to what Racket uses can be made to work. However, this may require a standard model of the Adamant AST. Code compilations should be built on top of macro and compile time function execution functionality. As with Racket, there should probably be ways to easily create languages with macro like facilities and ways to take full control of lexing, parsing, and translation. A default lexer should be provided that is capable of lexing a superset of Adamant. For example, in additional to supporting all Adamant tokens it may contain rules for tokenizing other symbols into operator tokens. 41 | 42 | ### Languages as Macros 43 | 44 | Code expressions and blocks can be the ultimate form of macro. Any place a pair of curly braces is expected, a code block can be used instead. Code expressions could be allowed in place of declarations after modifiers. The one challenge is how to allow the specification of which language or macro. One option would be to allow a name before the expression indicating the language. Another would be to allow attributes to control the language selection. Of course, in some cases, it would make sense to just allow the developer to rewrite code tagged with a certain attribute. 45 | 46 | ````adamant 47 | public class Example 48 | { 49 | public fn example() 50 | ```asm 51 | # Whole function body is inline assembly 52 | ld r3 4; 53 | ret 54 | ``` 55 | 56 | // The word macro here is not a keyword, it is the name of the language used to create this declaration 57 | public macro macro_name `#(x◊exp, y◊exp)` 58 | ``` 59 | // syntax rules here 60 | ``` 61 | 62 | #metadata // specifies that the code expression is metadata 63 | protected `key := this is a metadata value`; 64 | 65 | // Alternatively, the regular expression limitations could match the first part of the code expression 66 | protected `metadata key2 := another metadata value`; 67 | 68 | #reactive // Attribute triggers code rewriting to turn this into a property that raises change events 69 | public var x: int; 70 | } 71 | ```` 72 | -------------------------------------------------------------------------------- /src/planned-operators.md: -------------------------------------------------------------------------------- 1 | ## Operator Features 2 | 3 | ### Operator Overloading for `?.` and `??` Operators 4 | 5 | These operators should support overloading. It isn't clear exactly how this should work. Presumably, the behavior of the two operators should somehow be linked. It is an outstanding question whether there are every situations where the two operators need to be overloaded fully independently. As a general rule, having them independent would provide greater flexibility to developers who might find one or the other operators useful is a very different context. 6 | 7 | The first step of both is to evaluate their left hand side operand. The result of which is either a valid value to continue executing with, or a "none" value. In the case of the `?.` operator the "none" value should be returned without evaluating the rest of the expression. In the case of the `??` operator the "none" value causes the right hand operand to be evaluated and its value used. It was thought this could be done by an overload of the "`?`" pseudo operator which returned an optional type. However, this does not allow the none value to be anything besides `none`. 8 | 9 | The coalesce operator would require its own overload. This would either take as a parameter the left hand value or perhaps the "none" value returned by the first step of evaluation. This could be very useful. For example imagine coalescing two `Result[T, E]` values where both are errors. The first step would unwrap the error value from the left hand side. The second step would then combine this with the error value from the right hand side to produce a composite error. If this operator overload is fully independent from the `?.` then it may make sense to have apply the same approach as is used when overload `and`, and `or`. 10 | 11 | ## Logical Operator Overloading 12 | 13 | Support for overloading the logical operators `and`, `or`, and `not`. Note that the `not` operator is included here for consistency rather than because of any issues supporting overloads of it. 14 | 15 | The logical operators `and` and `or` are special because they perform short circuit evaluation. That is, their second argument may not be evaluated. Thus they can not be simple method calls. One approach would be to make the second argument a closure of the rest of the expression. However, the performance of that would need to be evaluated before that could be adopted. Another approach, similar to the one used by C# is to evaluate these operators in two steps. This approach is described below. 16 | 17 | Each operator has two overloads. The first determines when the second argument isn't evaluated, the second provides the value when it is. The first must return an optional type. If the result is `none` then the second argument will be evaluated and the binary form called. The two overloads for an operator must be overloaded together. When overloaded for some type, `x and y` is effectively evaluated as `x.and() ?? x.and(y)` and `x or y` is effectively evaluated as `x.or() ?? x.or(y)`. Note the first argument will only be evaluated once. Not that for the `and` operator, the unary version returns either its "false" value or `none` when it is "true". Another challenge of this approach is that the unary version of the operator can't easily be overloaded based on the type of the right operand. In the example code below, this is handled using a type parameter. However, this is awkward without a type equality operator for the where clause. Even with one, it makes overloading have to account for constraint clauses. 18 | 19 | An issue with this approach is that one may want to transform the value before passing it to the second overload. Perhaps the result needs to be some other type that allows a value to be passed in the "none" case? Another issue is that the placeholders for the operands will fuse with the token for the operator because `_and_` and `_or_` are valid identifiers. This may require spaces or special handling in the compiler. 20 | 21 | ```adamant 22 | // In `fuzzy_bool` 23 | public operator[Other] _and_(self) -> fuzzy_bool? 24 | where Other: fuzzy_bool 25 | { 26 | return if Value == 0 => self else => none; 27 | } 28 | 29 | public operator and(self, other: fuzzy_bool) -> fuzzy_bool 30 | { 31 | return new fuzzy_bool(value.min(other.value)); 32 | } 33 | 34 | public operator[Other] _or_(self) -> fuzzy_bool? 35 | where Other: fuzzy_bool 36 | { 37 | return if value == 1 => self else => none; 38 | } 39 | 40 | public operator _or_(self, other: fuzzy_bool) -> fuzzy_bool 41 | { 42 | return new fuzzy_bool(value.max(other.value)); 43 | } 44 | ``` 45 | 46 | ## Member Access Operator Overloading 47 | 48 | It may be necessary or useful to allow overloading of the member access operators "`.`". However, it is unclear how to handle access to methods on a type that overloads the member access operator. Note that the ability to overload the dereference operator "`^`" partially mitigates the need for overloading the member access operator because it allows for types that act pointer like. However, there may be situations where types need to behave like references and support member access. 49 | -------------------------------------------------------------------------------- /src/planned-qualifier.md: -------------------------------------------------------------------------------- 1 | ## Global and Package Qualifiers 2 | 3 | ## Package Qualifier 4 | 5 | The package qualifier `::.` can be used to qualify which package to resolve a name in. Sometimes a name may be declared in multiple packages. In that case, the name can be disambiguated by prefixing it with the package name followed by `::.`. Names following this operator are resolved from the root of that package. 6 | 7 | ### Package Alias 8 | 9 | In the project file you can specify a package alias. This name replaces the standard package name for the purposes of the package qualifier. 10 | 11 | **TODO:** Maybe there should be a special way of referring to the current package. Rust uses `crate::` as the qualifier for the current crate. The word "`package`" has been reserved so it could be used for this. 12 | 13 | ## Global Qualifier 14 | 15 | To start name resolution from the global namespace, a name can be prefixed with the global qualifier "`::.`". This is the same operator used for qualifying names with a package, but no package name is specified. 16 | 17 | ## Package Qualified Using Directives 18 | 19 | A using directive can qualify the namespace with a package qualifier. This restricts the using statement to bring in names inside that package only. Package qualifiers are either the official package name or if an alias for the package is specified, the alias for the package. Note the dot after the double colon. Use of the global qualifier in using directives is not supported because names are always fully qualified starting from the global namespace. 20 | 21 | ```adamant 22 | using my.package::.foo.bar; 23 | ``` 24 | -------------------------------------------------------------------------------- /src/planned-types.md: -------------------------------------------------------------------------------- 1 | ## Additional Types 2 | 3 | ### 128-bit Integer Types 4 | 5 | Additional integer types for signed and unsigned 128-bit integers. 6 | 7 | ```grammar 8 | integer_type 9 | : ... 10 | | "int128" 11 | | "uint128" 12 | ; 13 | ``` 14 | 15 | ### 128-bit Floating Point Type 16 | 17 | Additional floating point type for IEEE 754 binary128 floating point math. 18 | 19 | ```grammar 20 | floating_point_type 21 | : ... 22 | | "float128" 23 | ; 24 | ``` 25 | 26 | ### Fixed Point Types 27 | 28 | Fixed point types are an additional class of simple numeric types. 29 | 30 | ```grammar 31 | numeric_type 32 | : ... 33 | | fixed_point_type 34 | ; 35 | 36 | fixed_point_type 37 | : "fixed" [0-9]+ "." [0-9]+ 38 | | "ufixed" [0-9]+ "." [0-9]+ 39 | ; 40 | ``` 41 | 42 | Let *f* be the number of fractional bits, *m* be the number of magnitude or integer bits and *b* be the total number of bits. For *b*=*m*+*f* where *b*=8 or *b*=16 or *b*=32 or *b*=64 and *m* > 0, *f* > 0, the signed fixed point type is `fixed`*m*`.`*f* and the unsigned type is `ufixed`*m*.*f*. 43 | 44 | Note: 128 bit fixed types are not supported because of issues with handling multiply. 45 | 46 | ### Decimal Types 47 | 48 | Decimal types are an additional class of simple numeric types. 49 | 50 | ```grammar 51 | numeric_type 52 | : ... 53 | | decimal_type 54 | ; 55 | 56 | decimal_type 57 | : "decimal32" 58 | | "decimal" 59 | | "decimal128" 60 | ; 61 | ``` 62 | 63 | They provide IEEE 754 32-bit, 64-bit and 128-bit decimal numbers respectively (binary representation preferred). 64 | 65 | ### DEC64 Type 66 | 67 | The "`system.math`" package will define the `dec64` type following the standard defined on [dec64.org](http://dec64.org). 68 | 69 | ### Rational Type 70 | 71 | The "`system.math`" package will define the `rational` type. It is an arbitrary precision rational number. This may be represented either as [integer/uinteger](https://en.wikipedia.org/wiki/Rational_data_type#Representation) or the one described in [A New Representation of the Rational Numbers 72 | for Fast Easy Arithmetic](http://www.cs.toronto.edu/~hehner/ratno.pdf) by Eric C. R. Hehner and R. N. S. Horspool. 73 | 74 | ### Real Types 75 | 76 | Arbitrary precision real number types could be very useful. However, their behavior for representing an irrational numbers would need to be clarified. For an example of how this type might work, see the Java `System.Numerics.BigDecimal` type. It may also be useful to have types with a fixed number of fractional digits. These could be simple types or defined in the "`system.math`" namespace. 77 | 78 | ```grammar 79 | numeric_type 80 | : ... 81 | | real_type 82 | ; 83 | 84 | real_type 85 | : "real" 86 | | "real." [0-9]+ 87 | ; 88 | ``` 89 | 90 | If these types are defined in the standard library, then "`real\.\d+`" could instead be "`real[f]`". 91 | 92 | ### Money Type 93 | 94 | Although C# recommends using the "`decimal`" type for money because it exhibits decimal rounding, its underflow and overflow behavior is not ideal for money. Perhaps there should be a decimal type or types more suitable for money. 95 | -------------------------------------------------------------------------------- /src/pointer-types.md: -------------------------------------------------------------------------------- 1 | ## Pointer Types 2 | 3 | Pointer types are used by unsafe code to directly address memory. Only pointers to value types are supported. This ensures that pointers can be implemented as simple addresses compatible with pointers in languages like C. Reference types may require fat pointers to enable virtual method dispatch. Thus pointers to reference types are not valid. However, a pointer to a class value type is valid. 4 | 5 | Pointer types are non-nullable. To represent a nullable pointer use an optional pointer type (i.e. `@int?`). 6 | 7 | ```grammar 8 | pointer_type 9 | : "@" value_type 10 | | "@" "Any" 11 | ; 12 | ``` 13 | 14 | ### `@Any` Type 15 | 16 | The type "`@Any`", is a pointer type that places no limits on what may be at the memory pointed to. It is the equivalent of a void pointer in C/C++ or Rust. 17 | 18 | References can be compared for reference equality by converting them to "`@Any`". 19 | 20 | ```adamant 21 | let f = new Foo(); 22 | let g = f; 23 | 24 | if f as @Any == g as @Any // reference equality 25 | { 26 | // ... 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /src/pointers.md: -------------------------------------------------------------------------------- 1 | ## Pointers 2 | 3 | Sometimes, low level control of memory and pointers is needed. For this purpose, pointers can be used. Pointers are very similar to pointers in languages like C, C++ and Rust. Many operations on pointers are inherently dangerous and require an unsafe context. 4 | 5 | Only pointers to value types can be declared. Because reference types allow for a subtype relationship, references are "fat." Meaning they contain both a pointer to the data and a pointer to a vtable. Pointers don't support this, they are "thin." Thus it must be possible to know the exact type and size at compile time. This is true of value types. 6 | 7 | ### Address Of 8 | 9 | A pointer to a value can be obtained using the address of operator `@`. For value types and references to variables, the address of operator takes the address of the variable so the pointer points to the variable which is typically on the stack. Taking the address of something is a safe operation. 10 | 11 | ```adamant 12 | let x = 6; 13 | let ptr_x = @x; // points to `x` on the stack 14 | let y = ref x; 15 | let ptr_y = @y; // points to `y` on the stack 16 | ``` 17 | 18 | References can be safely cast to a void pointer (see below). This is used to good effect to compare two references for reference equality. 19 | 20 | ```adamant 21 | let f = new Foo(); 22 | let g = f; 23 | 24 | if f as @void == g as @void // reference equality 25 | { 26 | // ... 27 | } 28 | ``` 29 | 30 | The address operator is read as either "address of x" or "pointer to x" 31 | 32 | ### Pointer Types 33 | 34 | Pointer types are simply prefixed with the `@` sign. The type is then read as either "address of T" or "pointer to T". Just as with references, pointers refer to immutable values by default and must use `mut` to allow mutation of the value. Pointer types can be freely combined with references to produce complex types. 35 | 36 | ```adamant 37 | public struct Foo {} 38 | public struct Bar {} 39 | 40 | let f: Foo = new Foo(); 41 | let ptr_f: @Foo = @f; 42 | let b: mut Bar = new Bar(); 43 | let ptr_b: @mut Bar = @mut b; 44 | let x: int = 6; 45 | let ptr_x: @int = @x; 46 | var y: int = 7; 47 | let ptr_y: @mut int = @mut y; // similar to taking `ref var` 48 | let r: ref var int = ref var y; 49 | let ptr_r: @ ref var int = @r; 50 | ``` 51 | 52 | ### Converting References to Pointers 53 | 54 | Given an existing reference to a value type, it can also be cast to a pointer. 55 | 56 | ```adamant 57 | let x: int = 5; 58 | let ref_x: ref int = ref x; 59 | let ptr_x: @int = ref_x as @int; 60 | ``` 61 | 62 | ### Dereferencing Pointers 63 | 64 | Accessing the value a pointer refers to is called dereferencing. This is done using the dereferencing operator `^` which is a prefix operator. To dereference and call a method, the special dereference access operator `^.` can be used. Dereferencing is an unsafe operation. 65 | 66 | ```adamant 67 | unsafe 68 | { 69 | let x: int = 5; 70 | let ptr_x: @int = @x; 71 | let y: int = ^ptr_x; 72 | let f: Foo = new Foo(); 73 | let ptr_f: @Foo = @f; 74 | ptr_f^.method(); 75 | } 76 | ``` 77 | 78 | The dereference operator can also be used to dereference variable references if needed. This is a safe operation since variable references are always valid. 79 | 80 | ```adamant 81 | var x: int = 6; 82 | let y: ref var int = ref var x; 83 | ^y = 7; // assigns 7 into x 84 | y = 8; // because of types involved, dereference is implied. The same thing doesn't happen with pointers 85 | ``` 86 | 87 | ### Nullable Pointers 88 | 89 | By default, all pointer types are non-nullable. That is, they may refer to invalid memory locations, but they can never be null. To create a nullable pointer type, use optional types. So a nullable pointer to `T` would be `@T?`. Note that the `@` has higher precedence than `?` so this is parsed as `(@T)?` rather than `@(T?)`. 90 | 91 | ### Void Pointers 92 | 93 | There are special pointer types for pointers without reference to the type of data they hold. These are void pointers, `@void` and `@mut void`. They can't be dereferenced, but must be cast to some other pointer type to be used. 94 | 95 | ### Ownership and Pointers 96 | 97 | A pointer doesn't "own" or even borrow the value it points to. The pointed to value will be disposed of whenever it would normally be disposed. Note, that the pointer type doesn't track ownership and it is up to the developer to ensure that the memory is deleted. 98 | -------------------------------------------------------------------------------- /src/ref-types.md: -------------------------------------------------------------------------------- 1 | ## Variable Reference Types 2 | 3 | Variable reference types are the types of references to variables. Note that while they are reference types in the sense that they reference a value, they are not classed as reference types in Adamant. The reason for this is that they differ from the reference types in important ways. A variable reference never "owns" its value as the value is always owed by the variable being referenced. Furthermore, a variable reference does not need to be a fat pointer because no vtable is necessary. Lastly, it is possible to chain variable references to produce references to references. 4 | 5 | ```grammar 6 | variable_reference_type 7 | : "ref" type 8 | | "ref" "var" type 9 | ; 10 | ``` 11 | -------------------------------------------------------------------------------- /src/reference-types.md: -------------------------------------------------------------------------------- 1 | ## Reference Types 2 | 3 | Reference types are either object reference types or function reference types. Additionally, the special any reference type may refer to an object or function of any type. 4 | 5 | ```grammar 6 | reference_type 7 | : object_type 8 | | function_type 9 | | any_type 10 | ; 11 | ``` 12 | 13 | ### Object Reference types 14 | 15 | An object reference type (object type for short) is a reference to an object. This object could be of a concrete class type or a trait type or one a several built in object types. The two special object types are `Type` and `Metatype` 16 | 17 | ```grammar 18 | object_type 19 | : identifier 20 | | "Type" 21 | | "Metatype" 22 | ; 23 | ``` 24 | 25 | #### `Type` and `MetaType` 26 | 27 | The type "`Type`" is the type of all objects that represent types during reflection and in generics. The default type of a generic parameter is "`Type`" (thus "`foo[T]`" and "`foo[T: Type]` are equivalent). Any generic parameter with this type is referred to as a "type parameter". Note that "`Type`" is a reference type. This was necessary to allow metatypes to be subtypes of it. 28 | 29 | Objects representing the type of classes and structs have a type that is a subtype of "`Type`". When calling associated functions as "`Example.function()`", the expression "`Example`" has a type that is a subtype of "`Type`". Metatypes describe the associated functions and constructors on the type object. The meta type of a type can be accessed using the "`Type`" property, for example "`Example.Type`". 30 | 31 | These types can be very confusing. For some, an example clarifies the relationship. 32 | 33 | ```adamant 34 | let example: Example = new Example(); 35 | let metaType: Example.Type = Example; 36 | let type: Type = metaType; // Example.Type <: Type 37 | ``` 38 | 39 | ### Function Types 40 | 41 | Function types are the parameter and return types of a function. Note that function types are contravariant in their parameter types and covariant in their return types. 42 | 43 | ```grammar 44 | function_type 45 | : "(" type_list ")" "->" type 46 | ; 47 | 48 | type_list 49 | : type{",", 0, *} 50 | ``` 51 | 52 | ### `Any` Type 53 | 54 | ```grammar 55 | any_type 56 | : "Any" 57 | ; 58 | ``` 59 | 60 | All reference types (object, and function) are a subtype of the "`Any`" type. Note that variable references are not included in this. 61 | -------------------------------------------------------------------------------- /src/reserved-words.md: -------------------------------------------------------------------------------- 1 | ## Reserved Words 2 | 3 | Reserved words are identifier like character sequences that are reserved for possible future use as keywords. They can't be used as identifiers except by escaping them. Some reserved words are specifically reserved as types because they follow the pattern of existing types. 4 | 5 | Use of reserved words is a fatal error. However, the compiler should treat all reserved words as identifiers for the purposes of further analysis. There is one exception to this. The reserved word `continue` is treated as a synonym for `next`, but will generate a nonfatal error. 6 | 7 | The grammar below notes some possible uses of the reserved words or justifications for reserving them. They may be used for these purposes or others, or may eventually be removed from the reserved word list and be made available for use as identifiers. 8 | 9 | ```grammar 10 | reserved_word 11 | : reserved_type 12 | | "alias" // Planned Type Alias Feature | 13 | | "case" // Useful for switch like constructs 14 | | "cast" // Casting 15 | | "checked" // Checked Operations 16 | | "const_cast" // Casting 17 | | "continue" // Useful for control flow 18 | | "default" // Useful for switch like constructs and default values 19 | | "defer" // Swift style "`defer`" statements 20 | | "do" // "`do while`" loop or Swift style "`do`" block 21 | | "dynamic_cast" // Casting 22 | | "extend" // Extensions 23 | | "extension" // Extensions 24 | | "fallthrough" // Useful for switch like constructs 25 | | "for" // Common Keyword 26 | | "from" // Common Keyword 27 | | "guard" // Swift style guard statements 28 | | "internal" // Common Keyword 29 | | "null" // Null value for pointers 30 | | "otherwise" // Loop Else 31 | | "package" // Qualify names with the current package (i.e. `package::.name`) 32 | | "partial" // Partial Classes 33 | | "private" // Common Keyword 34 | | "reinterpret_cast" // Casting 35 | | "repeat" // Swift style "`repeat {} while condition;`" loop 36 | | "replace" // Partial Classes 37 | | "select" // C# style query 38 | | "sizeof" | "size_of" // Size of Operator 39 | | "switch" // Useful for switch like constructs 40 | | "symmetric" // Symmetric operators 41 | | "transmute" // Reinterpret Cast 42 | | "then" // Python style loop else 43 | | "type" // Type aliases and declarations 44 | | "unchecked" // Unchecked Operations 45 | | "unless" // Ruby style `if not` statement or `unless break` for Python style loop else 46 | | "when" // C# style exception filters 47 | | "xor" // Logical exclusive or operator 48 | | "yield" // Generators 49 | ; 50 | ``` 51 | 52 | Note: The "`then`" keyword would allow for "`while condition { } then { } else { }`". The "else" block would be executed if the condition is false the first time it is evaluated. The "then" block would be executed if control flow left the end of the loop body (i.e. the loop body executed at least once and wasn't exited with a "`break`" expression). It is unclear of the semantics should be that a "`then`" without an "`else`" would execute even if the loop never did. (see also `unless break`) 53 | 54 | ### Reserved Types 55 | 56 | Some identifiers are reserved for use as type names. They generally follow the pattern of existing types. However, the `fixed`, `ufixed`, and `real` types include a period which no identifier does. Note that even patterns whether the digit portion starts with zero are reserved. 57 | 58 | ```grammar 59 | reserved_type 60 | : "int" [0-9]+ - keyword // unused integer types 61 | | "uint" [0-9]+ - keyword // unused unsigned integer types 62 | | "float" [0-9]+ - keyword // unused floating point types 63 | | "fixed" 64 | | "fixed" [0-9]+ "." [0-9]+ 65 | | "ufixed" 66 | | "ufixed" [0-9]+ "." [0-9]+ 67 | | "decimal" 68 | | "decimal" [0-9]+ 69 | | "real" 70 | | "real." [0-9]+ 71 | ; 72 | ``` 73 | -------------------------------------------------------------------------------- /src/simple-types.md: -------------------------------------------------------------------------------- 1 | ## Simple Types 2 | 3 | The simple types are value types predefined by the Adamant language. They are identified by keywords. To declare an identifier with the same name as one of these, use an [escaped name](identifiers.md#escaped-identifiers). These keywords are fully distinct from the escaped name with the same letters. So the "`bool`" type and a "`\bool`" identifer are fully distinct and can never refer to each other. 4 | 5 | ```grammar 6 | simple_type 7 | : numeric_type 8 | | "bool" 9 | ; 10 | 11 | numeric_type 12 | : integer_type 13 | | floating_point_type 14 | ; 15 | ``` 16 | 17 | ### Integer Types 18 | 19 | The integer types provide signed and unsigned integers of various sizes. The "`int`" and "`uint`" types are each 32 bits. 20 | 21 | ```grammar 22 | integer_type 23 | : "int8" 24 | | "byte" 25 | | "int16" 26 | | "uint16" 27 | | "int" 28 | | "uint" 29 | | "int64" 30 | | "uint64" 31 | | "size" 32 | | "offset" 33 | ; 34 | ``` 35 | 36 | #### "`size`" and "`offset`" Types 37 | 38 | * `size` 39 | * `offset` 40 | 41 | The bit sizes of these are system dependent. "`size`" is an unsigned number large enough to hold the maximum size of an array on the system. The size type is used to index into collections. "`offset`" is a signed number, with the same number of bits as "`size`", used to represent differences between array indexes and pointers. 42 | 43 | ### Floating Point Types 44 | 45 | The types "`float32`" and "`float`" are floating-point types represented as IEEE 754 binary32 and binary64 values. 46 | 47 | ```grammar 48 | floating_point_type 49 | : "float32" 50 | | "float" 51 | ; 52 | ``` 53 | 54 | Floating point operations never cause abandonment. In exceptional situations, they produce zero, positive or negative infinity or not a number (NaN). Floating point operations may be performed with higher precision than the result type of the operation. Some hardware architectures support higher precision operations and implicitly perform all floating-point operations using this higher precision type. Only with a performance impact can they be made to perform floating point operations with lower precision. This rarely has an impact on the results of a numeric calculation. However in expressions like "`x * y / z`" where the multiplication produces results outside the floating point range and the division brings the temporary result back in range, the higher precision may cause a finite result to be produced rather than infinity. 55 | 56 | ### "`bool`" Type 57 | 58 | The "`bool`" type represents boolean values. The possible values of a boolean are "`true`" and "`false`". The boolean logical operators "`and`" and "`or`" operate on boolean types. The condition of an "`if`" expression and "`while`" loop must evaluate to booleans. 59 | 60 | No standard conversions exist between "`bool`" and other types. The bool type is distinct and separate from the integer types. A bool value cannot be used in place of an integer value, and vice versa. 61 | -------------------------------------------------------------------------------- /src/statements.md: -------------------------------------------------------------------------------- 1 | # Statements 2 | 3 | ## Variable Declarations 4 | 5 | ## Expressions Statements 6 | 7 | ```grammar 8 | expression_statement 9 | : choice_expression 10 | | loop_expression 11 | | expression ";" 12 | | ";" // empty statement 13 | ; 14 | ``` 15 | -------------------------------------------------------------------------------- /src/std-lib-global-namespace.md: -------------------------------------------------------------------------------- 1 | ## Global Namespace 2 | 3 | The standard library aliases a number of types into the global namespace so that they are not accidentally superseded by declarations with the same name in other namespaces. These include: 4 | 5 | * `system.text.String` 6 | * `system.collections.Array` 7 | * `system.collections.List` 8 | -------------------------------------------------------------------------------- /src/struct-constructors.md: -------------------------------------------------------------------------------- 1 | ## Struct Constructors 2 | 3 | Structs are initialized using [struct initializers](struct-initializers.md) instead of constructors. However, reference structs masquerade as references and may allocate space on the heap as part of their initialization. To allow reference structs to fully operate as references, they are allowed to have constructors. 4 | 5 | Struct constructors are declared with the `new` keyword and called like constructors using the `new` keyword. However, semantically, they behave exactly like initializers. By convention, these should only be used for initializers that allocate new data structures on the heap. The lifetime of the struct returned from a constructor is "`owned`". 6 | 7 | A struct with a constructor will not have a default initalizer created for it. 8 | -------------------------------------------------------------------------------- /src/struct-initializers.md: -------------------------------------------------------------------------------- 1 | ## Struct Initializers 2 | 3 | Structs are allocated on the stack instead of the heap. To reflect this, structs are not constructed using the `new` keyword. Instead, they are initialized using initializer functions. Initializer functions are similar to constructors in that they enforce definite assignment. However, they are called more like associated functions. 4 | 5 | An initializer may be named or unnamed. They are declared using the `init` keyword. 6 | 7 | ```adamant 8 | public move struct Example 9 | { 10 | public init() 11 | { 12 | } 13 | 14 | public init named() 15 | { 16 | } 17 | } 18 | 19 | let x = Example(); 20 | let y = Example.named(); 21 | ``` 22 | 23 | Like constructors, initializers have an implicit self parameter. However, this parameter is passed `ref var self` so no copying or initialization is necessary to invoke the initializer. 24 | 25 | **TODO:** This is an example where the distinction between `var` and `mut` may make sense on struct types. It might make sense to have the parameter type be `ref mut self` so you can mutate self, but not assign it a new value. However, C# allows assignment to `this` in struct constructors. 26 | 27 | ### Default Initializers 28 | 29 | A struct without any initializers will have a default constructor generated for it. 30 | 31 | ### Field Initialization Shorthand 32 | 33 | Just like constructors, there is a shorthand for initializing fields. 34 | 35 | ```adamant 36 | public init(.field) 37 | { 38 | } 39 | ``` 40 | 41 | ### Definite Assignment 42 | 43 | Like constructors, definite assignment of fields is enforced. Since a struct never has a base class. The transition point to fully initialized is always implicit. 44 | 45 | ### Copy Initializers 46 | 47 | **TODO:** document how copy works on struct types. Or should struct copy be required to be bitwise? 48 | -------------------------------------------------------------------------------- /src/struct-types.md: -------------------------------------------------------------------------------- 1 | ## Struct Types 2 | -------------------------------------------------------------------------------- /src/structs.md: -------------------------------------------------------------------------------- 1 | # Structs 2 | 3 | Structs are similar to classes. They contain field and function members. Unlike classes, structs are value types and by default are allocated on the stack instead of the heap. A variable with a struct type directly contains the data of the struct. A variable of a class type contains a reference to the object which contains the data. 4 | 5 | There are three main kinds of structs. This kind determines the semantics of assigning values of that type. Additionally, there are enum structs documented in their own section. 6 | 7 | | Struct Type | Declaration Syntax | Semantics | 8 | | ------------------------ | ------------------ | ---------------------------------------------------------------------- | 9 | | Move Structs | `move struct` | Struct values "move" when assigned so that they are no longer be used. | 10 | | Copy Structs | `copy struct` | Struct values are implicitly copied when assigned. | 11 | | Pseudo-reference Structs | `ref struct` | Appear to behave like references. | 12 | 13 | The kind of struct determines the default assignment behavior, but it is often possible to explicitly cause the other behavior. 14 | -------------------------------------------------------------------------------- /src/syntactic-analysis.md: -------------------------------------------------------------------------------- 1 | ## Syntactic Analysis 2 | 3 | The syntactic grammar of Adamant is defined throughout this reference. The terminals of the syntactic grammar are the tokens defined by the lexical grammar. The syntactic grammar specifies how tokens are combined to make programs. 4 | 5 | Every source file in an Adamant program must match the `compilation_unit` production of the syntactic grammar ([Compilation Units](namespaces.md#compilation-units)). 6 | -------------------------------------------------------------------------------- /src/syntax-reference.md: -------------------------------------------------------------------------------- 1 | # Appendix: Syntax Reference 2 | 3 | A brief statement of the syntax for easy reference. 4 | 5 | ## Identifiers 6 | 7 | | Syntax | Identifier Kind | 8 | | ----------------- | --------------------------------------------------------------------- | 9 | | `Name` | Identifer | 10 | | `\class` | Escaped Identifier (keywords, contextual keywords and reserved words) | 11 | | `\"Hello World!"` | String Identifier | 12 | 13 | ## Comments 14 | 15 | | Syntax | Meaning | 16 | | ------------------ | --------------------- | 17 | | `// something` | Line Comment | 18 | | `/* something */` | Multiline Comment | 19 | | `/// Markdown text | Documentation Comment | 20 | 21 | ## Literals 22 | 23 | | Syntax | Meaning | 24 | | ---------------------------------- | --------------------------- | 25 | | `true`, `false` | Boolean Literals | 26 | | `-10`, `100 000` | Integer literals | 27 | | `0xAF 6D` | Hexadecimal Literals | 28 | | `0b1010 0101` | Binary Literals | 29 | | `-1.2`, `0.324`, `1561e-46` | Real Literals | 30 | | `'c'`, `'2018-09-28'` | User Literals | 31 | | `"Hello!"` | String Literal | 32 | | `" Hello \(name)"` | Interpolated String Literal | 33 | | `#"He said "\Whoa\""#` | Raw String Literals | 34 | | `"""`
`Multiple lines`
`"""` | Multiline String Literal | 35 | 36 | ## Operators 37 | 38 | | Operator | Meaning | 39 | | ------------------ | ------------------------------ | 40 | | `x + y` † | Addition | 41 | | `x - y` † | Subtraction | 42 | | `x * y` † | Multiplication | 43 | | `x / y` † | Division | 44 | | `x.y` | Member Access | 45 | | `^x` | Dereference | 46 | | `x^.y` | Deference and Access Member | 47 | | `@x` | Address Of (i.e. pointer to) | 48 | | `x??y` | Coalesce Operator | 49 | | `x?.y` | Conditional Access Operator | 50 | | `x and y` | Logical And (Short Circuiting) | 51 | | `x or y` | Logical Or (Short Circuiting) | 52 | | `..` | Infinite Range | 53 | | `x..y` | Inclusive Range | 54 | | `x..` | Inclusive Range to Infinity | 55 | | `..y` | Inclusive Range from Infinity | 56 | | `x.. y` | Greater Than | 67 | | `x ≥ y`, `x >= y` | Greater Than or Equal | 68 | | `=> x` | Result of Block or Expression | 69 | | `x and y` | Logical And (short circuiting) | 70 | | `x or y` | Logical Or (short circuiting) | 71 | | `not x` | Logical Not | 72 | | `T1 \| T2` | Sum Type | 73 | | `T1 & T2` | Intersection Type | 74 | | `T1 <: T2` | Subtype | 75 | 76 | † Can be combined with assignment (i.e. `+=`) \ 77 | ‡ All ranges can be infinite on either or both sides as shown for inclusive ranges 78 | 79 | ## Types 80 | 81 | | Syntax | Meaning | 82 | | ------------------------------ | -------------------------------------------------- | 83 | | `Type` | Type Name | 84 | | `Type[T, R]` | Generic parameters and compile time code execution | 85 | | `@T` | Non-null Pointer to `T` (i.e. Address of `T`) | 86 | | `@T?` | Nullable Pointer to `T` | 87 | | `@void`, `@mut void` | Void Pointers? | 88 | | `(Type1, Type2) -> ReturnType` | Function Type | 89 | 90 | ## Statements 91 | 92 | | Syntax | Meaning | 93 | | --------------------------- | -------------------- | 94 | | *expression*`;` | Expression Statement | 95 | | `if c { } else { }` | If Statement | 96 | | `match v { p1 { }, p2 {} }` | Match Statement | 97 | | `loop { }` | Loop | 98 | | `while c { }` | While Loop | 99 | | `foreach v in s { }` | Foreach Loop | 100 | 101 | ## Expressions 102 | 103 | | Syntax | Meaning | 104 | | -------------------------------- | --------------------------------------- | 105 | | `break`, `break v`, `break $l v` | Break Loop Expression | 106 | | `continue`, `next $l` | Next Loop Iteration Expression | 107 | | `#[x, y]` | List/Array Initializer | 108 | | `#{x, y}` | Set Initializer | 109 | | `#(x, y)` | Tuple Initializer | 110 | | `fn(x) { ... }` | Anonymous Function | 111 | | `fn(x) -> Type {...}` | Anonymous Function with Return Type | 112 | | `fn(x) => expression` | Anonymous Function with Expression Body | 113 | 114 | ## Attributes 115 | 116 | | Syntax | Meaning | 117 | | ---------------------- | ------- | 118 | | `#Attribute public...` | | 119 | -------------------------------------------------------------------------------- /src/system.collections.specialized.md: -------------------------------------------------------------------------------- 1 | ## `system.collections.specialized` Namespace 2 | 3 | # `system.collections.specialized.Raw_Array` 4 | 5 | The `Raw_Array[T]` type is a fixed size array that performs no checking on array bounds and does not track the array size. This type is rarely used, but is the foundation for many of the other collection types. As with other objects, the Adamant compiler is able to optimize `Raw_Array` so that the memory allocation occurs on the stack rather than the heap when it can determine the array is small enough for this to be beneficial. The effective declaration of `Raw_Array[T]` is: 6 | 7 | ```adamant 8 | public mut ref struct Raw_Array[T] 9 | { 10 | public new(count: size); 11 | public unsafe init from_pointer[$ref](elements: @T); 12 | public unsafe at(ref mut self, index: size) -> ref var T; 13 | public unsafe at(ref self, index: size) -> ref T; 14 | public slice(ref mut self, index: size) -> mut Raw_Array[T] $< ref; 15 | public slice(ref self, index: size) -> Raw_Array[T] $< ref; 16 | public unsafe delete(); 17 | } 18 | ``` 19 | 20 | **TODO:** given that the compiler needs special handling to optimize this, should a special construct be added to the language to either replace raw array or allow it to be implemented without special compiler support. 21 | 22 | # `system.collections.specialized.Unsafe_Array` 23 | 24 | The `Unsafe_Array[T]` type is a fixed sized array with a length that tracks the array size and performs array bounds checked. However, the array starts uninitialized and so is unsafe. This type is rarely used, but is the foundation for many of the other collection types. An unsafe is essentially a wrapper around a `Raw_Array[T]`. The effective declaration of `Unsafe_Array[T]` is: 25 | 26 | ```adamant 27 | public mut ref struct Unsafe_Array[T] 28 | { 29 | public let count: size; 30 | public new(count: size); 31 | public unsafe at(ref mut self, index: size) -> ref var T; 32 | requires index < count 33 | public unsafe at(ref self, index: size) -> ref T; 34 | requires index < count 35 | public slice(ref mut self, index: size) $ref -> mut Raw_Array[T]; 36 | requires start < count 37 | requires start + length <= count 38 | public slice(ref self, index: size) $ref -> Raw_Array[T]; 39 | requires start < count 40 | requires start + length <= count 41 | public unsafe delete(); 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /src/system.io.md: -------------------------------------------------------------------------------- 1 | ## `system.io` Namespace 2 | 3 | Package for file and network IO 4 | 5 | ### Paths 6 | 7 | To support paths cross platform, Adamant uses special path objects to represent paths. This ensures that any path from any platform can be safely and correctly represented. It also means that any character can be supported in a file name without some weird scheme of escaping the path separator. For example, if there were a platform that allows '/' in file names, that wouldn't be a problem. There are then methods for converting from and to the platform native path string and a property for what the native path operator is. 8 | 9 | ##### Path Combination 10 | 11 | The `/` operator is defined on paths to mean path combination. 12 | -------------------------------------------------------------------------------- /src/system.math.md: -------------------------------------------------------------------------------- 1 | ## `system.math` Namespace 2 | 3 | ### `integer` and `uinteger` 4 | 5 | The `integer` and `uinteger` structs are arbitrary precision signed and unsigned integers respectively. 6 | -------------------------------------------------------------------------------- /src/system.md: -------------------------------------------------------------------------------- 1 | ## `system` Namespace 2 | 3 | ### `system.Iterable` 4 | 5 | ### `system.Iterator` 6 | 7 | #### Enumerate 8 | 9 | The standard library provides an `enumerate()` method for counting. This provides the index of each value in the iterator sequence. 10 | 11 | ```adamant 12 | foreach #(i, x) in (5..10).enumerate() 13 | { 14 | console.write_line("i = \(i), x = \(x)"); 15 | } 16 | ``` 17 | 18 | Outputs: 19 | 20 | ```console 21 | i = 0, x = 5 22 | i = 1, x = 6 23 | i = 2, x = 7 24 | i = 3, x = 8 25 | i = 4, x = 9 26 | i = 5, x = 10 27 | ``` 28 | 29 | ### Ranges 30 | 31 | *Note:* The exact range API has not been determined. There may need to be a distinction between ranges of continous types and ranges of descrete types. See Swift 3 ranges and Stridable for an example https://oleb.net/blog/2016/09/swift-3-ranges/. 32 | 33 | Any type `T where T: Integral` implements the range operator `X..Y` so that is returns a `Range[T, 1]` inclusive of `X` and `Y`. If `X > Y` then the range returned is the empty range. Note the type of `Range` is `Range[T, Step:T=1] where T: number`. This needs to fit in with an `Interval` type defined in `system.math`. To construct a range exclusive of the end value use the `..<` operator. Likewise the `<..` and `<..<` operators are used to construct ranges exclusive of the start and exclusive of both the start and end respectively. 34 | 35 | Related types: 36 | 37 | * ReverseRange - instead of building negative step into range 38 | * RangeFrom - range only bounded below 39 | * RangeTo - range only bounded above 40 | * RangeFull/FullRange - unbounded range 41 | * InclusiveRange - range inclusive of the end value 42 | * StepBy? - a way to represent stepping ranges 43 | * Interval 44 | * Distribution used by random etc. 45 | -------------------------------------------------------------------------------- /src/system.memory.md: -------------------------------------------------------------------------------- 1 | ## `system.memory` Namespace 2 | 3 | The standard library provides functions for allocating memory directly. These don't normally need to be used. Consistent with the limitations that pointers can only point to value types, these functions restrict the types they are allocating for to value types. 4 | 5 | ### Allocating Raw Memory 6 | 7 | The `allocate()` and `free()` provide manual memory management. The memory contents are not initialized. 8 | 9 | ```adamant 10 | let x: @byte = allocate(45); // allocates 45 bytes 11 | unsafe free(x); 12 | ``` 13 | 14 | ## Allocating Memory For Arrays 15 | 16 | The memory contents are not initialized. 17 | 18 | ```adamant 19 | let x: @Struct_Type = allocate[Struct_Type](10); // allocates enough memory for 10 Struct_Type structs, does not initialize them 20 | unsafe free(x); // free memory, does not destruct the points 21 | ``` 22 | 23 | ## Resizing Memory 24 | 25 | ```adamant 26 | var x: @mut Point = allocate[Point](2); 27 | x = reallocate[Point](x, 4); // copies contents as raw bytes, may return new pointer value 28 | x = reallocate[Point](x, 8, 2); // resize to 8, only copy 2 items 29 | x = reallocate[Point](x, 2); // just drops memory contents after first two 30 | free(x); 31 | ``` 32 | 33 | ### Allocating Structs on the Heap 34 | 35 | Structs can be allocated on the heap using the `system.memory.Boxed[T]` type. `Boxed[T]` is a move type and will automatically free the memory when it goes out of scope. 36 | 37 | ```adamant 38 | let x = new Boxed[Struct_Type](); 39 | ``` 40 | -------------------------------------------------------------------------------- /src/system.text.md: -------------------------------------------------------------------------------- 1 | ## `system.text` Namespace 2 | 3 | The text types are those types in addition to `String` which support text/string manipulation. 4 | 5 | ### Encoding 6 | 7 | Following the principle of [UTF-8 Everywhere](http://utf8everywhere.org/), strings are encoded in UTF-8 by default in memory and all other places. The encodings classes must be used to read and write in other encodings. 8 | 9 | ### Line Endings 10 | 11 | To optimally support cross platform development, newlines are represented in code as simply `\n`. Libraries should convert them when needed for output (for example to the console). Text reading libraries should support the new lines types. The default is `Mixed` which can accept a mix of any of the standard newline forms and converts them to `\n`. When writing text, libraries should support the new line types. The default is line feed `\n`, but other types can be selected, including native. 12 | 13 | ### `system.text.String` 14 | 15 | The standard string type. This encodes strings in UTF-8 and supports string literal values. 16 | 17 | #### Lifetime Issues 18 | 19 | There is a potential problem with strings and ownership. If a string is an array of bytes, then some string objects will "own" their array slice and need to delete it. But other strings won't really own their array slices, they will be parts of other strings. Those string will need to not delete their array slice. There is some question as to how this should work and how to express it. The Rust prototype demonstrates it should be possible to unify these cases since they are both essentially pointers. The question is how. It seems like there needs to be a lifetime parameter that can be either some lifetime or "own" that indicates how the string holds its array. Of course, then the question becomes how array slices really work. Do they have the same problem with their underlying memory? 20 | 21 | ### `system.text.String_Builder` 22 | 23 | Represents a mutable string value. This class makes it easy to construct string values. 24 | 25 | ### `system.text.unicode.code_point` 26 | 27 | A 32-bit type used for a [Unicode Code Point](https://unicode.org/glossary/#code_point). Code points support user literals of a single character such as "`'c'`" or "`'♠'`". As defined in the Unicode standard, a code point is any value in the Unicode codespace. That is, the range of integers from 0 to 0x10FFFF. 28 | 29 | ### `system.text.unicode.scalar_value` 30 | 31 | A 32-bit type used for a [Unicode Scalar Value](https://unicode.org/glossary/#unicode_scalar_value). Scalar values support user literals of a single character such as "`'c'`" or "`'♠'`". As defined in the Unicode standard, a scalar value is any Unicode code point except high-surrogate and low-surrogate code points. In other words, the ranges of integers 0 to 0xD7FF and 0xE000 to 0x10FFFF inclusive. 32 | -------------------------------------------------------------------------------- /src/tokens.md: -------------------------------------------------------------------------------- 1 | ## Tokens 2 | 3 | Tokens form the terminals for the syntatic grammar and are comprised of [identifiers](identifiers.md), [keywords](keywords.md), [reserved words](reserved-words.md), [literals](literals.md), [operators, and punctuators](operators-and-punctuators.md). Whitespace and comments are not tokens, but may separate tokens. 4 | 5 | ```grammar 6 | token 7 | : identifier 8 | | keyword 9 | | reserved_word 10 | | literal 11 | | operator_or_punctuator 12 | ; 13 | ``` 14 | -------------------------------------------------------------------------------- /src/traits.md: -------------------------------------------------------------------------------- 1 | # Traits 2 | 3 | A trait defines an interface for a type. Traits can't have fields, but they can define behavior. That behavior is then inherited by all classes that are subtypes of the trait. A class can implement multiple traits by listing them in the base type list. Traits can be subtypes of other types. Trait members that do not provide implementations are not marked "`abstract`". 4 | 5 | ```adamant 6 | public trait Example <: SomeClass 7 | { 8 | } 9 | ``` 10 | 11 | ```grammar 12 | trait_declaration 13 | : access_modifier "trait" identifier generic_parameters? base_types trait_body 14 | ; 15 | 16 | trait_body 17 | : "{" trait_member* "}" 18 | ; 19 | 20 | trait_member 21 | : named_function 22 | | get_function 23 | | set_function 24 | | operator_overload 25 | ; 26 | ``` 27 | -------------------------------------------------------------------------------- /src/tuple-types.md: -------------------------------------------------------------------------------- 1 | ## Tuple Types 2 | 3 | The tuple types are a family of generic types. Like the simple types, they are identified by the keyword "`Tuple`". A tuple is an ordered list of values that can be of different types. 4 | 5 | Tuples are value types. If all the type parameters are implicitly copyable, then the tuple is implicitly copyable. If all the type parameters are implicitly *or explicitly copyable* then the tuple is explicitly copyable. If any type parameter is not copyable, the tuple is a move type. 6 | 7 | ```grammar 8 | tuple_type 9 | : "Tuple" "[" type{",", 0, *} "]" 10 | ; 11 | ``` 12 | 13 | ### Tuple Size 14 | 15 | The number of values in a tuple can be accessed using the "`count`" field which is of type "`size`". 16 | 17 | ### Tuple Initializer 18 | 19 | Tuples can be constructed using the tuple initializer syntax. 20 | 21 | ```adamant 22 | let t = #(1, "something"); // t: Tuple[int, string] 23 | ``` 24 | 25 | ### Destructing With `let` 26 | 27 | The individual fields of a tuple can be accessed using a destructuring let. 28 | 29 | ```adamant 30 | let #(x, y, z) = #(1, 2, 3); 31 | console.WriteLine("x = \(x)"); 32 | ``` 33 | 34 | ### Indexing Tuples 35 | 36 | Tuple fields are accessed by a zero based index. This is consistent with arrays which are zero based and makes more sense for the "`at`" method. Tuple values can be directly accessed as fields by escaping the index name. 37 | 38 | ```adamant 39 | let t = #(1, 2, 3); 40 | 41 | let x = t.\0; 42 | let y = t.\1; 43 | let z = t.\2; 44 | ``` 45 | -------------------------------------------------------------------------------- /src/type-expressions.md: -------------------------------------------------------------------------------- 1 | ## Type Expressions 2 | 3 | Type expressions allow other data types to be composed into further types. All type operators have the same precedence. The order of type operations can be controlled using parenthesized expressions. 4 | 5 | ```grammar 6 | type_expression 7 | : union_type 8 | | intersection_type 9 | | "(" type ")" 10 | ; 11 | ``` 12 | 13 | ### Domains 14 | 15 | For any type expression, all the type operated on must be from a single domain. There are two ways type domains are formed. 16 | 17 | 1. All reference and optional reference types form a domain. 18 | 2. All of the enum members of each enum struct form a separate domain. 19 | 3. All of the optional variants of enum members of each enum struct form a separate domain. 20 | 21 | It is important to note that for the reference types, a single domain contains them all. Whereas for the enum struct types, each enum struct creates two separate domains, one for the optional variant and one for the non-optional variant. 22 | 23 | ### Union Types 24 | 25 | Union types are the union of other data types. That is, a value of any of the types being unioned is a legal value for the union type. Methods and properties common to all types in the union type can be directly accessed on a value of the union type. Other methods and properties require that the value be destructured to the specific type using `match` etc. 26 | 27 | ```grammar 28 | union_type 29 | : type "|" type 30 | ; 31 | ``` 32 | 33 | ### Intersection Types 34 | 35 | Intersection types are the intersection of other data types. For a value to be of the intersection type, it must be a subtype of all the types being intersected. Instances of an intersection type have all the methods and properties of all the types being intersected. 36 | 37 | ```grammar 38 | intersection_type 39 | : type "&" type 40 | ; 41 | ``` 42 | -------------------------------------------------------------------------------- /src/types.md: -------------------------------------------------------------------------------- 1 | # Types 2 | 3 | There are three main categories of types in Adamant: *empty types*, *value types* and *reference types*. In addition to those, there are a number of other categories of types that are special in some way. 4 | 5 | The *empty types* are those types for which there is no value of that type. Both value and reference types may be *generic types*, which take *generic parameters*. Generic parameters can be types (both value and reference types) or constant values. Generic parameters that are types are called *type parameters* and can be used as types. *Optional types* modify value types or reference types to add a `none` value. *Variable reference types* aka *ref types* are a special form of reference with their own rules. Additionally, *type expressions* allow the construction of new types by intersecting and unioning existing types within certain limitations. 6 | 7 | ```grammar 8 | type 9 | : empty_type 10 | | value_type 11 | | reference_type 12 | | type_parameter 13 | | optional_type 14 | | variable_reference_type 15 | | type_expression 16 | ; 17 | ``` 18 | 19 | The values types can be further divided into a number of subcategories. Most value types are *struct types*. The *simple types* are predefined value types identified with keywords. The *tuple type* is a predefined type for tuples values. *Class value types* are value types for each of the non-trait object types. *Pointer types* allow unsafe code to directly use pointers to addresses in memory. 20 | 21 | ```grammar 22 | value_type 23 | : struct_type 24 | | simple_type 25 | | tuple_type 26 | | class_value_type 27 | | pointer_type 28 | ; 29 | ``` 30 | 31 | Note that while value types are declared to be subtypes of reference types, the reality is more complicated. Given value type `S` that is a subtype of reference type `T`, then `ref S <: T`. There is also an implicit boxing conversion from `S` to `T$owned` which consumes the value (i.e. the value is moved into the box). 32 | -------------------------------------------------------------------------------- /src/unsafe.md: -------------------------------------------------------------------------------- 1 | ## Unsafe 2 | 3 | ### Unsafe Context 4 | The `unsafe` keyword is used to create an unsafe context. An unsafe block is introduced with the `unsafe` keyword followed by a block. An unsafe expression is introduced by the `unsafe` keyword followed by an expression in parenthesis. 5 | 6 | ```adamant 7 | unsafe 8 | { 9 | // unsafe code 10 | } 11 | x = unsafe(/* unsafe expression */); 12 | ``` 13 | 14 | An unsafe context is necessary to: 15 | 16 | 1. Access or modify a pointer 17 | 2. Access or update a mutable static variable 18 | 3. Call unsafe functions 19 | 4. Call untrusted functions (see [Trusted Packages](#TrustedPackages)) 20 | 21 | It is a compile time error to perform any of those operations outside of an unsafe context. 22 | 23 | ### Unsafe and Safe Functions 24 | 25 | Any function containing an unsafe context must be marked as either `unsafe` or `safe`. This makes explicit the safety contract provided by the API. A function marked `safe` is promising that even though it contains unsafe code, using the function is safe. 26 | 27 | Functions that do not contain unsafe contexts may also be marked `unsafe` to indicate that their use can lead to undefined behavior in some other way. It is recommended this be used sparingly. However, it might be appropriate if the method code violates invariants of the class. For example, `string` guarantees that it contains a valid utf8 encoding. If there were a slice method that returned a string slice that may not be valid utf8 because it didn't check if the index split a code point, that method should be marked `unsafe`. 28 | 29 | It is a compile time error to mark a function `safe` that contains no unsafe contexts. 30 | 31 | ### Unsafe and Safe Classes and Structs? 32 | 33 | **TODO:** should it be necessary to mark the class as a whole as safe or unsafe? Exactly where would the use of an unsafe type cause an error? Does an unsafe class mean all of its constructors are unsafe? 34 | 35 | ### Trusted Packages 36 | 37 | Packages referenced from adamantforge.com may be trusted or untrusted. By default all packages in the `system` namespace are trusted while all other packages are untrusted. This can be changed for individual packages using the `trusted` attribute of the project file. Any function marked `unsafe` or `safe` in an untrusted package is untrusted. Note that this can propagate from one package to another. Any function that calls an untrusted function is untrusted. Calling an untrusted function requires an unsafe context. 38 | 39 | ### Docs 40 | 41 | In API docs, `unsafe` functions are marked as such. `safe` functions are not distinguished from `unsafe` functions. Rather the docs indicate which functions require trust and what packages must be trusted to call them. 42 | 43 | ### Rationale 44 | 45 | Both Rust and C# treat unsafe as always creating an unsafe context even when applied to functions or traits. This means that an unsafe function may carry out an unsafe operation at any point. While it is certainly true that "safe" code in the method may introduce a bug in the "unsafe" code, there is value in highlighting the segments of code that are unsafe. 46 | 47 | Rust and C# have no equivalent of the `safe` keyword. Requiring developers to mark the safety of the function adds an extra safety check, ensuring that a developer explicitly acknowledges whether they are promising safety to their callers. 48 | 49 | ### Unsafe References 50 | 51 | An old implementation of optional types indicated that it might be useful to have references that may not be initialized. The idea is that a reference could be marked `unsafe`. Then you could set it to `uninitialized` to indicate you didn't want to initialize it or that the current value should be thrown away. Accessing an unsafe reference is unsafe because the compiler can't guarantee it has been initialized. 52 | 53 | ### Constructing and Destructing Values in Place 54 | 55 | When allocating memory, you are responsible for constructing and destructing the values. This is done with placement init and explicit delete. 56 | 57 | ```adamant 58 | let x: @mut Point = allocate[Point](2); 59 | unsafe 60 | { 61 | // Call Constructors with Placement New 62 | init(x) Point(1, 3); 63 | init(x+1) Point(9, 4); 64 | // Call Destructors 65 | x^.delete(); // calling delete is only valid on pointers 66 | (x+1)^.delete(); 67 | // Deallocate memory 68 | free(x); // frees memory, does not destruct points 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /src/whitespace.md: -------------------------------------------------------------------------------- 1 | ## Whitespace 2 | 3 | Whitespace is not significant except in that it may separate tokens. Whitespace includes all code points in the Unicode class Zs which includes the standard space character (U+20). 4 | 5 | ```grammar 6 | whitespace 7 | : \p{Zs} // Unicode whitespace class (Zs) 8 | | \u(0009) // Horizontal tab 9 | | \u(000B) // Vertical tab 10 | | \u(000C) // Form feed character 11 | ; 12 | ``` 13 | --------------------------------------------------------------------------------