#ifndef TSP_INTUIT_MORE_H
#define TSP_INTUIT_MORE_H
#include <stdbool.h>
#include <stddef.h>
#include "tsp_intuit_keywords.h"
static inline bool tsp_im_isdigit(int c) { return c >= '0' && c <= '9'; }
static inline bool tsp_im_isalpha(int c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static inline bool tsp_im_isword(int c) {
return tsp_im_isalpha(c) || tsp_im_isdigit(c) || c == '_';
}
static inline bool tsp_im_isblank(int c) { return c == ' ' || c == '\t'; }
static inline bool tsp_im_in(const char *set, int c) {
if (c == 0) return false;
for (; *set; set++)
if ((unsigned char)*set == (unsigned char)c) return true;
return false;
}
static bool tsp_regcurly(const char *s, int len) {
const char *e = s + len;
bool min_present = false;
bool max_present = false;
if (s >= e || *s++ != '{') return false;
while (s < e && tsp_im_isblank(*s)) s++;
if (s < e && tsp_im_isdigit(*s)) {
min_present = true;
do { s++; } while (s < e && tsp_im_isdigit(*s));
}
while (s < e && tsp_im_isblank(*s)) s++;
if (s < e && *s == ',') {
s++;
while (s < e && tsp_im_isblank(*s)) s++;
if (s < e && tsp_im_isdigit(*s)) {
max_present = true;
do { s++; } while (s < e && tsp_im_isdigit(*s));
}
while (s < e && tsp_im_isblank(*s)) s++;
}
if (s >= e || *s != '}' || (!min_present && !max_present)) return false;
return true;
}
static bool tsp_intuit_more(const char *s, int len) {
const char *e = s + len;
if (s >= e) return true;
if (s[0] == '{') {
return !tsp_regcurly(s, len);
}
if (s[0] != '[') return true;
s++;
if (s >= e) return true;
if (s[0] == ']' || s[0] == '^') return false;
const char *send = NULL;
for (const char *p = s; p < e; p++) {
if (*p == ']') { send = p; break; }
}
if (!send) return true;
if (tsp_im_isdigit(s[0]) && send - s <= 2 &&
(send - s == 1 || tsp_im_isdigit(s[1]))) {
return true;
}
int weight = (s[0] == '$') ? -1 : 2;
unsigned char seen[256] = {0};
unsigned char un_char = 0;
bool first_time = true;
for (; s < send; s++, first_time = false) {
unsigned char prev_un_char = un_char;
un_char = (unsigned char)s[0];
switch (s[0]) {
case '@':
case '&':
case '$':
weight -= seen[un_char] * 10;
if (s + 1 < e && tsp_im_isword((unsigned char)s[1])) {
weight -= 10;
}
else if (s[0] == '$' && s + 1 < e && s[1] &&
tsp_im_in("[#!%*<>()-=", (unsigned char)s[1])) {
char s2 = (s + 2 < e) ? s[2] : 0;
if (tsp_im_in("])} =", (unsigned char)s2)) weight -= 10;
else weight -= 1;
}
break;
case '\\':
if (s + 1 < e && s[1]) {
if (tsp_im_in("wds]", (unsigned char)s[1])) weight += 100;
else if (seen[(unsigned char)'\''] || seen[(unsigned char)'"']) weight += 1;
else if (tsp_im_in("abcfnrtvx", (unsigned char)s[1])) weight += 40;
else if (tsp_im_isdigit((unsigned char)s[1])) {
weight += 40;
while (s + 1 < send && tsp_im_isdigit((unsigned char)s[1])) s++;
}
}
else weight += 100;
break;
case '-':
if (s + 1 < e && s[1] == '\\') weight += 50;
if (!first_time && tsp_im_in("aA01! ", prev_un_char)) weight += 30;
if (s + 1 < e && tsp_im_in("zZ79~", (unsigned char)s[1])) weight += 30;
if (first_time && s + 1 < e &&
(tsp_im_isdigit((unsigned char)s[1]) || s[1] == '$')) {
weight -= 5;
}
break;
default:
if ((first_time || (!tsp_im_isword(prev_un_char)
&& prev_un_char != '$'
&& prev_un_char != '@'
&& prev_un_char != '&'))
&& tsp_im_isalpha((unsigned char)s[0])
&& s + 1 < send && tsp_im_isalpha((unsigned char)s[1])) {
const char *d = s;
while (s < send && tsp_im_isalpha((unsigned char)s[0])) s++;
if (tsp_is_perl_keyword(d, (int)(s - d))) weight -= 150;
}
if (!first_time && un_char == (unsigned char)(prev_un_char + 1)) {
weight += 5;
}
weight -= seen[un_char];
break;
}
seen[un_char]++;
}
if (weight >= 0) return false;
return true;
}
#endif