Source files

The parts of an Antril source file - imports in all their forms, namespaces with into namespace, the global namespace, what goes at the top level, and the Main entry point.

Last updated 6 min read Edit on GitHub

On this page

A program or library is a set of .ant files compiled together. In a project, every .ant file under the project's folder is part of it (see Project files). The order of the files, and of the declarations in them, does not matter: a declaration can be used before the place where it is written.

The shape of a file

A file is a list of import directives followed by declarations:

AntrilRun
import System;
import List from System;

into namespace Inventory
{
	public class Item
	{
		public:
			str Name;
			new(str Name);
	}

	const i32 Limit = 3;

	Count(List<Item> items) : i32 => items.Count;
}

Main()
{
	List<Inventory.Item> items = new List<Inventory.Item>();
	items.Add(new Inventory.Item("pen"));
	Console.WriteLine(Inventory.Count(items) + " of " + Inventory.Limit);
}

All imports come first. After them, the top level of a file, and of a namespace block, holds:

  • types: classes, values, records, interfaces, enums, exceptions and attributes (see Declarations);
  • functions (see Functions);
  • constants, const T Name = value;;
  • extension blocks;
  • nested into namespace blocks.

A namespace holds no variables: a field at the top level is error AN1103. Keep state in a type, for example a static class.

Namespaces

into namespace Name { … } puts its declarations into the namespace Name. A name can have several parts, and blocks can nest:

Antril
into namespace Geo.Shapes
{
	const i32 Sides = 4;
}

into namespace Geo
{
	into namespace Inner      // Geo.Inner
	{
		Hello() : str => "inner";
	}

	Twice() : i32 => Shapes.Sides * 2;   // Geo.Shapes.Sides, found from Geo
}

A namespace is open: the same namespace can be continued in any number of blocks, files and libraries.

Declarations outside any into namespace block are in the global namespace. They are found from everywhere without an import.

into namespace in a header

A type or a namespace-level function can name its namespace in its header, after its bases or its result type. It is the same as wrapping it in a block, and the name is relative to where the declaration is written:

AntrilRun
import System;

public class Shape into namespace Geo
{
	public:
		static Sides() : i32 => 4;
}

Area(f64 side) : f64 into namespace Geo => side * side;

Main()
{
	Console.WriteLine(Geo.Area(3) + Geo.Shape.Sides());
}

A member of a type stays in its type: into namespace on a method is error AN0039.

Imports

An import brings names into scope for the whole file.

Form What it brings
import D; Every name of the namespace D.
import D as X; The namespace under another name: X.Type, X.F(), X.Constant. The names of D are not brought one by one.
import A, B, C from D; Only those names of D.
import A, B, C from D as E, F, G; Those names, each under the alias in the same position: E is D.A.

A name taken with from can be a type (its nested types come with it: E.Inner), a function, a constant, or a sub-namespace (import Shapes from Geo;, then Shapes.Sides). from is a word only inside an import.

AntrilRun
import System.Text as T;
import Console, Math from System;
import List, Dictionary from System as L, D;

Main()
{
	L<i32> values = new L<i32>();
	values.Add(3);
	Console.WriteLine(Math.Max(values[0], 2));
	Console.WriteLine(T.JsonValue.Parse("[1,2]").ToString());
}
Terminal
larva run
3
[1,2]
Error When
AN0034 A list of names without from, or a different number of aliases and names.
AN1084 The namespace does not exist, or has no such name.
AN1085 The same name or alias is imported twice.

Warning

Not yet available: importing a generic type by its number of type parameters (import List<> from System;, import KeyValuePair<,> from System;) and a family of variadic types (import Tuple<...> from System;). Import the name without <…>.

How a name is found

A name written in code is looked up:

  1. in the scope where it is written: locals, parameters, members of the enclosing types;
  2. in the enclosing namespace, then in its parent namespaces, up to the global namespace;
  3. among the names taken with from and the aliases of the file;
  4. in the namespaces imported whole.

A name can always be written in full: System.Console.WriteLine("hi"), Geo.Shapes.Sides.

Main

A program starts at a free function called Main, in any namespace. There is exactly one per program; a second one is error AN1035. A library has none.

Antril
Main()
{
	Console.WriteLine("hello");
}

Main can take integer, floating-point or bool parameters. Larva reads them from the command line, one per parameter:

AntrilRun
import System;

Main(i32 width, i32 height)
{
	Console.WriteLine(width * height);
}
Terminal
larva build -o area.exe Program.ant
area.exe (llvm back end, O3, entry area::$Module.Main)
area 6 7
42
area
usage: C:\work\area.exe <arguments> (2 integer arguments)

A Main with no parameters accepts no arguments: started with some, the program prints the usage line and ends with exit code 2.

Main can also give a result: Main() : i32. Larva prints that result when the program ends, and the program's exit code stays 0.

Warning

Not yet available:

  • Main(str[] args) with the command-line arguments as text. Environment.GetCommandLineArgs() exists in System.Core, but an executable cannot be started with text arguments yet.
  • The result of Main() : i32 as the program's exit code: it is printed instead.

An exception that nothing catches ends the program with exit code 1 (see Exceptions).

Was this page helpful?

No address is stored; your user name is, if you are signed in.

Esc