BRiX
Advanced Computing Environment
Hosted by SourceForge
brix-os project page

Previous: none ----- Up: Contents ----- Next: Lexical Syntax

Basics

Declaration and Assignment
Different scopes within the language support either declaration and assignment or only declaration. Declarations are noted by the presence of a : on the left side of the = operator, either annotating the symbol with a type or combined as the := operator. Assignments require an existing object or pre-declared symbol to set and do not allow the : to be present on the left side of the = operator. Declaration only scopes disallow the := operator and use = for all declarations. Function and extension scopes use both modes and most other scopes (modules and types) are declaration only.

	// := and = operator definitions
	defop `infix := (left:Object, right:Object)
	  -> tetra.compiler.declare_assign(left, right)
	defop `infix = (left:Object, right:Object)
	  -> tetra.compiler.declare_assign(left, right)

	// declarations in module scope
	symbol:type = value // type annotation
	symbol = value // type infered from value

	// declarations in function or extension scope
	symbol:type = value // type annotation
	symbol := value // type infered from value

	// assignment in function or extension scope
	variable = new_value
Declaration and assignment can not be handled in the same expression due to the potential readability problems.
	b := 0
	(a:Int, b) = (1, 2) // invalid

Declaration and assignment are only available at the top level of block expressions. They may not be used in parameter or data lists, where the = operator is reserved to bind keys to values.


Module -- a namespace and binary container for functions, extensions, types, data and submodules. Submodules may be stored in separate binaries and imported independantly of their parent module.

	// module
	defmodule foo {
	  ...
	  // submodule
	  bar = defmodule {
	    ...
	  }
	}

Function -- a code block that is executed at run-time.

	// definition
	foo := def (..parameters..) ..expression_or_block..

	foo(1, 2, 3) // dispatch
	bar := foo // reference

Extension -- same as a function but executed at compile-time, used for simple macros and powerful constructs to extend the language. All commands (DEF, DEFX, DEFTYPE, IF, DO, WHILE, INHERIT, IMPORT) are extensions. Simple extensions use a function-style dispatch while complex extensions have an open syntax and consume tokens based on a dynamic context. Open extensions with unclear termination rules must use be terminated with a semicolon.

	// definition
	foo := defx (..parameters..) ..expression_or_block..
	bar := defx `open (..parameters..) ..expression_or_block..

	foo(1, 2, 3) // function-style dispatch
	bar 1 // open-style dispatch

Type -- a double namespace container with one holding definitions for the type properties and the other defining the structure of objects produced by the type. The type is evaluated at compile-time like an extension to produce a Type object containing its properties. This means that types may be called with function-style or an open-ended style, just like extensions.

Object types are infered by the compiler but type annotations may be bound to literals or objects using the : operator which invokes the constructor with the literal or object as a parameter. Symbols are declared by annotating them with a type and then assigning them with a value which is passed to the constructor.

	// definition
	Point := deftype (T:Type) {
	  constructor (x:T, y:T)
	  slot (x, y) = (x, y)
	  getter (self.x, self.y)
	  setter (x, y) -> (self.x, self.y) = (x, y)
	}

	foo := Point // reference
	bar := Point(Int) // parameterized reference
	p1:Point(Int) = (0,0) // constructor
	p1 = (1,2) // setter
	p2 := p1 // p1 getter to p2 setter

	n:(Number from 1..10) = 1 // open-ended parameterized type

Object -- a data structure constructed from a type. Objects can exist on the stack or heap and are removed by the system garbage collector when no longer in use. The compiler infers types for every object and uses dynamic typing when it can't. Type annotations are optional and may be added later to improve runtime stability and speed.

Slot -- a data field within an object, type or module. Slots are usually prefixed with the parent container unless they have been exported to the root namespace (module slots only). Slots themselves contain objects and the slots in those can also be accessed by continuous chaining with the dot operator.

	foo.bar = 1 // set slot bar in object foo

	Foo.bar() // dispatch property bar in type foo

	foo.bar() // dispatch function bar in module foo

Method -- a function dispatched on an object.

	foo.bar() // dispatch method bar in object foo

Property -- slots and functions accessilbe on the type instead of the objects instantiated from it. Property slots are exist for each type parameter and property functions may be added for special functionality.

	p:Point(Int) = (0, 0)
	T := (typeOf p).T // slot T = Int

	// invoke the Number.add() property
	defop `infix + (l:Number, r:Number) -> Number.add(l, r)

Variable -- a named location referencing an object within a function scope. All variables must be declared and initialized prior to use.

	// mutable declarations
	a := 1 // type infered
	a:Int = 1 // annotated

	// constant declarations
	`const a := 1 // type infered
	`const a:Int = 1 // annotated

	// assignment
	a := 1 // declare
	a = 2 // reassign

Prototype -- a key::value block may be used to define an extensible object with an anonymous type.

	p := {x::0, y::0}
	echof("point = (%d, %d)\n", p.x, p.y)

Tuple -- a list of objects passed between various constructs in the language. There is no Tuple type and the actual implementation varies between instances. Represented by two or more expressions wrapped in parentheses or zero or more expression wrapped in braces or brackets.

Function parameters -- tuple passed in registers or on the stack to a function call
	foo(1, 2, 3)
Multiple return values -- tuple passed in registers or on the stack when exiting a function call
	return (1, 2)
Constructor parameters -- tuple processed by compile-time or runtime code to generate an object
	p:Point = (1, 2)
Read accessors -- an object can return a tuple of values when requested so by the compiler to feed a constructor, write accessor or multiple assignment
	p2:Point = p
Write accessors -- an object set from a tuple of values or an expression returning a tuple of values
	p = (3, 4)
Multiple assignment -- tuple of variables set from a tuple of values or an expression returning a tuple of values
	(x, y) := p
	(a, b) := (1, 2)

Operator -- a prefix or infix identifier whose arguments are rewritten with a pattern.

	.        slot access
	..       range
	:        type annotation
	::       key::value pair
	:=       declaration
	=        assignment
	->       function interface

The -> operator takes input and output types and produces a Function interface type. It is also used as a syntax keyword for DEF to specify the function return type. And also used by various other extensions as a syntax keyword to apply a set of inputs to a body of code, expression or pattern.

Body vs X-Body -- extensions that take a block of code will have it listed as body or X-body. Both are compiled to binaries but body is executed at runtime and X-body is executed like an extension at compile-time.


All identifiers (modules, functions, extensions, types, slots, methods, properties and variables) are case-insensitive. The case of each identifier is preserved and may be restored by the IDE when typed in the wrong case.

Functions, extensions and types defined in the module scope share the same namespace. Identifiers declared inside functions, extensions and types are registered in the module namespace but only for the scope of the declaration. Flags exist in their own namespace and don't require declaration.

Previous: none ----- Up: Contents ----- Next: Lexical Syntax