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).

/misc.c (f4721e475becad42db92127a2b256711c7515933) (14458 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
// Released under the BSD license
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>

#include "misc.h"

int pjass_flags;

int fno;
int lineno;
int haderrors;
int ignorederrors;
int totlines;
int islinebreak;
int isconstant;
int inconstant;
int infunction;
int inblock;
int strict;
int returnbug;
int fnannotations;
int annotations;
int didparse;
int inloop;
int afterendglobals;
int *showerrorlevel;

struct hashtable functions;
struct hashtable globals;
struct hashtable locals;
struct hashtable params;
struct hashtable types;
struct hashtable initialized;

struct hashtable *curtab;

const struct typenode *retval;
const char *curfile;
struct typenode *gInteger, *gReal, *gBoolean, *gString, *gCode, *gHandle, *gNothing, *gNull, *gAny, *gNone, *gEmpty;
struct typenode *gCodeReturnsNoBoolean, *gCodeReturnsBoolean;
struct typenode *gEmpty;
struct funcdecl *fCurrent;
struct funcdecl *fFilter, *fCondition;


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

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

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

void put(struct hashtable *h, const char *name, void *val){
    if( !ht_put(h, name, val) ){
        char ebuf[1024];
        snprintf(ebuf, 1024, "Symbol %s multiply defined", name);
        yyerrorline(3, islinebreak ? lineno - 1 : lineno, ebuf);
    }
}


#define min(a, b) (((a) < (b)) ? (a) : (b))

int abs(int i){
    if(i < 0)
        return -i;
    return i;
}

static int editdistance(const char *s, const char *t, int cutoff){
    if(!strcmp(s, t)) return 0;

    int a = strlen(s);
    int b = strlen(t);

    if(a==0) return b;
    if(b==0) return a;

    if(abs(a-b) > cutoff){
        return cutoff + 1;
    }

    int *v[3];
    for(int i = 0; i != 3; i++) {
        v[i] = malloc(sizeof(int) * (size_t)(b+1));
    }

    for(int i = 0; i != b+1; i++){
        v[0][i] = i;
    }

    int pcur;
    int ppcur;
    int cur = 1;
    for(int i = 0; i != a; i++){
        cur = (cur+1) % 3;
        pcur = cur -1;
        if(pcur < 0) pcur += 3;
        ppcur = pcur -1;
        if(ppcur < 0) ppcur += 3;

        v[cur][0] = i + 1;

        int minDistance = INT_MAX;

        int j;
        for(j = 0; j != b; j++){
            int cost = (s[i] == t[j]) ? 0 : 1;

            v[cur][j+1] = min(v[cur][j] + 1, min(v[pcur][j+1] + 1, v[pcur][j] + cost));

            if(i > 0 && j > 0 && s[i] == t[j-1] && s[i-1] == t[j]){
                v[cur][j+1] = min(v[cur][j+1], v[ppcur][j-1] + cost);
            }

            if(v[cur][j+1] < minDistance){
                minDistance = v[cur][j+1];
            }
        }

        if(minDistance > cutoff){
            return cutoff + 1;
        }
    }
    pcur = cur -1;
    if(pcur < 0) pcur += 3;
    int d = v[pcur][b];
    for(int i = 0; i != 3; i++)
        free(v[i]);
    return d;
}

void getsuggestions(const char *name, char *buff, size_t buffsize, int nTables, ...)
{
    va_list ap;

    int len = strlen(name);
    int cutoff = (int)((len+2)/4.0);
    int count = 0;

    struct {int distance; const char *name;} suggestions[3];
    for(int i = 0; i != 3; i++){
        suggestions[i].distance = INT_MAX;
        suggestions[i].name = NULL;
    }

    va_start(ap, nTables);

    for(int i = 0; i != nTables; i++){
        struct hashtable *ht = va_arg(ap, struct hashtable*);

        for(size_t x = 0; x != ht->size; x++){
            if(ht->bucket[x].name){
                const struct typeandname *tan = ht->bucket[x].val;
                if(typeeq(tan->ty, gAny)){
                    continue;
                }

                int dist = editdistance(ht->bucket[x].name, name, cutoff);
                if(dist <= cutoff){
                    count++;
                    for(int j = 0; j != 3; j++){
                        if(suggestions[j].distance > dist){
                            if(i == 0){
                                suggestions[2] = suggestions[1];
                                suggestions[1] = suggestions[0];
                            }else if(i == 1){
                                suggestions[2] = suggestions[1];
                            }
                            suggestions[j].distance = dist;
                            suggestions[j].name = ht->bucket[x].name;

                            break;
                        }
                    }

                }
            }
        }

    }
    va_end(ap);

    char hbuff[1024];
    if(count == 1){
        snprintf(hbuff, 1024, ". Maybe you meant %s", suggestions[0].name);
        strncat(buff, hbuff, buffsize);
    }else if(count == 2){
        snprintf(hbuff, 1024, ". Maybe you meant %s or %s", suggestions[0].name, suggestions[1].name);
        strncat(buff, hbuff, buffsize);
    }else if(count >= 3){
        snprintf(hbuff, 1024, ". Maybe you meant %s, %s or %s", suggestions[0].name, suggestions[1].name, suggestions[2].name);
        strncat(buff, hbuff, buffsize);
    }
}


const struct typeandname *getVariable(const char *varname)
{
    char ebuf[1024];
    struct typeandname *result;

    result = ht_lookup(&locals, varname);
    if (result) return result;

    result = ht_lookup(&params, varname);
    if (result) return result;

    result = ht_lookup(&globals, varname);
    if (result) return result;

    snprintf(ebuf, 1024, "Undeclared variable %s", varname);
    getsuggestions(varname, ebuf, 1024, 3, &locals, &params, &globals);
    yyerrorline(2, islinebreak ? lineno - 1 : lineno, ebuf);

    // Store it as unidentified variable
    const struct typeandname *newtan = newtypeandname(gAny, varname);
    put(curtab, varname, (void*)newtan);
    if(infunction && !ht_lookup(&initialized, varname)){
        put(&initialized, varname, (void*)1);
    }
    return newtan;
}

void validateGlobalAssignment(const char *varname)
{
    char ebuf[1024];
    struct typeandname *result;
    result = ht_lookup(&globals, varname);
    if (result) {
        snprintf(ebuf, 1024, "Assignment to global variable %s in constant function", varname);
        yyerrorline(2, lineno - 1, ebuf);
    }
}


void checkParameters(const struct paramlist *func, const struct paramlist *inp, bool mustretbool)
{
    const struct typeandname *fi = func->head;
    const struct typeandname *pi = inp->head;
    while(true) {
        if (fi == NULL && pi == NULL)
            return;
        if (fi == NULL && pi != NULL) {
            yyerrorex(3, "Too many arguments passed to function");
            return;
        }
        if (fi != NULL && pi == NULL) {
            yyerrorex(3, "Not enough arguments passed to function");
            return;
        }
        canconvert(pi->ty, fi->ty, 0);
        bool has_flag = (pjass_flags & flag_filter) || (fnannotations & flag_filter);
        if(has_flag && mustretbool && typeeq(pi->ty, gCodeReturnsNoBoolean)){
            yyerrorex(semanticerror, "Function passed to Filter or Condition must return a boolean");
            return;
        }
        pi = pi->next;
        fi = fi->next;
    }
}
const struct typenode *binop(const struct typenode *a, const struct typenode *b)
{
    a = getPrimitiveAncestor(a);
    b = getPrimitiveAncestor(b);
    if (typeeq(a, gInteger) && typeeq(b, gInteger))
        return gInteger;
    if (typeeq(a, gAny))
        return b;
    if (typeeq(b, gAny))
        return a;
    if ((!typeeq(a, gInteger) && !typeeq(a, gReal)) || (!typeeq(b, gInteger) && !typeeq(b, gReal))) {
        yyerrorline(3, islinebreak ? lineno - 1 : lineno, "Bad types for binary operator");
    }
    return gReal;
}

const struct typenode *combinetype(const struct typenode *n1, const struct typenode *n2)
{
    uint8_t ret = getTypeTag(n1) & getTypeTag(n2);
    if ((typeeq(n1, gNone)) || (typeeq(n2, gNone)))
        return mkretty(gNone, ret);
    if (typeeq(n1, n2))
        return mkretty(n1, ret);
    if (typeeq(n1, gNull))
        return mkretty(n2, ret);
    if (typeeq(n2, gNull))
        return mkretty(n1, ret);

    n1 = getPrimitiveAncestor(n1);
    n2 = getPrimitiveAncestor(n2);

    if (typeeq(n1, n2))
        return mkretty(n1, ret);
    if (typeeq(n1, gNull))
        return mkretty(n2, ret);
    if (typeeq(n2, gNull))
        return mkretty(n1, ret);
    if ((typeeq(n1, gInteger)) && (typeeq(n2, gReal)))
        return mkretty(gReal, ret);
    if ((typeeq(n1, gReal)) && (typeeq(n2, gInteger)))
        return mkretty(gInteger, ret);
    return mkretty(gNone, ret);
}

// this is used for reducing expressions in many places (if/exitwhen conditions, assignments etc.)
int canconvert(const struct typenode *ufrom, const struct typenode *uto, const int linemod)
{
    const struct typenode *from = ufrom, *to = uto;
    char ebuf[1024];
    if (from == NULL || to == NULL)
        return 0;
    if (typeeq(from, gAny) || typeeq(to, gAny))
        return 1;
    if (isDerivedFrom(from, to))
        return 1;
    if (getTypePtr(from)->typename == NULL || getTypePtr(to)->typename == NULL)
        return 0;
    if (typeeq(from, gNone) || typeeq(to, gNone))
        return 0;
    from = getPrimitiveAncestor(from);
    to = getPrimitiveAncestor(to);
    if (typeeq(from, gNull) && !typeeq(to, gInteger) && !typeeq(to, gReal) && !typeeq(to, gBoolean))
        return 1;
    if (strict) {
        if (typeeq(ufrom, gInteger) && (typeeq(to, gReal) || typeeq(to, gInteger)))
            return 1;
        if (typeeq(ufrom, to) && (typeeq(ufrom, gBoolean) || typeeq(ufrom, gString) || typeeq(ufrom, gReal) || typeeq(ufrom, gInteger) || typeeq(ufrom, gCode)))
            return 1;
    } else {
        if (typeeq(from, gInteger) && (typeeq(to, gReal) || typeeq(to, gInteger)))
            return 1;
        if (typeeq(from, to) && (typeeq(from, gBoolean) || typeeq(from, gString) || typeeq(from, gReal) || typeeq(from, gInteger) || typeeq(from, gCode)))
            return 1;
    }

    snprintf(ebuf, 1024, "Cannot convert %s to %s", ufrom->typename, uto->typename);
    yyerrorline(3, lineno + linemod, ebuf);
    return 0;
}

// this is used for return statements only
void canconvertreturn(const struct typenode *ufrom, const struct typenode *uto, const int linemod)
{
    const struct typenode *from = ufrom, *to = uto;
    char ebuf[1024];
    if(typeeq(from, NULL) || typeeq(to, NULL))
        return; // garbage

    if (typeeq(from, gAny) || typeeq(to, gAny))
        return; // we don't care

    if (isDerivedFrom(from, to))
        return; // eg. from = unit, to = handle

    if (getTypePtr(from)->typename == NULL || getTypePtr(to)->typename == NULL)
        return; // garbage

    if (typeeq(from, gNone) || typeeq(to, gNone))
        return; // garbage


    from = getPrimitiveAncestor(from);
    to = getPrimitiveAncestor(to);
    if ((typeeq(to, gReal)) && (typeeq(from, gInteger))) {
        // can't return integer when it expects a real (added 9.5.2005)
        snprintf(ebuf, 1024, "Cannot convert returned value from %s to %s", getTypePtr(from)->typename, getTypePtr(to)->typename);
        yyerrorline(1, lineno + linemod, ebuf);
        return;
    }

    if ((typeeq(from, gNull)) && (!typeeq(to, gInteger)) && (!typeeq(to, gReal)) && (!typeeq(to, gBoolean)))
        return; // can't return null when it expects integer, real or boolean (added 9.5.2005)

    if (strict) {
        if (isDerivedFrom(ufrom, uto))
            return;
    } else if (typeeq(ufrom, uto)){
        return;
    }

    snprintf(ebuf, 1024, "Cannot convert returned value from %s to %s", getTypePtr(ufrom)->typename, getTypePtr(uto)->typename);
    yyerrorline(1, lineno + linemod, ebuf);
    return;
}

void isnumeric(const struct typenode *ty)
{
    ty = getPrimitiveAncestor(ty);
    if (!(ty == gInteger || ty == gReal || ty == gAny))
        yyerrorline(3, islinebreak ? lineno - 1 : lineno, "Cannot be converted to numeric type");
}

void checkcomparisonsimple(const struct typenode *a)
{
    const struct typenode *pa;
    pa = getPrimitiveAncestor(a);
    if (typeeq(pa, gString) || typeeq(pa, gHandle) || typeeq(pa, gCode) || typeeq(pa, gBoolean)) {
        yyerrorex(3, "Comparing the order/size of 2 variables only works on reals and integers");
        return;
    }
    if (typeeq(pa, gNull))
        yyerrorex(3, "Comparing null is not allowed");
}

void checkcomparison(const struct typenode *a, const struct typenode *b)
{
    const struct typenode *pa, *pb;
    pa = getPrimitiveAncestor(a);
    pb = getPrimitiveAncestor(b);
    if (typeeq(pa, gString) || typeeq(pa, gHandle) || typeeq(pa, gCode) || typeeq(pa, gBoolean) || typeeq(pb, gString) || typeeq(pb, gCode) || typeeq(pb, gHandle) || typeeq(pb, gBoolean)) {
        yyerrorex(3, "Comparing the order/size of 2 variables only works on reals and integers");
        return;
    }
    if (typeeq(pa, gNull) && typeeq(pb, gNull))
        yyerrorex(3, "Comparing null is not allowed");
}

void checkeqtest(const struct typenode *a, const struct typenode *b)
{
    const struct typenode *pa, *pb;
    pa = getPrimitiveAncestor(a);
    pb = getPrimitiveAncestor(b);
    if ((typeeq(pa, gInteger) || typeeq(pa, gReal)) && (typeeq(pb, gInteger) || typeeq(pb, gReal)))
        return;
    if (typeeq(pa, gNull) || typeeq(pb, gNull))
        return;
    if (!typeeq(pa, pb)) {
        yyerrorex(3, "Comparing two variables of different primitive types (except real and integer) is not allowed");
        return;
    }
}


int updateannotation(int cur, char *txt){
    char sep[] = " \t\n";
    char *ann;
    memset(txt, ' ', strlen("//#"));
    for(ann = strtok(txt, sep); ann; ann = strtok(NULL, sep)){
        char *name = ann+1;
        char sgn = ann[0];
        int flag = 0;

        if(! strcmp(name, "rb")){
            flag = flag_rb;
        } else if(! strcmp(name, "filter") ){
            flag = flag_filter;
        }

        if(sgn == '+') {
            cur |= flag;
        } else if(sgn == '-') {
            cur &= ~flag;
        }
    }
    return cur;
}


Mode Type Size Ref File
100644 blob 168 c1ca22975fdbd25aedc5c81f51b87f059bbc4140 .gitignore
100644 blob 2517 c2029f6871ff6141604ec2a2262266e15b27919a Makefile
100644 blob 391 1f49d20bfca22ddee132b62a37c103610d08e211 check.sh
100644 blob 251 77cc7498114662cff955ad94eb1b1809c89b2233 fail.sh
100644 blob 215 e2d0c0f230e4387accdf5037eca5ee512c90a8a0 funcdecl.c
100644 blob 236 99ca506caaa5e457089f7391b005351fe8a0f2de funcdecl.h
100644 blob 26156 a2c1f9bf730ff7c04d749ba3dae1d4f921b7c66d grammar.y
100644 blob 2635 6fd3bb88318b11382e1e853efb9eb80d1f4413c6 hashtable.c
100644 blob 430 905a180f67713aad61271f6cc099195ff4d82a1f hashtable.h
100644 blob 4642 b8a962ba3d310ba722618545c2308a38a48589e1 main.c
100644 blob 14458 f4721e475becad42db92127a2b256711c7515933 misc.c
100644 blob 2744 de716879b5a96d40169f5f8fdf3ff6dd68e9d87b misc.h
100644 blob 1302 13d65e05aca85cddd2f539b14d98cfbf1c622752 notes.txt
100644 blob 330 92a4597d6df5097c58649391541d54f06b3bda1b paramlist.c
100644 blob 256 bb253362ef26a0ea881856aebeb8503fb82646fb paramlist.h
100644 blob 764 270df8583e78c80f366a8ab431fab673d89446d5 readme.txt
040000 tree - 2f736e3985eab5b3c8ba4181754a986901e1a073 tests
100644 blob 5871 dd8455821811ca52a9aef65a01031ec1d196a111 token.l
100644 blob 1720 6cb071ecb4b14a156d31b873a76859e8328ff4d4 typeandname.c
100644 blob 845 58e1d731d067c711d3b0dc781ed2f7d2fc573ac0 typeandname.h
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