Cone Language Reference

Extended BNF

On this page

The syntax is specified below using XML's Extended BNF Notation. The diagrams do not attempt to convey operator precedence within a rule (see Operator and Precedence for that information).

Statements#

	program ::= dcl_stmt*
	dcl_stmt ::= mod_stmt | modtrait_stmt | var_stmt | fn_stmt | use_stmt | import_stmt
	mod_stmt ::= 'pub'? 'mod' ident_token ('extends' ident_token)? ('is' ident_token)? defaultfold? ';'
		/* the first statement of a module's designated file, or of a one-file module's file; 'extends' names one module already in reach, a sister or one this module imports;
		   'is' names the one module trait this module conforms to, by one name it can reach */
	modtrait_stmt ::= 'pub'? 'mod' 'trait' ident_token ('{' modtraitmember* '}' | ';')
	modtraitmember ::= 'pub'? (fn_type block? | perm_ident ident_token type? ('=' literal)? ';')
		/* a function or a global: with a body or an initial value a default, without one a requirement.
		   Not generic, and no overload name. Anywhere among a module's statements */
	defaultfold ::= 'use' ('*' ('but' ident_token (',' ident_token)*)? | namelist | '{' namelist ','? '}')
	namelist ::= ident_token (',' ident_token)*
		/* what a bare import of this module folds by default; each name a public name of the module.
		   Folds into importers, not into this module; an importer's own clause replaces it. No 'as', no 'pub' */
	import_stmt ::= 'pub'? 'import' (ident_token | text_token) ('as' ident_token)? foldclause? ';'
		/* 'as' binds the module under that name instead of its own, which is then not bound here.
		   A module imports another once: a second import is refused, identical or not, whatever name each binds it under */
	use_stmt ::= 'pub'? 'use' genqualname (folditems | '{' folditems ','? '}')? ';'
		/* folds in a namespace the module reaches without an import: an enum's variants, the enum named alone, never an instance of a generic one;
		   or a submodule's public names, the submodule named by its name or a path through submodules. Never an imported module */
	var_stmt ::= perm_ident ident_token type? (',' ident_token type?)* ('=' append_exp (',' append_exp)*)? foldclause? ';'
		/* a fold clause only on a module's global, an 'extern' one included: a local, a parameter and a type's static have no namespace to fold into */
	fn_stmt ::= fn_type block
	block ::= '{' stmt* '}'
	stmt ::= if_stmt | letelse_stmt | match_stmt | block | while_stmt | break_stmt | cont_stmt | each_stmt | par_each_stmt | return_stmt | yield_stmt | exp_stmt | ';'
	if_stmt ::= 'if' logic_exp block ( 'elif' logic_exp block )* ( 'else' block )?
	letelse_stmt ::= ('imm' | 'mut') ident_token type '=' exp 'else' block ';'
		/* binds a variant of the value (the type names only the variant) or leaves: the block must end in 'return', 'break', 'continue' or a call that does not return */
	match_stmt ::= 'match' exp ('using' exp)? ('into' var_list)? ('with' exp ('into' var_list)? block)+ ('else' block)?
	while_stmt ::= (lifetime_token ':')? 'while' logic_exp? block ('else' block)?
		/* with no condition the loop never runs out, and takes no 'else'; with a condition, the loop is an expression only when it has an 'else',
		   whose block (outside the loop) gives the value when the condition fails */
	each_stmt  ::= (lifetime_token ':')? 'each' ident_token (',' ident_token)* 'in' exp ('by' exp)? ('if' logic_exp)? block ('else' block)?
		/* 'by' follows a numeric range, an exp that is a range_exp ('a .. b' through b, 'a ..< b' before b); a range gives one variable, and the other sources
		   (an array, a slice, a list) one as well; a cursor, or something that gives one, one or several that unpack a tuple item;
		   'if' skips the items for which its condition is false; the loop is an expression only when it has an 'else' */
	yield_stmt ::= 'yield' 'each'? exp ';'
		/* only in the body of a function declared 'yields' (a generator), and a statement of its own, never part of an expression;
		   'yield exp' hands the caller a value, 'yield each src' hands on every value of another generator or of anything with a 'next' */
	par_each_stmt ::= 'parallel' 'each' ident_token 'in' exp ('if' logic_exp)? block
		/* 'parallel' is a word only directly before 'each', and a name anywhere else; the exp is a number range counting up by one,
		   an exp that is a range_exp (no 'by'), or an array, a slice or a list; one variable; no 'else', and a statement only */
	break_stmt ::= 'break' lifetime_token? this_exp? ('if' logic_exp)? ';'
	cont_stmt  ::= 'continue' lifetime_token? ('if' logic_exp)? ';'
	return_stmt ::= 'return' this_exp? ('if' logic_exp)? ';'
		/* a trailing 'if' makes the statement conditional; only these three take one. An 'if' expression as the value
		   is told from it by the block that follows its condition */
	exp_stmt   ::= this_exp ';'
	
	this_exp ::= exp (('using' exp)? block)?
	var_list ::= perm_ident? ident_token type (',' perm_ident? ident_token type)*

Type declarations#

	type ::= genqualname | nbrtype | arraytype | optiontype | ptrtype | reftype | ownreftype | fntype | structtype
	nbrtype ::= 'bool' | 'i8' | 'i16' | 'i32' | 'i64' | 'isize' | 'u8' | 'u16' | 'u32' | 'u64' | 'usize' | 'f32' | 'f64'
	arraytype ::= 'Array' '[' type (',' integer_token)+ ']'
		/* the element type, then one size for each dimension, the first the outermost:
		   'Array[T, n, m]' is 'Array[Array[T, m], n]'. 'Array' is a name every module
		   reaches unless it declares the name itself */
	optiontype ::= '?' type
	ptrtype ::= '*' type
	reftype ::= ('&' | '&<') lifetime_token? perm_type? type ('+' lifetime_token)?
		/* a borrowed reference names no region: '&mut Point', a virtual one '&=' | '==') lifetime_token
		/* ''a >= 'b': ''a' lasts at least as long as ''b'; '==' both ways. On any function, of lifetimes its
		   signature names, and on a struct, of lifetimes it declares */
	baseclause ::= isaclause | extendsclause   /* either order, each at most once; 'extends' not on a trait */
	isaclause ::= 'is' genqualname (',' genqualname)*
		/* every name is an open trait: an enum anywhere in the list is refused.
		   On a struct or a trait the first is its base; an enum and a variant have none of their own */
	extendsclause ::= 'extends' genqualname   /* the concrete base this type enriches with methods */
	structstmt ::= var_stmt | fn_stmt | usestmt
		/* 'mixin', retired, is refused: a type names what it complies with in its isaclause */
	usestmt ::= 'use' genqualname folditems? ';'   /* folds in a sibling: a type that declared the same 'extends' base */
	foldclause ::= 'pub'? 'use' ('*' ('but' ident_token (',' ident_token)*)? | folditems | '{' folditems ','? '}')
		/* written after the type of a struct's field or of a module's global, or after an import's module; 'pub' not on a field's.
		   'pub' comes first, as on every declaration: 'use pub' is refused */
	folditems ::= ident_token ('as' ident_token)? (',' ident_token ('as' ident_token)?)*
		| 'but' ident_token (',' ident_token)*   /* omitted admits every member of the sibling named */
	traittype ::= 'trait' ident_token? isaclause? ('{' structstmt* '}')?   /* a synonym for 'struct trait' */
	actortype ::= 'actor' ident_token '{' (structstmt | behaviourstmt)* '}'
		/* its fields are its private state, never 'pub'; each behaviour is a message, whose parameters
		   and returned value are Sendable; each 'fn' method is synchronous and never 'pub' ('init' and
		   'final' as a struct's); no static, macro, 'extern' or 'use'. 'new Name(args)' runs its 'init'
		   and answers its handle. Needs 'import actors' */
	behaviourstmt ::= 'pub'? 'async' 'do' ident_token '(' fnparm (',' fnparm)* ')' type? block
		/* an actor's behaviour: its first parameter 'self'; 'async' and 'do' mean nothing apart */
	unbuiltkind ::= 'actor' 'trait' ident_token? ('{' structstmt* '}')?   /* accepted, not implemented */
	enumtype ::= 'enum' '@unsized'? ident_token genparms? nbrtype? (('extends' genqualname) | isaclause)? whereclause? '{' enumstmt* '}'
		/* 'extends' names another enum, a generic one with its type arguments, whose variants
		   this one holds copies of; such an enum declares only variants in its body, writes
		   no integer type of its own, and names no isaclause: its base's comes with the copies.
		   An isaclause gives every variant the traits' requirements and defaults */
	enumstmt ::= structstmt | variantlist | 'struct' ident_token ('=' integer_token)? isaclause? structbody?
		/* a variant's isaclause names open traits beyond its enum, which is its base unwritten */
	variantlist ::= variant (',' variant)* ';'
	variant ::= ident_token ('=' integer_token)?
	tagtype ::= 'tag'

General use expressions (with possible assignments)#

	exp ::= assgn_exp
	assgn_exp ::= tuple_exp ((('=' | '+='|'-='|'*='|'/='|'%='|'&='|'|='|'^='|'<<='|'>>=') exp) | ('<-' entries))
	tuple_exp ::= simple_exp (',' simple_exp)*
	entries ::= entry (',' entry)*
		/* the list after '<-', running to the statement's end, across lines; each entry is appended in turn */
	entry ::= ('fill' contents_exp) | (contents_exp (('of' | ':') contents_exp)?) | ('(' entries ')') | each_entry
	each_entry ::= 'each' (simple_exp | ident (',' ident)* 'in' simple_exp ('by' simple_exp)? ('if' simple_exp)? 'yield' simple_exp (':' simple_exp)?)
		/* 'each src' appends every item of src; with variables, the loop's own header, and the 'yield' says
		   what each pass appends ('yield k: v' a pair) */
		/* 'of' and 'fill' are words only here, names elsewhere; 'fill' is the word when a space and a value
		   follow it. 'n of x': n values, x evaluated for each; 'fill x': x until the collection's len()
		   reaches its capacity(); 'k: v': a key and its value, given to a '<-' that takes both */
	contents_exp ::= simple_exp ('<-' entry)?
		/* inside a comma list, a construction's contents take one entry: several are parenthesized */

Expressions without any assignments#

	simple_exp ::= if_exp | match_exp | while_exp | each_exp | loop_exp | or_exp
	or_exp ::= and_exp ('or' and_exp)?
	and_exp ::= not_exp ('and' not_exp)?
	not_exp ::= '!'* compare_exp
	compare_exp ::= range_exp (('=='|'!='|'==='|'!=='|'~~'|'<=>'|'<'|'<='|'>'|'>=') range_exp)?
	range_exp ::= bit_exp (('..' | '..<') bit_exp)?
	bit_exp ::= shift_exp (('|'|'&'|'^') shift_exp)*
	shift_exp ::= arith_exp (('<<' | '>>') arith_exp)?
	arith_exp ::= cast_exp (('+'|'-'|'*'|'/'|'%') cast_exp)*
	as_exp ::= prefix_exp ('as' type)*
		/* 'into' is retired: a value converts with 'f32.from(x)', and a reference narrows to a variant by a match or a bound 'if' */
	prefix_exp ::= ('.'|'<-'|'-'|'~'|'*'|'++'|'--'|'await')* term | addr | plus_alloc
		/* 'await' only in an actor's behaviour; built on a behaviour of an actor that returns a value */
	addr ::= ('&'|'&<'|'&[]') perm_type? (fn_stmt | (term suffix*))
		/* '&[]' only borrows: there is no owning array reference, and '+[]' is refused */
	plus_alloc ::= '?'? '+' ident_token ('-' perm_type)? prefix_exp
		/* the retired allocation spelling, refused: every allocation is a new_exp, 'new Rc[i32](5)'.
		   It stays only as a match pattern's owning reference type, '+Rc-mut Circle', whose spelling
		   is an open design question */
	suffix_exp ::= term suffix*
	suffix ::= '++'|'--'| ('.' property) | ('(' (contents_exp (',' contents_exp)*)? ')') | ('[' (contents_exp (',' contents_exp)*)? ']')
		/* square brackets hold type arguments or an index, and never a value's contents: an enum variant is
		   constructed with parentheses, 'Some(x)', and an empty one by its name alone, 'None'. A struct's value
		   is built with 'new' */
	property ::= ident_token | symbol_token | integer_token
	term ::=  literal | ident_token | pseudo | '(' exp ')' | new_exp
	new_exp ::= ('new' | 'trynew') ident_token ('.' ident_token)* ('[' type (',' type)* ']')* ('(' (contents_exp (',' contents_exp)*)? ')')?
		/* a construction, its type then its initializer's arguments: 'new Point(1, 2)', and its contents
		   after '<-' (entries): 'new List[i32](100) <- fill 0'; an array takes contents only. A managed reference
		   type, or an alias of one, allocates: 'new Rc[mut, Node](1)', or, given one finished value of the
		   value type, moves it in: 'new Rc[i32](5)'. 'trynew' takes only a managed reference
		   type, and gives an Option of it, None when memory runs out */
	genqualname ::= ident_token ('.' ident_token)* ('[' typearg (',' typearg)* ']')?
	typearg ::= type | lifetime_token
		/* a lifetime only in a type of a function's signature or of a struct's field: a struct's lifetimes,
		   named positionally ('Cursor['a]', 'Parser['s, 'r]', 'Cursor['a, T]') or not at all; never in a
		   construction's type, a variable's or a function's brackets */
	pseudo ::= 'self' | 'this' | 'selfactor'
		/* 'selfactor' only in an actor's method: the actor's own handle */

Literals and other tokens#

	literal ::= float_token | integer_token | text_token | symbol_token | 'true' | 'false' | 'null' | array_lit
		/* 'null' is the null raw pointer, of the pointer type it is wanted as */
	array_lit ::= '[' contents_exp (',' contents_exp)* ']'
		/* '[n; x]', the fill literal, is retired: 'new Array[T, n] <- fill x' */
	float_token ::= (('0'-'9') ('0'-'9')*) '.' ('0'-'9')* (('e'|'E') ('-'|'+')? ('0'-'9')+)?
	integer_token ::= (('0'-'9') ('0'-'9')*) | '0x' (('0'-'9')|('a'-'f')|('A'-'F'))+
	lifetime_token ::= '\'' ident_token
	text_token ::= '"' any_char '"'
	symbol_token ::= "'" any_char "'"

	ident_token ::= '`' any_char* '`' | ('_' | alpha_char) (alpha_char | ('0'-'9') | '$' | '_')*

Railroad diagrams courtesy of the Railroad Diagram Generator.