From e6dc510c593f308f842c5381fe59620639dfdfb2 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Thu, 23 Aug 2018 10:46:29 +0200 Subject: [PATCH 1/2] + add: TinyExpr - logical operators (0.0 : false, <>0.0 : true) --- tinyexpr/test.c | 95 ++++++++++++++++++++++++++++++ tinyexpr/tinyexpr.c | 139 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 224 insertions(+), 10 deletions(-) diff --git a/tinyexpr/test.c b/tinyexpr/test.c index c772950a8..a44628dff 100644 --- a/tinyexpr/test.c +++ b/tinyexpr/test.c @@ -672,6 +672,100 @@ void test_combinatorics() { } +void test_logic() { + test_case cases[] = { + {"1 && 1", 1}, + {"1 && 0", 0}, + {"0 && 1", 0}, + {"0 && 0", 0}, + {"1 || 1", 1}, + {"1 || 0", 1}, + {"0 || 1", 1}, + {"0 || 0", 0}, + {"!0", 1}, + {"!1", 0}, + {"!2", 0}, + + {"!-2", 0}, + {"-!2", 0}, + {"!!0", 0}, + {"!!1", 1}, + {"!!2", 1}, + {"!!-2", 1}, + {"!-!2", 1}, + {"-!!2", -1}, + {"--!!2", 1}, + + {"1 < 2", 1}, + {"2 < 2", 0}, + {"2 <= 2", 1}, + {"2 > 1", 1}, + {"2 > 2", 0}, + {"2 >= 2", 1}, + {"2 > -2", 1}, + {"-2 < 2", 1}, + + {"0 == 0", 1}, + {"0 != 0", 0}, + {"2 == 2", 1}, + {"2 != 2", 0}, + {"2 == 3", 0}, + {"2 != 3", 1}, + {"2 == 2.0001", 0}, + {"2 != 2.0001", 1}, + + {"1 < 2 && 2 < 3", 1}, + {"1 < 2 && 3 < 2", 0}, + {"2 < 1 && 2 < 3", 0}, + {"2 < 1 && 3 < 2", 0}, + {"1 < 2 || 2 < 3", 1}, + {"1 < 2 || 3 < 2", 1}, + {"2 < 1 || 2 < 3", 1}, + {"2 < 1 || 3 < 2", 0}, + + {"1 < 1+1", 1}, + {"1 < 1*2", 1}, + {"1 < 2/2", 0}, + {"1 < 2^2", 1}, + + {"5+5 < 4+10", 1}, + {"5+(5 < 4)+10", 15}, + {"5+(5 < 4+10)", 6}, + {"(5+5 < 4)+10", 10}, + {"5+!(5 < 4)+10", 16}, + {"5+!(5 < 4+10)", 5}, + {"!(5+5 < 4)+10", 11}, + +#ifdef TE_POW_FROM_RIGHT + {"!0^2", 1}, + {"!0^-1", 0}, + {"-!0^2", -1}, +#else + {"!0^2", 1}, + {"!0^-1", 1}, + {"-!0^2", 1}, +#endif + + }; + + + int i; + for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { + const char *expr = cases[i].expr; + const double answer = cases[i].answer; + + int err; + const double ev = te_interp(expr, &err); + lok(!err); + lfequal(ev, answer); + + if (err) { + printf("FAILED: %s (%d)\n", expr, err); + } + } +} + + int main(int argc, char *argv[]) { lrun("Results", test_results); @@ -685,6 +779,7 @@ int main(int argc, char *argv[]) lrun("Optimize", test_optimize); lrun("Pow", test_pow); lrun("Combinatorics", test_combinatorics); + lrun("Logic", test_logic); lresults(); return lfails != 0; diff --git a/tinyexpr/tinyexpr.c b/tinyexpr/tinyexpr.c index 6b10ef3fd..30e87a559 100644 --- a/tinyexpr/tinyexpr.c +++ b/tinyexpr/tinyexpr.c @@ -228,6 +228,19 @@ static double divide(double a, double b) {return a / b;} static double negate(double a) {return -a;} static double comma(double a, double b) {(void)a; return b;} +static double greater(double a, double b) {return a > b;} +static double greater_eq(double a, double b) {return a >= b;} +static double lower(double a, double b) {return a < b;} +static double lower_eq(double a, double b) {return a <= b;} +static double equal(double a, double b) {return a == b;} +static double not_equal(double a, double b) {return a != b;} +static double logical_and(double a, double b) {return a != 0.0 && b != 0.0;} +static double logical_or(double a, double b) {return a != 0.0 || b != 0.0;} +static double logical_not(double a) {return a == 0.0;} +static double logical_notnot(double a) {return a != 0.0;} +static double negate_logical_not(double a) {return -(a == 0.0);} +static double negate_logical_notnot(double a) {return -(a != 0.0);} + void next_token(state *s) { s->type = TOK_NULL; @@ -284,6 +297,51 @@ void next_token(state *s) { case '/': s->type = TOK_INFIX; s->function = divide; break; case '^': s->type = TOK_INFIX; s->function = pow; break; case '%': s->type = TOK_INFIX; s->function = fmod; break; + case '!': + if (s->next++[0] == '=') { + s->type = TOK_INFIX; s->function = not_equal; + } else { + s->next--; + s->type = TOK_INFIX; s->function = logical_not; + } + break; + case '=': + if (s->next++[0] == '=') { + s->type = TOK_INFIX; s->function = equal; + } else { + s->type = TOK_ERROR; + } + break; + case '<': + if (s->next++[0] == '=') { + s->type = TOK_INFIX; s->function = lower_eq; + } else { + s->next--; + s->type = TOK_INFIX; s->function = lower; + } + break; + case '>': + if (s->next++[0] == '=') { + s->type = TOK_INFIX; s->function = greater_eq; + } else { + s->next--; + s->type = TOK_INFIX; s->function = greater; + } + break; + case '&': + if (s->next++[0] == '&') { + s->type = TOK_INFIX; s->function = logical_and; + } else { + s->type = TOK_ERROR; + } + break; + case '|': + if (s->next++[0] == '|') { + s->type = TOK_INFIX; s->function = logical_or; + } else { + s->type = TOK_ERROR; + } + break; case '(': s->type = TOK_OPEN; break; case ')': s->type = TOK_CLOSE; break; case ',': s->type = TOK_SEP; break; @@ -396,20 +454,48 @@ static te_expr *base(state *s) { static te_expr *power(state *s) { - /* = {("-" | "+")} */ + /* = {("-" | "+" | "!")} */ int sign = 1; while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) { if (s->function == sub) sign = -sign; next_token(s); } + int logical = 0; + while (s->type == TOK_INFIX && (s->function == add || s->function == sub || s->function == logical_not)) { + if (s->function == logical_not) { + if (logical == 0) { + logical = -1; + } else { + logical = -logical; + } + } + next_token(s); + } + te_expr *ret; if (sign == 1) { - ret = base(s); + if (logical == 0) { + ret = base(s); + } else if (logical == -1) { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = logical_not; + } else { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = logical_notnot; + } } else { - ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); - ret->function = negate; + if (logical == 0) { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = negate; + } else if (logical == -1) { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = negate_logical_not; + } else { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = negate_logical_notnot; + } } return ret; @@ -420,14 +506,16 @@ static te_expr *factor(state *s) { /* = {"^" } */ te_expr *ret = power(s); - int neg = 0; + const void *left_function = NULL; te_expr *insertion = 0; - if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { + if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && + (ret->function == negate || ret->function == logical_not || ret->function == logical_notnot || + ret->function == negate_logical_not || ret->function == negate_logical_notnot)) { + left_function = ret->function; te_expr *se = ret->parameters[0]; free(ret); ret = se; - neg = 1; } while (s->type == TOK_INFIX && (s->function == pow)) { @@ -447,9 +535,9 @@ static te_expr *factor(state *s) { } } - if (neg) { + if (left_function) { ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); - ret->function = negate; + ret->function = left_function; } return ret; @@ -487,7 +575,7 @@ static te_expr *term(state *s) { } -static te_expr *expr(state *s) { +static te_expr *sum_expr(state *s) { /* = {("+" | "-") } */ te_expr *ret = term(s); @@ -502,6 +590,37 @@ static te_expr *expr(state *s) { } +static te_expr *test_expr(state *s) { + /* = {(">" | ">=" | "<" | "<=" | "==" | "!=") } */ + te_expr *ret = sum_expr(s); + + while (s->type == TOK_INFIX && (s->function == greater || s->function == greater_eq || + s->function == lower || s->function == lower_eq || s->function == equal || s->function == not_equal)) { + te_fun2 t = s->function; + next_token(s); + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, sum_expr(s)); + ret->function = t; + } + + return ret; +} + + +static te_expr *expr(state *s) { + /* = {("&&" | "||") } */ + te_expr *ret = test_expr(s); + + while (s->type == TOK_INFIX && (s->function == logical_and || s->function == logical_or)) { + te_fun2 t = s->function; + next_token(s); + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, test_expr(s)); + ret->function = t; + } + + return ret; +} + + static te_expr *list(state *s) { /* = {"," } */ te_expr *ret = expr(s); From 9ffb945923f50125abfd1247a11ecf20467250c0 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Thu, 23 Aug 2018 16:44:23 +0200 Subject: [PATCH 2/2] =?UTF-8?q?+=20add:=20TinyExpr=20evaluation=20of=20cur?= =?UTF-8?q?rent=20line=20triggered=20by=20insertion=20of=20"=3D=3F"=20(rep?= =?UTF-8?q?lacing=20'=3F'=20by=20result)?= --- src/Notepad3.c | 259 +++++++++++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 103 deletions(-) diff --git a/src/Notepad3.c b/src/Notepad3.c index 153bbdfb2..b08118e93 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -5983,6 +5983,154 @@ void OpenHotSpotURL(DocPos position, bool bForceBrowser) } + +//============================================================================= +// +// _HandleAutoCloseTags() +// +static void __fastcall _HandleAutoIndent(int const charAdded) { + // in CRLF mode handle LF only... + if (((SC_EOL_CRLF == g_iEOLMode) && (charAdded != '\r')) || (SC_EOL_CRLF != g_iEOLMode)) + { + DocPos const iCurPos = SciCall_GetCurrentPos(); + DocLn const iCurLine = SciCall_LineFromPosition(iCurPos); + + // Move bookmark along with line if inserting lines (pressing return within indent area of line) because Scintilla does not do this for us + if (iCurLine > 0) + { + //DocPos const iPrevLineLength = Sci_GetNetLineLength(iCurLine - 1); + if (SciCall_GetLineEndPosition(iCurLine - 1) == SciCall_GetLineIndentPosition(iCurLine - 1)) + { + int const bitmask = SciCall_MarkerGet(iCurLine - 1); + if (bitmask & (1 << MARKER_NP3_BOOKMARK)) + { + SciCall_MarkerDelete(iCurLine - 1, MARKER_NP3_BOOKMARK); + SciCall_MarkerAdd(iCurLine, MARKER_NP3_BOOKMARK); + } + } + } + + if (iCurLine > 0/* && iLineLength <= 2*/) + { + DocPos const iPrevLineLength = SciCall_LineLength(iCurLine - 1); + char* pLineBuf = NULL; + bool bAllocLnBuf = false; + if (iPrevLineLength < TEMPLINE_BUFFER) { + pLineBuf = g_pTempLineBufferMain; + } + else { + bAllocLnBuf = true; + pLineBuf = AllocMem(iPrevLineLength + 1, HEAP_ZERO_MEMORY); + } + if (pLineBuf) + { + SciCall_GetLine(iCurLine - 1, pLineBuf); + *(pLineBuf + iPrevLineLength) = '\0'; + for (char* pPos = pLineBuf; *pPos; pPos++) { + if (*pPos != ' ' && *pPos != '\t') + *pPos = '\0'; + } + if (*pLineBuf) { + _BEGIN_UNDO_ACTION_; + SciCall_AddText(lstrlenA(pLineBuf), pLineBuf); + _END_UNDO_ACTION_; + } + if (bAllocLnBuf) { FreeMem(pLineBuf); } + } + } + } +} + + +//============================================================================= +// +// _HandleAutoCloseTags() +// +static void __fastcall _HandleAutoCloseTags() +{ + //int lexerID = (int)SendMessage(g_hwndEdit,SCI_GETLEXER,0,0); + //if (lexerID == SCLEX_HTML || lexerID == SCLEX_XML) + { + DocPos const iCurPos = SciCall_GetCurrentPos(); + DocPos const iHelper = iCurPos - (DocPos)(COUNTOF(g_pTempLineBufferMain) - 1); + DocPos const iStartPos = max(0, iHelper); + DocPos const iSize = iCurPos - iStartPos; + + if (iSize >= 3) + { + const char* pBegin = SciCall_GetRangePointer(iStartPos, iSize); + + if (pBegin[iSize - 2] != '/') { + + const char* pCur = &pBegin[iSize - 2]; + + while (pCur > pBegin && *pCur != '<' && *pCur != '>') { --pCur; } + + int cchIns = 2; + StringCchCopyA(g_pTempLineBufferMain, FNDRPL_BUFFER, "'; + g_pTempLineBufferMain[cchIns] = '\0'; + + if (cchIns > 3 && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "
", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && + StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1)) + { + _BEGIN_UNDO_ACTION_; + SciCall_ReplaceSel(g_pTempLineBufferMain); + SciCall_SetSel(iCurPos, iCurPos); + _END_UNDO_ACTION_; + } + } + } + } +} + + +//============================================================================= +// +// _HandleTinyExpr() - called on '?' insert +// +static void __fastcall _HandleTinyExpr() +{ + DocPos const iCurPos = SciCall_GetCurrentPos(); + DocPos const iPosBefore = SciCall_PositionBefore(iCurPos); + char const chBefore = SciCall_GetCharAt(iPosBefore - 1); + if (chBefore == '=') // got "=?" evaluate expression trigger + { + DocPos const iLnCaretPos = SciCall_GetCurLine(COUNTOF(g_pTempLineBufferMain), g_pTempLineBufferMain); + g_pTempLineBufferMain[(iLnCaretPos > 1) ? (iLnCaretPos-2) : 0] = '\0'; // breakbefore "=?" + + int iExprErr = 1; + const char* pBegin = &g_pTempLineBufferMain[0]; + double dExprEval = 0.0; + + while (*pBegin && iExprErr) { + dExprEval = te_interp(pBegin++, &iExprErr); + } + if (*pBegin && !iExprErr) { + char chExpr[64] = { '\0' }; + StringCchPrintfA(chExpr, COUNTOF(chExpr), "%.6G", dExprEval); + SciCall_SetSel(iPosBefore, iCurPos); + SciCall_ReplaceSel(chExpr); + } + } +} + + //============================================================================= // // MsgNotify() - Handles WM_NOTIFY @@ -6158,112 +6306,17 @@ LRESULT MsgNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) case SCN_CHARADDED: { // Auto indent - if (bAutoIndent && (scn->ch == '\x0D' || scn->ch == '\x0A')) + if (bAutoIndent && (scn->ch == '\r' || scn->ch == '\n')) { - // in CRLF mode handle LF only... - if ((SC_EOL_CRLF == g_iEOLMode && scn->ch != '\x0A') || SC_EOL_CRLF != g_iEOLMode) - { - const DocPos iCurPos = SciCall_GetCurrentPos(); - const DocLn iCurLine = SciCall_LineFromPosition(iCurPos); - - // Move bookmark along with line if inserting lines (pressing return within indent area of line) because Scintilla does not do this for us - if (iCurLine > 0) - { - //const DocPos iPrevLineLength = Sci_GetNetLineLength(iCurLine - 1); - if (SciCall_GetLineEndPosition(iCurLine - 1) == SciCall_GetLineIndentPosition(iCurLine - 1)) - { - int bitmask = SciCall_MarkerGet(iCurLine - 1); - if (bitmask & (1 << MARKER_NP3_BOOKMARK)) - { - SciCall_MarkerDelete(iCurLine - 1, MARKER_NP3_BOOKMARK); - SciCall_MarkerAdd(iCurLine, MARKER_NP3_BOOKMARK); - } - } - } - - if (iCurLine > 0/* && iLineLength <= 2*/) - { - const DocPos iPrevLineLength = SciCall_LineLength(iCurLine - 1); - char* pLineBuf = NULL; - bool bAllocLnBuf = false; - if (iPrevLineLength < TEMPLINE_BUFFER) { - pLineBuf = g_pTempLineBufferMain; - } - else { - bAllocLnBuf = true; - pLineBuf = AllocMem(iPrevLineLength + 1, HEAP_ZERO_MEMORY); - } - if (pLineBuf) - { - SciCall_GetLine(iCurLine - 1, pLineBuf); - *(pLineBuf + iPrevLineLength) = '\0'; - for (char* pPos = pLineBuf; *pPos; pPos++) { - if (*pPos != ' ' && *pPos != '\t') - *pPos = '\0'; - } - if (*pLineBuf) { - _BEGIN_UNDO_ACTION_; - SciCall_AddText(lstrlenA(pLineBuf), pLineBuf); - _END_UNDO_ACTION_; - } - if (bAllocLnBuf) { FreeMem(pLineBuf); } - } - } - } + _HandleAutoIndent(scn->ch); } // Auto close tags else if (bAutoCloseTags && scn->ch == '>') { - //int lexerID = (int)SendMessage(g_hwndEdit,SCI_GETLEXER,0,0); - //if (lexerID == SCLEX_HTML || lexerID == SCLEX_XML) - { - const DocPos iCurPos = SciCall_GetCurrentPos(); - const DocPos iHelper = iCurPos - (DocPos)(COUNTOF(g_pTempLineBufferMain) - 1); - const DocPos iStartPos = max(0, iHelper); - const DocPos iSize = iCurPos - iStartPos; - - if (iSize >= 3) - { - const char* pBegin = SciCall_GetRangePointer(iStartPos, iSize); - - if (pBegin[iSize - 2] != '/') { - - const char* pCur = &pBegin[iSize - 2]; - - while (pCur > pBegin && *pCur != '<' && *pCur != '>') - --pCur; - - int cchIns = 2; - StringCchCopyA(g_pTempLineBufferMain, FNDRPL_BUFFER, "'; - g_pTempLineBufferMain[cchIns] = '\0'; - - if (cchIns > 3 && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "
", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1) && - StringCchCompareINA(g_pTempLineBufferMain, COUNTOF(g_pTempLineBufferMain), "", -1)) - { - _BEGIN_UNDO_ACTION_; - SciCall_ReplaceSel(g_pTempLineBufferMain); - SciCall_SetSel(iCurPos, iCurPos); - _END_UNDO_ACTION_; - } - } - } - } + _HandleAutoCloseTags(); + } + else if (scn->ch == '?') { + _HandleTinyExpr(); } else if (g_bAutoCompleteWords && !SendMessage(g_hwndEdit, SCI_AUTOCACTIVE, 0, 0)) { EditCompleteWord(g_hwndEdit, false); @@ -8312,6 +8365,7 @@ static void __fastcall _UpdateStatusbarDelayed(bool bForceRedraw) // try calculate expression of selection static WCHAR tchExpression[32] = { L'\0' }; static int s_iExprError = -3; + static char chExpression[1024] = { '\0' }; if (g_iStatusbarVisible[STATUS_TINYEXPR]) { @@ -8322,13 +8376,12 @@ static void __fastcall _UpdateStatusbarDelayed(bool bForceRedraw) if (bIsSelCountable) { - char chExpression[1024] = { '\0' }; + if (SciCall_GetSelText(NULL) < COUNTOF(chExpression)) { SciCall_GetSelText(chExpression); //StrDelChrA(chExpression, " \r\n\t\v"); StrDelChrA(chExpression, "\r\n"); - g_dExpression = te_interp(chExpression, &g_iExprError); if (!g_iExprError) {