Second version of Pinn. Archived.
Pinn is a statically typed, imperative language for computers.
- Syntax taken mostly from Go.
- Carefully chosen ideas from Swift.
swift run pinn
. plays an optimal game of tic-tac-toe.
int
. Always a signed 64-bit.bool
. Standard,true
orfalse
.string
. Immutable Unicode."string"
"this is a double quote:\""
decimal
. Decimal.self
. Only allowed in a pointer--points to itself.
- Scalar is a single element.
map
. A map produces a unique value for each key. The key is always a string. Like Go, a missing string returns the zero value for an element.array
. An array is a group of elements with a constant size. It is only produced in a variable declaration. It never shares its storage, even if sliced.slice
. A slice is an array that can be grown. It can be produced by variable declaration, slicing, and ranges. It shares a reference on assignment.tuple
. A tuple is a group of elements with a constant size and possibly different types.pointer
. A pointer is a tuple that can be nil. The zero value is nil.
<ID>
- Consists of the letters
a
throughz
,A
throughZ
,_
, and digits0
-9
. May not start with a digit.
- Consists of the letters
<FLOAT>
<STRING>
- Starts and ends with a
"
. In a string, use\"
to refer to a"
character. Use\\
to refer to a\
character. Otherwise,\
is illegal.
- Starts and ends with a
<NIL>
nil
. The null value.
- From highest precedence
<ID> "[" (<expr>? (: | @) <expr>? | <expr>) "]"
Index expression. With a:
or@
, slices theID
."[" <expr_list> "]"
Array literal- All expressions must be of the same type.
..."[" <expr_list> "]"
Slice literal"{" <STRING> ":" <expr> { "," <STRING> ":" <expr> } }
Map literal- Expressions must be of the same type.
+ - !
Unary<op> := + - * / %
Binary (simplified precedence), string concatenation== != < <= > >=
Comparison, string comparisons&& ||
Short-circuit AND and OR<ID> "(" <expr_list>? ")"
Call function"(" <expr> ")"
Parenthesize expression"(" <expr_list> ")"
Tuple literal*"(" <expr-list> ")"
Pointer literal<expr> ( ":" | "@" ) <expr>
Range generator. Both generate one through ten:1:11 1@10
.<expr> "?" <expr> ":" <expr>
Ternary conditional. Firstexpr
is evaluated. If true, resolve to secondexpr
. If false, resolve to thirdexpr
.<ID> <FLOAT> <INT> <BOOL> <STRING>
Tokens representing symbols, decimals, integers, booleans, and strings.
( <function> | <statement>)+ <EOF>
func <ID> "(" (<fVarDecl> { , <fVarDecl> } )? ")" <kind>? <block>
fVarDecl := <ID> ...? <kind>
A function calls a piece of code, assigning each variable in the parameter list to each the value in the calling list. The ...
is allowed for the last parameter. It is expanded into an array carrying zero or more calling expressions.
"{" { <statement> } "}"
- A block is a series of zero or more statements.
<L-expr> [ "[" <expr> "]" ] = <expr>
Simple set- The
expr
to the right of the=
is assigned to theL-expr
.
- The
<L-expr> <op> = <expr>
Compound setx += 2
is equivalent tox = x + 2
<ID> ( "[" <expr> "]" )*
- An
L-expr
can be assigned to.
- An
<expr> { , <expr> }
- Used in various productions. Note that at least one
expr
is required.
- Used in various productions. Note that at least one
int | bool | string | decimal | self
- Primitive types.
"[" (map | <expr>)? "]" <kind>
map
declares a map. Anexpr
declares an array. Empty brackets indicates a slice.
"(" <kind> ( , <kind>)* ")"
- Declares a tuple type.
*"(" kind> ( , <kind> )* ")"
, declares a pointer type.
<var_decl> ;
while <expr> <block>
- Evaluate
expr
. If true, executeblock
and repeat this line. If false, go on.
- Evaluate
repeat <block> while <expr> ;
- Execute
block
. Evaluateexpr
. If true, repeat this line.
- Execute
return [<expr>] ;
- Return from function. The
expr
must match the return type, or empty if there is none. If global, there is no return type.
- Return from function. The
if <expr> <statement> [else <statement>]
- Evaluate
expr
. If true, exectue firststatement
. If false, either move on or execute secondstatement
.
- Evaluate
guard <expr> else <block>
- Evaluate
expr
. If false, executeblock
. The block must relinquish control, with areturn
,break
, orcontinue
.
- Evaluate
for <ID> [, <ID>] = range <expr> <block>
- If
id
is alone, it becomes the values of theexpr
. If a secondid
is present, it becomes the values and the firstid
are the keys. Theexpr
must evaluate to an array, slice, or map. The block iterates through the elements. Note thatid1
andid2
must be predeclared.
- If
"{" { <statement> } "}"
Block statementmatch <expr> "{" { when <expr_list> : { <statement> } } [ default : { <statement> } ] "}"
expr
is compared to eachwhen
clause in order.( break | continue | fallthrough ) ;
break
stops execution of the current loop. It then exits the loop.continue
stops execution of the current loop. It then continues execution of the next loop.fallthrough
falls through to the nextwhen
in amatch
block.
<expr> ;
Evalutateexpr
. The result is thrown away.;
Empty statement.<ID> | <FLOAT> | <INT> | <BOOL> | <STRING> | <NIL>
Literals.
var <ID> <kind> ;
- Declare
id
ofkind
type.
- Declare
<ID> ":=" <expr> ;
- Short declaration. The
id
is set to the type and value of theexpr
.
- Short declaration. The
The grammar is clean of implementation language and is written in ANTLR as a visitor to the tree.
exit()
- Exit the program. No parameters allowed.
len(<expr>)
- Return the length of the expression. For compound types, return the count of data. For strings, return the length of the string.
print(<expr_list>)
- Print each
expr
in the list, with a space between them.
- Print each
println(<expr_list>)
print()
with a line feed at the end.
stringValue(<expr>)
- Returns a string representation of the
expr
--the same printed withprint
.
- Returns a string representation of the
delete(<expr>, <expr>)
- Delete key(second
expr
) from map(firstexpr
). Returnbool
indicating whether the key existed.
- Delete key(second
key(<expr>, <expr>)
- Determine if key(second
expr
) exists in map(firstexpr
).
- Determine if key(second
| alternation
() grouping
[] option (0 or 1 times)
{} repetition (0 to n times)
:= assign to token or production on left
literal literal characters as typed--"literal"
<prod> grammar rule specified elsewhere
<TOKEN> lexer rule specified elsewhere
"{" "[" ... notation elements as literals
EMPTY := 0;
TIE := 3;
PLAYER_A := 1;
PLAYER_B := 2;
SIZE := 3;
var rHashMap [map]int;
func rHash(board [SIZE][SIZE]int, player int) int {
rt := 0;
var v int;
var x int;
var y int;
for x = range 0:SIZE {
for y = range 0:SIZE {
v = board[x][y];
rt *= SIZE;
rt += v;
}
}
rt *= 2;
rt += player - 1;
return rt;
}
func printBoard (board [SIZE][SIZE]int) {
var y int;
var x int;
for y = range 0:SIZE {
print (board[0][y]);
for x = range 1:SIZE {
print (" ");
print (board[x][y]);
}
println();
}
println ("---");
}
func full (board [SIZE][SIZE]int) bool {
var x int;
var y int;
for x = range 0:SIZE {
for y = range 0:SIZE {
if board[x][y] == EMPTY {
return false;
}
}
}
return true;
}
func line (board [SIZE][SIZE]int, x int, y int, dx int, dy int) int {
comp := board[x][y];
if comp == EMPTY {
return TIE;
}
while x + dx < SIZE && y + dy < SIZE {
x += dx;
y += dy;
if board[x][y] != comp {
return TIE;
}
}
return comp;
}
func winner (board [SIZE][SIZE]int) int {
var i int;
var rt int;
for i = range 0:SIZE {
rt = line(board, i, 0, 0, 1);
if rt != TIE
return rt;
rt = line(board, 0, i, 1, 0);
if rt != TIE
return rt;
}
rt = line(board, 0, 0, 1, 1);
if rt != TIE
return rt;
rt = line(board, 0, SIZE - 1, 1, -1);
return rt;
}
func opposite (x int) int { return x == PLAYER_A ? PLAYER_B : PLAYER_A; }
func minimax (player int, board [SIZE][SIZE]int) int
{
var result int;
best := opposite(player);
result = winner(board);
if result != TIE {
return result;
}
if full(board) {
return TIE;
}
var x int;
var y int;
var strRHash string;
for x = range 0:SIZE {
for y = range 0:SIZE {
if board[x][y] == EMPTY {
board[x][y] = player;
strRHash = stringValue(rHash(board, player));
if rHashMap[strRHash] > 0 {
result = rHashMap[strRHash];
} else {
result = minimax(opposite(player), board);
rHashMap[strRHash] = result;
}
if result == player {
return player;
}
if result == TIE {
best = TIE;
}
board[x][y] = EMPTY;
}
}
}
return best;
}
func main() {
var board [SIZE][SIZE]int;
println("Initial board");
printBoard(board);
result := minimax (PLAYER_A, board);
resultString := "";
match result {
when TIE:
resultString = "Tie.";
when PLAYER_A:
resultString = "Player A.";
when PLAYER_B:
resultString = "Player B.";
}
println ("Winner:", resultString);
}
main();