// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Format file for printing AST nodes.

ast "ast";
token "token";

// ----------------------------------------------------------------------------
// Basic types, support rules

array =
	*;

ptr =
	*;

string =
	"%s";

char =
	"%c";

bytes =
	{*};

empty =
	;

exists =
	*:empty;

ast.Expr =
	*;

ast.Stmt =
	*;

ast.Decl =
	*;

// ----------------------------------------------------------------------------
// Tokens and comments

token.Token =
	@:string;

ast.Comment =
	// TODO this doesn't indent properly after //-style comments because
	//      the '\n'-char is printed as part of the comment - need to
	//      address this
	Text:string [Text:isMultiLineComment "\n"];

ast.Comments =
	{*};


// ----------------------------------------------------------------------------
// Expressions & Types

ast.Field =
	[Names:exists {Names / ", "} " "] Type;

ast.BadExpr =
	"BAD EXPR";

ast.Ident =
	Value;

ast.Ellipsis =
	"...";

ast.IntLit =
	Value:string;

ast.FloatLit =
	Value:string;

ast.CharLit =
	Value:string;

ast.StringLit =
	Value:string;

ast.StringList =
	{Strings / "\n"};

ast.FuncLit =
	Type " " Body @:clearOptSemi;  // no optional ; after a func literal body

ast.CompositeLit =
	Type "{" {Elts / ", "} "}";

ast.ParenExpr =
	"(" X ")";

ast.SelectorExpr =
	X "." Sel;

ast.IndexExpr =
	X "[" Index [":" End] "]";

ast.TypeAssertExpr =
	X ".(" Type ")";

ast.CallExpr =
	Fun "(" {Args / ", "} ")";

ast.StarExpr =
	"*" X;

ast.UnaryExpr =
	Op X;

ast.BinaryExpr =
	X " " Op " " Y;

ast.KeyValueExpr =
	Key ": " Value;

ast.ArrayType =
	"[" [Len] "]" Elt;

ast.StructType =
	"struct"
	[Lbrace:isValidPos " {"]
	[	Fields:exists
		( "\t" >> "\n"
			{Fields / ";\n"}
		) "\n"
	]
	[Rbrace:isValidPos "}"];

signature =
	"(" {Params / ", "} ")" [Results:exists " (" {Results / ", "} ")"];

funcSignature =
	*:signature;

ast.FuncType =
	[Position:isValidPos "func"] @:signature;

ast.InterfaceType =
	"interface"
	[Lbrace:isValidPos " {"]
	[	Methods:exists
		( "\t" >> "\n"
			{Methods / ";\n"}
		) "\n"
	]
	[Rbrace:isValidPos "}"];

ast.MapType =
	"map[" Key "]" Value;

ast.ChanType =
	(	Dir:isSend Dir:isRecv
		"chan "
	|	Dir:isSend
		"chan <- "
	|	"<-chan "
	)
	Value;


// ----------------------------------------------------------------------------
// Statements

ast.BadStmt =
	"BAD STMT";

ast.DeclStmt =
	Decl;

ast.EmptyStmt =
	;

ast.LabeledStmt =
	Label ":\t" Stmt;

ast.ExprStmt =
	X;

ast.IncDecStmt =
	X Tok;

ast.AssignStmt =
	{Lhs / ", "} " " Tok " " {Rhs / ", "};

ast.GoStmt =
	"go " Call;

ast.DeferStmt =
	"defer " Call;

ast.ReturnStmt =
	"return" {" " Results / ","};

ast.BranchStmt =
	Tok [" " Label];

stmtList =
	{@ / @:optSemi "\n"};

blockStmt =  // like ast.BlockStmt but w/o indentation
	"{"
	[List:exists
	"\n"
		List:stmtList
	"\n"
	]
	"}" @:setOptSemi;

blockStmtPtr =
	*:blockStmt;

ast.BlockStmt =
	"{"
	[List:exists
	( "\t" >> "\n"
		List:stmtList
	) "\n"
	]
	"}" @:setOptSemi;

ast.IfStmt =
	"if " [Init "; "] [Cond " "] Body [" else " Else];

ast.CaseClause =
	(	Values:exists "case " {Values / ", "}
	|	"default"
	)
	":"
	[Body:exists
	( "\t" >> "\n"
		Body:stmtList
	)
	];

ast.SwitchStmt =
	"switch " [Init "; "] [Tag " "]
	Body:blockStmtPtr;

ast.TypeCaseClause =
	(	Type:exists "case " Type
	|	"default"
	)
	":"
	[Body:exists
	( "\t" >> "\n"
		Body:stmtList
	)
	];

ast.TypeSwitchStmt =
	"switch " Assign " "
	Body:blockStmtPtr;

ast.CommClause =
	(	"case " [Lhs " " Tok " "] Rhs
	|	"default"
	)
	":"
	[Body:exists
	( "\t" >> "\n"
		Body:stmtList
	)
	];

ast.SelectStmt =
	"select "
	Body:blockStmtPtr;

ast.ForStmt =
	"for "
	[	(Init:exists | Post:exists)
		[Init] "; " [Cond] "; " [Post " "]
	|	Cond " "
	]
	Body;

ast.RangeStmt =
	"for " Key [", " Value] " " Tok " range " X
	" "
	Body;


// ----------------------------------------------------------------------------
// Declarations

ast.Spec =
	*;

ast.ImportSpec =
	Doc
	[Name] "\t" {Path};

ast.ValueSpec =
	{Names / ", "} [" " Type] [Values:exists " = " {Values / ", "}];

ast.TypeSpec =
	Name " "  // TODO using "\t" instead of " " screws up struct field alignment
	Type;

ast.BadDecl =
	"BAD DECL";

ast.GenDecl =
	Doc
	Tok " "
	(	Lparen:isValidPos "("
		[Specs:exists
		( "\t" >> "\n"
			{Specs / ";\n"}
		) "\n"
		]
		")" @:setOptSemi
	|	{Specs / ";\n"}
	);

ast.FuncDecl =
	"func " ["(" Recv ") "] Name Type:funcSignature
	[" " Body]
	"\n";


// ----------------------------------------------------------------------------
// Program

ast.Program =
	Doc
	"package " Name "\n\n"
	{Decls / "\n\n"};
