ref: 9f73400b5ec23decb92f5051564a49f7adda35da
parent: ac187c5de6db760389b4c47c36650fbc20465488
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Jan 18 19:18:21 EST 2017
Rearrange topics and start documenting operators.
--- a/doc/lang.txt
+++ b/doc/lang.txt
@@ -21,11 +21,9 @@
4.3. Generics
4.4. Type Inference
5. VALUES AND EXPRESSIONS
- 5.1. Declarations
- 5.2. Literal Values
+ 5.1. Literal Values
+ 5.2. Expressions
5.3. Control Constructs
- 5.4. Expressions
- 5.6. Packages and Uses
6. CONTROL FLOW
6.1. Blocks
6.2. Conditionals
@@ -350,12 +348,236 @@
for all files included through use statements.
-4. VALUES AND EXPRESSIONS:
- 4.3. Literal Values
+4. TYPES:
- 4.3.1. Atomic Literals:
+ 4.1. Data Types:
+ The language defines a number of built in primitive types. These
+ are not keywords, and in fact live in a separate namespace from
+ the variable names. Yes, this does mean that you could, if you want,
+ define a variable named 'int'.
+
+ There are no implicit conversions within the language. All types
+ must be explicitly cast if you want to convert, and the casts must
+ be of compatible types, as will be described later.
+
+ 4.1.1. Primitive types:
+
+ void
+ bool char
+ int8 uint8
+ int16 uint16
+ int32 uint32
+ int64 uint64
+ int uint
+ long ulong
+ float32 float64
+
+ 'void' is a type and a value although for the sake of
+ genericity, you can assign between void types, return values
+ of void, and so on. This allows generics to not have to
+ somehow work around void being a toxic type. The void value is
+ named `void`.
+
+ It is interesting to note that these types are not keywords,
+ but are instead merely predefined identifiers in the type
+ namespace.
+
+ bool is a type that can only hold true and false. It can be
+ assigned, tested for equality, and used in the various boolean
+ operators.
+
+ char is a 32 bit integer type, and is guaranteed to hold
+ exactly one Unicode codepoint. It can be assigned integer
+ literals, tested against, compared, and all the other usual
+ numeric types.
+
+ The various [u]intXX types hold, as expected, signed and
+ unsigned integers of the named sizes respectively.
+ Similarly, floats hold floating point types with the
+ indicated precision.
+
+ var x : int declare x as an int
+ var y : float32 declare y as a 32 bit float
+
+
+ 4.1.2. Composite types:
+
+ pointer
+ slice array
+
+ Pointers are, as expected, values that hold the address of
+ the pointed to value. They are declared by appending a '#'
+ to the type. Pointer arithmetic is not allowed. They are
+ declared by appending a '#' to the base type
+
+ Arrays are a group of N values, where N is part of the type.
+ Arrays of different sizes are incompatible. Arrays in
+ Myrddin, unlike many other languages, are passed by value.
+ They are declared by appending a '[SIZE]' to the base type.
+
+ Slices are similar to arrays in many contemporary languages.
+ They are reference types that store the length of their
+ contents. They are declared by appending a '[,]' to the base
+ type.
+
+ foo# type: pointer to foo
+ foo[123] type: array of 123 foo
+ foo[,] type: slice of foo
+
+ 4.1.3. Aggregate types:
+
+ tuple struct
+ union
+
+ Tuples are the traditional product type. They are declared
+ by putting the comma separated list of types within square
+ brackets.
+
+ Structs are aggregations of types with named members. They
+ are declared by putting the word 'struct' before a block of
+ declaration cores (ie, declarations without the storage type
+ specifier).
+
+ Unions are the traditional sum type. They consist of a tag
+ (a keyword prefixed with a '`' (backtick)) indicating their
+ current contents, and a type to hold. They are declared by
+ placing the keyword 'union' before a list of tag-type pairs.
+ They may also omit the type, in which case, the tag is
+ sufficient to determine which option was selected.
+
+ [int, int, char] a tuple of 2 ints and a char
+
+ struct a struct containing an int named
+ a : int 'a', and a char named 'b'.
+ b : char
+ ;;
+
+ union a union containing one of
+ `Thing int int or char. The values are not
+ `Other float32 named, but they are tagged.
+ ;;
+
+
+ 4.1.4. Generic types:
+
+ tyvar typaram
+ tyname
+
+ A tyname is a named type, similar to a typedef in C, however
+ it genuinely creates a new type, and not an alias. There are
+ no implicit conversions, but a tyname will inherit all
+ constraints of its underlying type.
+
+ A typaram is a parametric type. It is used in generics as
+ a placeholder for a type that will be substituted in later.
+ It is an identifier prefixed with '@'. These are only valid
+ within generic contexts, and may not appear elsewhere.
+
+ A tyvar is an internal implementation detail that currently
+ leaks in error messages out during type inference, and is a
+ major cause of confusing error messages. It should not be in
+ this manual, except that the current incarnation of the
+ compiler will make you aware of it. It looks like '@$type',
+ and is a variable that holds an incompletely inferred type.
+
+ type mine = int creates a tyname named
+ 'mine', equivalent to int.
+
+
+ @foo creates a type parameter
+ named '@foo'.
+ 4.2. Type Inference:
+
+ The myrddin type system is a system similar to the Hindley Milner
+ system, however, types are not implicitly generalized. Instead, type
+ schemes (type parameters, in Myrddin lingo) must be explicitly provided
+ in the declarations. For purposes of brevity, instead of specifying type
+ rules for every operator, we group operators which behave identically
+ from the type system perspective into a small set of classes. and define
+ the constraints that they require.
+
+ Type inference in Myrddin operates as a bottom up tree walk,
+ applying the type equations for the operator to its arguments.
+ It begins by initializing all leaf nodes with the most specific
+ known type for them as follows:
+
+ 4.6.1 Types for leaf nodes:
+
+ Variable Type
+ ----------------------
+ var foo $t
+
+ A type variable is the most specific type for a declaration
+ or function without any specified type
+
+ var foo : t t
+
+ If a type is specified, that type is taken for the
+ declaration.
+
+ "asdf" byte[:]
+
+ String literals are byte arrays.
+
+
+ 'a' char
+
+ Char literals are of type 'char'
+
+ void void
+
+ void is a literal value of type void.
+
+ true bool
+ false bool
+
+ true/false are boolean literals
+
+ 123 $t::(integral,numeric)
+
+ Integer literals get a fresh type variable of type with
+ the constraints for int-like types.
+
+ 123.1 $t::(floating,numeric)
+
+ Float literals get a fresh type variable of type with
+ the constraints for float-like types.
+
+ {a,b:t; } ($a,t -> $b)
+
+ Function literals get the most specific type that can
+ be determined by their signature.
+
+
+ num-binop:
+
+ + - * / %
+ += -= *= /= %
+
+ Number binops require the constraint 'numeric' for both the
+
+ num-unary:
+ - +
+ Number binops require the constraint 'numeric'.
+
+ int-binop:
+ | & ^ << >>
+ |= &= ^= <<= >>
+ int-unary:
+ ~ ++ --
+
+ bool-binop:
+ || && == !=
+ < <= > >=
+
+5. VALUES AND EXPRESSIONS:
+
+ 5.2. Literal Values
+
+ 5.1.1. Atomic Literals:
+
literal: strlit | chrlit | floatlit |
boollit | voidlit | intlit |
funclit | seqlit | tuplit
@@ -430,7 +652,7 @@
eg: true, false
- 4.3.2. Sequence and Tuple Literals:
+ 5.1.2. Sequence and Tuple Literals:
seqlit: "[" structelts | arrayelts "]"
tuplit: "(" tuplelts ")"
@@ -479,7 +701,7 @@
(1,), (1,'b',"three")
- 4.3.3. Function Literals:
+ 5.1.3. Function Literals:
funclit: "{" arglist "\n" blockbody "}"
arglist: (ident [":" type])*
@@ -520,7 +742,7 @@
}
- 4.3.4: Labels:
+ 5.1.4: Labels:
label: ":" ident
goto: "goto" ident
@@ -537,151 +759,9 @@
the ':' is not part of the label name.
- 4.4. Blocks:
-
- block: blockbody ";;"
- blockbody: (decl | stmt | tydef | "\n")*
- stmt: goto | break | continue | retexpr | label |
- ifstmt | forstmt | whilestmt | matchstmt
-
- Blocks are the basic building block of functionality in Myrddin. They
- are simply sequences of statements that are completed one after the
- other. They are generally terminated by a double semicolon (";;"),
- although they may be terminated by keywords if they are part of a more
- complex control flow construct.
-
- Any declarations within the block are scoped to within the block,
- and are not accessible outside of it. Their storage duration is
- limited to within the block, and any attempts to access the associated
- storage (via pointer, for example) is not valid.
-
- 4.5. Control Constructs:
-
- ifstmt: "if" cond "\n" blockbody
- ("elif" blockbody)*
- ["else" blockbody] ";;"
-
- forstmt: foriter | foreach
- foreach: "for" pattern "in" expr "\n" block
- foriter: "for" init "\n" cond "\n" step "\n" block
-
- whilestmt: "while" cond "\n" block
-
- matchstmt: "match" expr "\n" matchpat* ";;"
- matchpat: "|" pat ":" blockbody
-
-
- goto
-
- The control statements in Myrddin are similar to those in many other
- popular languages, and with the exception of 'match', there should
- be no surprises to a user of any of the Algol derived languages.
-
- Blocks are the "carriers of code" in Myrddin programs. They consist
- of series of expressions, typically ending with a ';;', although the
- function-level block ends at the function's '}', and in if
- statements, an 'elif' may terminate a block. They can contain any
- number of declarations, expressions, control constructs, and empty
- lines. Every control statement example below will (and, in fact,
- must) have a block attached to the control statement.
-
- If statements branch one way or the other depending on the truth
- value of their argument. The truth statement is separated from the
- block body
-
- if true
- std.put("The program always get here")
- elif elephant != mouse
- std.put("...eh.")
- else
- std.put("The program never gets here")
- ;;
-
- For statements come in two forms. There are the C style for loops
- which begin with an initializer, followed by a test condition,
- followed by an increment action. For statements run the initializer
- once before the loop is run, the test each on each iteration through
- the loop before the body, and the increment on each iteration after
- the body. If the loop is broken out of early (for example, by a goto),
- the final increment will not be run. The syntax is as follows:
-
- for init; test; increment
- blockbody()
- ;;
-
- The second form is the collection iteration form. This form allows
- for iterating over a collection of values contained within something
- which is iterable. Currently, only the built in sequences -- arrays
- and slices -- can be iterated, however, there is work going towards
- allowing user defined iterables.
-
- for pat in expr
- blockbody()
- ;;
-
- The pattern applied in the for loop is a full match statement style
- pattern match, and will filter any elements in the iteration
- expression which do not match the value.
-
- While loops are equivalent to for loops with empty initializers
- and increments. They run the test on every iteration of the loop,
- and exit only if it returns false.
-
- Match statements do pattern matching on values. They take as an
- argument a value of type 't', and match it against a list of other
- values of the same type. The patterns matched against can also contain
- free names, which will be bound to the sub-value matched against. The
- patterns are checked in order, and the first matching pattern has its
- body executed, after which no other patterns will be matched. This
- implies that if you have specific patterns mixed with by more general
- ones, the specific patterns must come first.
-
- Match patterns can be one of the following:
-
- - Union patterns
-
- These look like union constructors, only they define
- a value to match against.
-
- - Literal patterns
-
- Any literal value can be matched against.
-
- - Constant patterns
-
- Any constant value can be matched against.
-
- More types of pattern to match will be added over time.
-
- Match statements consist of the keyword 'match', followed by
- the expression to match against the patterns, followed by a
- newline. The body of the match statement consists of a list
- of pattern clauses. A patterned clause is a '|', followed by
- a pattern, followed by a ':', followed by a block body.
-
- An example of the syntax follows:
-
- const Val234 = `Val 234 /* set up a constant value */
- var v = `Val 123 /* set up variable to match */
- match v
- /* pattern clauses */
- | `Val 123:
- std.put("Matched literal union pat\n")
- | Val234:
- std.put("Matched const value pat\n")
- | `Val a:
- std.put("Matched pattern with capture\n")
- std.put("Captured value: a = {}\n", a)
- | a
- std.put("A top level bind matches anything.")
- | `Val 111
- std.put("Unreachable block.")
- ;;
-
-
- 4.6. Expressions:
+ 5.2. Expressions:
- 4.6.1. Summary and Precedence:
+ 5.2.1. Summary and Precedence:
expr: expr <binop> expr | prefixexpr | postfixexpr
postfixexpr: <prefixop> postfixexpr
@@ -698,9 +778,16 @@
summary of what they do is listed given. For the sake of clarity, 'x'
will stand in for any expression composed entirely of subexpressions
with higher precedence than the current current operator. 'e' will
- stand in for any expression. Unless marked otherwise, expressions are
- left associative.
+ stand in for any expression. Assignment is right associative. All
+ other expressions are left associative.
+ Arguments are evaluated in the order of associativity. That is,
+ if an operator is left associative, then the left hand side of
+ the operator will be evaluated before the right side. If the
+ operator is right associative, the opposite is true.
+
+ The specific semantics are covered in later parts of section 5.2.
+
Precedence 13:
x Atomic expression
literal Atomic expression
@@ -761,7 +848,7 @@
Precedence 2:
x || y Logical or
- Precedence 1:
+ Precedence 1: Assignment Operators
x = y Assign Right assoc
x += y Fused add/assign Right assoc
x -= y Fused sub/assign Right assoc
@@ -777,7 +864,7 @@
Precedence 0:
-> x Return expression
- 4.6.2. Atomic Expressions:
+ 5.2.2. Atomic Expressions:
atomicexpr: ident | gap | literal | "(" expr ")" |
"sizeof" "(" type ")" | castexpr
@@ -810,7 +897,7 @@
to persistently and manipulated by the programmer. An obvious
example of this would be a variable name, although
- 4.6.3. Cast Expressions:
+ 5.2.3. Cast Expressions:
Cast expressions convert a value from one type to another.
Casting proceeds according to the following rules:
@@ -901,241 +988,253 @@
- 4.6.4. Assignment:
+ 5.2.4. Assignments:
+
+ lval = rval, lval <op>= rval
- The assignment operators
+ The assignment operators, group from right to left. These are the
+ only operators that have right associativity. All of them require
+ the left operand to be an lvalue. The value of the right hand side
+ of the expression is stored on the left hand side after this
+ statement has executed.
- 4.6.5. Bitwise Expressions:
+ The fused assignment operators are equivalent to applying the
+ arithmetic or bitwise operator to the lhs and rhs of the
+ expression before storing into the lhs.
- 4.6.6: Arithmetic Expressons:
+ 5.2.5. Logical Or:
+
+ expr || expr
- 4.6.7: Postfix Expressiosn:
+ The `||` operator returns true if the left hand side evaluates to
+ true. Otherwise it returns the result of evaluating the lhs. It is
+ guaranteed if the rhs is true, the lhs will not be evaluated.
- 4.6.8: Prefix Expressions
+ 5.2.6. Logical And:
+ expr && expr
-5. TYPES:
+ The `&&` operator returns false if the left hand side evaluates to
+ false. Otherwise it returns the result of evaluating the lhs. It
+ is guaranteed if the rhs is true, the lhs will not be evaluated.
- 5.1. Data Types:
+ 5.2.7. Comparisons:
+
+ expr == expr, expr != expr, expr > expr,
+ expr >= expr, expr < expr, expr <= expr
- The language defines a number of built in primitive types. These
- are not keywords, and in fact live in a separate namespace from
- the variable names. Yes, this does mean that you could, if you want,
- define a variable named 'int'.
+ 5.2.8. Union
+
+ `Name rval:
- There are no implicit conversions within the language. All types
- must be explicitly cast if you want to convert, and the casts must
- be of compatible types, as will be described later.
+ 5.2.9. Bitwise:
- 5.1.1. Primitive types:
+ expr | expr, expr ^ expr, expr & expr
- void
- bool char
- int8 uint8
- int16 uint16
- int32 uint32
- int64 uint64
- int uint
- long ulong
- float32 float64
+ 5.2.10. Addition:
+
+ expr + expr, expr - expr:
- 'void' is a type and a value although for the sake of
- genericity, you can assign between void types, return values
- of void, and so on. This allows generics to not have to
- somehow work around void being a toxic type. The void value is
- named `void`.
+
+ 5.2.11. Multiplication:
+
+ expr * expr
- It is interesting to note that these types are not keywords,
- but are instead merely predefined identifiers in the type
- namespace.
+ 5.2.12. Division:
+
+ expr / expr, expr % expr:
- bool is a type that can only hold true and false. It can be
- assigned, tested for equality, and used in the various boolean
- operators.
+ 5.2.13. Shift:
- char is a 32 bit integer type, and is guaranteed to hold
- exactly one Unicode codepoint. It can be assigned integer
- literals, tested against, compared, and all the other usual
- numeric types.
+ expr >> expr, expr << expr
- The various [u]intXX types hold, as expected, signed and
- unsigned integers of the named sizes respectively.
- Similarly, floats hold floating point types with the
- indicated precision.
+ 5.2.14. Preincrement:
- var x : int declare x as an int
- var y : float32 declare y as a 32 bit float
+ ++expr, --expr
+ 5.2.25: Address:
- 5.1.2. Composite types:
+ &expr
- pointer
- slice array
+ 5.2.16: Dereference:
- Pointers are, as expected, values that hold the address of
- the pointed to value. They are declared by appending a '#'
- to the type. Pointer arithmetic is not allowed. They are
- declared by appending a '#' to the base type
+ &expr
- Arrays are a group of N values, where N is part of the type.
- Arrays of different sizes are incompatible. Arrays in
- Myrddin, unlike many other languages, are passed by value.
- They are declared by appending a '[SIZE]' to the base type.
+ 5.2.17: Sign Operators:
- Slices are similar to arrays in many contemporary languages.
- They are reference types that store the length of their
- contents. They are declared by appending a '[,]' to the base
- type.
+ -expr, +expr
- foo# type: pointer to foo
- foo[123] type: array of 123 foo
- foo[,] type: slice of foo
+ 5.2.18: Logical Negation:
- 5.1.3. Aggregate types:
+ !expr
- tuple struct
- union
+ 5.2.19: Member Lookup:
- Tuples are the traditional product type. They are declared
- by putting the comma separated list of types within square
- brackets.
+ expr.name
- Structs are aggregations of types with named members. They
- are declared by putting the word 'struct' before a block of
- declaration cores (ie, declarations without the storage type
- specifier).
+ 5.2.20: Postincrement:
- Unions are the traditional sum type. They consist of a tag
- (a keyword prefixed with a '`' (backtick)) indicating their
- current contents, and a type to hold. They are declared by
- placing the keyword 'union' before a list of tag-type pairs.
- They may also omit the type, in which case, the tag is
- sufficient to determine which option was selected.
+ expr++, expr--
- [int, int, char] a tuple of 2 ints and a char
+ 5.2.21: Dereference:
- struct a struct containing an int named
- a : int 'a', and a char named 'b'.
- b : char
- ;;
+ expr#
- union a union containing one of
- `Thing int int or char. The values are not
- `Other float32 named, but they are tagged.
- ;;
+ 5.2.22: Index:
+ expr[expr]
- 5.1.4. Generic types:
+ 5.2.23: Slice:
- tyvar typaram
- tyname
+ expr[expr:expr]
- A tyname is a named type, similar to a typedef in C, however
- it genuinely creates a new type, and not an alias. There are
- no implicit conversions, but a tyname will inherit all
- constraints of its underlying type.
+ 5.2.24: Call:
- A typaram is a parametric type. It is used in generics as
- a placeholder for a type that will be substituted in later.
- It is an identifier prefixed with '@'. These are only valid
- within generic contexts, and may not appear elsewhere.
+ expr(expr, expr, ...)
- A tyvar is an internal implementation detail that currently
- leaks in error messages out during type inference, and is a
- major cause of confusing error messages. It should not be in
- this manual, except that the current incarnation of the
- compiler will make you aware of it. It looks like '@$type',
- and is a variable that holds an incompletely inferred type.
- type mine = int creates a tyname named
- 'mine', equivalent to int.
+ 5.3. Blocks:
+ block: blockbody ";;"
+ blockbody: (decl | stmt | tydef | "\n")*
+ stmt: goto | break | continue | retexpr | label |
+ ifstmt | forstmt | whilestmt | matchstmt
- @foo creates a type parameter
- named '@foo'.
- 5.2. Type Inference:
+ Blocks are the basic building block of functionality in Myrddin. They
+ are simply sequences of statements that are completed one after the
+ other. They are generally terminated by a double semicolon (";;"),
+ although they may be terminated by keywords if they are part of a more
+ complex control flow construct.
- The myrddin type system is a system similar to the Hindley Milner
- system, however, types are not implicitly generalized. Instead, type
- schemes (type parameters, in Myrddin lingo) must be explicitly provided
- in the declarations. For purposes of brevity, instead of specifying type
- rules for every operator, we group operators which behave identically
- from the type system perspective into a small set of classes. and define
- the constraints that they require.
+ Any declarations within the block are scoped to within the block,
+ and are not accessible outside of it. Their storage duration is
+ limited to within the block, and any attempts to access the associated
+ storage (via pointer, for example) is not valid.
- Type inference in Myrddin operates as a bottom up tree walk,
- applying the type equations for the operator to its arguments.
- It begins by initializing all leaf nodes with the most specific
- known type for them as follows:
+ 5.5. Control Constructs:
- 4.6.1 Types for leaf nodes:
+ ifstmt: "if" cond "\n" blockbody
+ ("elif" blockbody)*
+ ["else" blockbody] ";;"
- Variable Type
- ----------------------
- var foo $t
+ forstmt: foriter | foreach
+ foreach: "for" pattern "in" expr "\n" block
+ foriter: "for" init "\n" cond "\n" step "\n" block
- A type variable is the most specific type for a declaration
- or function without any specified type
+ whilestmt: "while" cond "\n" block
- var foo : t t
+ matchstmt: "match" expr "\n" matchpat* ";;"
+ matchpat: "|" pat ":" blockbody
- If a type is specified, that type is taken for the
- declaration.
+
+ goto
- "asdf" byte[:]
+ The control statements in Myrddin are similar to those in many other
+ popular languages, and with the exception of 'match', there should
+ be no surprises to a user of any of the Algol derived languages.
- String literals are byte arrays.
+ Blocks are the "carriers of code" in Myrddin programs. They consist
+ of series of expressions, typically ending with a ';;', although the
+ function-level block ends at the function's '}', and in if
+ statements, an 'elif' may terminate a block. They can contain any
+ number of declarations, expressions, control constructs, and empty
+ lines. Every control statement example below will (and, in fact,
+ must) have a block attached to the control statement.
+ If statements branch one way or the other depending on the truth
+ value of their argument. The truth statement is separated from the
+ block body
- 'a' char
+ if true
+ std.put("The program always get here")
+ elif elephant != mouse
+ std.put("...eh.")
+ else
+ std.put("The program never gets here")
+ ;;
- Char literals are of type 'char'
+ For statements come in two forms. There are the C style for loops
+ which begin with an initializer, followed by a test condition,
+ followed by an increment action. For statements run the initializer
+ once before the loop is run, the test each on each iteration through
+ the loop before the body, and the increment on each iteration after
+ the body. If the loop is broken out of early (for example, by a goto),
+ the final increment will not be run. The syntax is as follows:
- void void
+ for init; test; increment
+ blockbody()
+ ;;
- void is a literal value of type void.
+ The second form is the collection iteration form. This form allows
+ for iterating over a collection of values contained within something
+ which is iterable. Currently, only the built in sequences -- arrays
+ and slices -- can be iterated, however, there is work going towards
+ allowing user defined iterables.
- true bool
- false bool
+ for pat in expr
+ blockbody()
+ ;;
- true/false are boolean literals
+ The pattern applied in the for loop is a full match statement style
+ pattern match, and will filter any elements in the iteration
+ expression which do not match the value.
- 123 $t::(integral,numeric)
+ While loops are equivalent to for loops with empty initializers
+ and increments. They run the test on every iteration of the loop,
+ and exit only if it returns false.
- Integer literals get a fresh type variable of type with
- the constraints for int-like types.
+ Match statements do pattern matching on values. They take as an
+ argument a value of type 't', and match it against a list of other
+ values of the same type. The patterns matched against can also contain
+ free names, which will be bound to the sub-value matched against. The
+ patterns are checked in order, and the first matching pattern has its
+ body executed, after which no other patterns will be matched. This
+ implies that if you have specific patterns mixed with by more general
+ ones, the specific patterns must come first.
- 123.1 $t::(floating,numeric)
+ Match patterns can be one of the following:
- Float literals get a fresh type variable of type with
- the constraints for float-like types.
+ - Union patterns
- {a,b:t; } ($a,t -> $b)
+ These look like union constructors, only they define
+ a value to match against.
- Function literals get the most specific type that can
- be determined by their signature.
+ - Literal patterns
+ Any literal value can be matched against.
- num-binop:
+ - Constant patterns
- + - * / %
- += -= *= /= %
+ Any constant value can be matched against.
- Number binops require the constraint 'numeric' for both the
+ More types of pattern to match will be added over time.
- num-unary:
- - +
- Number binops require the constraint 'numeric'.
+ Match statements consist of the keyword 'match', followed by
+ the expression to match against the patterns, followed by a
+ newline. The body of the match statement consists of a list
+ of pattern clauses. A patterned clause is a '|', followed by
+ a pattern, followed by a ':', followed by a block body.
- int-binop:
- | & ^ << >>
- |= &= ^= <<= >>
- int-unary:
- ~ ++ --
+ An example of the syntax follows:
- bool-binop:
- || && == !=
- < <= > >=
+ const Val234 = `Val 234 /* set up a constant value */
+ var v = `Val 123 /* set up variable to match */
+ match v
+ /* pattern clauses */
+ | `Val 123:
+ std.put("Matched literal union pat\n")
+ | Val234:
+ std.put("Matched const value pat\n")
+ | `Val a:
+ std.put("Matched pattern with capture\n")
+ std.put("Captured value: a = {}\n", a)
+ | a
+ std.put("A top level bind matches anything.")
+ | `Val 111
+ std.put("Unreachable block.")
+ ;;
+
+
6. GRAMMAR: