vrtc / pjass (public) (License: BSD) (since 2023-08-03) (hash sha1)
pjass is a free Jass scripting language parser. This repository is a fork of lep/pjass. The goal is to add Debian packaging. As of the time of this writing, it works for current stable, that is Debian 12 (bookworm).

/grammar.y (6b3bbeb49255a1bf6c841c38be4a77e340e60dc6) (26021 bytes) (mode 100644) (type blob)

// Jass2 parser for bison/yacc
// by Rudi Cilibrasi
// Sun Jun  8 00:51:53 CEST 2003
// thanks to Jeff Pang for the handy documentation that this was based
// on at http://jass.sourceforge.net
%{

#include <stdio.h>
#include <string.h>
#include "misc.h"

#define YYMAXDEPTH 100000

int yyerrorline (int errorlevel, int line, char *s)
{
  if (showerrorlevel[errorlevel]) {
    haderrors++;
    printf ("%s:%d: %s\n", curfile, line, s);
    return 0;
  }
  else
    ignorederrors++;
}

int yyerrorex (int errorlevel, char *s)
{
  if (showerrorlevel[errorlevel]) {
    haderrors++;
    printf ("%s:%d: %s\n", curfile, lineno, s);
    return 0;
  }
  else
    ignorederrors++;
}

int yyerror (char *s)  /* Called by yyparse on error */
{
  yyerrorex(0, s);
}

int main(int argc, char **argv)
{
  init(argc, argv);
  if (1)  {
		doparse(argc, argv);
  }
  else {
    for (;;) {
      int result = yylex();
      if (result == 0) break;
      printf("Got result %d, %s\n", result, yytext);
    }
  }
  if (!haderrors && didparse) {
		printf("Parse successful: %8d lines: %s\n", totlines, "<total>");
    if (ignorederrors)
	printf("%d errors ignored", ignorederrors);

    return 0;
  }
  else {
		if (haderrors)
			printf("Parse failed: %d error%s total\n", haderrors, haderrors == 1 ? "" : "s");
		else
			printf("Parse failed\n");
		if (ignorederrors)
		  printf("%d errors ignored", ignorederrors);
    return 1;
	}
}

#define YYSTYPE union node

%}

%token IF
%token THEN
%token TYPE
%token EXTENDS
%token HANDLE
%token NEWLINE
%token GLOBALS
%token ENDGLOBALS
%token CONSTANT
%token NATIVE
%token TAKES
%token RETURNS
%token FUNCTION
%token ENDFUNCTION
%token LOCAL
%token ARRAY
%token SET
%token CALL
%token ELSE
%token ELSEIF
%token ENDIF
%token LOOP
%token EXITWHEN
%token RETURN
%token DEBUG
%token ENDLOOP
%token NOT
%token TNULL
%token TTRUE
%token TFALSE
%token CODE
%token STRING
%token INTEGER
%token REAL
%token BOOLEAN
%token NOTHING
%token ID
%token COMMA
%token AND
%token OR
%token EQUALS
%token TIMES
%token DIV
%token PLUS
%token MINUS
%token LPAREN
%token RPAREN
%token LBRACKET
%token RBRACKET
%token LESS
%token GREATER
%token LEQ
%token GEQ
%token EQCOMP
%token NEQ
%token STRINGLIT
%token INTLIT
%token REALLIT
%token UNITTYPEINT

%right EQUALS
%left AND
%left OR
%left LESS GREATER EQCOMP NEQ LEQ GEQ
%left NOT
%left MINUS PLUS
%left TIMES DIV

%%

program: topscopes globdefs topscopes funcdefns
;

topscopes: topscope
       | topscopes topscope
;

topscope: typedefs  
       | funcdecls
;

funcdefns: /* empty */
       | funcdefns funcdefn
;

globdefs: /* empty */
         | GLOBALS NEWLINE vardecls ENDGLOBALS endglobalsmarker
         | GLOBALS vardecls ENDGLOBALS endglobalsmarker {yyerrorline(0, lineno - 1, "Missing linebreak before global declaration");}
;

endglobalsmarker: /* empty */  {afterendglobals = 1;}
;

vardecls: /* empty */
         | vd vardecls
;

vd:      NEWLINE
       | vardecl
;

funcdecls: /* empty */
         | fd funcdecls
;

fd:      NEWLINE
       | funcdecl
;

typedefs:  /* empty */
         | td typedefs
;

td:      NEWLINE
       | typedef
;

// Returns a typenode
expr: intexpr      { $$.ty = gInteger; }
      | realexpr   { $$.ty = gReal; }
      | stringexpr { $$.ty = gString; }
      | boolexpr   { $$.ty = gBoolean; }
      | FUNCTION rid { struct funcdecl *fd = lookup(&functions, $2.str);
                       if (fd == NULL) {
                           char ebuf[1024];
                           sprintf(ebuf, "Undefined function %s", $2.str);
                           getsuggestions($2.str, ebuf, 1, &functions);
                           yyerrorex(3, ebuf);
                           $$.ty = gCode;
                       } else {
                           if (fd->p->head != NULL) {
                               char ebuf[1024];
                               sprintf(ebuf, "Function %s must not take any arguments when used as code", $2.str);
                               yyerrorex(3, ebuf);
                           }
                           $$.ty = gCode;
                       }
                     }
      | TNULL { $$.ty = gNull; }
      | expr LEQ expr { checkcomparison($1.ty, $3.ty); $$.ty = gBoolean; }
      | expr GEQ expr { checkcomparison($1.ty, $3.ty); $$.ty = gBoolean; }
      | expr LESS expr { checkcomparison($1.ty, $3.ty); $$.ty = gBoolean; }
      | expr GREATER expr { checkcomparison($1.ty, $3.ty); $$.ty = gBoolean; }
      | expr EQCOMP expr { checkeqtest($1.ty, $3.ty); $$.ty = gBoolean; }
      | expr NEQ expr { checkeqtest($1.ty, $3.ty); $$.ty = gBoolean; }
      | expr AND expr { canconvert($1.ty, gBoolean, 0); canconvert($3.ty, gBoolean, 0); $$.ty = gBoolean; }
      | expr OR expr { canconvert($1.ty, gBoolean, 0); canconvert($3.ty, gBoolean, 0); $$.ty = gBoolean; }
      | NOT expr { canconvert($2.ty, gBoolean, 0); $$.ty = gBoolean; }
      | expr TIMES expr { $$.ty = binop($1.ty, $3.ty); }
      | expr DIV expr { $$.ty = binop($1.ty, $3.ty); }
      | expr MINUS expr { $$.ty = binop($1.ty, $3.ty); }
      | expr PLUS expr { 
                         if ($1.ty == gString && $3.ty == gString)
                           $$.ty = gString;
                         else
                           $$.ty = binop($1.ty, $3.ty); }
      | MINUS expr { isnumeric($2.ty); $$.ty = $2.ty; }
      | LPAREN expr RPAREN { $$.ty = $2.ty; }
      | funccall { $$.ty = $1.ty; }
      | rid LBRACKET expr RBRACKET {
          const struct typeandname *tan = getVariable($1.str);
          if (tan->ty != gAny) {
            if (!tan->isarray) {
              char ebuf[1024];
              sprintf(ebuf, "%s not an array", $1.str);
              yyerrorex(3, ebuf);
            }
            else {
              canconvert($3.ty, gInteger, 0);
            }
          }
          $$.ty = tan->ty;
       }
      | rid {
          const struct typeandname *tan = getVariable($1.str);
          if (tan->lineno == lineno && tan->fn == fno) {
            char ebuf[1024];
            sprintf(ebuf, "Use of variable %s before its declaration", $1.str);
            yyerrorex(3, ebuf);
          } else if (islinebreak && tan->lineno == lineno - 1 && tan->fn == fno) {
            char ebuf[1024];
            sprintf(ebuf, "Use of variable %s before its declaration", $1.str);
            yyerrorline(3, lineno - 1, ebuf);
          } else if (tan->isarray) {
            char ebuf[1024];
            sprintf(ebuf, "Index missing for array variable %s", $1.str);
            yyerrorex(3, ebuf);
          }
          if(infunction && lookup(curtab, $1.str) && !lookup(&initialized, $1.str) ){
            char ebuf[1024];
            sprintf(ebuf, "Variable %s is uninitialized", $1.str);
            //yyerrorex(3, ebuf);
            yyerrorline(3, lineno - 1, ebuf);
          }
          $$.ty = tan->ty;
       }
      | expr EQUALS expr {yyerrorex(0, "Single = in expression, should probably be =="); checkeqtest($1.ty, $3.ty); $$.ty = gBoolean;}
      | LPAREN expr {yyerrorex(0, "Mssing ')'"); $$.ty = $2.ty;}
      
      // incomplete expressions 
      | expr LEQ { checkcomparisonsimple($1.ty); yyerrorex(3, "Missing expression for comparison"); $$.ty = gBoolean; }
      | expr GEQ { checkcomparisonsimple($1.ty); yyerrorex(3, "Missing expression for comparison"); $$.ty = gBoolean; }
      | expr LESS { checkcomparisonsimple($1.ty); yyerrorex(3, "Missing expression for comparison"); $$.ty = gBoolean; }
      | expr GREATER { checkcomparisonsimple($1.ty); yyerrorex(3, "Missing expression for comparison"); $$.ty = gBoolean; }
      | expr EQCOMP { yyerrorex(3, "Missing expression for comparison"); $$.ty = gBoolean; }
      | expr NEQ { yyerrorex(3, "Missing expression for comparison"); $$.ty = gBoolean; }
      | expr AND { canconvert($1.ty, gBoolean, 0); yyerrorex(3, "Missing expression for logical and"); $$.ty = gBoolean; }
      | expr OR { canconvert($1.ty, gBoolean, 0); yyerrorex(3, "Missing expression for logical or"); $$.ty = gBoolean; }
      | NOT { yyerrorex(3, "Missing expression for logical negation"); $$.ty = gBoolean; }
;

funccall: rid LPAREN exprlistcompl RPAREN {
          struct funcdecl *fd = lookup(&functions, $1.str);
          if (fd == NULL) {
            char ebuf[1024];
            sprintf(ebuf, "Undeclared function %s", $1.str);
            getsuggestions($1.str, ebuf, 1, &functions);
            yyerrorex(3, ebuf);
            $$.ty = gAny;
          } else {
            if (inconstant && !(fd->isconst)) {
              char ebuf[1024];
              sprintf(ebuf, "Call to non-constant function %s in constant function", $1.str);
              yyerrorex(3, ebuf);
            }
            if (fd == fCurrent && fCurrent)
          		yyerrorex(3, "Recursive function calls are not permitted in local declarations");
            checkParameters(fd->p, $3.pl);
            $$.ty = fd->ret;
          }
       }
       |  rid LPAREN exprlistcompl NEWLINE {
          yyerrorex(0, "Missing ')'");
          struct funcdecl *fd = lookup(&functions, $1.str);
          if (fd == NULL) {
            char ebuf[1024];
            sprintf(ebuf, "Undeclared function %s", $1.str);
            getsuggestions($1.str, ebuf, 1, &functions);
            yyerrorex(3, ebuf);
            $$.ty = gAny;
          } else if (inconstant && !(fd->isconst)) {
            char ebuf[1024];
            sprintf(ebuf, "Call to non-constant function %s in constant function", $1.str);
            yyerrorex(3, ebuf);
            $$.ty = gAny;
          } else {
          	if (fd == fCurrent && fCurrent)
          		yyerrorex(3, "Recursive function calls are not permitted in local declarations");
            checkParameters(fd->p, $3.pl);
            $$.ty = fd->ret;
          }
       }
;

exprlistcompl: /* empty */ { $$.pl = newparamlist(); }
       | exprlist { $$.pl = $1.pl; }
;

exprlist: expr         { $$.pl = newparamlist(); addParam($$.pl, newtypeandname($1.ty, "")); }
       |  expr COMMA exprlist { $$.pl = $3.pl; addParam($$.pl, newtypeandname($1.ty, "")); }
;


stringexpr: STRINGLIT { $$.ty = gString; }
;

realexpr: REALLIT { $$.ty = gReal; }
;

boolexpr: boollit { $$.ty = gBoolean; }
;

boollit: TTRUE
       | TFALSE
;

intexpr:   INTLIT { $$.ty = gInteger; }
         | UNITTYPEINT { $$.ty = gInteger; }
;


funcdecl: nativefuncdecl { $$.fd = $1.fd; }
         | CONSTANT nativefuncdecl { $$.fd = $2.fd; }
         | funcdefncore { $$.fd = $1.fd; }
;

nativefuncdecl: NATIVE rid TAKES optparam_list RETURNS opttype
{
  if (lookup(&locals, $2.str) || lookup(&params, $2.str) || lookup(&globals, $2.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as variable", $2.str);
    yyerrorex(3, buf);
  } else if (lookup(&types, $2.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as type", $2.str);
    yyerrorex(3, buf);
  }
  $$.fd = newfuncdecl(); 
  $$.fd->name = strdup($2.str);
  $$.fd->p = $4.pl;
  $$.fd->ret = $6.ty;
  //printf("***** %s = %s\n", $2.str, $$.fd->ret->typename);
  $$.fd->isconst = isconstant;
  put(&functions, $$.fd->name, $$.fd);
  //showfuncdecl($$.fd);
}
;

funcdefn: NEWLINE
       | funcdefncore
       | statement { yyerrorex(0, "Statement outside of function"); }
;

funcdefncore: funcbegin localblock codeblock funcend {
            if(retval != gNothing) {
                //if ($3.ty == gAny || $3.ty == gNone)
                if(!getTypeTag($3.ty))
                    yyerrorline(1, lineno - 1, "Missing return");
                else if (returnbug)
                    canconvertreturn($3.ty, retval, -1);
            }
        }
       | funcbegin localblock codeblock {yyerrorex(0, "Missing endfunction"); clear(&params); clear(&locals); clear(&initialized); curtab = &globals;}
;

funcend: ENDFUNCTION { clear(&params); clear(&locals); clear(&initialized); curtab = &globals; inblock = 0; inconstant = 0; infunction = 0; }
;

returnorreturns: RETURNS
               | RETURN {yyerrorex(3,"Expected \"returns\" instead of \"return\"");}
;

funcbegin: FUNCTION rid TAKES optparam_list returnorreturns opttype {
  if (lookup(&locals, $2.str) || lookup(&params, $2.str) || lookup(&globals, $2.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as variable", $2.str);
    yyerrorex(3, buf);
  } else if (lookup(&types, $2.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as type", $2.str);
    yyerrorex(3, buf);
  }
  inconstant = 0;
  infunction = 1;
  curtab = &locals;
  $$.fd = newfuncdecl(); 
  $$.fd->name = strdup($2.str);
  $$.fd->p = $4.pl;
  $$.fd->ret = $6.ty;
  $$.fd->isconst = 0;
  put(&functions, $$.fd->name, $$.fd);
  fCurrent = lookup(&functions, $2.str);
  struct typeandname *tan = $4.pl->head;
  for (;tan; tan=tan->next) {
    tan->lineno = lineno;
    tan->fn = fno;
    put(&params, strdup(tan->name), newtypeandname(tan->ty, tan->name));
    if (lookup(&functions, tan->name)) {
      char buf[1024];
      sprintf(buf, "%s already defined as function", tan->name);
      yyerrorex(3, buf);
    } else if (lookup(&types, tan->name)) {
      char buf[1024];
      sprintf(buf, "%s already defined as type", tan->name);
      yyerrorex(3, buf);
    }
  }
  retval = $$.fd->ret;
  inblock = 1;
  inloop = 0;
  //showfuncdecl($$.fd);
}
       | CONSTANT FUNCTION rid TAKES optparam_list returnorreturns opttype {
  if (lookup(&locals, $3.str) || lookup(&params, $3.str) || lookup(&globals, $3.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as variable", $3.str);
    yyerrorex(3, buf);
  } else if (lookup(&types, $3.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as type", $3.str);
    yyerrorex(3, buf);
  }
  inconstant = 1;
  curtab = &locals;
  $$.fd = newfuncdecl(); 
  $$.fd->name = strdup($3.str);
  $$.fd->p = $5.pl;
  $$.fd->ret = $7.ty;
  $$.fd->isconst = 1;
  put(&functions, $$.fd->name, $$.fd);
  struct typeandname *tan = $5.pl->head;
  for (;tan; tan=tan->next) {
    tan->lineno = lineno;
    tan->fn = fno;
    put(&params, strdup(tan->name), newtypeandname(tan->ty, tan->name));
    if (lookup(&functions, tan->name)) {
      char buf[1024];
      sprintf(buf, "%s already defined as function", tan->name);
      yyerrorex(3, buf);
    } else if (lookup(&types, tan->name)) {
      char buf[1024];
      sprintf(buf, "%s already defined as type", tan->name);
      yyerrorex(3, buf);
    }
  }
  retval = $$.fd->ret;
  inblock = 1;
  inloop = 0;
  //showfuncdecl($$.fd);
}
;

codeblock: /* empty */ { $$.ty = gEmpty; }
       | statement codeblock {
            if(typeeq($2.ty, gEmpty))
                $$.ty = $1.ty;
            else
                $$.ty = $2.ty;
        }
;

statement:  NEWLINE { $$.ty = gNone; }
       | CALL funccall NEWLINE{ $$.ty = gNone;}
       /*1    2    3     4        5        6        7      8      9 */
       | IF expr THEN NEWLINE codeblock elsifseq elseseq ENDIF NEWLINE {
            canconvert($2.ty, gBoolean, -1);
            $$.ty = combinetype($5.ty, combinetype($6.ty, $7.ty));
       }
       | SET rid EQUALS expr NEWLINE { if (getVariable($2.str)->isarray) {
                                         char ebuf[1024];
                                         sprintf(ebuf, "Index missing for array variable %s", $2.str);
                                         yyerrorline(3, lineno - 1,  ebuf);
                                       }
                                       canconvert($4.ty, getVariable($2.str)->ty, -1);
                                       $$.ty = gNone;
                                       if (getVariable($2.str)->isconst) {
                                         char ebuf[1024];
                                         sprintf(ebuf, "Cannot assign to constant %s", $2.str);
                                         yyerrorline(3, lineno - 1, ebuf);
                                       }
                                       if (inconstant)
                                         validateGlobalAssignment($2.str);
                                       if(!lookup(&initialized, $2.str)){
                                         put(&initialized, $2.str, (void*)1);
                                       }
				    }
       | SET rid LBRACKET expr RBRACKET EQUALS expr NEWLINE{ 
           const struct typeandname *tan = getVariable($2.str);
           if (tan->ty != gAny) {
             canconvert($4.ty, gInteger, -1);
             $$.ty = gNone;
             if (!tan->isarray) {
               char ebuf[1024];
               sprintf(ebuf, "%s is not an array", $2.str);
               yyerrorline(3, lineno - 1, ebuf);
             }
             canconvert($7.ty, tan->ty, -1);
             if (inconstant)
               validateGlobalAssignment($2.str);
             }
           }
       | loopstart NEWLINE codeblock loopend NEWLINE {$$.ty = $3.ty;}
       | loopstart NEWLINE codeblock {$$.ty = $3.ty; yyerrorex(0, "Missing endloop");}
       | EXITWHEN expr NEWLINE { canconvert($2.ty, gBoolean, -1); if (!inloop) yyerrorline(0, lineno - 1, "Exitwhen outside of loop"); $$.ty = gNone;}
       | RETURN expr NEWLINE {
            $$.ty = mkretty($2.ty, 1);
            if(retval == gNothing)
                yyerrorline(1, lineno - 1, "Cannot return value from function that returns nothing");
            else if (!returnbug)
                canconvertreturn($2.ty, retval, 0);
         }
       | RETURN NEWLINE {
            if (retval != gNothing)
                yyerrorline(1, lineno - 1, "Return nothing in function that should return value");
                $$.ty = mkretty(gAny, 1);
            }
       | DEBUG statement {$$.ty = gNone;}
       /*1    2   3      4        5         6        7 */
       | IF expr THEN NEWLINE codeblock elsifseq elseseq {
            canconvert($2.ty, gBoolean, -1);
            $$.ty = combinetype($5.ty, combinetype($6.ty, $7.ty));
            yyerrorex(0, "Missing endif");
        }
       | IF expr NEWLINE {
            canconvert($2.ty, gBoolean, -1);
            $$.ty = gAny;
            yyerrorex(0, "Missing then or non valid expression");
        }
       | SET funccall NEWLINE{$$.ty = gNone; yyerrorline(0, lineno - 1, "Call expected instead of set");}
       | lvardecl {yyerrorex(0, "Local declaration after first statement");}
       | error {$$.ty = gNone; }
;

loopstart: LOOP {inloop++;}
;

loopend: ENDLOOP {inloop--;}
;

elseseq: /* empty */ { $$.ty = gNone; }
        | ELSE NEWLINE codeblock {
            $$.ty = $3.ty;
        }
;

elsifseq: /* empty */ { $$.ty = mkretty(gEmpty, 1); }
        /*   1     2    3    4         5         6 */
        | ELSEIF expr THEN NEWLINE codeblock elsifseq {
            canconvert($2.ty, gBoolean, -1);
            
            if(typeeq($6.ty, gEmpty)){
                if(typeeq($5.ty, gEmpty)){
                    $$.ty = mkretty(gNone, 0);
                }else{
                    $$.ty = $5.ty;
                }
            }else{
                $$.ty = combinetype($5.ty, $6.ty);
            }
        }
;

optparam_list: param_list { $$.pl = $1.pl; }
               | NOTHING { $$.pl = newparamlist(); }
;

opttype: NOTHING { $$.ty = gNothing; }
         | type { $$.ty = $1.ty; }
;

param_list: typeandname { $$.pl = newparamlist(); addParam($$.pl, $1.tan); }
          | typeandname COMMA param_list { addParam($3.pl, $1.tan); $$.pl = $3.pl; }
;

rid: ID
{ $$.str = strdup(yytext); }
;

vartypedecl: type rid {
  if (lookup(&functions, $2.str)) {
    char buf[1024];
    sprintf(buf, "Symbol %s already defined as function", $2.str);
    yyerrorex(3, buf);
  } else if (lookup(&types, $2.str)) {
    char buf[1024];
    sprintf(buf, "Symbol %s already defined as type", $2.str);
    yyerrorex(3, buf);
  }
  struct typeandname *tan = newtypeandname($1.ty, $2.str);
  $$.str = $2.str;
  struct typeandname *existing = lookup(&locals, $2.str);
  if (!existing) {
    existing = lookup(&params, $2.str);
    if (!existing)
      existing = lookup(&globals, $2.str);
  }
  if (existing) {
    tan->lineno = existing->lineno;
    tan->fn = existing->fn;
  } else {
    tan->lineno = lineno;
    tan->fn = fno;
  }
  put(curtab, $2.str, tan);  }
       | CONSTANT type rid {
  if (afterendglobals) {
    yyerrorex(3, "Local constants are not allowed");
  }
  if (lookup(&functions, $3.str)) {
    char buf[1024];
    sprintf(buf, "Symbol %s already defined as function", $3.str);
    yyerrorex(3, buf);
  } else if (lookup(&types, $3.str)) {
    char buf[1024];
    sprintf(buf, "Symbol %s already defined as type", $3.str);
    yyerrorex(3, buf);
  }
  struct typeandname *tan = newtypeandname($2.ty, $3.str);
  $$.str = $3.str;
  tan->isconst = 1;
  struct typeandname *existing = lookup(&locals, $3.str);
  if (!existing) {
    existing = lookup(&params, $3.str);
    if (!existing)
      existing = lookup(&globals, $3.str);
  }
  if (existing) {
    tan->lineno = existing->lineno;
    tan->fn = existing->fn;
  } else {
    tan->lineno = lineno;
    tan->fn = fno;
  }
  put(curtab, $3.str, tan); }
       | type ARRAY rid {
  if (lookup(&functions, $3.str)) {
    char buf[1024];
    sprintf(buf, "Symbol %s already defined as function", $3.str);
    yyerrorex(3, buf);
  } else if (lookup(&types, $3.str)) {
    char buf[1024];
    sprintf(buf, "Symbol %s already defined as type", $3.str);
    yyerrorex(3, buf);
  }
  if (getPrimitiveAncestor($1.ty) == gCode)
    yyerrorex(3, "Code arrays are not allowed");
  struct typeandname *tan = newtypeandname($1.ty, $3.str);
  $$.str = $3.str;
  tan->isarray = 1;
  struct typeandname *existing = lookup(&locals, $3.str);
  if (!existing) {
    char buf[1024];
    existing = lookup(&params, $3.str);
    if (afterendglobals && existing) {
    	sprintf(buf, "Symbol %s already defined as function parameter", $3.str);
    	yyerrorex(3, buf);
    }
    if (!existing) {
      existing = lookup(&globals, $3.str);
      if (afterendglobals && existing) {
      	sprintf(buf, "Symbol %s already defined as global variable", $3.str);
      	yyerrorex(3, buf);
      }
    }
  }
  if (existing) {
    tan->lineno = existing->lineno;
    tan->fn = existing->fn;
  } else {
    tan->lineno = lineno;
    tan->fn = fno;
  }
  put(curtab, $3.str, tan); }
  
 // using "type" as variable name 
      | type TYPE {
  yyerrorex(3, "Invalid variable name \"type\"");
  struct typeandname *tan = newtypeandname($1.ty, "type");
  $$.str = "type";
  struct typeandname *existing = lookup(&locals, "type");
  if (!existing) {
    existing = lookup(&params, "type");
    if (!existing)
      existing = lookup(&globals, "type");
  }
  if (existing) {
    tan->lineno = existing->lineno;
    tan->fn = existing->fn;
  } else {
    tan->lineno = lineno;
    tan->fn = fno;
  }
  put(curtab, "type", tan);  }
       | CONSTANT type TYPE {
  if (afterendglobals) {
    yyerrorex(3, "Local constants are not allowed");
  }
  yyerrorex(3, "Invalid variable name \"type\"");
  struct typeandname *tan = newtypeandname($2.ty, "type");
  $$.str = "type";
  tan->isconst = 1;
  struct typeandname *existing = lookup(&locals, "type");
  if (!existing) {
    existing = lookup(&params, "type");
    if (!existing)
      existing = lookup(&globals, "type");
  }
  if (existing) {
    tan->lineno = existing->lineno;
    tan->fn = existing->fn;
  } else {
    tan->lineno = lineno;
    tan->fn = fno;
  }
  put(curtab, "type", tan); }
       | type ARRAY TYPE {
  yyerrorex(3, "Invalid variable name \"type\"");
  struct typeandname *tan = newtypeandname($1.ty, "type");
  $$.str = "type";
  tan->isarray = 1;
  struct typeandname *existing = lookup(&locals, "type");
  if (!existing) {
    existing = lookup(&params, "type");
    if (!existing)
      existing = lookup(&globals, "type");
  }
  if (existing) {
    tan->lineno = existing->lineno;
    tan->fn = existing->fn;
  } else {
    tan->lineno = lineno;
    tan->fn = fno;
  }
  put(curtab, "type", tan); }
;

localblock: endlocalsmarker
        | lvardecl localblock
        | NEWLINE localblock
;

endlocalsmarker: /* empty */ { fCurrent = 0; }
;

lvardecl: LOCAL vardecl { }
        | CONSTANT LOCAL vardecl { yyerrorex(3, "Local variables can not be declared constant"); }
        | typedef { yyerrorex(3,"Types can not be extended inside functions"); }
;

vardecl: vartypedecl NEWLINE {
             const struct typeandname *tan = getVariable($1.str);
             if (tan->isconst) {
               yyerrorline(3, lineno - 1, "Constants must be initialized");
             }
             $$.ty = gNothing;
           }
        |  vartypedecl EQUALS expr NEWLINE {
             const struct typeandname *tan = getVariable($1.str);
             if (tan->isarray) {
               yyerrorex(3, "Arrays cannot be directly initialized");
             }
             if(infunction && !lookup(&initialized, tan->name)){
               put(&initialized, tan->name, (void*)1);
             }
             canconvert($3.ty, tan->ty, -1);
             $$.ty = gNothing;
           }
        | error
;

typedef: TYPE rid EXTENDS type {
  if (lookup(&types, $2.str)) {
     char buf[1024];
     sprintf(buf, "Multiply defined type %s", $2.str);
     yyerrorex(3, buf);
  } else if (lookup(&functions, $2.str)) {
    char buf[1024];
    sprintf(buf, "%s already defined as function", $2.str);
    yyerrorex(3, buf);
  }
  else
    put(&types, $2.str, newtypenode($2.str, $4.ty));
}
;

typeandname: type rid { $$.tan = newtypeandname($1.ty, $2.str); }
;
  
type: primtype { $$.ty = $1.ty; }
  | rid {
   if (lookup(&types, $1.str) == NULL) {
     char buf[1024];
     sprintf(buf, "Undefined type %s", $1.str);
     getsuggestions($1.str, buf, 1, &types);
     yyerrorex(3, buf);
     $$.ty = gAny;
   }
   else
     $$.ty = lookup(&types, $1.str);
}
;

primtype: HANDLE  { $$.ty = lookup(&types, yytext); }
 | INTEGER        { $$.ty = lookup(&types, yytext); }
 | REAL           { $$.ty = lookup(&types, yytext); }
 | BOOLEAN        { $$.ty = lookup(&types, yytext); }
 | STRING         { $$.ty = lookup(&types, yytext); }
 | CODE           { $$.ty = lookup(&types, yytext); }
;



Mode Type Size Ref File
100644 blob 730 d833c0c2ee6b5aa377f4f30d3e678d0fcac569ee Makefile
100644 blob 439 04f5f4c52ea3055fd29ddd78ff091326ee1b4177 Makefile.Whatever
100644 blob 564 2da1e0352a6d6e6f5a956f60424e1ee391131e98 Makefile.cygwin
100644 blob 26021 6b3bbeb49255a1bf6c841c38be4a77e340e60dc6 grammar.y
100644 blob 19641 0dfd52afe6720b9b117559d08e1751cb856d9f44 misc.c
100644 blob 3369 9603d7a0f63502ef428407ef7df18764f1322c4c misc.h
100644 blob 1302 13d65e05aca85cddd2f539b14d98cfbf1c622752 notes.txt
100644 blob 744 c8efdbf329d07cba0543a81c2aa696108bb97080 pjass.res
100644 blob 764 270df8583e78c80f366a8ab431fab673d89446d5 readme.txt
100644 blob 6387 22ec4a6f71e40e38ca426fc3db474636a1990061 token.l
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/pjass

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/pjass

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/pjass

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main