/******************************** -*- C -*- ****************************
 *
 *	GNU Smalltalk language grammar definition
 *
 *	$Revision: 1.7.5$
 *	$Date: 2000/05/28 16:56:52$
 *	$Author: pb$
 *
 ***********************************************************************/

/***********************************************************************
 *
 * Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
 * Written by Steve Byrne.
 *
 * This file is part of GNU Smalltalk.
 *
 * GNU Smalltalk is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later 
 * version.
 * 
 * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
 *
 ***********************************************************************/

%{
#include "gst.h"
#include "sym.h"
#include "tree.h"
#include "dict.h"
#include "comp.h"
#include "lex.h"
#include "lib.h"
#include <stdio.h>
#if defined(STDC_HEADERS)
#include <string.h>
#endif

#define YYPRINT(file, type, value)   yyprint (file, type, &value)

#define YYDEBUG 1

%}

/* make the parser re-entrant */
%pure_parser

/* a Smalltalk grammar without shift/reduce conflicts is possible, but
   less readable indeed */
%expect 4

/* definition of YYSTYPE */
%union{
  char		cval;
  double	fval;
  long		ival;
  char		*sval;
  ByteObject	boval;
  TreeNode	node;
}

/* single definite characters */     
%token BANG COLON UPARROW DOT ASSIGN SHARP SEMICOLON
%token OPEN_PAREN CLOSE_PAREN OPEN_BRACKET CLOSE_BRACKET
%token PRIMITIVE_START INTERNAL_TOKEN

/* larger lexical items */
%token <sval> IDENTIFIER KEYWORD STRING_LITERAL SYMBOL_KEYWORD BINOP
              VERTICAL_BAR 
%token <ival> INTEGER_LITERAL BYTE_LITERAL
%token <fval> FLOATING_LITERAL
%token <cval> CHAR_LITERAL
%token <boval> LARGE_INTEGER_LITERAL

%type <node> method message_pattern variable_name keyword_variable_list
	temporaries variable_names statements non_empty_statements expression
	assigns primary number small_number symbol_constant symbol
	character_constant string array_constant array
	array_constant_list byte_array byte_array_constant_list
	block opt_block_variables 
	block_variable_list unary_expression binary_expression
	keyword_expression keyword_binary_object_description_list
	cascaded_message_expression semi_message_list
	message_elt simple_expression literal message_expression
	array_constant_elt unary_object_description
	binary_object_description
%type <sval> unary_selector keyword binary_selector
%type <ival> primitive
%%

program:
	internal_marker method			{ compileMethod($2); }
	| file_in
	| /* empty */
	;

internal_marker:
	INTERNAL_TOKEN				{ clearMethodStartPos(); }
	;

file_in:
	doit_and_method_list
	| file_in doit_and_method_list
	;

doit_and_method_list:
	doit
	| doit internal_marker BANG				{ skipCompilation = false; }
	| doit internal_marker method_list BANG			{ skipCompilation = false; }

	;

doit:
	BANG
	| temporaries statements BANG
					{ if ($2 && !hadError) {
					    executeStatements($1, $2,
							    quietExecution); 
                                          }
					  hadError = false;
                                        }
	| error BANG			{ hadError = false;
					  yyerrok; }
	;

method_list:
	method BANG 			{ if (!hadError) {
					    if (skipCompilation) {
					      freeTree($1);
					    } else {
					      compileMethod($1);
					      clearMethodStartPos();
					    }
					  } else {
					    hadError = false;
					  }
					}
        | method_list method BANG	{ if (!hadError) {
					    if (skipCompilation) {
					      freeTree($2);
					    } else {
					      compileMethod($2);
					      clearMethodStartPos();
					    }
					  } else {
					    hadError = false;
					  }
					}
	;
     
method:
	message_pattern temporaries primitive statements
					{ $$ = makeMethod($1, $2, $3, $4); }
	;


message_pattern:
	unary_selector			{ $$ = makeUnaryExpr(nil, $1); }
	| binary_selector variable_name	{ $$ = makeBinaryExpr(nil, $1, $2); }
	| keyword_variable_list		{ $$ = makeKeywordExpr(nil, $1); }
	| error 			{ errorf("Invalid message pattern");
					  hadError = true;
					  /* 14-Jul-95 00:29:27 removed this yyerrok */
					  /* yyerrok; */
					  $$ = nil; }
	;

unary_selector:
	IDENTIFIER
	;

binary_selector:
	BINOP
	| VERTICAL_BAR
	;

variable_name:
	IDENTIFIER			{ $$ = makeVariable($1); }
	;

keyword_variable_list:
	keyword variable_name		{ $$ = makeKeywordList($1, $2); }
	| keyword_variable_list keyword variable_name
					{ addNode($1, makeKeywordList($2, $3));
					  $$ = $1; }
	;

keyword:
	KEYWORD
	;

primitive:
	/* empty */						{ $$ = 0; }
	| PRIMITIVE_START INTEGER_LITERAL primitive_end		{ $$ = $2; }
	| PRIMITIVE_START BYTE_LITERAL primitive_end		{ $$ = $2; }
	;

primitive_end: BINOP			{ if (strcmp($1, ">") != 0) YYFAIL; }

temporaries:
	/* empty */			{ $$ = nil; }
	| VERTICAL_BAR VERTICAL_BAR	{ $$ = nil; }
	| VERTICAL_BAR variable_names VERTICAL_BAR
					{ $$ = $2; }
	;

variable_names:
	variable_name			{ $$ = makeVariableList($1); }
	| variable_names variable_name	{ addNode($1, makeVariableList($2));
					  $$ = $1; }
	;

statements:
	/* empty */			{ $$ = nil; }
	| non_empty_statements
	;

non_empty_statements:
	UPARROW expression optional_dot	
				{ $$ = makeStatementList(makeReturn($2),
				       			nil); }
	| expression		{ $$ = makeStatementList($1, nil); }
	| expression DOT statements
			 	/* I don't know if I like this production */
				{ $$ = makeStatementList($1, $3); }
	| error	BANG		{ $$ = nil;
				  yyerrok;
				  hadError = true;
				}
	| error DOT statements  { $$ = $3;
				  yyerrok;
				  hadError = true;
				}
	;
	
optional_dot:
	/* empty */
 	| DOT
	;

expression:
	simple_expression
	| assigns simple_expression	{ $$ = makeAssign($1, $2); }
	;

assigns:
	variable_name ASSIGN		{ $$ = makeVariableList($1); }
	| assigns variable_name ASSIGN
					{ addNode($1, makeVariableList($2));
					  $$ = $1; }
	;

simple_expression:
	primary
	| message_expression
	| cascaded_message_expression
	;

primary:
	variable_name
	| literal
	| block				
	| OPEN_PAREN expression CLOSE_PAREN { $$ = $2; }
	;

literal:
	number
	| small_number
	| symbol_constant
	| character_constant
	| string
	| array_constant
	;

number:
	INTEGER_LITERAL			{ $$ = makeIntConstant($1); }
	| FLOATING_LITERAL		{ $$ = makeFloatConstant($1); }
	| LARGE_INTEGER_LITERAL		{ $$ = makeByteObjectConstant($1); }
	;

small_number:
	BYTE_LITERAL			{ $$ = makeIntConstant($1); }

symbol_constant:
	SHARP symbol			{ $$ = makeSymbolConstant($2); }
	| SHARP STRING_LITERAL		{ $$ = makeSymbolConstant(internIdent($2)); }
	;

symbol:
	IDENTIFIER			{ $$ = internIdent($1); }
	| binary_selector		{ $$ = internBinOP($1); }
	| SYMBOL_KEYWORD		{ $$ = internIdent($1); }
	| KEYWORD			{ $$ = internIdent($1); }
	;


character_constant:
	CHAR_LITERAL			{ $$ = makeCharConstant($1); }
	;

string:
	STRING_LITERAL			{ $$ = makeStringConstant($1); }
	;

array_constant:
	SHARP array			{ $$ = $2; }
	| SHARP byte_array		{ $$ = $2; }
	;

array:
	OPEN_PAREN CLOSE_PAREN		{ $$ = makeArrayConstant(nil); }
	| OPEN_PAREN array_constant_list CLOSE_PAREN
					{ $$ = makeArrayConstant($2); }
	;

array_constant_list:
	array_constant_elt		{ $$ = makeArrayElt($1); }
	| array_constant_list array_constant_elt
			      		{ addNode($1, makeArrayElt($2));
					  $$ = $1; }
	;

array_constant_elt:
	array
	| byte_array
	| literal
	| symbol			{ $$ = makeSymbolConstant($1); }
	;

byte_array:
	OPEN_BRACKET CLOSE_BRACKET	{ $$ = makeByteArrayConstant(nil); }
	| OPEN_BRACKET byte_array_constant_list CLOSE_BRACKET
					{ $$ = makeByteArrayConstant($2); }
	;

byte_array_constant_list:
	small_number			{ $$ = makeArrayElt($1); }
	| byte_array_constant_list small_number
			      		{ addNode($1, makeArrayElt($2));
					  $$ = $1; }
	;

block:
	OPEN_BRACKET opt_block_variables temporaries statements CLOSE_BRACKET
					{ $$ = makeBlock($2, $3, $4); }
	;

opt_block_variables:
	/* empty */			{ $$ = nil; }
	| block_variable_list binary_selector
					{ if ($2[0] != '|') {
					    YYFAIL;
					  } else if ($2[1] == '\0') {	/* | */
					  } else if ($2[1] == '|') {   /* || */
					    unreadChar('|');
					  }
					}
	;

/* syntax for blocks with temporaries is just args and vertical bar (if
 * any followed by a standard temporaries declarations */

block_variable_list:
	COLON variable_name		{ $$ = makeVariableList($2); }
	| block_variable_list COLON variable_name
				    	{ addNode($1, makeVariableList($3));
					  $$ = $1; }
	;

message_expression:
	unary_expression
	| binary_expression
	| keyword_expression
	;

unary_expression:
	unary_object_description unary_selector { $$ = makeUnaryExpr($1, $2); }
	;

unary_object_description:
	primary
	| unary_expression
	;

binary_expression:
	binary_object_description binary_selector unary_object_description
					{ $$ = makeBinaryExpr($1, $2, $3); }
	;

binary_object_description:
	unary_object_description
	| binary_expression
	;

keyword_expression:
	binary_object_description keyword_binary_object_description_list
					{ $$ = makeKeywordExpr($1, $2); }
 	;

keyword_binary_object_description_list:
	keyword binary_object_description
					{ $$ = makeKeywordList($1, $2); }
	| keyword_binary_object_description_list keyword
	  binary_object_description	{ addNode($1, makeKeywordList($2, $3));
					  $$ = $1; }
	;

cascaded_message_expression:
	message_expression semi_message_list
			   		{ $$ = makeCascadedMessage($1, $2); }
	;

semi_message_list:
	SEMICOLON message_elt		{ $$ = makeMessageList($2); }
	| semi_message_list SEMICOLON message_elt
					{ addNode($1, makeMessageList($3));
					  $$ = $1; }
	;

message_elt:
	unary_selector			{ $$ = makeUnaryExpr(nil, $1); }
	| binary_selector unary_object_description
					{ $$ = makeBinaryExpr(nil, $1, $2); }
	| keyword_binary_object_description_list
					{ $$ = makeKeywordExpr(nil, $1); }
	;


%%
/*     
 * ADDITIONAL C CODE
 */

