From 71206d3e950cf159a4d1b4add9fc8855a48e565b Mon Sep 17 00:00:00 2001 From: "[GPL]Ed" Date: Fri, 1 Dec 2023 17:54:22 +0000 Subject: [PATCH] `expr` overhaul: Replace old Shunting Yard implementation with flex/bison (speedup), add `trunc`, `sec`, `arcsec`, `csc`, `arccsc`, fix high return values, fix ternary operator. --- .gitignore | 3 + Makefile | 16 ++- doc/man1/expr.1 | 4 +- examples/speedtest.out | 22 ++-- sloccount.txt | 24 ++-- src/dispatch.hpp | 62 +++++++--- src/expr.cpp | 275 ----------------------------------------- src/expr.l | 87 +++++++++++++ src/expr.y | 90 ++++++++++++++ src/global.hpp | 63 ++-------- src/main.cpp | 24 ++-- src/sighandler.hpp | 4 +- src/variable.hpp | 8 +- 13 files changed, 290 insertions(+), 392 deletions(-) delete mode 100644 src/expr.cpp create mode 100644 src/expr.l create mode 100644 src/expr.y diff --git a/.gitignore b/.gitignore index dd86541..2c534af 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ bin/zrc */*.sloc */*.filecount corebuf/bin/* +src/y.tab.c +src/y.tab.h +src/lex.yy.c diff --git a/Makefile b/Makefile index 9dcd3ee..98aa7db 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,17 @@ UNAME := $(shell uname -o) +CC=$(CXX) -all: +all: expr mkdir -p bin - g++ src/main.cpp -o bin/zrc + g++ -w src/lex.yy.c src/y.tab.c src/main.cpp -o bin/zrc strip bin/zrc -nozledit: +nozledit: expr mkdir -p bin - g++ src/main.cpp -o bin/zrc -DUSE_ZLINEEDIT=0 + g++ -w src/lex.yy.c src/y.tab.c src/main.cpp -o bin/zrc -DUSE_ZLINEEDIT=0 strip bin/zrc -nohash: +nohash: expr mkdir -p bin - g++ src/main.cpp -o bin/zrc -DUSE_HASHCACHE=0 + g++ -w src/lex.yy.c src/y.tab.c src/main.cpp -o bin/zrc -DUSE_HASHCACHE=0 strip bin/zrc sloc: sloccount . | tee sloccount.txt @@ -23,6 +24,9 @@ endif corebuf: cd corebuf; \ ./build.zrc +expr: + bison -d src/expr.y -o src/y.tab.c + flex -o src/lex.yy.c src/expr.l config: [ -f $(HOME) ] || cp .zrc $(HOME) clean: diff --git a/doc/man1/expr.1 b/doc/man1/expr.1 index 7e1b40c..3229d90 100644 --- a/doc/man1/expr.1 +++ b/doc/man1/expr.1 @@ -37,7 +37,7 @@ which writes to stdout, so square brackets should be used to access it. .RB * , / , // , % ; .SS Precedence level 13: implicit .SS Functions -.RB log10 , log2 , log , sqrt , sin , cos , ctg , tg , floor , ceil , abs , round; +.RB log10 , log2 , ln / log , sqrt , sin , asin / arcsin , cos , acos / arccos , ctg / cot , actg / acot / arcctg / arccot , tg / tan , atg / atan / arctg / arctan , sec , arcsec / asec , csc / cosec , acsc / acosec / arccsc / arccosec / floor , ceil , abs , round , trunc; .SS Other words .RB nan , false , true ; .SS Unary operators @@ -58,7 +58,7 @@ to not clobber files. (and other obvious reasons, like double substitution. See echo [expr 1&&2||(1?2:0)+5&(3<<1)] echo [expr 1 + 2/ 3 + 4] - # RPN expression + # RPN expressions used to be supported, but now they have been removed. echo [expr -r {1 2 + 3 '*' 2 '<<'}] echo [expr {((1+2)*3)<<2}] diff --git a/examples/speedtest.out b/examples/speedtest.out index 5272539..dfcdfe0 100644 --- a/examples/speedtest.out +++ b/examples/speedtest.out @@ -1,26 +1,26 @@ Bash will always be faster: -0.10user 0.08system 0:00.19elapsed 97%CPU (0avgtext+0avgdata 7108maxresident)k +0.16user 0.13system 0:00.30elapsed 96%CPU (0avgtext+0avgdata 7072maxresident)k 0inputs+0outputs (0major+1115minor)pagefaults 0swaps Fish is also fast, but uses more memory: -0.18user 0.11system 0:00.85elapsed 35%CPU (0avgtext+0avgdata 17140maxresident)k -2704inputs+0outputs (5major+2713minor)pagefaults 0swaps +0.26user 0.12system 0:00.87elapsed 45%CPU (0avgtext+0avgdata 17268maxresident)k +2672inputs+0outputs (5major+2675minor)pagefaults 0swaps Csh will use more CPU power: -1.39user 3.50system 0:04.99elapsed 98%CPU (0avgtext+0avgdata 4480maxresident)k -624inputs+0outputs (5major+324082minor)pagefaults 0swaps +1.11user 2.72system 0:03.96elapsed 96%CPU (0avgtext+0avgdata 4524maxresident)k +608inputs+0outputs (46major+323466minor)pagefaults 0swaps Zrc will also have lower memory consumption: -4.30user 10.99system 0:15.31elapsed 99%CPU (0avgtext+0avgdata 5680maxresident)k -0inputs+0outputs (3major+1260626minor)pagefaults 0swaps +4.98user 12.44system 0:17.21elapsed 101%CPU (0avgtext+0avgdata 5680maxresident)k +0inputs+0outputs (172major+1288733minor)pagefaults 0swaps Rc is way slower compared to Zrc in terms of performance: -9.73user 23.39system 0:33.51elapsed 98%CPU (0avgtext+0avgdata 3432maxresident)k -0inputs+0outputs (0major+3888094minor)pagefaults 0swaps +10.27user 27.52system 0:38.53elapsed 98%CPU (0avgtext+0avgdata 3304maxresident)k +192inputs+0outputs (228major+3875509minor)pagefaults 0swaps Powershell performs the worst: -39.90user 67.96system 1:46.73elapsed 101%CPU (0avgtext+0avgdata 121192maxresident)k -131992inputs+264outputs (130major+784640minor)pagefaults 0swaps +64.93user 100.87system 2:37.94elapsed 104%CPU (0avgtext+0avgdata 125964maxresident)k +124856inputs+264outputs (398major+782808minor)pagefaults 0swaps CONCLUSION: Zrc doesn't totally suck ;) It's just very lightweight diff --git a/sloccount.txt b/sloccount.txt index ff7ea8a..126847c 100644 --- a/sloccount.txt +++ b/sloccount.txt @@ -13,13 +13,17 @@ Have a non-directory at the top, so creating directory src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/config.hpp to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/dispatch.hpp to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/exec.hpp to src_top_dir -Adding /home/edward/.dots/work/etc/zrc.git/./src/expr.cpp to src_top_dir +Adding /home/edward/.dots/work/etc/zrc.git/./src/expr.l to src_top_dir +Adding /home/edward/.dots/work/etc/zrc.git/./src/expr.y to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/global.hpp to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/lex.hpp to src_top_dir +Adding /home/edward/.dots/work/etc/zrc.git/./src/lex.yy.c to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/main.cpp to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/sighandler.hpp to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/subst.hpp to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/variable.hpp to src_top_dir +Adding /home/edward/.dots/work/etc/zrc.git/./src/y.tab.c to src_top_dir +Adding /home/edward/.dots/work/etc/zrc.git/./src/y.tab.h to src_top_dir Adding /home/edward/.dots/work/etc/zrc.git/./src/zlineedit.hpp to src_top_dir Categorizing files. Finding a working MD5 command.... @@ -41,7 +45,6 @@ WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/is_prime.zrc has unkn WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/lexical_scoping.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/loops.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/man.cgi.zrc has unknown start: #!/usr/lib/zrc/bin/zrc -WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/mandelbrot.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/mgcsquare.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/minesweeper.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/oji_arhitect.zrc has unknown start: #!../bin/zrc @@ -62,6 +65,7 @@ WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/unique.zrc has unknow WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/whereis.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/windowmgrmgr.zrc has unknown start: #!/usr/bin/env zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/zsms.zrc has unknown start: #!/usr/lib/zrc/bin/zrc +WARNING! File /home/edward/.dots/work/etc/zrc.git/examples/mandelbrot.zrc has unknown start: #!../bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/stdlib/cfuncs.zrc has unknown start: #!/usr/lib/zrc/bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/stdlib/ipc.zrc has unknown start: #!/usr/lib/zrc/bin/zrc WARNING! File /home/edward/.dots/work/etc/zrc.git/stdlib/list.zrc has unknown start: #!/usr/lib/zrc/bin/zrc @@ -76,7 +80,7 @@ Computing results. SLOC Directory SLOC-by-Language (Sorted) -3034 src_top_dir cpp=3034 +3022 src_top_dir cpp=2851,lex=87,yacc=84 0 bin (none) 0 corebuf (none) 0 doc (none) @@ -87,18 +91,20 @@ SLOC Directory SLOC-by-Language (Sorted) Totals grouped by language (dominant language first): -cpp: 3034 (100.00%) +cpp: 2851 (94.34%) +lex: 87 (2.88%) +yacc: 84 (2.78%) -Total Physical Source Lines of Code (SLOC) = 3,034 -Development Effort Estimate, Person-Years (Person-Months) = 0.64 (7.70) +Total Physical Source Lines of Code (SLOC) = 3,022 +Development Effort Estimate, Person-Years (Person-Months) = 0.64 (7.67) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) -Schedule Estimate, Years (Months) = 0.45 (5.43) +Schedule Estimate, Years (Months) = 0.45 (5.42) (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) -Estimated Average Number of Developers (Effort/Schedule) = 1.42 -Total Estimated Cost to Develop = $ 86,648 +Estimated Average Number of Developers (Effort/Schedule) = 1.41 +Total Estimated Cost to Develop = $ 86,288 (average salary = $56,286/year, overhead = 2.40). SLOCCount, Copyright (C) 2001-2004 David A. Wheeler SLOCCount is Open Source Software/Free Software, licensed under the GNU GPL. diff --git a/src/dispatch.hpp b/src/dispatch.hpp index e55e0e3..4cc1841 100644 --- a/src/dispatch.hpp +++ b/src/dispatch.hpp @@ -1,6 +1,6 @@ // Macro for laziness #define FOUND_FN(X) (funcs.find(argv[X]) != funcs.end()) -#define OK(X) expr(X, ExprType::INFIX) +#define OK(X) expr(X) #define de(X) { #X, zrc_builtin_##X } #define ce(X,Y) { #X, zrc_builtin_##Y }, { #Y, zrc_builtin_##Y } @@ -92,6 +92,35 @@ class BlockHandler } }; +/** Converts a number to a std::string object. + * + * @param void + * @return void + */ +Inline std::string +ldtos(long double x) +{ + std::string str; + size_t len; + /* empty */ { + std::stringstream ss; + ss << std::fixed << x; + str = ss.str(); + } + if (str.find('.') != std::string::npos) { + while (!str.empty() && str.back() == '0') + str.pop_back(); + if (!str.empty() && str.back() == '.') + str.pop_back(); + } + return str; +} + +/** Print directory stack contents. + * + * @param {stack}sp + * @return void + */ static inline void prints(std::stack sp) { @@ -107,7 +136,7 @@ prints(std::stack sp) #define EXIT_SESSION \ ret_val = getvar($RETURN);\ exit((is_number(ret_val) && !ret_val.empty())\ - ? std::stoi(ret_val)\ + ? expr(ret_val)\ : EXIT_SUCCESS)\ @@ -128,15 +157,8 @@ Command(string) { return string(argc, argv); } Command(concat) { return combine(argc, argv, 1); } /** Clear screen **/ Command(clear) { std::cout << CLRSCR; NoReturn; } - /** Evaluates an arithmetic expression **/ -Command(expr) { - ExprType et = INFIX; - if (argc > 1 && !strcmp(argv[1], "-r")) - et = RPN, - --argc, ++argv; - return itoa(expr(combine(argc, argv, 1), et)); -} +Command(expr) { return ldtos(expr(combine(argc, argv, 1))); } /** Executes a block if an expression evaluates non-zero **/ Command(if) { @@ -494,10 +516,10 @@ Command(read) { case 'n': if (d != '\n') syntax_error(se); - n = atoi(optarg); + n = (ull)expr(optarg); break; case 'f': - fd = atoi(optarg); + fd = (ull)expr(optarg); break; case 'p': std::cout << optarg << std::flush; @@ -549,9 +571,9 @@ Command(inc) { if (argc < 2) syntax_error(" [val]"); if (argc >= 3) - val = expr(combine(argc, argv, 2)); + val = combine(argc, argv, 2); var = getvar(argv[1]); - ret_val = expr(zrc_fmt("(%s)+(%s)", var.data(), val.data())); + ret_val = ldtos(expr(zrc_fmt("(%s)+(%s)", var.data(), val.data()))); setvar(argv[1], ret_val); NoReturn; } @@ -573,10 +595,10 @@ Command(set) { size_t len = strlen(argv[i]); if (argv[i][len-1] == '=') { argv[i][len-1] = '\0'; - setvar(argv[i-1], expr(zrc_fmt("(%s)%s(%s)", + setvar(argv[i-1], ldtos(expr(zrc_fmt("(%s)%s(%s)", getvar(argv[i-1]).data(), argv[i], - argv[i+1]))); + argv[i+1])))); } else syntax_error(se); } } @@ -589,8 +611,8 @@ Command(chr) { syntax_error(""); std::string t; auto ret = expr(combine(argc, argv, 1)); - if (is_number(ret)) - t += (char)std::stoi(ret); + if (ret != NAN) + t += (char)ret; return t; } Command(ord) { @@ -795,7 +817,7 @@ Command(shift) { if (argc > 2) syntax_error("[]"); if (argc == 2) - howmuch = atoi(argv[1]); + howmuch = (ull)expr(argv[1]); //shift to left for (i = 0; i < len-howmuch; ++i) @@ -831,7 +853,7 @@ Command(rlimit) { if (argc != 2) syntax_error(""); - rlim_t memory = std::stoull(argv[1]); + rlim_t memory = (ull)expr(argv[1]); struct rlimit rlm; int err; short exp = 0; diff --git a/src/expr.cpp b/src/expr.cpp deleted file mode 100644 index de1d6d9..0000000 --- a/src/expr.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#define syn std::cerr << errmsg - -#define ld long double -#define ll long long -#define isnum(X) (isdigit(X) || X == '.' || islower(X)) -#define INVALIDSYN {\ - syn << old << '\n';\ - return NAN;\ -} - -/** Checks if a string contains only mathematical chars. - * - * @param void - * @return void - */ -extern inline bool -is_expr(std::string_view str) -{ - return str.find_first_not_of( - ".0123456789 \t\n\r\v\f" - "<=>+-*/%^~!?:()<>&|" - "qwertyuiopasdfghjklzxcvbnm" - ) == std::string::npos; -} - -std::unordered_map prio = { - { '(', -13 }, { ')', -13 }, - { '?', -12 }, { ':', -12 }, - { OR, -11 }, - { AND, -10 }, - { '|', -9 }, - { '^', -8 }, - { '&', -7 }, - { EQU, -6 }, { NEQ, -6 }, - { '<', -5 }, { LEQ, -5 }, { '>', -5 }, { GEQ, -5 }, - { SPC, -4 }, - { SHL, -3 }, { SHR, -3 }, - { '+', -2 }, { '-', -2 }, - { '*', -1 }, { '/', -1 }, { IND, -1 }, { '%', -1 } - //{ implicit 0 } -}; - -#define du(X,Y) {X,[](ld x){return Y;}} -DispatchTable> unary_table = { - // unary ops - du('!', !x), du('m', -x), du('p', +x), du('~', ~(ll)x), - - // 1 arg functions - du(LOG10, log10(x)), du(LOG2, log2(x)), du(LOG, log(x)), - du(SQRT, sqrt(x)), du(SIN, sin(x)), du(COS, cos(x)), - du(CTG, cos(x)/sin(x)), du(TG, tan(x)), du(FLOOR, floor(x)), - du(CEIL, ceil(x)), du(ABS, abs(x)), du(ROUND, round(x)) -}; - -#define db(X,Y) { X,[](ld x,ld y){return Y;}} -DispatchTable> binary_table = { - // binops - db(SHL, (ll)x << (ll)y), - db(SHR, (ll)x >> (ll)y), - db('&', (ll)x & (ll)y), - db('|', (ll)x | (ll)y), - db('^', (ll)x ^ (ll)y), - db(AND, x && y), - db(OR, x || y), - db(POW, pow(x, y)), - db(IND, floor(x/y)), - db('+', x + y), - db('-', x - y), - db('*', x * y), - db('/', x / y), - db('%', fmod(x, y)), - db('<', x < y), - db('>', x > y), - db(LEQ, x <= y), - db(GEQ, x >= y), - db(NEQ, x != y), - db(EQU, x == y), - db(SPC, xy?1:0)/*x <=> y*/) -}; - -enum ExprType { - INFIX, - RPN -}; - -/** Check left-associative operators. - * - * @param {char}op - * @return bool - */ -#define lassoc(X) (!(strchr("!~pm", X))) - -/** Converts a number to a std::string object. - * - * @param void - * @return void - */ -static Inline std::string -ldtos(ld x) -{ - std::string str; - size_t len; - /* empty */ { - std::stringstream ss; - ss << std::fixed << x; - str = ss.str(); - } - if (str.find('.') != std::string::npos) { - while (!str.empty() && str.back() == '0') - str.pop_back(); - if (!str.empty() && str.back() == '.') - str.pop_back(); - } - return str; -} - -/** "Execute" operator in the Shunting-Yard alg. - * - * @param {char}op,{stack&}nums,{stack&}ops - * @return bool - */ -static bool -expr_op(char op, std::stack& nums, std::stack& ops) -{ - ll len = nums.size(); - - /******************************* - * Unary operators & functions * - *******************************/ - if (unary_table.find(op) != unary_table.end()) { - if (!len) - return false; - ld x1 = nums.top(); nums.pop(); - nums.push(unary_table[op](x1)); - - /******************** - * Ternary operator * - ********************/ - } else if (op == '?') { - ops.push(op); - } else if (op == ':') { - if (ops.top() != '?' || len < 3) - return false; - ops.pop(); - ld x3 = nums.top(); nums.pop(); - ld x2 = nums.top(); nums.pop(); - ld x1 = nums.top(); nums.pop(); - nums.push(x1?x2:x3); - - /******************** - * Binary operators * - ********************/ - } else { - if (len < 2) - return false; - ld x2 = nums.top(); nums.pop(); - ld x1 = nums.top(); nums.pop(); - nums.push(binary_table[op](x1, x2)); - } - return true; -} - -/** Evaluates an arithmetic expression. - * - * @param void - * @return void - */ -ld -expr(std::string e, ExprType mode) -{ - std::stack ops; - std::stack nums; - std::string tok; - std::string old = e; - - str_subst(e); - if (!is_expr(e)) - INVALIDSYN; - - //=====functions===== =====operators===== - REP("log10", LOG10); REP("&&" , AND); - REP("log2" , LOG2 ); REP("||" , OR ); - REP("log" , LOG ); REP("<<" , SHL); - REP("sqrt" , SQRT ); REP(">>" , SHR); - REP("sin" , SIN ); REP("<=>", SPC); - REP("cos" , COS ); REP("<=" , LEQ); - REP("ctg" , CTG ); REP(">=" , GEQ); - REP("tg" , TG ); REP("==" , EQU); - REP("floor", FLOOR); REP("!=" , NEQ); - REP("ceil" , CEIL ); REP("**" , POW); - REP("abs" , ABS ); REP("//" , IND); - REP("round", ROUND); - //=====alt===== - REP("and" , AND); - REP("or" , OR ); - REP("false", '0'); - REP("true" , '1'); - - if (mode == INFIX) { - e.erase(remove_if(e.begin(), e.end(), - [](char x){return isspace(x);}), e.end()); - for (int i = 0, len = e.length(); i < len; ++i) { - /// number - if (isnum(e[i])) { - std::string buf; - while (isnum(e[i]) && i < len) - buf += e[i++]; - --i; - try - { nums.push(std::stold(buf)); } - catch (std::exception& ex) - { } - - /// opening paren - } else if (e[i] == '(') { - ops.push('('); - - /// closing paren - } else if (e[i] == ')') { - while (!ops.empty() && ops.top() != '(') { - if (!expr_op(ops.top(), nums, ops)) - INVALIDSYN; - ops.pop(); - } - if (!ops.empty()) - ops.pop(); - - /// other (operator) - } else { - if (!i || !isnum(e[i-1]) && e[i-1] != ')') { - if (e[i] == '+') e[i] = 'p'; - if (e[i] == '-') e[i] = 'm'; - } - while (!ops.empty() - && prio[e[i]] <= prio[ops.top()] - && lassoc(e[i])) { - if (!expr_op(ops.top(), nums, ops)) - INVALIDSYN; - ops.pop(); - } - ops.push(e[i]); - } - } - while (!ops.empty()) { - if (!expr_op(ops.top(), nums, ops)) - INVALIDSYN; - ops.pop(); - } - } else { - std::stringstream ss{e}; - ld val; - while (ss >> tok) { - bool isop = false; - try - { val = std::stold(tok); } - catch (std::exception& ex) - { isop = true; } - if (isop && !nums.empty()) { - if (!expr_op(tok[0], nums, ops)) - INVALIDSYN; - } else { - nums.push(val); - } - } - } - - if (!nums.empty()) - return nums.top(); - else - INVALIDSYN; -} - -// Default action -std::string expr(std::string e) - { return itoa(expr(e, ExprType::INFIX)); } diff --git a/src/expr.l b/src/expr.l new file mode 100644 index 0000000..17422d5 --- /dev/null +++ b/src/expr.l @@ -0,0 +1,87 @@ +%{ +#define YYSTYPE long double + +#include +#include "y.tab.h" +int yyparse(); +%} +%option noyywrap + +num ([0-9]+\.?|[0-9]*\.[0-9]+)|(0x[0-9A-Fa-f]+)|inf|nan + +%% +{num} { + yylval = std::stold(yytext); + return NUM; +} + +true { yylval = true; return NUM; } +false { yylval = false; return NUM; } + +[ \t\r\n\v\f\r]+ ; +. return *yytext; + +"**" return POW; +"//" return FDIV; +"<<" return SHL; +">>" return SHR; +"<=" return LEQ; +">=" return GEQ; +"==" return EQ; +"!=" return NEQ; +"<=>" return SPC; +"&&"|and return AND; +"||"|or return OR; + +log10|log return LOG10; +log2 return LOG2; +ln return LN; +sqrt return SQRT; +sin return SIN; +cos return COS; +ctg return CTG; +tg|tan return TG; +sec return SEC; +csc|cosec return CSC; +arcsin|asin return ARCSIN; +arccos|acos return ARCCOS; +arcctg|arccot|actg|acot return ARCCTG; +arctg|arctan|atg|atan return ARCTG; +arcsec|asec return ARCSEC; +arccsc|arccosec|acsc|acosec return ARCCSC; +floor return FLOOR; +trunc return TRUNC; +ceil return CEIL; +abs return ABS; +round return ROUND; +%% +#include +#include +#include "global.hpp" +#define YYSTYPE long double + +extern YYSTYPE last_ret; +extern bool str_subst(std::string&); + +/** Evaluates arithmetic expressions. + * + * @param {string}s + * @return ld + */ +ld +expr(std::string s) +{ + if (!str_subst(s)) + return NAN; + auto len = s.size(); + char *buf = new char[len+2]; + strcpy(buf, s.c_str()); + // Apparently yacc needs double null-termination. + buf[len+1] = '\0'; + + YY_BUFFER_STATE sbuf = yy_scan_string(buf); + yy_switch_to_buffer(sbuf); + yyparse(); + yy_delete_buffer(sbuf); + return last_ret; +} diff --git a/src/expr.y b/src/expr.y new file mode 100644 index 0000000..5e37c0a --- /dev/null +++ b/src/expr.y @@ -0,0 +1,90 @@ +%{ +#include +#include +#include "global.hpp" +#define YYSTYPE ld + +int yylex(); +YYSTYPE last_ret = NAN; + +void yyerror(const char *msg) + { std::cerr << msg << '\n'; } +%} + +%define parse.error verbose + +%token YYEOF 0 +%token NUM +%token LOG10 LOG2 LN SQRT SIN COS CTG TG SEC CSC ARCSIN ARCCOS ARCCTG ARCTG ARCSEC ARCCSC FLOOR TRUNC CEIL ABS ROUND + +%right '?' ':' +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQ NEQ +%left '<' LEQ '>' GEQ +%left SPC +%left SHL SHR +%left '+' '-' +%left '*' '/' '%' POW FDIV +%right '!' '~' LOG10 LOG2 LN SQRT SIN COS CTG TG SEC CSC ARCSIN ARCCOS ARCCTG ARCTG ARCSEC ARCCSC FLOOR TRUNC CEIL ABS ROUND +%left '(' ')' +%% +goal : expr YYEOF { last_ret = $1; YYACCEPT; } + | YYEOF { last_ret = 0; YYACCEPT; } + ; + +expr : expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { $$ = $1 / $3; } + | expr '%' expr { $$ = $1 - trunc($1/$3)*$3; } + | expr '|' expr { $$ = (ll)$1 | (ll)$3; } + | expr '&' expr { $$ = (ll)$1 & (ll)$3; } + | expr '^' expr { $$ = (ll)$1 ^ (ll)$3; } + | expr '<' expr { $$ = $1 < $3; } + | expr '>' expr { $$ = $1 > $3; } + | expr SHL expr { $$ = (ll)$1 << (ll)$3; } + | expr SHR expr { $$ = (ll)$1 >> (ll)$3; } + | expr AND expr { $$ = $1 && $3; } + | expr OR expr { $$ = $1 || $3; } + | expr POW expr { $$ = pow($1, $3); } + | expr EQ expr { $$ = $1 == $3; } + | expr NEQ expr { $$ = $1 != $3; } + | expr LEQ expr { $$ = $1 <= $3; } + | expr GEQ expr { $$ = $1 >= $3; } + | expr SPC expr { $$ = $1 < $3 ? -1: ($1 > $3 ? 1 : 0); } + | expr FDIV expr { $$ = floor($1/$3); } + | LOG10 expr { $$ = log10($2); } + | LOG2 expr { $$ = log2($2); } + | LN expr { $$ = log($2); } + | SQRT expr { $$ = sqrt($2); } + | SIN expr { $$ = sin($2); } + | COS expr { $$ = cos($2); } + | CTG expr { $$ = cos($2)/sin($2); } + | TG expr { $$ = sin($2)/cos($2); } + | SEC expr { $$ = 1.0/cos($2); } + | CSC expr { $$ = 1.0/sin($2); } + | ARCSIN expr { $$ = asin($2); } + | ARCCOS expr { $$ = acos($2); } + | ARCCTG expr { $$ = atan(1.0/$2); } + | ARCTG expr { $$ = atan($2); } + | ARCSEC expr { $$ = acos(1.0/$2); } + | ARCCSC expr { $$ = asin(1.0/$2); } + | FLOOR expr { $$ = floor($2); } + | TRUNC expr { $$ = trunc($2); } + | CEIL expr { $$ = ceil($2); } + | ABS expr { $$ = abs($2); } + | ROUND expr { $$ = round($2); } + | '-' expr { $$ = -$2; } + | '+' expr { $$ = +$2; } + | '!' expr { $$ = !$2; } + | '~' expr { $$ = ~(ll)$2; } + | expr '?' expr ':' expr { $$ = $1 ? $3 : $5; } + | '(' expr ')' { $$ = $2; } + | '(' ')' { $$ = 0; } + | NUM { $$ = $1; } + ; +%% diff --git a/src/global.hpp b/src/global.hpp index 506e1ba..8a7f5ff 100644 --- a/src/global.hpp +++ b/src/global.hpp @@ -1,3 +1,14 @@ +typedef std::string FunctionName; +typedef std::string CodeBlock; +typedef std::string AliasName; +typedef std::string Path; +typedef int Jid; +typedef long long ll; +typedef unsigned long long ull; +typedef long double ld; +#define OrderedDispatchTable std::map +#define DispatchTable std::unordered_map + #define TERMINAL isatty(fileno(stdin)) && cin_eq_in #define NORMAL_MODE 0 #define JOB_MODE 1 @@ -35,17 +46,7 @@ #define Inline inline __attribute__((always_inline)) // Number to str #define itoa ldtos -/** Stringify anything **/ -template Inline -std::string S(T const& t) -{ - std::ostringstream ss; - ss << t; - return ss.str(); -} - -inline std::string Sc(char t) - { return S(t); } +#define syn std::cerr << errmsg /** \c... **/ #define KEY_ESC 27 @@ -57,43 +58,3 @@ inline std::string Sc(char t) chk_exit = 1; #define ever (;;) #define sub static Inline void - -#define REP(X, Y) {\ - char y[2];\ - y[0] = Y;\ - y[1] = '\0';\ - for (size_t pos = 0;;pos += strlen(y)) {\ - pos = e.find(X, pos);\ - if (pos == std::string::npos)\ - break;\ - e.erase(pos, strlen(X));\ - e.insert(pos, y);\ - }\ -} - -// ugly kludge -// don't ever do this type of thing -#define LOG10 'Q' -#define LOG2 'W' -#define LOG 'E' -#define SQRT 'R' -#define SIN 'T' -#define COS 'Y' -#define CTG 'U' -#define TG 'I' -#define FLOOR 'O' -#define CEIL 'P' -#define ROUND 'V' - -#define AND 'A' -#define OR 'S' -#define SHL 'D' -#define SHR 'F' -#define SPC 'G' -#define LEQ 'H' -#define GEQ 'J' -#define EQU 'K' -#define NEQ 'L' -#define POW 'Z' -#define ABS 'X' -#define IND 'C' diff --git a/src/main.cpp b/src/main.cpp index ac52e64..879546a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,14 +41,18 @@ #include #include -typedef std::string FunctionName; -typedef std::string CodeBlock; -typedef std::string AliasName; -typedef std::string Path; -typedef int Jid; -#define OrderedDispatchTable std::map -#define DispatchTable std::unordered_map #include "global.hpp" +/** Stringify anything (macro hack) **/ +template Inline +std::string S(T const& t) +{ + std::ostringstream ss; + ss << t; + return ss.str(); +} + +Inline std::string Sc(char t) + { return S(t); } #include "config.hpp" /** See if we can print $PS1. **/ @@ -209,10 +213,7 @@ struct Fifo { bool str_subst (std::string& ); // SUBST.HPP - static inline bool lassoc (char ); - static inline std::string ldtos (ld ); - extern inline bool is_expr (std::string_view ); - std::string expr (std::string ); + extern ld expr (std::string ); // EXPR.CPP, EXPR.HPP template std::string combine (int , T, int ); @@ -244,7 +245,6 @@ struct Fifo { #include "lex.hpp" #include "variable.hpp" - #include "expr.cpp" #include "dispatch.hpp" #include "sighandler.hpp" #include "subst.hpp" diff --git a/src/sighandler.hpp b/src/sighandler.hpp index db0f94a..aed600c 100644 --- a/src/sighandler.hpp +++ b/src/sighandler.hpp @@ -151,14 +151,14 @@ bg_fg(int argc, char *argv[]) if (argc != 2 || (!isdigit(*argv[1]) && *argv[1] != '%')) syntax_error("|<%jid>"); if (*argv[1] == '%') { - int num = atoi(&argv[1][1]); + int num = (ull)expr(argv[1]+1); if (jt.find(num) != jt.end()) { j = &jt[num]; index = num; } } else { for (auto& it : jt) { - if (it.second.pid == atoi(argv[1])) { + if (it.second.pid == (ull)expr(argv[1])) { j = &it.second; index = it.first; break; diff --git a/src/variable.hpp b/src/variable.hpp index 75aed86..9488d56 100644 --- a/src/variable.hpp +++ b/src/variable.hpp @@ -233,7 +233,7 @@ string(int argc, char *argv[]) if (!strcmp(argv[1], "index")) { if (argc != 4) USAGE - return std::string(1, argv[2][atoi(argv[3])]); + return std::string(1, argv[2][(ull)expr(argv[3])]); } // % string length if (!strcmp(argv[1], "length")) return std::to_string(strlen(argv[2])); @@ -258,7 +258,7 @@ string(int argc, char *argv[]) if (argc != 5) USAGE std::string temp = argv[2]; - temp[std::stoi(argv[3])] = argv[4][0]; + temp[(ull)expr(argv[3])] = argv[4][0]; return temp; } @@ -268,8 +268,8 @@ string(int argc, char *argv[]) USAGE std::string temp = argv[2]; - int i1 = atoi(argv[3]); - int i2 = atoi(argv[4]); + int i1 = (ull)expr(argv[3]); + int i2 = (ull)expr(argv[4]); if (i1 < 0) i1 = 0; if (i2 > temp.length())