From 997894edc04e87b0ba0c81712c92a83ec7f5c121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Fri, 20 Sep 2019 21:37:01 +0200 Subject: [PATCH] core: add calculation of expression in evaluation of expressions with "calc:..." (issue #997) --- ChangeLog.adoc | 1 + doc/de/autogen/user/weechat_commands.adoc | 124 ++++---- doc/en/autogen/user/weechat_commands.adoc | 8 +- doc/en/weechat_dev.en.adoc | 1 + doc/en/weechat_plugin_api.en.adoc | 19 +- doc/fr/autogen/user/weechat_commands.adoc | 8 +- doc/fr/weechat_dev.fr.adoc | 1 + doc/fr/weechat_plugin_api.fr.adoc | 19 +- doc/it/autogen/user/weechat_commands.adoc | 8 +- doc/it/weechat_plugin_api.it.adoc | 20 +- doc/ja/autogen/user/weechat_commands.adoc | 108 +++---- doc/ja/weechat_dev.ja.adoc | 2 + doc/ja/weechat_plugin_api.ja.adoc | 20 +- doc/pl/autogen/user/weechat_commands.adoc | 106 +++---- po/POTFILES.in | 2 + po/cs.po | 11 +- po/de.po | 12 +- po/es.po | 11 +- po/fr.po | 22 +- po/hu.po | 11 +- po/it.po | 11 +- po/ja.po | 12 +- po/pl.po | 12 +- po/pt.po | 11 +- po/pt_BR.po | 11 +- po/ru.po | 11 +- po/srcfiles.cmake | 2 + po/tr.po | 11 +- po/weechat.pot | 11 +- src/core/CMakeLists.txt | 1 + src/core/Makefile.am | 2 + src/core/wee-calc.c | 355 ++++++++++++++++++++++ src/core/wee-calc.h | 25 ++ src/core/wee-command.c | 9 +- src/core/wee-eval.c | 23 +- tests/CMakeLists.txt | 1 + tests/Makefile.am | 1 + tests/tests.cpp | 1 + tests/unit/core/test-core-calc.cpp | 111 +++++++ tests/unit/core/test-core-eval.cpp | 12 + 40 files changed, 903 insertions(+), 244 deletions(-) create mode 100644 src/core/wee-calc.c create mode 100644 src/core/wee-calc.h create mode 100644 tests/unit/core/test-core-calc.cpp diff --git a/ChangeLog.adoc b/ChangeLog.adoc index cd58133b6..5dca4dfe9 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -25,6 +25,7 @@ Bug fixes:: New features:: + * core: add calculation of expression in evaluation of expressions with "calc:..." (issue #997) * script: add options "-ol" and "-il" in command /script to send translated string with list of scripts loaded, display "No scripts loaded" if no scripts are loaded Build:: diff --git a/doc/de/autogen/user/weechat_commands.adoc b/doc/de/autogen/user/weechat_commands.adoc index bdecb1668..a4f3ec153 100644 --- a/doc/de/autogen/user/weechat_commands.adoc +++ b/doc/de/autogen/user/weechat_commands.adoc @@ -266,65 +266,66 @@ infolists: zeigt Information über die Infolists an /eval [-n|-s] [-n] -c - -n: gibt das Ergebnis aus, ohne das dieses in den Buffer gesendet wird (debug Modus) - -s: teilt Ausdrücke bevor sie evaluiert werden (mehrere Befehle können durch Semikolon getrennt werden) - -c: Auswertung als Bedingung: nutzt Operatoren und runde Klammern, Rückgabewert als Boolean-Wert ("0" oder "1") -expression: Ausdruck welcher verarbeitet werden soll. Variablen im Format ${variable} werden ersetzt (siehe unten); mehrere Befehle werden durch ein Semikolon voneinander getrennt - operator: ein logischer oder vergleichender Operand: - - logische Operanden: - && boolean "und" - || boolean "oder" - - vergleichende Operanden: - == gleich - != ungleich - <= kleiner oder gleich - < kleiner - >= größer oder gleich - > größer - =~ stimmt mit regulärem POSIX Ausdruck überein - !~ stimmt NICHT mit regulärem POSIX Ausdruck überein - =* stimmt mit Maske überein (Platzhalter "*" ist erlaubt) - !* stimmt mit Maske NICHT überein (Platzhalter "*" ist erlaubt) + -n: display result without sending it to buffer (debug mode) + -s: split expression before evaluating it (many commands can be separated by semicolons) + -c: evaluate as condition: use operators and parentheses, return a boolean value ("0" or "1") +expression: expression to evaluate, variables with format ${variable} are replaced (see below); many commands can be separated by semicolons + operator: a logical or comparison operator: + - logical operators: + && boolean "and" + || boolean "or" + - comparison operators: + == equal + != not equal + <= less or equal + < less + >= greater or equal + > greater + =~ is matching POSIX extended regex + !~ is NOT matching POSIX extended regex + =* is matching mask (wildcard "*" is allowed) + !* is NOT matching mask (wildcard "*" is allowed) -Ein Ausdruck gilt als "wahr" sofern das Ergebnis weder NULL, nicht leer und von "0" abweichend ist. -Für einen Vergleich werden Fließkommazahlen genutzt sofern es sich bei beiden Ausdrücken um gültige Nummer handelt, folgende Formate werden unterstützt: - - Integer (Beispiele: 5, -7) - - Fließkommazahl (Beispiele: 5.2, -7.5, 2.83e-2) - - hexadezimale Zahl (Beispiele: 0xA3, -0xA3) -Um einen Vergleich zwischen zwei Zeichenketten zu erzwingen, müssen die Ausdrücke in Anführungszeichen gesetzt werden, zum Beispiel: +An expression is considered as "true" if it is not NULL, not empty, and different from "0". +The comparison is made using floating point numbers if the two expressions are valid numbers, with one of the following formats: + - integer (examples: 5, -7) + - floating point number (examples: 5.2, -7.5, 2.83e-2) + - hexadecimal number (examples: 0xA3, -0xA3) +To force a string comparison, you can add double quotes around each expression, for example: 50 > 100 ==> 0 "50" > "100" ==> 1 -Einige Variablen werden im Ausdruck, mittels der Formatierung ${variable}, ersetzt. Mögliche Variablen sind, nach Reihenfolge ihrer Priorität: - 1. eine evaluierte Teilzeichenkette (Format: "eval:xxx") - 2. eine Zeichenkette mit Escapesequenzen (Format: "esc:xxx" oder "\xxx") - 3. Zeichen welche in einer Zeichenkette nicht dargestellt werden sollen (Format: "hide:Zeichen,Zeichenkette") - 4. eine Zeichenkette mit einer maximalen Anzahl an Zeichen (Format: "cut:+Max,Suffix,Zeichenkette") - oder maximale Anzahl an Zeichen die auf dem Bildschirm angezeigt werden sollen (Format: "cutscr:Max,Suffix,Zeichenkette oder "cutscr:+Max,Suffix,Zeichenkette") - 5. Ende einer Zeichenkette nutzen (Format: "rev:xxx") - 6. Wiederholung einer Zeichenkette (Format: "repeat:Anzahl,Zeichenkette") - 7. eine Farbe (Format: "color:xxx", siehe "Anleitung für API Erweiterung", Funktion "color") - 8. eine Info (Format: "info:Name,Argumente", Argumente sind optional) - 9. aktuelles Datum/Uhrzeit (Format: "date" oder "date:format") - 10. eine Umgebungsvariable (Format: "env:XXX") - 11. ein Dreifachoperand (Format: "if:Bedingung?Wert_falls_wahr:Wert_falls_unwahr") - 12. eine Option (Format: "file.section.option") - 13. der Name einer lokalen Variablen eines Buffer - 14. ein hdata Name/Variable (der Wert wird automatisch in eine Zeichenkette konvertiert), standardmäßig wird für "window" und "buffer" das aktuelle Fenster/Buffer verwendet. -Das Format für hdata kann wie folgt aufgebaut sein: - hdata.var1.var2...: startet mit hdata (der Pointer muss bekannt sein) und fragt eine Variable nach der anderen ab (weitere hdata können folgen) - hdata[list].var1.var2...: startet hdata mittels einer Liste, zum Beispiel: - ${buffer[gui_buffers].full_name}: der vollständige Name des ersten Buffers, in der verknüpften Liste aller Buffer - ${plugin[weechat_plugins].name}: Name der ersten Erweiterung, in der verknüpften Liste aller Erweiterungen - hdata[pointer].var1.var2...: startet hdata mittels einem Pointer, zum Beispiel: - ${buffer[0x1234abcd].full_name}: vollständiger Name eines Buffers und des dazugehörigen Pointers (kann in triggern benutzt werden) -Die vorhandenen Namen für hdata und Variablen sind in der "Anleitung für API Erweiterung", Bereich "weechat_hdata_get". beschrieben +Some variables are replaced in expression, using the format ${variable}, variable can be, by order of priority: + 1. an evaluated sub-string (format: "eval:xxx") + 2. a string with escaped chars (format: "esc:xxx" or "\xxx") + 3. a string with chars to hide (format: "hide:char,string") + 4. a string with max chars (format: "cut:max,suffix,string" or "cut:+max,suffix,string") + or max chars displayed on screen (format: "cutscr:max,suffix,string" or "cutscr:+max,suffix,string") + 5. a reversed string (format: "rev:xxx") + 6. a repeated string (format: "repeat:count,string") + 7. a color (format: "color:xxx", see "Plugin API reference", function "color") + 8. an info (format: "info:name,arguments", arguments are optional) + 9. current date/time (format: "date" or "date:format") + 10. an environment variable (format: "env:XXX") + 11. a ternary operator (format: "if:condition?value_if_true:value_if_false") + 12. result of an expression with parentheses and operators + - * / // % (format: "calc:xxx") + 13. an option (format: "file.section.option") + 14. a local variable in buffer + 15. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. +Format for hdata can be one of following: + hdata.var1.var2...: start with a hdata (pointer must be known), and ask variables one after one (other hdata can be followed) + hdata[list].var1.var2...: start with a hdata using a list, for example: + ${buffer[gui_buffers].full_name}: full name of first buffer in linked list of buffers + ${plugin[weechat_plugins].name}: name of first plugin in linked list of plugins + hdata[pointer].var1.var2...: start with a hdata using a pointer, for example: + ${buffer[0x1234abcd].full_name}: full name of the buffer with this pointer (can be used in triggers) +For name of hdata and variables, please look at "Plugin API reference", function "weechat_hdata_get". -Beispiele (einfache Zeichenketten): +Examples (simple strings): /eval -n ${info:version} ==> 0.4.3 /eval -n ${env:HOME} ==> /home/user /eval -n ${weechat.look.scroll_amount} ==> 3 - /eval -n ${sec.data.freenode_password} ==> geheim + /eval -n ${sec.data.freenode_password} ==> secret /eval -n ${window} ==> 0x2549aa0 /eval -n ${window.buffer} ==> 0x2549320 /eval -n ${window.buffer.full_name} ==> core.weechat @@ -337,17 +338,18 @@ Beispiele (einfache Zeichenketten): /eval -n ${if:${info:term_width}>80?big:small} ==> big /eval -n ${rev:Hello} ==> olleH /eval -n ${repeat:5,-} ==> ----- + /eval -n ${calc:(5+2)*3} ==> 21 -Beispiele (Bedingungen): - /eval -n -c ${window.buffer.number} > 2 ==> 0 - /eval -n -c ${window.win_width} > 100 ==> 1 - /eval -n -c (8 > 12) || (5 > 2) ==> 1 - /eval -n -c (8 > 12) && (5 > 2) ==> 0 - /eval -n -c abcd =~ ^ABC ==> 1 - /eval -n -c abcd =~ (?-i)^ABC ==> 0 - /eval -n -c abcd =~ (?-i)^abc ==> 1 - /eval -n -c abcd !~ abc ==> 0 - /eval -n -c abcd =* a*d ==> 1 +Examples (conditions): + /eval -n -c ${window.buffer.number} > 2 ==> 0 + /eval -n -c ${window.win_width} > 100 ==> 1 + /eval -n -c (8 > 12) || (5 > 2) ==> 1 + /eval -n -c (8 > 12) && (5 > 2) ==> 0 + /eval -n -c abcd =~ ^ABC ==> 1 + /eval -n -c abcd =~ (?-i)^ABC ==> 0 + /eval -n -c abcd =~ (?-i)^abc ==> 1 + /eval -n -c abcd !~ abc ==> 0 + /eval -n -c abcd =* a*d ==> 1 ---- [[command_weechat_filter]] diff --git a/doc/en/autogen/user/weechat_commands.adoc b/doc/en/autogen/user/weechat_commands.adoc index 89aad68d6..2cf3cf4c1 100644 --- a/doc/en/autogen/user/weechat_commands.adoc +++ b/doc/en/autogen/user/weechat_commands.adoc @@ -308,9 +308,10 @@ Some variables are replaced in expression, using the format ${variable}, variabl 9. current date/time (format: "date" or "date:format") 10. an environment variable (format: "env:XXX") 11. a ternary operator (format: "if:condition?value_if_true:value_if_false") - 12. an option (format: "file.section.option") - 13. a local variable in buffer - 14. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. + 12. result of an expression with parentheses and operators + - * / // % (format: "calc:xxx") + 13. an option (format: "file.section.option") + 14. a local variable in buffer + 15. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. Format for hdata can be one of following: hdata.var1.var2...: start with a hdata (pointer must be known), and ask variables one after one (other hdata can be followed) hdata[list].var1.var2...: start with a hdata using a list, for example: @@ -337,6 +338,7 @@ Examples (simple strings): /eval -n ${if:${info:term_width}>80?big:small} ==> big /eval -n ${rev:Hello} ==> olleH /eval -n ${repeat:5,-} ==> ----- + /eval -n ${calc:(5+2)*3} ==> 21 Examples (conditions): /eval -n -c ${window.buffer.number} > 2 ==> 0 diff --git a/doc/en/weechat_dev.en.adoc b/doc/en/weechat_dev.en.adoc index ce45c06e3..26a1030f8 100644 --- a/doc/en/weechat_dev.en.adoc +++ b/doc/en/weechat_dev.en.adoc @@ -120,6 +120,7 @@ WeeChat "core" is located in following directories: | core/ | Core functions: entry point, internal structures. |    wee-arraylist.c | Array lists. |    wee-backtrace.c | Display a backtrace after a crash. +|    wee-calc.c | Calculate result of expressions. |    wee-command.c | WeeChat core commands. |    wee-completion.c | Default completions. |    wee-config-file.c | Configuration file management. diff --git a/doc/en/weechat_plugin_api.en.adoc b/doc/en/weechat_plugin_api.en.adoc index f076adcd1..3a16f07c8 100644 --- a/doc/en/weechat_plugin_api.en.adoc +++ b/doc/en/weechat_plugin_api.en.adoc @@ -2148,7 +2148,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test" ==== string_eval_expression -_WeeChat ≥ 0.4.0, updated in 0.4.2, 0.4.3, 1.0, 1.1, 1.2, 1.3, 1.6, 1.8 and 2.0._ +_WeeChat ≥ 0.4.0, updated in 0.4.2, 0.4.3, 1.0, 1.1, 1.2, 1.3, 1.6, 1.8, 2.0 and 2.7._ Evaluate an expression and return result as a string. Special variables with format `+${variable}+` are expanded (see table below). @@ -2535,6 +2535,23 @@ expanded to last): `+${if:${info:term_width}>80?big:small}+` | `+big+` +| `+${calc:xxx}+` + + (_WeeChat ≥ 2.7_) | + Result of expression, where parentheses and the following operators are + supported: + + `+`: addition + + `-`: subtraction + + `*`: multiplication + + `/`: division + + `//`: result of division without fractional part + + `%`: remainder of division. | + `+${calc:5+2*3}+` + + `+${calc:(5+2)*3}+` + + `+${calc:9.2%3}+` | + `+11+` + + `+21+` + + `+0.2+` + | `+${sec.data.name}+` | Value of the secured data `name`. | `+${sec.data.freenode_pass}+` | diff --git a/doc/fr/autogen/user/weechat_commands.adoc b/doc/fr/autogen/user/weechat_commands.adoc index 816a58bf5..11b23825a 100644 --- a/doc/fr/autogen/user/weechat_commands.adoc +++ b/doc/fr/autogen/user/weechat_commands.adoc @@ -308,9 +308,10 @@ Des variables sont remplacées dans l'expression, en utilisant le format ${varia 9. la date/heure courante (format : "date" ou "date:format") 10. une variable d'environnement (format : "env:XXX") 11. un opérateur ternaire (format : "if:condition?valeur_si_vrai:valeur_si_faux") - 12. une option (format : "fichier.section.option") - 13. une variable locale du tampon - 14. un hdata/variable (la valeur est automatiquement convertie en chaîne), par défaut "window" et "buffer" pointent vers la fenêtre et le tampon courants. + 12. le résultat d'une expression avec parenthèses et les opérateurs + - * / // % (format: "calc:xxx") + 13. une option (format : "fichier.section.option") + 14. une variable locale du tampon + 15. un hdata/variable (la valeur est automatiquement convertie en chaîne), par défaut "window" et "buffer" pointent vers la fenêtre et le tampon courants. Le format du hdata peut être le suivant : hdata.var1.var2... : démarrer avec un hdata (le pointeur doit être connu), et demander les variables l'une après l'autre (d'autres hdata peuvent être suivis) hdata[liste].var1.var2... : démarrer avec un hdata en utilisant une liste, par exemple : @@ -337,6 +338,7 @@ Exemples (chaînes simples) : /eval -n ${if:${info:term_width}>80?big:small} ==> big /eval -n ${rev:Hello} ==> olleH /eval -n ${repeat:5,-} ==> ----- + /eval -n ${calc:(5+2)*3} ==> 21 Exemples (conditions) : /eval -n -c ${window.buffer.number} > 2 ==> 0 diff --git a/doc/fr/weechat_dev.fr.adoc b/doc/fr/weechat_dev.fr.adoc index 6dfa34114..a94037c98 100644 --- a/doc/fr/weechat_dev.fr.adoc +++ b/doc/fr/weechat_dev.fr.adoc @@ -122,6 +122,7 @@ Le cœur de WeeChat est situé dans les répertoires suivants : | core/ | Fonctions du cœur : point d'entrée, structures internes. |    wee-arraylist.c | Listes avec tableau (« arraylists »). |    wee-backtrace.c | Afficher une trace après un plantage. +|    wee-calc.c | Calcul du résultat d'expressions. |    wee-command.c | Commandes du cœur de WeeChat. |    wee-completion.c | Complétions par défaut. |    wee-config-file.c | Gestion des fichiers de configuration. diff --git a/doc/fr/weechat_plugin_api.fr.adoc b/doc/fr/weechat_plugin_api.fr.adoc index 0f21e7879..9ee096314 100644 --- a/doc/fr/weechat_plugin_api.fr.adoc +++ b/doc/fr/weechat_plugin_api.fr.adoc @@ -2188,7 +2188,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test" ==== string_eval_expression -_WeeChat ≥ 0.4.0, mis à jour dans la 0.4.2, 0.4.3, 1.0, 1.1, 1.2, 1.3, 1.6, 1.8 et 2.0._ +_WeeChat ≥ 0.4.0, mis à jour dans la 0.4.2, 0.4.3, 1.0, 1.1, 1.2, 1.3, 1.6, 1.8, 2.0 et 2.7._ Évaluer l'expression et retourner le résultat sous forme de chaîne. Les variables spéciales avec le format `+${variable}+` sont étendues (voir le @@ -2582,6 +2582,23 @@ première étendue à la dernière) : `+${if:${info:term_width}>80?grand:petit}+` | `+grand+` +| `+${calc:xxx}+` + + (_WeeChat ≥ 2.7_) | + Résultat de l'expression, où les parenthèses et les opérateurs suivants sont + supportés : + + `+` : addition + + `-` : soustraction + + `*` : multiplication + + `/` : division + + `//` : résultat de la division sans la partie décimale + + `%` : reste de la division. | + `+${calc:5+2*3}+` + + `+${calc:(5+2)*3}+` + + `+${calc:9.2%3}+` | + `+11+` + + `+21+` + + `+0.2+` + | `+${sec.data.nom}+` | Valeur de la donnée sécurisée `nom`. | `+${sec.data.freenode_pass}+` | diff --git a/doc/it/autogen/user/weechat_commands.adoc b/doc/it/autogen/user/weechat_commands.adoc index ff30151bc..ee8b89271 100644 --- a/doc/it/autogen/user/weechat_commands.adoc +++ b/doc/it/autogen/user/weechat_commands.adoc @@ -308,9 +308,10 @@ Some variables are replaced in expression, using the format ${variable}, variabl 9. current date/time (format: "date" or "date:format") 10. an environment variable (format: "env:XXX") 11. a ternary operator (format: "if:condition?value_if_true:value_if_false") - 12. an option (format: "file.section.option") - 13. a local variable in buffer - 14. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. + 12. result of an expression with parentheses and operators + - * / // % (format: "calc:xxx") + 13. an option (format: "file.section.option") + 14. a local variable in buffer + 15. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. Format for hdata can be one of following: hdata.var1.var2...: start with a hdata (pointer must be known), and ask variables one after one (other hdata can be followed) hdata[list].var1.var2...: start with a hdata using a list, for example: @@ -337,6 +338,7 @@ Examples (simple strings): /eval -n ${if:${info:term_width}>80?big:small} ==> big /eval -n ${rev:Hello} ==> olleH /eval -n ${repeat:5,-} ==> ----- + /eval -n ${calc:(5+2)*3} ==> 21 Examples (conditions): /eval -n -c ${window.buffer.number} > 2 ==> 0 diff --git a/doc/it/weechat_plugin_api.it.adoc b/doc/it/weechat_plugin_api.it.adoc index f45eb124f..1b547fa40 100644 --- a/doc/it/weechat_plugin_api.it.adoc +++ b/doc/it/weechat_plugin_api.it.adoc @@ -2242,7 +2242,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test" ==== string_eval_expression // TRANSLATION MISSING -_WeeChat ≥ 0.4.0, updated in 0.4.2, 0.4.3, 1.0, 1.1, 1.2, 1.3, 1.6, 1.8 and 2.0._ +_WeeChat ≥ 0.4.0, updated in 0.4.2, 0.4.3, 1.0, 1.1, 1.2, 1.3, 1.6, 1.8, 2.0 and 2.7._ // TRANSLATION MISSING Evaluate an expression and return result as a string. @@ -2642,6 +2642,24 @@ expanded to last): `+${if:${info:term_width}>80?big:small}+` | `+big+` +// TRANSLATION MISSING +| `+${calc:xxx}+` + + (_WeeChat ≥ 2.7_) | + Result of expression, where parentheses and the following operators are + supported: + + `+`: addition + + `-`: subtraction + + `*`: multiplication + + `/`: division + + `//`: result of division without fractional part + + `%`: remainder of division. | + `+${calc:5+2*3}+` + + `+${calc:(5+2)*3}+` + + `+${calc:9.2%3}+` | + `+11+` + + `+21+` + + `+0.2+` + | `+${sec.data.name}+` | Value of the secured data `name`. | `+${sec.data.freenode_pass}+` | diff --git a/doc/ja/autogen/user/weechat_commands.adoc b/doc/ja/autogen/user/weechat_commands.adoc index 2f7e6ee2d..e1bce21d2 100644 --- a/doc/ja/autogen/user/weechat_commands.adoc +++ b/doc/ja/autogen/user/weechat_commands.adoc @@ -266,61 +266,62 @@ infolists: インフォリストに関する情報を表示 /eval [-n|-s] [-n] -c - -n: 結果をバッファに送信せずに表示 (デバッグモード) - -s: 評価前に式を分割する (複数のコマンドを指定する場合はセミコロンで区切ってください) - -c: 条件として評価: 演算子と括弧をを使い、ブール値 ("0" または "1") を返します -expression: 評価する式、${variable} 型の書式の変数は置換されます (以下を参照してください); 複数のコマンドを指定する場合はセミコロンで区切ってください - operator: 論理演算子や比較演算子: - - 論理演算子: - && ブール演算の "and" - || ブール演算の "or" - - 比較演算子: - == 等しい - != 等しくない - <= 以下 - < より少ない - >= 以上 - > より大きい - =~ POSIX 拡張正規表現にマッチ - !~ POSIX 拡張正規表現にマッチしない - =* マスクにマッチ (ワイルドカード "*" を使えます) - !* マスクにマッチしない (ワイルドカード "*" を使えます) + -n: display result without sending it to buffer (debug mode) + -s: split expression before evaluating it (many commands can be separated by semicolons) + -c: evaluate as condition: use operators and parentheses, return a boolean value ("0" or "1") +expression: expression to evaluate, variables with format ${variable} are replaced (see below); many commands can be separated by semicolons + operator: a logical or comparison operator: + - logical operators: + && boolean "and" + || boolean "or" + - comparison operators: + == equal + != not equal + <= less or equal + < less + >= greater or equal + > greater + =~ is matching POSIX extended regex + !~ is NOT matching POSIX extended regex + =* is matching mask (wildcard "*" is allowed) + !* is NOT matching mask (wildcard "*" is allowed) -式が NULL でない場合、空でない場合、"0" でない場合、式は "真" と評価されます。 -浮動小数点数として比較される数値表現の書式は以下です: - - 整数 (例: 5、-7) - - 浮動小数点数 (例: 5.2、-7.5、2.83e-2) - - 16 進数 (例: 0xA3、-0xA3) -数値表現を二重引用符で括ることで、文字列として比較されます。例: +An expression is considered as "true" if it is not NULL, not empty, and different from "0". +The comparison is made using floating point numbers if the two expressions are valid numbers, with one of the following formats: + - integer (examples: 5, -7) + - floating point number (examples: 5.2, -7.5, 2.83e-2) + - hexadecimal number (examples: 0xA3, -0xA3) +To force a string comparison, you can add double quotes around each expression, for example: 50 > 100 ==> 0 "50" > "100" ==> 1 -式中の ${variable} 型の書式の変数は置換されます。変数は以下の優先順位に従います: - 1. 評価済みのサブ文字列 (書式: "eval:xxx") - 2. エスケープ文字を含む文字列 (書式: "esc:xxx" または "\xxx") - 3. 隠す文字を含む文字列 (書式: "hide:char,string") - 4. 最大文字数を指定した文字列 (書式: "cut:max,suffix,string" または "cut:+max,suffix,string") - または最大文字表示幅を指定した文字列 (書式: "cutscr:max,suffix,string" または "cutscr:+max,suffix,string") - 5. 文字順を反転させた文字列 (書式: "rev:xxx") - 6. 繰り返し文字列 (書式: "repeat:count,string") - 7. 色 (書式: "color:xxx"、"プラグイン API リファレンス" の "color" 関数を参照してください) - 8. 情報 (書式: "info:name,arguments"、arguments は任意) - 9. 現在の日付/時刻 (書式: "date" または "date:format") - 10. 環境変数 (書式: "env:XXX") - 11. 三項演算子 (書式: "if:condition?value_if_true:value_if_false") - 12. オプション (書式: "file.section.option") - 13. バッファのローカル変数 - 14. hdata の名前/変数 (値は自動的に文字列に変換されます)、デフォルトでは "window" と "buffer" は現在のウィンドウ/バッファを指します。 -hdata の書式は以下の 1 つです: - hdata.var1.var2...: hdata (ポインタは既知) で開始し、1 個ずつ変数を続ける (他の hdata を続けることも可能) - hdata[list].var1.var2...: リストを使う hdata で開始する、例: - ${buffer[gui_buffers].full_name}: バッファリストにリンクされた最初のバッファのフルネーム - ${plugin[weechat_plugins].name}: プラグインリストにリンクされた最初のプラグインの名前 - hdata[pointer].var1.var2...: ポインタを使う hdata で開始する、例: - ${buffer[0x1234abcd].full_name}: 与えたポインタを持つバッファの完全な名前 (トリガ中で使うことが可能です) -hdata と変数の名前については、"プラグイン API リファレンス" の "weechat_hdata_get" 関数を参照してください。 +Some variables are replaced in expression, using the format ${variable}, variable can be, by order of priority: + 1. an evaluated sub-string (format: "eval:xxx") + 2. a string with escaped chars (format: "esc:xxx" or "\xxx") + 3. a string with chars to hide (format: "hide:char,string") + 4. a string with max chars (format: "cut:max,suffix,string" or "cut:+max,suffix,string") + or max chars displayed on screen (format: "cutscr:max,suffix,string" or "cutscr:+max,suffix,string") + 5. a reversed string (format: "rev:xxx") + 6. a repeated string (format: "repeat:count,string") + 7. a color (format: "color:xxx", see "Plugin API reference", function "color") + 8. an info (format: "info:name,arguments", arguments are optional) + 9. current date/time (format: "date" or "date:format") + 10. an environment variable (format: "env:XXX") + 11. a ternary operator (format: "if:condition?value_if_true:value_if_false") + 12. result of an expression with parentheses and operators + - * / // % (format: "calc:xxx") + 13. an option (format: "file.section.option") + 14. a local variable in buffer + 15. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. +Format for hdata can be one of following: + hdata.var1.var2...: start with a hdata (pointer must be known), and ask variables one after one (other hdata can be followed) + hdata[list].var1.var2...: start with a hdata using a list, for example: + ${buffer[gui_buffers].full_name}: full name of first buffer in linked list of buffers + ${plugin[weechat_plugins].name}: name of first plugin in linked list of plugins + hdata[pointer].var1.var2...: start with a hdata using a pointer, for example: + ${buffer[0x1234abcd].full_name}: full name of the buffer with this pointer (can be used in triggers) +For name of hdata and variables, please look at "Plugin API reference", function "weechat_hdata_get". -例 (単純な文字列): +Examples (simple strings): /eval -n ${info:version} ==> 0.4.3 /eval -n ${env:HOME} ==> /home/user /eval -n ${weechat.look.scroll_amount} ==> 3 @@ -335,10 +336,11 @@ hdata と変数の名前については、"プラグイン API リファレン /eval -n ${cut:+3,+,test} ==> te+ /eval -n ${date:%H:%M:%S} ==> 07:46:40 /eval -n ${if:${info:term_width}>80?big:small} ==> big - /eval -n ${rev:Hello} ==> olleH\n" - /eval -n ${repeat:5,-} ==> -----\n" + /eval -n ${rev:Hello} ==> olleH + /eval -n ${repeat:5,-} ==> ----- + /eval -n ${calc:(5+2)*3} ==> 21 -例 (条件): +Examples (conditions): /eval -n -c ${window.buffer.number} > 2 ==> 0 /eval -n -c ${window.win_width} > 100 ==> 1 /eval -n -c (8 > 12) || (5 > 2) ==> 1 diff --git a/doc/ja/weechat_dev.ja.adoc b/doc/ja/weechat_dev.ja.adoc index f914e6b88..c80138d16 100644 --- a/doc/ja/weechat_dev.ja.adoc +++ b/doc/ja/weechat_dev.ja.adoc @@ -126,6 +126,8 @@ WeeChat "core" は以下のディレクトリに配置されています: | core/ | コア関数: エントリポイント、内部構造体 |    wee-arraylist.c | 配列リスト |    wee-backtrace.c | クラッシュした際にバックトレースを表示 +// TRANSLATION MISSING +|    wee-calc.c | Calculate result of expressions. |    wee-command.c | WeeChat コアコマンド |    wee-completion.c | デフォルト補完 |    wee-config-file.c | 設定ファイル管理 diff --git a/doc/ja/weechat_plugin_api.ja.adoc b/doc/ja/weechat_plugin_api.ja.adoc index b3a7658d5..d26562c93 100644 --- a/doc/ja/weechat_plugin_api.ja.adoc +++ b/doc/ja/weechat_plugin_api.ja.adoc @@ -2161,7 +2161,7 @@ str3 = weechat.string_input_for_buffer("//test") # "/test" ==== string_eval_expression -_WeeChat バージョン 0.4.0 以上で利用可、バージョン 0.4.2、0.4.3、1.0、1.1、1.2、1.3、1.6、1.8、2.0 で更新。_ +_WeeChat バージョン 0.4.0 以上で利用可、バージョン 0.4.2、0.4.3、1.0、1.1、1.2、1.3、1.6、1.8、2.0, 2.7 で更新。_ 式を評価して文字列として返す。`+${variable}+` という書式で書かれた特殊変数は展開されます (以下の表を参照)。 @@ -2546,6 +2546,24 @@ str5 = weechat.string_eval_expression("password=abc password=def", {}, {}, optio `+${if:${info:term_width}>80?big:small}+` | `+big+` +// TRANSLATION MISSING +| `+${calc:xxx}+` + + (_WeeChat バージョン 2.7 以上で利用可_) | + Result of expression, where parentheses and the following operators are + supported: + + `+`: addition + + `-`: subtraction + + `*`: multiplication + + `/`: division + + `//`: result of division without fractional part + + `%`: remainder of division. | + `+${calc:5+2*3}+` + + `+${calc:(5+2)*3}+` + + `+${calc:9.2%3}+` | + `+11+` + + `+21+` + + `+0.2+` + | `+${sec.data.name}+` | セキュアデータ `name` の値 | `+${sec.data.freenode_pass}+` | diff --git a/doc/pl/autogen/user/weechat_commands.adoc b/doc/pl/autogen/user/weechat_commands.adoc index a4019d33b..d1dc22327 100644 --- a/doc/pl/autogen/user/weechat_commands.adoc +++ b/doc/pl/autogen/user/weechat_commands.adoc @@ -266,65 +266,66 @@ infolists: wyświetla informacje o infolistach /eval [-n|-s] [-n] -c - -n: wyświetla wynik bez wysyłania go do buforu (tryb debugowania) - -s: podziel wyrażenie przed przetworzeniem go (wiele komend może być oddzielonych średnikami) - -c: przetwarza jako warunek: użyj operatorów i nawiasów, zwraca wartość logiczną ("0" lub "1") -wyrażenie: wyrażenie do przetworzenia, zmienne o formacie ${zmienna} są zastępowane (zobacz niżej); wiele komend można oddzielić średnikami - operator: operator logiczny lub porównania: - - operatory logiczne: - && logiczne "i" - || logiczne "lub" - - operatory porównania: - == równy - != różny - <= mniejszy lub równy - < mniejszy - >= większy lub równy - > większy - =~ pasuje do rozszerzonego wyrażenia regularnego POSIX - !~ NIE pasuje do rozszerzonego wyrażenia regularnego POSIX - =* pasuje do maski (dzika karta "*" dozwolona) - !* NIE pasuje do maski (dzika karta "*" dozwolona) + -n: display result without sending it to buffer (debug mode) + -s: split expression before evaluating it (many commands can be separated by semicolons) + -c: evaluate as condition: use operators and parentheses, return a boolean value ("0" or "1") +expression: expression to evaluate, variables with format ${variable} are replaced (see below); many commands can be separated by semicolons + operator: a logical or comparison operator: + - logical operators: + && boolean "and" + || boolean "or" + - comparison operators: + == equal + != not equal + <= less or equal + < less + >= greater or equal + > greater + =~ is matching POSIX extended regex + !~ is NOT matching POSIX extended regex + =* is matching mask (wildcard "*" is allowed) + !* is NOT matching mask (wildcard "*" is allowed) -Wyrażenie jest uznawane za „prawdziwe” jeśli nie jest NULL, nie jest puste, oraz różni się od "0". -Porównania dokonuje się z użyciem liczb całkowitych jeśli oba wyrażenia są liczbami całkowitymi, w jednym z następujących formatów: - - liczby całkowite (przykłady: 5, -7) - - liczby zmiennoprzecinkowe (przykłady: 5.2, -7.5, 2.83e-2) - - liczby szesnastkowe (przykłady: 0xA3, -0xA3) -W celu wymuszenia porównywania ciągów, należy umieścić każde wyrażenie w cudzysłowie, na przykład: +An expression is considered as "true" if it is not NULL, not empty, and different from "0". +The comparison is made using floating point numbers if the two expressions are valid numbers, with one of the following formats: + - integer (examples: 5, -7) + - floating point number (examples: 5.2, -7.5, 2.83e-2) + - hexadecimal number (examples: 0xA3, -0xA3) +To force a string comparison, you can add double quotes around each expression, for example: 50 > 100 ==> 0 "50" > "100" ==> 1 -Niektóre zmienne w wyrażeniach są zamieniane, poprzez zastosowanie formatu ${zmienna}, według priorytetu zmienną może być: - 1. przetworzony ciąg (format: "eval:xxx") - 2. ciąg z wyescapowanymi znakami (format: "esc:xxx" lub "\xxx") - 3. ciąg ze znakami do ukrycia (format: "hide:char,string") - 4. ciąg o maksymalnej długości (format: "cut:max,suffix,string" lub "cut:+max,suffix,string") - lub maksymalna ilość znaków wyświetlanych na ekranie (format: "cutscr:max,suffix,string" lub "cutscr:+max,suffix,string") - 5. odwrócony ciąg (format: "rev:xxx") - 6. powtórzony ciąg (format: "repeat:ilość,ciąg") - 7. kolor (format "color:xxx", zobacz „Opis API wtyczek”, funkcja "color") - 8. informacja (format: "info:nazwa,argumenty", argumenty są opcjonalne) - 9. obecna data/czas (format: "date" lub "date:format") - 10. zmienna środowiskowa (format: "env:XXX") - 11. wyrażenie warunkowe (format: "if:condition?value_if_true:value_if_false") - 12. opcja (format: plik.sekcja.opcja) - 13. zmienna lokalna w buforze - 14. nazwa hdatay/zmiennej (wartość jest automatycznie konwertowana na ciąg znaków), domyślnie "window" i "buffer" wskazują na aktualne okno/bufor. -Format dla hdata może być jednym z poniższych: - hdata.zmienna1.zmienna2...: inicjuje hdata (wskaźnik musi być znany), następnie wypytuje zmienne jedna po drugiej (inne hdata mogą być następne) - hdata[list].zmienna1.zmienna2...: inicjuje hdata z wykorzystaniem listy, na przykład: - ${buffer[gui_buffers].full_name}: pełna nazwa buforu w połączonej liście buforów - ${plugin[weechat_plugins].name}: nazwa pierwszej wtyczki w połączonej liście wtyczek - hdata[wskaźnik].zmienna1.zmienna2...: zacznij z hdata używając wskaźnika, na przykład: - ${buffer[0x1234abcd].full_name}: pełna nazwa buforu z tym wskaźnikiem (może zostać użyte w triggerach) -Nazwy hdata i zmiennych, można znaleźć w „Opisie API wtyczek”, funkcja "weechat_hdata_get". +Some variables are replaced in expression, using the format ${variable}, variable can be, by order of priority: + 1. an evaluated sub-string (format: "eval:xxx") + 2. a string with escaped chars (format: "esc:xxx" or "\xxx") + 3. a string with chars to hide (format: "hide:char,string") + 4. a string with max chars (format: "cut:max,suffix,string" or "cut:+max,suffix,string") + or max chars displayed on screen (format: "cutscr:max,suffix,string" or "cutscr:+max,suffix,string") + 5. a reversed string (format: "rev:xxx") + 6. a repeated string (format: "repeat:count,string") + 7. a color (format: "color:xxx", see "Plugin API reference", function "color") + 8. an info (format: "info:name,arguments", arguments are optional) + 9. current date/time (format: "date" or "date:format") + 10. an environment variable (format: "env:XXX") + 11. a ternary operator (format: "if:condition?value_if_true:value_if_false") + 12. result of an expression with parentheses and operators + - * / // % (format: "calc:xxx") + 13. an option (format: "file.section.option") + 14. a local variable in buffer + 15. a hdata name/variable (the value is automatically converted to string), by default "window" and "buffer" point to current window/buffer. +Format for hdata can be one of following: + hdata.var1.var2...: start with a hdata (pointer must be known), and ask variables one after one (other hdata can be followed) + hdata[list].var1.var2...: start with a hdata using a list, for example: + ${buffer[gui_buffers].full_name}: full name of first buffer in linked list of buffers + ${plugin[weechat_plugins].name}: name of first plugin in linked list of plugins + hdata[pointer].var1.var2...: start with a hdata using a pointer, for example: + ${buffer[0x1234abcd].full_name}: full name of the buffer with this pointer (can be used in triggers) +For name of hdata and variables, please look at "Plugin API reference", function "weechat_hdata_get". -Przykłady (proste ciągi): +Examples (simple strings): /eval -n ${info:version} ==> 0.4.3 /eval -n ${env:HOME} ==> /home/user /eval -n ${weechat.look.scroll_amount} ==> 3 - /eval -n ${sec.data.freenode_password} ==> secret + /eval -n ${sec.data.freenode_password} ==> secret /eval -n ${window} ==> 0x2549aa0 /eval -n ${window.buffer} ==> 0x2549320 /eval -n ${window.buffer.full_name} ==> core.weechat @@ -337,8 +338,9 @@ Przykłady (proste ciągi): /eval -n ${if:${info:term_width}>80?big:small} ==> big /eval -n ${rev:Hello} ==> olleH /eval -n ${repeat:5,-} ==> ----- + /eval -n ${calc:(5+2)*3} ==> 21 -Przykłady (warunki): +Examples (conditions): /eval -n -c ${window.buffer.number} > 2 ==> 0 /eval -n -c ${window.win_width} > 100 ==> 1 /eval -n -c (8 > 12) || (5 > 2) ==> 1 diff --git a/po/POTFILES.in b/po/POTFILES.in index 6d13cb5c2..0e93fe0c8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -41,6 +41,8 @@ ./src/core/wee-backtrace.h ./src/core/weechat.c ./src/core/weechat.h +./src/core/wee-calc.c +./src/core/wee-calc.h ./src/core/wee-command.c ./src/core/wee-command.h ./src/core/wee-completion.c diff --git a/po/cs.po b/po/cs.po index e6ec8150a..6375c9f9a 100644 --- a/po/cs.po +++ b/po/cs.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:31+0200\n" "Last-Translator: Ondřej Súkup \n" "Language-Team: weechat-dev \n" @@ -1579,9 +1579,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1616,6 +1618,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/de.po b/po/de.po index 1f1dda2f5..ea0d1736e 100644 --- a/po/de.po +++ b/po/de.po @@ -24,7 +24,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-09-19 21:52+0200\n" "Last-Translator: Nils Görs \n" "Language-Team: German \n" @@ -1661,6 +1661,7 @@ msgstr "evaluierter Ausdruck" msgid "[-n|-s] || [-n] -c " msgstr "[-n|-s] || [-n] -c " +#, fuzzy msgid "" " -n: display result without sending it to buffer (debug mode)\n" " -s: split expression before evaluating it (many commands can be " @@ -1715,9 +1716,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1752,6 +1755,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/es.po b/po/es.po index 2cff2f72e..fa951f724 100644 --- a/po/es.po +++ b/po/es.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:31+0200\n" "Last-Translator: Elián Hanisch \n" "Language-Team: weechat-dev \n" @@ -1610,9 +1610,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1647,6 +1649,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/fr.po b/po/fr.po index 1a717c4f2..0491a7839 100644 --- a/po/fr.po +++ b/po/fr.po @@ -21,8 +21,8 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" -"PO-Revision-Date: 2019-09-19 21:18+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" +"PO-Revision-Date: 2019-09-20 21:32+0200\n" "Last-Translator: Sébastien Helleu \n" "Language-Team: weechat-dev \n" "Language: fr\n" @@ -1684,9 +1684,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1721,6 +1723,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" @@ -1793,9 +1796,11 @@ msgstr "" " 10. une variable d'environnement (format : \"env:XXX\")\n" " 11. un opérateur ternaire (format : \"if:condition?valeur_si_vrai:" "valeur_si_faux\")\n" -" 12. une option (format : \"fichier.section.option\")\n" -" 13. une variable locale du tampon\n" -" 14. un hdata/variable (la valeur est automatiquement convertie en chaîne), " +" 12. le résultat d'une expression avec parenthèses et les opérateurs + - " +"* / // % (format: \"calc:xxx\")\n" +" 13. une option (format : \"fichier.section.option\")\n" +" 14. une variable locale du tampon\n" +" 15. un hdata/variable (la valeur est automatiquement convertie en chaîne), " "par défaut \"window\" et \"buffer\" pointent vers la fenêtre et le tampon " "courants.\n" "Le format du hdata peut être le suivant :\n" @@ -1832,6 +1837,7 @@ msgstr "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Exemples (conditions) :\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/hu.po b/po/hu.po index 7ea4bc4a4..b68812891 100644 --- a/po/hu.po +++ b/po/hu.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:31+0200\n" "Last-Translator: Andras Voroskoi \n" "Language-Team: weechat-dev \n" @@ -1498,9 +1498,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1535,6 +1537,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/it.po b/po/it.po index ac9deb062..84b520900 100644 --- a/po/it.po +++ b/po/it.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:31+0200\n" "Last-Translator: Esteban I. Ruiz Moreno \n" "Language-Team: weechat-dev \n" @@ -1593,9 +1593,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1630,6 +1632,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/ja.po b/po/ja.po index 98447ffba..df75bbb66 100644 --- a/po/ja.po +++ b/po/ja.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-06-22 08:40+0200\n" "Last-Translator: AYANOKOUZI, Ryuunosuke \n" "Language-Team: Japanese || [-n] -c " msgstr "[-n|-s] || [-n] -c " +#, fuzzy msgid "" " -n: display result without sending it to buffer (debug mode)\n" " -s: split expression before evaluating it (many commands can be " @@ -1646,9 +1647,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1683,6 +1686,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/pl.po b/po/pl.po index 90ca27874..d8c252585 100644 --- a/po/pl.po +++ b/po/pl.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-09-07 15:45+0200\n" "Last-Translator: Krzysztof Korościk \n" "Language-Team: Polish \n" @@ -1627,6 +1627,7 @@ msgstr "przetwórz wyrażenie" msgid "[-n|-s] || [-n] -c " msgstr "[-n|-s] || [-n] -c " +#, fuzzy msgid "" " -n: display result without sending it to buffer (debug mode)\n" " -s: split expression before evaluating it (many commands can be " @@ -1681,9 +1682,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1718,6 +1721,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/pt.po b/po/pt.po index 1b03cff4f..f10038ea7 100644 --- a/po/pt.po +++ b/po/pt.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:32+0200\n" "Last-Translator: Vasco Almeida \n" "Language-Team: Portuguese <>\n" @@ -1651,9 +1651,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1688,6 +1690,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/pt_BR.po b/po/pt_BR.po index 0ff39e353..85ad42c6e 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:32+0200\n" "Last-Translator: Eduardo Elias \n" "Language-Team: weechat-dev \n" @@ -1655,9 +1655,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1692,6 +1694,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/ru.po b/po/ru.po index a8b1573fc..5cb6cd347 100644 --- a/po/ru.po +++ b/po/ru.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:32+0200\n" "Last-Translator: Aleksey V Zapparov AKA ixti \n" "Language-Team: weechat-dev \n" @@ -1520,9 +1520,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1557,6 +1559,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/srcfiles.cmake b/po/srcfiles.cmake index 037ee003e..d22fd914f 100644 --- a/po/srcfiles.cmake +++ b/po/srcfiles.cmake @@ -42,6 +42,8 @@ SET(WEECHAT_SOURCES ./src/core/wee-backtrace.h ./src/core/weechat.c ./src/core/weechat.h +./src/core/wee-calc.c +./src/core/wee-calc.h ./src/core/wee-command.c ./src/core/wee-command.h ./src/core/wee-completion.c diff --git a/po/tr.po b/po/tr.po index 5f8c8419d..15864b93d 100644 --- a/po/tr.po +++ b/po/tr.po @@ -20,7 +20,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2019-05-13 21:32+0200\n" "Last-Translator: Hasan Kiran \n" "Language-Team: weechat-dev \n" @@ -1351,9 +1351,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1388,6 +1390,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/po/weechat.pot b/po/weechat.pot index 6a4a5fd91..ef514054a 100644 --- a/po/weechat.pot +++ b/po/weechat.pot @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2019-09-19 21:16+0200\n" +"POT-Creation-Date: 2019-09-20 21:31+0200\n" "PO-Revision-Date: 2014-08-16 10:27+0200\n" "Last-Translator: Sébastien Helleu \n" "Language-Team: weechat-dev \n" @@ -1353,9 +1353,11 @@ msgid "" " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: \"if:condition?value_if_true:value_if_false" "\")\n" -" 12. an option (format: \"file.section.option\")\n" -" 13. a local variable in buffer\n" -" 14. a hdata name/variable (the value is automatically converted to " +" 12. result of an expression with parentheses and operators + - * / // % " +"(format: \"calc:xxx\")\n" +" 13. an option (format: \"file.section.option\")\n" +" 14. a local variable in buffer\n" +" 15. a hdata name/variable (the value is automatically converted to " "string), by default \"window\" and \"buffer\" point to current window/" "buffer.\n" "Format for hdata can be one of following:\n" @@ -1390,6 +1392,7 @@ msgid "" " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" +" /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 55c1e1112..b37a5522e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,6 +23,7 @@ set(LIB_CORE_SRC weechat.c weechat.h wee-arraylist.c wee-arraylist.h wee-backtrace.c wee-backtrace.h + wee-calc.c wee-calc.h wee-command.c wee-command.h wee-completion.c wee-completion.h wee-config.c wee-config.h diff --git a/src/core/Makefile.am b/src/core/Makefile.am index df03ed39a..82f364b32 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -27,6 +27,8 @@ lib_weechat_core_a_SOURCES = weechat.c \ wee-arraylist.h \ wee-backtrace.c \ wee-backtrace.h \ + wee-calc.c \ + wee-calc.h \ wee-command.c \ wee-command.h \ wee-completion.c \ diff --git a/src/core/wee-calc.c b/src/core/wee-calc.c new file mode 100644 index 000000000..80797d775 --- /dev/null +++ b/src/core/wee-calc.c @@ -0,0 +1,355 @@ +/* + * wee-calc.c - calculate result of an expression + * + * Copyright (C) 2019 Sébastien Helleu + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 WeeChat. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "weechat.h" +#include "wee-arraylist.h" +#include "wee-string.h" + + +/* + * Callback called to free a value or op in the arraylist. + */ + +void +calc_list_free_cb (void *data, struct t_arraylist *arraylist, void *pointer) +{ + /* make C compiler happy */ + (void) data; + (void) arraylist; + + free (pointer); +} + +/* + * Returns the precedence of an operator: + * - '*' and '/': 2 + * - '+' and '-': 1 + * - any other: 0 + */ + +int +calc_operator_precedence (char *operator) +{ + if (!operator) + return 0; + + if ((strcmp (operator, "*") == 0) + || (strcmp (operator, "/") == 0) + || (strcmp (operator, "//") == 0) + || (strcmp (operator, "%") == 0)) + { + return 2; + } + + if ((strcmp (operator, "+") == 0) + || (strcmp (operator, "-") == 0)) + { + return 1; + } + + return 0; +} + +/* + * Pops an integer value from the stack of values. + */ + +double +calc_pop_value (struct t_arraylist *list_values) +{ + int size_values; + double *ptr_value, value; + + size_values = arraylist_size (list_values); + + if (size_values < 1) + return 0; + + ptr_value = arraylist_get (list_values, size_values - 1); + value = *ptr_value; + + arraylist_remove (list_values, size_values - 1); + + return value; +} + +/* + * Calculates result of an operation using an operator and two values. + */ + +double +calc_operation (char *operator, double value1, double value2) +{ + if (strcmp (operator, "+") == 0) + return value1 + value2; + + if (strcmp (operator, "-") == 0) + return value1 - value2; + + if (strcmp (operator, "*") == 0) + return value1 * value2; + + if (strcmp (operator, "/") == 0) + return (value2 != 0) ? value1 / value2 : 0; + + if (strcmp (operator, "//") == 0) + return (value2 != 0) ? floor (value1 / value2) : 0; + + if (strcmp (operator, "%") == 0) + return (value2 != 0) ? fmod (value1, value2) : 0; + + return 0; +} + +/* + * Calculates result of an operation using the operator on the operators stack + * and the two values on the values stack. + * + * The result is pushed on values stack. + */ + +void +calc_operation_stacks (struct t_arraylist *list_values, + struct t_arraylist *list_ops) +{ + int size_ops; + double value1, value2, result, *ptr_result; + char *ptr_operator; + + size_ops = arraylist_size (list_ops); + if (size_ops < 1) + return; + + ptr_operator = arraylist_get (list_ops, size_ops - 1); + + value2 = calc_pop_value (list_values); + value1 = calc_pop_value (list_values); + + result = calc_operation (ptr_operator, value1, value2); + + ptr_result = malloc (sizeof (result)); + *ptr_result = result; + arraylist_add (list_values, ptr_result); + + arraylist_remove (list_ops, size_ops - 1); +} + +/* + * Formats the result as a decimal number (locale independent): remove any + * extra "0" at the and the decimal point if needed. + */ + +void +calc_format_result (double value, char *result, int max_size) +{ + char *pos_point; + int i; + + /* + * local-independent formatting of value, so that a decimal point is always + * used (instead of a comma in French for example) + */ + setlocale (LC_ALL, "C"); + snprintf (result, max_size, "%.10f", value); + setlocale (LC_ALL, ""); + + pos_point = strchr (result, '.'); + + i = strlen (result) - 1; + while (i >= 0) + { + if (!isdigit (result[i]) && (result[i] != '-')) + { + result[i] = '\0'; + break; + } + if (pos_point && (result[i] == '0')) + { + result[i] = '\0'; + i--; + } + else + break; + } +} + +/* + * Calculates an expression, which can contain: + * - integer and decimal numbers (ie 2 or 2.5) + * - operators: + * +: addition + * -: subtraction + * *: multiplication + * /: division + * \: division giving an integer as result + * %: remainder of division + * - parentheses: ( ) + * + * The value returned is a string representation of the result, which can be + * an integer or a double, according to the operations and numbers in input. + * + * Note: result must be freed after use (if not NULL). + */ + +char * +calc_expression (const char *expr) +{ + struct t_arraylist *list_values, *list_ops; + char str_result[64], *ptr_operator, *operator; + int i, i2, index_op, decimals; + double value, *ptr_value; + + list_values = NULL; + list_ops = NULL; + + /* return 0 by default in case of error */ + snprintf (str_result, sizeof (str_result), "0"); + + if (!expr) + goto end; + + /* stack with values */ + list_values = arraylist_new (32, 0, 1, + NULL, NULL, + &calc_list_free_cb, NULL); + if (!list_values) + goto end; + + /* stack with operators */ + list_ops = arraylist_new (32, 0, 1, + NULL, NULL, + &calc_list_free_cb, NULL); + if (!list_ops) + goto end; + + for (i = 0; expr[i]; i++) + { + if (expr[i] == ' ') + { + /* ignore spaces */ + continue; + } + else if (expr[i] == '(') + { + ptr_operator = string_strndup (expr + i, 1); + arraylist_add (list_ops, ptr_operator); + } + else if (isdigit (expr[i]) || (expr[i] == '.')) + { + value = 0; + decimals = 0; + while (expr[i] && (isdigit (expr[i]) || (expr[i] == '.'))) + { + if (expr[i] == '.') + { + if (decimals == 0) + decimals = 10; + } + else + { + if (decimals) + { + value = value + (((double)(expr[i] - '0')) / decimals); + decimals *= 10; + } + else + { + value = (value * 10) + (expr[i] - '0'); + } + } + i++; + } + i--; + ptr_value = malloc (sizeof (value)); + *ptr_value = value; + arraylist_add (list_values, ptr_value); + } + else if (expr[i] == ')') + { + index_op = arraylist_size (list_ops) - 1; + while (index_op >= 0) + { + ptr_operator = arraylist_get (list_ops, index_op); + if (strcmp (ptr_operator, "(") == 0) + break; + calc_operation_stacks (list_values, list_ops); + index_op--; + } + /* remove "(" from operators */ + index_op = arraylist_size (list_ops) - 1; + if (index_op >= 0) + arraylist_remove (list_ops, index_op); + } + else + { + /* operator */ + i2 = i + 1; + while (expr[i2] && (expr[i2] != ' ') && (expr[i2] != '(') + && (expr[i2] != ')') && (expr[i2] != '.') + && !isdigit (expr[i2])) + { + i2++; + } + operator = string_strndup (expr + i, i2 - i); + i = i2 - 1; + if (operator) + { + index_op = arraylist_size (list_ops) - 1; + while (index_op >= 0) + { + ptr_operator = arraylist_get (list_ops, index_op); + if (calc_operator_precedence (ptr_operator) < + calc_operator_precedence (operator)) + break; + calc_operation_stacks (list_values, list_ops); + index_op--; + } + arraylist_add (list_ops, operator); + } + } + } + + while (arraylist_size (list_ops) > 0) + { + calc_operation_stacks (list_values, list_ops); + } + + value = calc_pop_value (list_values); + calc_format_result (value, str_result, sizeof (str_result)); + +end: + if (list_values) + arraylist_free (list_values); + if (list_ops) + arraylist_free (list_ops); + + return strdup (str_result); +} diff --git a/src/core/wee-calc.h b/src/core/wee-calc.h new file mode 100644 index 000000000..d4c308640 --- /dev/null +++ b/src/core/wee-calc.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Sébastien Helleu + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 WeeChat. If not, see . + */ + +#ifndef WEECHAT_CALC_H +#define WEECHAT_CALC_H + +extern char *calc_expression (const char *expr); + +#endif /* WEECHAT_CALC_H */ diff --git a/src/core/wee-command.c b/src/core/wee-command.c index 1f5debd96..d5d3846dd 100644 --- a/src/core/wee-command.c +++ b/src/core/wee-command.c @@ -7363,9 +7363,11 @@ command_init () " 10. an environment variable (format: \"env:XXX\")\n" " 11. a ternary operator (format: " "\"if:condition?value_if_true:value_if_false\")\n" - " 12. an option (format: \"file.section.option\")\n" - " 13. a local variable in buffer\n" - " 14. a hdata name/variable (the value is automatically converted " + " 12. result of an expression with parentheses and operators " + "+ - * / // % (format: \"calc:xxx\")\n" + " 13. an option (format: \"file.section.option\")\n" + " 14. a local variable in buffer\n" + " 15. a hdata name/variable (the value is automatically converted " "to string), by default \"window\" and \"buffer\" point to current " "window/buffer.\n" "Format for hdata can be one of following:\n" @@ -7401,6 +7403,7 @@ command_init () " /eval -n ${if:${info:term_width}>80?big:small} ==> big\n" " /eval -n ${rev:Hello} ==> olleH\n" " /eval -n ${repeat:5,-} ==> -----\n" + " /eval -n ${calc:(5+2)*3} ==> 21\n" "\n" "Examples (conditions):\n" " /eval -n -c ${window.buffer.number} > 2 ==> 0\n" diff --git a/src/core/wee-eval.c b/src/core/wee-eval.c index ab4ed94da..547a4406b 100644 --- a/src/core/wee-eval.c +++ b/src/core/wee-eval.c @@ -30,6 +30,7 @@ #include "weechat.h" #include "wee-eval.h" +#include "wee-calc.h" #include "wee-config-file.h" #include "wee-hashtable.h" #include "wee-hdata.h" @@ -301,9 +302,10 @@ end: * 11. current date/time (format: date or date:xxx) * 12. an environment variable (format: env:XXX) * 13. a ternary operator (format: if:condition?value_if_true:value_if_false) - * 14. an option (format: file.section.option) - * 15. a buffer local variable - * 16. a hdata variable (format: hdata.var1.var2 or hdata[list].var1.var2 + * 14. calculate result of an expression (format: calc:xxx) + * 15. an option (format: file.section.option) + * 16. a buffer local variable + * 17. a hdata variable (format: hdata.var1.var2 or hdata[list].var1.var2 * or hdata[ptr].var1.var2) * * See /help in WeeChat for examples. @@ -625,7 +627,16 @@ eval_replace_vars_cb (void *data, const char *text) return (value) ? value : strdup (""); } - /* 14. option: if found, return this value */ + /* + * 14. calculate the result of an expression + * (with number, operators and parentheses) + */ + if (strncmp (text, "calc:", 5) == 0) + { + return calc_expression (text + 5); + } + + /* 15. option: if found, return this value */ if (strncmp (text, "sec.data.", 9) == 0) { ptr_value = hashtable_get (secure_hashtable_data, text + 9); @@ -658,7 +669,7 @@ eval_replace_vars_cb (void *data, const char *text) } } - /* 15. local variable in buffer */ + /* 16. local variable in buffer */ ptr_buffer = hashtable_get (eval_context->pointers, "buffer"); if (ptr_buffer) { @@ -667,7 +678,7 @@ eval_replace_vars_cb (void *data, const char *text) return strdup (ptr_value); } - /* 16. hdata */ + /* 17. hdata */ value = NULL; hdata_name = NULL; list_name = NULL; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 41d174816..635b704c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,6 +26,7 @@ include_directories(${CPPUTEST_INCLUDE_DIRS} ${PROJECT_BINARY_DIR} ${PROJECT_SOU set(LIB_WEECHAT_UNIT_TESTS_CORE_SRC unit/test-plugins.cpp unit/core/test-core-arraylist.cpp + unit/core/test-core-calc.cpp unit/core/test-core-eval.cpp unit/core/test-core-hashtable.cpp unit/core/test-core-hdata.cpp diff --git a/tests/Makefile.am b/tests/Makefile.am index 1aef3830d..17641cecd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,6 +23,7 @@ noinst_LIBRARIES = lib_weechat_unit_tests_core.a lib_weechat_unit_tests_core_a_SOURCES = unit/test-plugins.cpp \ unit/core/test-core-arraylist.cpp \ + unit/core/test-core-calc.cpp \ unit/core/test-core-eval.cpp \ unit/core/test-core-hashtable.cpp \ unit/core/test-core-hdata.cpp \ diff --git a/tests/tests.cpp b/tests/tests.cpp index ac4c9e239..bf9902683 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -61,6 +61,7 @@ extern "C" /* core */ IMPORT_TEST_GROUP(Plugins); IMPORT_TEST_GROUP(CoreArraylist); +IMPORT_TEST_GROUP(CoreCalc); IMPORT_TEST_GROUP(CoreEval); IMPORT_TEST_GROUP(CoreHashtable); IMPORT_TEST_GROUP(CoreHdata); diff --git a/tests/unit/core/test-core-calc.cpp b/tests/unit/core/test-core-calc.cpp new file mode 100644 index 000000000..308708abe --- /dev/null +++ b/tests/unit/core/test-core-calc.cpp @@ -0,0 +1,111 @@ +/* + * test-core-calc.cpp - test calculations functions + * + * Copyright (C) 2019 Sébastien Helleu + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat 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 3 of the License, or + * (at your option) any later version. + * + * WeeChat 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 WeeChat. If not, see . + */ + +#include "CppUTest/TestHarness.h" + +extern "C" +{ +#include "src/core/wee-calc.h" +} + +#define WEE_CHECK_CALC(__result, __expr) \ + value = calc_expression (__expr); \ + STRCMP_EQUAL(__result, value); \ + free (value); + +TEST_GROUP(CoreCalc) +{ +}; + +/* + * Tests functions: + * calc_expression + */ + +TEST(CoreCalc, Expression) +{ + char *value; + + /* invalid expressions */ + WEE_CHECK_CALC("0", NULL); + WEE_CHECK_CALC("0", ""); + WEE_CHECK_CALC("0", "("); + WEE_CHECK_CALC("0", ")"); + WEE_CHECK_CALC("0", "+"); + WEE_CHECK_CALC("0", "-"); + WEE_CHECK_CALC("0", "*"); + WEE_CHECK_CALC("0", "/"); + WEE_CHECK_CALC("0", "0/0"); + WEE_CHECK_CALC("0", "0//0"); + WEE_CHECK_CALC("0", "0%0"); + + /* no operator */ + WEE_CHECK_CALC("123", "123"); + WEE_CHECK_CALC("-2", "-2"); + WEE_CHECK_CALC("1.5", "1.5"); + + /* addition */ + WEE_CHECK_CALC("-3", "-4+1"); + WEE_CHECK_CALC("3", "1+2"); + WEE_CHECK_CALC("4", " 1 + 3 "); + + /* subtraction */ + WEE_CHECK_CALC("5", "8-3"); + WEE_CHECK_CALC("-5", "3-8"); + + /* multiplication */ + WEE_CHECK_CALC("20", "10*2"); + WEE_CHECK_CALC("-8", "-2*4"); + WEE_CHECK_CALC("152415765279684", "12345678*12345678"); + + /* division */ + WEE_CHECK_CALC("2", "6/3"); + WEE_CHECK_CALC("2.5", "10/4"); + + /* floor division */ + WEE_CHECK_CALC("2", "10//4"); + + /* modulo */ + WEE_CHECK_CALC("4", "9%5"); + WEE_CHECK_CALC("0.2", "9.2%3"); + + /* multiple operators */ + WEE_CHECK_CALC("11", "5+2*3"); + + /* expressions with decimal numbers */ + WEE_CHECK_CALC("12.5", "10.5+2"); + WEE_CHECK_CALC("3.3333333333", "10/3"); + WEE_CHECK_CALC("0.1428571429", "1/7"); + WEE_CHECK_CALC("0.0008103728", "1/1234"); + WEE_CHECK_CALC("0.0000810045", "1/12345"); + WEE_CHECK_CALC("0.0000081001", "1/123456"); + WEE_CHECK_CALC("0.00000081", "1/1234567"); + WEE_CHECK_CALC("0.000000081", "1/12345678"); + WEE_CHECK_CALC("0.0000000081", "1/123456789"); + WEE_CHECK_CALC("0.0000000008", "1/1234567890"); + WEE_CHECK_CALC("0.0000000001", "1/12345678901"); + WEE_CHECK_CALC("0", "1/123456789012"); + + /* expressions with parentheses */ + WEE_CHECK_CALC("21", "(5+2)*3"); + WEE_CHECK_CALC("3.15", "(1.5+2)*(1.8/2)"); + WEE_CHECK_CALC("-1.26", "(1.5+2)*(1.8/(2-7))"); +} diff --git a/tests/unit/core/test-core-eval.cpp b/tests/unit/core/test-core-eval.cpp index f0e333b79..4a208c1d0 100644 --- a/tests/unit/core/test-core-eval.cpp +++ b/tests/unit/core/test-core-eval.cpp @@ -145,6 +145,8 @@ TEST(CoreEval, EvalCondition) WEE_CHECK_EVAL("0", "${test2} == value2"); WEE_CHECK_EVAL("0", "${buffer.number} == 2"); WEE_CHECK_EVAL("0", "${window.buffer.number} == 2"); + WEE_CHECK_EVAL("0", "${calc:2+3} < 5"); + WEE_CHECK_EVAL("0", "${calc:1.5*3} < 4.5"); /* conditions evaluated as true */ WEE_CHECK_EVAL("1", "1"); @@ -203,6 +205,8 @@ TEST(CoreEval, EvalCondition) WEE_CHECK_EVAL("1", "${test2} =="); WEE_CHECK_EVAL("1", "${buffer.number} == 1"); WEE_CHECK_EVAL("1", "${window.buffer.number} == 1"); + WEE_CHECK_EVAL("1", "${calc:2+3} >= 5"); + WEE_CHECK_EVAL("1", "${calc:1.5*3} >= 4.5"); /* evaluation of extra_vars */ hashtable_set (options, "extra", "eval"); @@ -407,6 +411,14 @@ TEST(CoreEval, EvalExpression) WEE_CHECK_EVAL("0", "${if:${rev:${if:42==42?hello:bye}}==eyb}"); WEE_CHECK_EVAL("1", "${if:${rev:${if:42==42?hello:bye}}==olleh}"); + /* test calc */ + WEE_CHECK_EVAL("0", "${calc:}"); + WEE_CHECK_EVAL("123", "${calc:123}"); + WEE_CHECK_EVAL("4", "${calc:1+3}"); + WEE_CHECK_EVAL("8", "${calc:5+1*3}"); + WEE_CHECK_EVAL("18", "${calc:(5+1)*3}"); + WEE_CHECK_EVAL("123129", "${calc:${repeat:2,123}+2*3}"); + /* test option */ snprintf (str_value, sizeof (str_value), "%d", CONFIG_INTEGER(config_look_scroll_amount));