commit 93d838f14c3e8abfcb51919a2605c6ea59f7e48d Author: anon <anon@anon.anon> Date: Thu Feb 9 16:10:24 2023 +0100 v1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b2ba7b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +ati.out +obj/*.o diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..4da57ad --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +CXX:=g++ +CFLAGS:=-O0 -ggdb -std=c++17 +CPPFLAGS:=-D DEBUG +LDLIBS:= +LDFLAGS:= +COMP:=$(CXX) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(LDLIBS) + +OBJD:=obj/ +SRCD:=src/ +SRC:=main.cpp lexer.cpp algo.cpp +SRC:=$(addprefix ${SRCD},${SRC}) +OBJ:=$(subst .cpp,.o,$(subst ${SRCD},${OBJD},${SRC})) + +OUTPUT:=ati.out + +.PHONY: main clean run + +main: ${OBJ} + ${COMP} ${OBJ} -o ${OUTPUT} + +obj/%.o: src/%.cpp + ${COMP} -c $< -o ${OBJD}/$*.o + +clean: + -rm ${OBJD}/* + -rm ./${OUTPUT} + +run: + ./${OUTPUT} + +gdb: + sudo gdb --directory=./src -p $(shell pgrep ${OUTPUT}) + +algo: + make -C debug/ diff --git a/NOTES.txt b/NOTES.txt new file mode 100644 index 0000000..e69de29 diff --git a/TODO.list b/TODO.list new file mode 100644 index 0000000..9ccd267 --- /dev/null +++ b/TODO.list @@ -0,0 +1,3 @@ +/*--------------------------------------------Todos------------------------------------------------\\ +| +\\-------------------------------------------------------------------------------------------------*/ diff --git a/debug/Makefile b/debug/Makefile new file mode 100644 index 0000000..cdde5e2 --- /dev/null +++ b/debug/Makefile @@ -0,0 +1,4 @@ +m4: + for i in *.m4; do \ + m4 $$i > $$(basename $$i .m4); \ + done diff --git a/debug/assignment.algo b/debug/assignment.algo new file mode 100644 index 0000000..fea73ea --- /dev/null +++ b/debug/assignment.algo @@ -0,0 +1,2 @@ +1;0;i;=;1;0;0 +1;0;h;=;2;0;0 diff --git a/debug/eval.algo b/debug/eval.algo new file mode 100644 index 0000000..b1d9b75 --- /dev/null +++ b/debug/eval.algo @@ -0,0 +1,3 @@ +1;0;i;=;1;0;0 +1;0;h;=;2;0;0 +1;0;k;=;3;*;h diff --git a/debug/i.algo b/debug/i.algo new file mode 100644 index 0000000..c96fc60 --- /dev/null +++ b/debug/i.algo @@ -0,0 +1,47 @@ +" Lehmer generator +0;lehmer;=;4;0;0 +1;7;0;0;0;0 +0;argv-1;=;argv-1;*;48271; +0;argv-1;=;argv-1;%;2147483647 +1;return;0;0;0 +" swap +0;swap;=;10;0;0 +1;14;0;0;0;0 +0;buf;=;argv-1;0;0 +0;argv-1;=;argv-2;0;0 +0;argv-2;=;buf;0;0 +1;return;0;0;0 +" call +0;i;=;0;0;0 +0;argv-1;=;11;0;0 +1;lehmer;1;0;0;0 +0;argv-2-i;=;argv-1;0;0 +0;i;=;i;+;1 +3;17;0;i;=;10 +0;argv-1;=;10;0;0 +1;24;1;0;0;0 +1;return;0;0;0 +" selection sort +0;n;=;argv-1;0;0 +0;i;=;0;0;0; +0;a-i;=;argv-2-i;0;0 +0;i;=;i;+;1 +3;27;0;i;=;10 +" body +0;i;=;0;0;0; +3;return;0;i;<;n +0;min;=;i;0;0 +0;j;=;i;+;1 +3;40;0;j;<;n +3;38;0;a-j;<;a-min +0;min;=;j;0;0 +0;j;=;1;+;j +1;35;0;0;0 +2;46;0;min;=;i +0;argv-1;=;a-i;0;0 +0;argv-2;=;a-min;0;0 +1;swap;1;0;0;0 +0;a-i;=;argv-1;0;0 +0;a-min;=;argv-2;0;0 +0;i;=;i;+;1 +1;32;0;0;0 diff --git a/debug/i.algo.m4 b/debug/i.algo.m4 new file mode 100644 index 0000000..afa5dac --- /dev/null +++ b/debug/i.algo.m4 @@ -0,0 +1,47 @@ +" Lehmer generator +0;lehmer;=;eval(__line__+2);0;0 +1;eval(__line__+4);0;0;0;0 +0;argv-1;=;argv-1;*;48271; +0;argv-1;=;argv-1;%;2147483647 +1;return;0;0;0 +" swap +0;swap;=;eval(__line__+2);0;0 +1;eval(__line__+5);0;0;0;0 +0;buf;=;argv-1;0;0 +0;argv-1;=;argv-2;0;0 +0;argv-2;=;buf;0;0 +1;return;0;0;0 +" call +0;i;=;0;0;0 +0;argv-1;=;11;0;0 +1;lehmer;1;0;0;0 +0;argv-2-i;=;argv-1;0;0 +0;i;=;i;+;1 +3;eval(__line__-3);0;i;=;10 +0;argv-1;=;10;0;0 +1;eval(__line__+2);1;0;0;0 +1;return;0;0;0 +" selection sort +0;n;=;argv-1;0;0 +0;i;=;0;0;0; +0;a-i;=;argv-2-i;0;0 +0;i;=;i;+;1 +3;eval(__line__-2);0;i;=;10 +" body +0;i;=;0;0;0; +3;return;0;i;<;n +0;min;=;i;0;0 +0;j;=;i;+;1 +3;eval(__line__+5);0;j;<;n +3;eval(__line__+2);0;a-j;<;a-min +0;min;=;j;0;0 +0;j;=;1;+;j +1;eval(__line__-4);0;0;0 +2;eval(__line__+6);0;min;=;i +0;argv-1;=;a-i;0;0 +0;argv-2;=;a-min;0;0 +1;swap;1;0;0;0 +0;a-i;=;argv-1;0;0 +0;a-min;=;argv-2;0;0 +0;i;=;i;+;1 +1;eval(__line__-15);0;0;0 diff --git a/docs/ALGO_DETAIL.txt b/docs/ALGO_DETAIL.txt new file mode 100644 index 0000000..3d13e6b --- /dev/null +++ b/docs/ALGO_DETAIL.txt @@ -0,0 +1,152 @@ +{ + int myArray[] = {34, 23, 78, 41, 7, 87, 52, 36, 29, 42}; + // + void selectoinSort(int a[], const int &n){ + int i = 0; + while(i < n){ + int min = i; + int j = i + 1; + while(j < n){ + if(a[j] < a[min]){ + min = j; + } + j = j + 1; + } + if(min != i){ + swap(a[i], a[min]) + } + i = i + 1; + } + } + // + selectionSort(myArray, 10); +} + | ID | PTR | INDEX | VALUE | + :------:-------:---------:-----------: + | x | seq | 0 | + // array + | 1 | int | 0 | 34 | + ... + | 1 | int | 9 | 42 | + + class fun{ + WORD* argv[]; + }; + +// | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +// 1 +// A +// | +//reserverd bit +// +// | typename | var-0 | var-1 | var-2 | +// byte-0 addr-1 addr-2 addr-3 + Atom: + +-----atom-----+ + | +--------+ | + | | sor id | | + | +--------+ | + +--------------+ + >nem tudunk csak atomra ugrani + >az atomok eddig instrukció fajtától függően határoztuk meg, viszont kreatívabban is lehetne + +### Original ### + Folyamatábra_elem : "Folyamatábra elem"; atom név + AD_ID : "AlgoDetail ID"; atom id ^ ^ ^ \_ elsődleges kulcs + Ssz : "?!"; sor id . . . . . . . . . / + V : "Változó"; cím + V_ndx : "Változó iNDeX"; cím offset // >>>NOTE1 + VM : "Változó Művelet" + func : "FUNCió"; függvény hívás // >>>NOTE3 + J id : "Jump ID"; ugrás célpont; egy atom id + // azt hiszem nyújt egy fajta biztonságot + ( \ + V1 : "Változó 1" \ + V_ndx : "Változó iNDeX"; inkonzisztens módon nincs megszámozva /* >>>NOTE1 */\ + C1 : "Constant 1" \ + ) \ + AM : "Alg Művelet" } >>>NOTE2 + ( / + V2 / + V_ndx / + C2 / + ) / + VB1 \ + CB1 \ + F1 \ + VJ1 \ + CJ1 \ + LM : "?! Művelet" } >>>NOTE2 + VB2 / + CB2 / + F2 / + VJ2 / + CJ2 / + + NOTE: + >hunglish + >1*: a változónév alapból magasszintű absztraktció; csak az olvashatóság/parsibleness kedvéért van értelme külön kezelni + >3*: teljesen redundáns az ugrás mező miatt, hacsak nem a paramétereket is belekódoljuk. + ebben az esetben kell egy módszer amivel egy kifejezésben utalhatunk egy tömb N. elemére ami inkonzistens + a [Név] [Index] oszlop felépítéssel + >2*: ismétéls; a 2 igazán indokolatlan szám; logikusan vagy 1 feltételt mentek és tovább kezelem, + vagy végtelelen feltételt határozhatok meg egy sorban; az előbbi a logikus + az oszlop számok homogenitása miatt + + 30 oszlop + legalább 10 redundáns + +### Revoked ### + enum JMP { + no, + always, + wt, //when true + wf //when false + } + (IN) (OUT) + | LN | JMP | DEST | ASSIGNEMT | OPR1 | OPERATOR | OPR2 | COMMENT | + 1 0 i = 0 0 0 + 2 3 return 0 i < &argv[2] + 3 0 min = i 0 0 + 4 0 j = i + 1 + 5 3 9 0 j < n + 6 3 8 0 a[j] < a[min] + 7 0 min = j 0 0 + 8 0 j += 1 0 0 + 9 2 13 0 min = i + 10 0 call[0] = &a[i] 0 0 + 11 0 call[1] = &a[min] 0 0 + 12 1 &swap 0 call 0 0 + 13 0 i += 1 + 14 1 return 0 0 0 0 + + 9 oszlop + 0 redundancia + + + (IN) (OUT) + | Address | PK2 | JMP | DEST | ASSIGNEMT | OPR1 | OPERATOR | OPR2 | COMMENT | + 1 1 0 i = 0 0 0 + 2 1 3 return 0 i < &argv[2] + 3 1 0 min = i 0 0 + 3 2 0 j = i + 1 + 4 1 3 9 0 j < n + 5 1 3 8 0 a[j] < a[min] + 6 1 0 min = j 0 0 + 6 2 0 j += 1 0 0 + 7 1 2 13 0 min = i + 8 1 0 call[0] = &a[i] 0 0 + 8 2 0 call[1] = &a[min] 0 0 + 8 3 1 &swap 0 call 0 0 + 9 1 0 i += 1 + 10 1 1 return 0 0 0 0 + + 10 oszlop + az atom fogalma megmaradt -> mivel JT-ben nincs ilyen mekötés ezért értelmetlen megtartani + +// can oracle/mysql fork? +// is varchar allocated inline? + +since JT is ordered insertion is a nightmare + +the deletion problem is actually self evident. an elems life time is as long as we have any data on it. diff --git a/docs/fibonacci_algo.table b/docs/fibonacci_algo.table new file mode 100644 index 0000000..9269cfd --- /dev/null +++ b/docs/fibonacci_algo.table @@ -0,0 +1,45 @@ ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| Folyamatábra_elem | AD_ID | Ssz | V | V_ndx | VM | func | J id | ( | V1 | V_ndx | C1 | ) | AM | ( | V2 | V_ndx | C2 | ) | VB1 | CB1 | F1 | VJ1 | CJ1 | LM | VB2 | CB2 | F2 | VJ2 | CJ2 | AD_ID=Alg._detail_table, V=változó+index., VM=Változó múvelete,J id =Jump | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| Start | 1 | 1 | 2 | Start :indul a projekt,ami az AD_ID=2 Dataimput ID-val kezdődik/folytatódik | | | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| DataInput | 2 | 1 | a | =' | | | 1 | Definició+ default constans értékadás Fibonecci 1. elemének | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 2 | 2 | b | =' | | | 2 | Definició+ default constans értékadás Fibonecci 2. elemének | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 2 | 3 | f | =' | | 0 | Definició+ default constans értékadás aktuális Fibonacci számnak | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 2 | 4 | ft | =' | 10 | Definiálom Fibonacci tömb max_tömbelemét | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 2 | 5 | x | =' | | | 0 | Definiálom az "x" ciklus változót | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 2 | 6 | 3 | AD_ID=3 ugrás objektum ciklus ini részére | | | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| Ciklus Ini dönt. pont | 3 | 1 | 4 | | | x | < | 10 | Számítási ciklus, addig ugrik a AD_ID=4-re amíg x<10 | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 3 | 2 | 5 | x | >= | 10 | Else ág (X>=10) az 5-re ugrik | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| C_mag számítás | 4 | 1 | f | =' | | a | +' | | b | | Akt_Fibo = előző két elem összege (a+b) | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 4 | 2 | ft | x | =' | f | | | | Fibonacci tömb x index oszlopába tesszük az előzőleg számolt Fibo_akt értékét! | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 4 | 3 | a | =' | b | A kisebbik szám egyenlő lesz a nagyobbik számmal | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 4 | 4 | b | =' | f | A nagyobbik szám egyenlő lesz a Fibo_szám-mal, ami a sorozat új eleme | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 4 | 5 | x | =' | x | +' | 1 | Növelem a ciklusváltozót | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 4 | 6 | 3 | Itt van vége a ciklus_magnak és a ciklus AD_ID=3 Dpontjára [4,5] | | | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| Ciklus Ini dönt. pont | 5 | 1 | 6 | | | x | < | 10 | Adatkiíró ciklus ini ,addig ugrik a 6-re amíg x<10 | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 5 | 2 | 7 | x | >= | 10 | else ág => a 7-re ugrik | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| C_mag output | 6 | 1 | f | x | Out | Fibo_tomb x indexű értékét 1= képernyőre, 2=pdf file, 3=cvs file-be jelenítem meg | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 6 | 2 | x | =' | x | +' | 1 | x ciklus változó értékének növelése | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| | 6 | 3 | 5 | Itt van vége a ciklus_magnak és a ciklus AD_ID=5 döntéspontjára ugrik | | | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ +| End | 7 | 1 | null | Vége a proginak | | | | | | | | | | | | | | | | | | | | | | | | | | | ++------------------------+-------+-----+------+--------------------------------------------------------------------------------+-----+--------------------------------------------------------------------------------------------+------------------------------------------------------------------------+------------------------------------------------------------------+----------------------------------------------------------+-----------------------------------------------------------------------------------------+------------------------------------------------+---+----+---+----+-------+----+---+-----+-----+----+-----+-----+----+-----+-----+----+-----+-----+--------------------------------------------------------------------------------+ diff --git a/docs/problems.txt b/docs/problems.txt new file mode 100644 index 0000000..29a9c70 --- /dev/null +++ b/docs/problems.txt @@ -0,0 +1,6 @@ +### Where does jokertao.value end? ### +Surely its not a problem when using VARCHAR2, however i highly doubt there a formal definition for that thing. +Its not a trivial type. Meaning as is now JT cannot actually be stored everything. Its JT+VARCHAR2 that stores everything. +Now, we either fixate the value size, or normalize the struct. // perhaps it can somehow be stored as a JT, tho unmengling it for every entry doesnt sound very pleasant + ¤ by normalizing we get more columns + ¤ by fixing the size we get multi cell values diff --git a/obj/.placeholder b/obj/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/src/JokerTao.cpp b/src/JokerTao.cpp new file mode 100644 index 0000000..81d0680 --- /dev/null +++ b/src/JokerTao.cpp @@ -0,0 +1,36 @@ +#include "JokerTao.h" + +unsigned int JokerTao::id_ = JOKERTAO::END; + +namespace jt{ + inline std::vector<JokerTao> table; + + unsigned findf_relationship(int attr_, std::string value_){ + unsigned last_id = JOKERTAO::END - 1; + for(auto &&i : table[i]){ + if(i.id == last_id){ + continue; + } + + if(i.attr == attr && i.value == value_){ + return i.index; + } + } + + return JOKERTAO::INVALID; + } + + unsigned find_attribute(unsigned id_, JokerTao* attr_, unsigned index_ = 0){ + for(auto &&i : table[i]){ + if(i.id != id_){ + continue; + } + + if(i.attr == attr){ + return i.index; + } + } + + return JOKERTAO::INVALID; + } +} diff --git a/src/JokerTao.h b/src/JokerTao.h new file mode 100644 index 0000000..e0744ab --- /dev/null +++ b/src/JokerTao.h @@ -0,0 +1,33 @@ +#pragma once + +#include <vector> + +enum JOKERTAO{ + INVALID, + NAME, + VALUE + END +}; + +struct JokerTao{ + unsigned id; + JokerTao *attr; + unsigned index; + std::string value; //void* value = nullptr; + + inline unsigned last_id(){ return id__; } + JokerTao(attr_ = nullptr, value_ = "", id_ = id__++, index_ = 0) : + id(id_), attr(attr_), index(index_), value(value_){ + } + delete JokerTao(); + + private: + static unsigned id__; +}; + +namespace jt{ + extern std::vector<JokerTao> table; + + unsigned findf_relationship(unsigned attr_, std::string value_); + unsigned find_attribute(unsigned index_, JokerTao* attr_, unsigned index_ = 0); +} diff --git a/src/algo.cpp b/src/algo.cpp new file mode 100644 index 0000000..4adb8e4 --- /dev/null +++ b/src/algo.cpp @@ -0,0 +1,240 @@ +#include "algo.h" + +namespace algo{ + /* VARS */ + inline std::stack<struct frame> callstack; + inline std::unordered_map<std::string, std::string> vars; + /**/ + + bool init(){ + const char *const *c = standard_vars-1; + while((c++, *c != NULL)){ + vars[*c] = EMPTY_CELL; + } + callstack.emplace(); + + return true; + } + + void assign(const std::string &name, const std::string& val){ + vars[name] = val; + //unsigned index = findf_relationship(NAME, v[ALGO::DESTINATION]); + //if(index == JOKERTAO::INVALID){ + // jt::table.emplace_back(JOKERTAO::NAME, v[ALGO::DESTINATION]); + // jt::table.emplace_back(JOKERTAO::VALUE, v[ALGO::VALUE], 0); + //}else{ + // jt[index].find_attribute() + //} + } + + void pop(unsigned &head){ + if(callstack.size() == 1){ + #ifdef DEBUG + printf(YELLOW "Return on empty callstack: exiting.\n" NORMAL); + #endif + raise(SIGINT); + } + callstack.pop(); + head = callstack.top().bp + 1; + } + void push(const unsigned &head){ + callstack.top().bp = head; + callstack.emplace(); + } + + std::string arref(std::string s){ + for(int i = s.size()-1, last = i; i >= 0; i--){ + if(s[i] == '-'){ + s.replace(i+1, last-i, deref(s.substr(i+1, last-i))); + } + } + + return s; + } + + std::string deref(std::string s){ + s = arref(s); + return (vars.find(s) != vars.end()) ? deref(vars[s]) : s; + } + + bool jump(unsigned &head, const jmp &do_jump, const std::string &dest, const char &push){ + bool ret = false; + switch(do_jump){ + case algo::jmp::nop: + break; + case algo::jmp::jmp: + algo::call(head, dest, (push != algo::EMPTY_CELL)); + ret = true; + break; + case algo::jmp::je: + if(algo::vars["eval"] == algo::true_){ + algo::call(head, dest, (push != algo::EMPTY_CELL)); + ret = true; + } + break; + case algo::jmp::jne: + if(algo::vars["eval"] == algo::false_){ + algo::call(head, dest, (push != algo::EMPTY_CELL)); + ret = true; + } + break; + } + #ifdef DEBUG + if(ret){ + printf(MAGENTA "Jumped to '%s' (%s). " NORMAL, dest.c_str(), algo::deref(dest).c_str()); + } + printf(MAGENTA "Head at %d. " NORMAL, head); + #endif + return ret; + } + + void call(unsigned &head, const std::string &dest, const bool &is_push){ + #ifdef DEBUG + long debug_stacklen = algo::callstack.size(); + #endif + if(dest == "return"){ + pop(head); + goto RETURN; + } + + if(is_push){ push(head); } + + try{ + head = std::stoi(deref(dest)); + }catch(...){ + #ifdef DEBUG + printf("Invalid jump destination '%s'", dest.c_str()); + #endif + goto RETURN; + } + + RETURN: + #ifdef DEBUG + if(algo::callstack.size() != debug_stacklen){ + printf(YELLOW "[Stack: %ld->%ld] " NORMAL, debug_stacklen, algo::callstack.size()); + } + #endif + } + + int aint(std::string s){ + static const char c[] = "0123456789"; + int i = 0; + bool sign = true; + while(true){ + switch(s[i]){ + case '-': + sign = !(sign xor false); + ++i; + continue; + case '+': + sign = !(sign xor true); + ++i; + continue; + default: + goto BR; + } + } + BR: + s.erase(0, i); + + for(i = 0; i < s.size(); i++){ + bool is_any = false; + for(int h = 0, end2 = sizeof(c)-1; h < end2; h++){ + if(s[i] == c[h]){ + is_any = true; + break; + } + } + if(not is_any){ + s.erase(i, 1); + --i; + } + } + if(s == ""){ s = '0'; } + + return std::stoi(s) * (sign ? 1 : -1); + } + + + void do_eval(const std::string &opr1, char oper, const std::string &opr2){ // convert to use assign() + #define CASECHAR(c){ \ + case (char)(*#c):\ + vars["eval"] = std::to_string(aint(deref(opr1)) c aint(deref(opr2))); \ + } + #define CASELCHAR(c){ \ + case (char)(*#c):\ + aint(deref(opr1)) c aint(deref(opr2)) ? (vars["eval"] = true_) : (vars["eval"] = false_); \ + } + //is_cmp = (oper == EMPTY_CELL); + vars["eval"] = ""; + switch(oper){ + case EMPTY_CELL: + vars["eval"] = deref(opr1); + return; + case '.': + vars["eval"] = deref(opr1) + deref(opr2); + return; + case '\'': + vars["eval"] = deref(opr1).substr(0, opr1.size()-aint(deref(opr2))); + return; + CASECHAR(-) + return; + CASECHAR(+) + return; + CASECHAR(/) + return; + CASECHAR(*) + return; + CASECHAR(%) + return; + CASELCHAR(<) + return; + CASELCHAR(>) + return; + case '=': + aint(deref(opr1)) == aint(deref(opr2)) ? (vars["eval"] = true_) : (vars["eval"] = false_); \ + return; + CASELCHAR(|) + return; + CASELCHAR(&) + return; + CASELCHAR(^) + return; + } + } +} + + + //int aint(std::string s){ + // static const char c[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // ?! + // bool sign = true; + // int roof = 1; + // + // for(int i = 0, int end = s.size(); i < end; i++){ + // int h = 0; + // for(int e = sizeof(c)-1; h < e; h++){ + // if(c[h] == s[i]){ + // while(pow(2, roof) < h){ + // ++roof; + // } + // goto CONT; + // } + // } + // switch(s[i]){ + // case '-': + // sign = !(sign xor false); + // s.rease(i, 1); + // --i; + // continue; + // case '+': + // sign = !(sign xor true); + // s.rease(i, 1); + // --i; + // continue; + // default: + // s[i] = '0'; + // continue; + // } + // CONT: + // } + //} diff --git a/src/algo.h b/src/algo.h new file mode 100644 index 0000000..7725fde --- /dev/null +++ b/src/algo.h @@ -0,0 +1,76 @@ +#include <math.h> +#include <signal.h> +#include <string> +#include <stack> +#include <unordered_map> +#ifdef DEBUG +# include "debug.h" +#endif + +namespace algo{ + /* CONST */ + enum{ +// LINE, + JUMP, + DESTINATION, + ASSIGNMENT, + OPR1, + OPERATOR, + OPR2, + COMMENT + }; + + enum class jmp{ + nop, + jmp, + je, + jne + }; + + const char EMPTY_CELL = '0'; + + const char true_[] = "TRUE"; + const char false_[] = "FALSE"; + + struct frame{ + unsigned bp; + }; + + const char* const standard_vars[] = { + "eval", + "ret", + "argv-0", + "argv-1", + "argv-2", + "argv-3", + "argv-4", + "argv-5", + "argv-6", + "argv-7", + "argv-8", + "argv-9", + NULL + }; + /**/ + + /* VARS */ + extern std::stack<struct frame> callstack; + extern std::unordered_map<std::string, std::string> vars; + /**/ + + bool init(); + + std::string arref(std::string s); + std::string deref(std::string s); + int aint(std::string s); + + void assign(const std::string &name, const std::string& val); + + void do_eval(const std::string &opr1, char oper, const std::string &opr2); + + void pop(unsigned &head); + void push(const unsigned &head); + + bool jump(unsigned &head, const jmp &do_jump, const std::string &dest, const char &push); + void call(unsigned &head, const std::string &dest, const bool &is_push); +} diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..8a92e03 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,23 @@ +#define DEBUG_COLOR + +#ifdef DEBUG_COLOR +# define NORMAL "\033[0m" +# define BOLD "\033[1m" +# define RED "\033[31m" +# define GREEN "\033[32m" +# define YELLOW "\033[33m" +# define BLUE "\033[34m" +# define MAGENTA "\033[35m" +# define CYAN "\033[36m" +# define WHITE "\033[37m" +#else +# define NORMAL "" +# define BOLD "" +# define RED "" +# define GREEN "" +# define YELLOW "" +# define BLUE "" +# define MAGENTA "" +# define CYAN "" +# define WHITE "" +#endif diff --git a/src/lexer.cpp b/src/lexer.cpp new file mode 100644 index 0000000..c64a430 --- /dev/null +++ b/src/lexer.cpp @@ -0,0 +1,87 @@ +#include "lexer.h" + +#include "rapidcsv.h" +#include "algo.h" + + +inline unsigned long long instruction_counter = 1; + +void lex_file(const char* const f){ + rapidcsv::Document doc(f, rapidcsv::LabelParams(-1, -1), + rapidcsv::SeparatorParams(';') + ); + std::vector<std::string> buf; + + for(unsigned head = 1, end = doc.GetRowCount()+1; head < end; head++){ + #ifdef DEBUG + printf(BOLD BLUE "%0#6llx" NORMAL "|", instruction_counter++); + printf(BOLD "%s:%d: " NORMAL, f, head); + #endif + const std::string c = doc.GetCell<std::string>(0, head-1); + if(c[0] == '"'){ // skip comments + #ifdef DEBUG + printf(GREEN "Comment: %s\n" NORMAL, c.c_str()); + #endif + continue; + } + buf = doc.GetRow<std::string>(head-1); + lex_row(buf, head); + } +} + +void lex_row(std::vector<std::string> &v, unsigned &head){ + /* --- MUST COMPUTE --- */ + algo::do_eval(v[algo::OPR1], v[algo::OPERATOR][0], v[algo::OPR2]); + /* --- */ + + /* --- INSTRUCTION BREANCHING --- */ + /* Is jump*/ + if(v[algo::JUMP][0] != algo::EMPTY_CELL){ + algo::jmp j; + try{ + j = (algo::jmp)(stoi(v[algo::JUMP])); + }catch(...){ + puts(RED "Non sensical jump condition." NORMAL); + return; + } + #ifdef DEBUG + printf(MAGENTA "Jump condition %s (%s) %s %s (%s) with %d. " NORMAL, + v[algo::OPR1].c_str(), + algo::deref(v[algo::OPR1]).c_str(), + v[algo::OPERATOR].c_str(), + algo::deref(v[algo::OPR2]).c_str(), + v[algo::OPR2].c_str(), + (int)j + ); + #endif + if(algo::jump(head, j, v[algo::DESTINATION], v[algo::ASSIGNMENT][0])){ + --head; + #ifdef DEBUG + puts(MAGENTA "Continueing." NORMAL); + #endif + }else{ + #ifdef DEBUG + puts(MAGENTA "Falling through." NORMAL); + #endif + } + return; + } + /* Is assignemt */ + if(v[algo::ASSIGNMENT][0] != algo::EMPTY_CELL){ + algo::assign(algo::arref(v[algo::DESTINATION]), algo::vars["eval"]); + #ifdef DEBUG + printf(CYAN "Assigned '%s' to '%s' (%s). (%s %s %s %c %s)\n" NORMAL, + algo::vars["eval"].c_str(), + v[algo::DESTINATION].c_str(), + algo::arref(v[algo::DESTINATION]).c_str(), + // + v[algo::DESTINATION].c_str(), + v[algo::ASSIGNMENT].c_str(), + v[algo::OPR1].c_str(), + v[algo::OPERATOR][0], + v[algo::OPR2].c_str()); + #endif + return; + } + /* --- */ +} diff --git a/src/lexer.h b/src/lexer.h new file mode 100644 index 0000000..75b7323 --- /dev/null +++ b/src/lexer.h @@ -0,0 +1,8 @@ +#include <vector> +#include <string> +#include <iostream> + +void lex_file(const char* const f); +void lex_row(std::vector<std::string> &v, unsigned &head); + +extern unsigned long long instruction_counter; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b8d2062 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,68 @@ +#include <string.h> +#include <signal.h> +#include <iostream> +#include <vector> +#include <string> +#include <algorithm> +//#include "JokerTao.h" +#include <unordered_map> +// +#include "algo.h" +#include "lexer.h" + +using namespace std; + +char* f; + +[[ noreturn ]] void end(int ignore = 0){ + #ifdef DEBUG + printf(NORMAL); + #endif + + #ifdef DEBUG + printf(GREEN "\" Standard variables\n" NORMAL); + const char *const *c = algo::standard_vars-1; + while((c++, *c != NULL)){ + printf("%s: %s\n", *c, algo::vars[*c].c_str()); + algo::vars.erase(*c); + } + + printf(GREEN "\" User variables\n" NORMAL); + vector<char*> vpbuf; + for(auto &&i : algo::vars){ + char *a; + asprintf(&a, "%s: %s\n", i.first.c_str(), i.second.c_str()); + vpbuf.push_back(a); + } + std::sort(vpbuf.begin(), vpbuf.end(), [](char* a, char* b){ return (strcmp(a, b) < 1); }); + for(auto &&i : vpbuf){ + cout << i; + } + #endif + + exit(2); +} + +bool init(){ + ios::sync_with_stdio(true); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + signal(SIGINT, end); + + printf("[init] "); + algo::init(); + puts(""); + + return true; +} + +signed main(int argc, char* argv[]){ + if(argc < 2){ return 0; } + + init(); + + f = argv[1]; + lex_file(f); + + end(); +} diff --git a/src/rapidcsv.h b/src/rapidcsv.h new file mode 100644 index 0000000..9cc38ae --- /dev/null +++ b/src/rapidcsv.h @@ -0,0 +1,1821 @@ +/* + * rapidcsv.h + * + * URL: https://github.com/d99kris/rapidcsv + * Version: 8.70 + * + * Copyright (C) 2017-2022 Kristofer Berggren + * All rights reserved. + * + * rapidcsv is distributed under the BSD 3-Clause license, see LICENSE for details. + * + */ + +#pragma once + +#include <algorithm> +#include <cassert> +#include <cmath> +#ifdef HAS_CODECVT +#include <codecvt> +#include <locale> +#endif +#include <fstream> +#include <functional> +#include <iostream> +#include <limits> +#include <map> +#include <sstream> +#include <string> +#include <typeinfo> +#include <vector> + +#if defined(_MSC_VER) +#include <BaseTsd.h> +typedef SSIZE_T ssize_t; +#endif + +namespace rapidcsv +{ +#if defined(_MSC_VER) + static const bool sPlatformHasCR = true; +#else + static const bool sPlatformHasCR = false; +#endif + + /** + * @brief Datastructure holding parameters controlling how invalid numbers (including + * empty strings) should be handled. + */ + struct ConverterParams + { + /** + * @brief Constructor + * @param pHasDefaultConverter specifies if conversion of non-numerical strings shall be + * converted to a default numerical value, instead of causing + * an exception to be thrown (default). + * @param pDefaultFloat floating-point default value to represent invalid numbers. + * @param pDefaultInteger integer default value to represent invalid numbers. + * @param pNumericLocale specifies whether to honor LC_NUMERIC locale (default + * true). + */ + explicit ConverterParams(const bool pHasDefaultConverter = false, + const long double pDefaultFloat = std::numeric_limits<long double>::signaling_NaN(), + const long long pDefaultInteger = 0, + const bool pNumericLocale = true) + : mHasDefaultConverter(pHasDefaultConverter) + , mDefaultFloat(pDefaultFloat) + , mDefaultInteger(pDefaultInteger) + , mNumericLocale(pNumericLocale) + { + } + + /** + * @brief specifies if conversion of non-numerical strings shall be converted to a default + * numerical value, instead of causing an exception to be thrown (default). + */ + bool mHasDefaultConverter; + + /** + * @brief floating-point default value to represent invalid numbers. + */ + long double mDefaultFloat; + + /** + * @brief integer default value to represent invalid numbers. + */ + long long mDefaultInteger; + + /** + * @brief specifies whether to honor LC_NUMERIC locale. + */ + bool mNumericLocale; + }; + + /** + * @brief Exception thrown when attempting to access Document data in a datatype which + * is not supported by the Converter class. + */ + class no_converter : public std::exception + { + /** + * @brief Provides details about the exception + * @returns an explanatory string + */ + virtual const char* what() const throw() + { + return "unsupported conversion datatype"; + } + }; + + /** + * @brief Class providing conversion to/from numerical datatypes and strings. Only + * intended for rapidcsv internal usage, but exposed externally to allow + * specialization for custom datatype conversions. + */ + template<typename T> + class Converter + { + public: + /** + * @brief Constructor + * @param pConverterParams specifies how conversion of non-numerical values to + * numerical datatype shall be handled. + */ + Converter(const ConverterParams& pConverterParams) + : mConverterParams(pConverterParams) + { + } + + /** + * @brief Converts numerical value to string representation. + * @param pVal numerical value + * @param pStr output string + */ + void ToStr(const T& pVal, std::string& pStr) const + { + if (typeid(T) == typeid(int) || + typeid(T) == typeid(long) || + typeid(T) == typeid(long long) || + typeid(T) == typeid(unsigned) || + typeid(T) == typeid(unsigned long) || + typeid(T) == typeid(unsigned long long) || + typeid(T) == typeid(float) || + typeid(T) == typeid(double) || + typeid(T) == typeid(long double) || + typeid(T) == typeid(char)) + { + std::ostringstream out; + out << pVal; + pStr = out.str(); + } + else + { + throw no_converter(); + } + } + + /** + * @brief Converts string holding a numerical value to numerical datatype representation. + * @param pVal numerical value + * @param pStr output string + */ + void ToVal(const std::string& pStr, T& pVal) const + { + try + { + if (typeid(T) == typeid(int)) + { + pVal = static_cast<T>(std::stoi(pStr)); + return; + } + else if (typeid(T) == typeid(long)) + { + pVal = static_cast<T>(std::stol(pStr)); + return; + } + else if (typeid(T) == typeid(long long)) + { + pVal = static_cast<T>(std::stoll(pStr)); + return; + } + else if (typeid(T) == typeid(unsigned)) + { + pVal = static_cast<T>(std::stoul(pStr)); + return; + } + else if (typeid(T) == typeid(unsigned long)) + { + pVal = static_cast<T>(std::stoul(pStr)); + return; + } + else if (typeid(T) == typeid(unsigned long long)) + { + pVal = static_cast<T>(std::stoull(pStr)); + return; + } + } + catch (...) + { + if (!mConverterParams.mHasDefaultConverter) + { + throw; + } + else + { + pVal = static_cast<T>(mConverterParams.mDefaultInteger); + return; + } + } + + try + { + if (mConverterParams.mNumericLocale) + { + if (typeid(T) == typeid(float)) + { + pVal = static_cast<T>(std::stof(pStr)); + return; + } + else if (typeid(T) == typeid(double)) + { + pVal = static_cast<T>(std::stod(pStr)); + return; + } + else if (typeid(T) == typeid(long double)) + { + pVal = static_cast<T>(std::stold(pStr)); + return; + } + } + else + { + if ((typeid(T) == typeid(float)) || + (typeid(T) == typeid(double)) || + (typeid(T) == typeid(long double))) + { + std::istringstream iss(pStr); + iss >> pVal; + if (iss.fail() || iss.bad() || !iss.eof()) + { + throw std::invalid_argument("istringstream: no conversion"); + } + return; + } + } + } + catch (...) + { + if (!mConverterParams.mHasDefaultConverter) + { + throw; + } + else + { + pVal = static_cast<T>(mConverterParams.mDefaultFloat); + return; + } + } + + if (typeid(T) == typeid(char)) + { + pVal = static_cast<T>(pStr[0]); + return; + } + else + { + throw no_converter(); + } + } + + private: + const ConverterParams& mConverterParams; + }; + + /** + * @brief Specialized implementation handling string to string conversion. + * @param pVal string + * @param pStr string + */ + template<> + inline void Converter<std::string>::ToStr(const std::string& pVal, std::string& pStr) const + { + pStr = pVal; + } + + /** + * @brief Specialized implementation handling string to string conversion. + * @param pVal string + * @param pStr string + */ + template<> + inline void Converter<std::string>::ToVal(const std::string& pStr, std::string& pVal) const + { + pVal = pStr; + } + + template<typename T> + using ConvFunc = std::function<void (const std::string & pStr, T & pVal)>; + + /** + * @brief Datastructure holding parameters controlling which row and column should be + * treated as labels. + */ + struct LabelParams + { + /** + * @brief Constructor + * @param pColumnNameIdx specifies the zero-based row index of the column labels, setting + * it to -1 prevents column lookup by label name, and gives access + * to all rows as document data. Default: 0 + * @param pRowNameIdx specifies the zero-based column index of the row labels, setting + * it to -1 prevents row lookup by label name, and gives access + * to all columns as document data. Default: -1 + */ + explicit LabelParams(const ssize_t pColumnNameIdx = 0, const ssize_t pRowNameIdx = -1) + : mColumnNameIdx(pColumnNameIdx) + , mRowNameIdx(pRowNameIdx) + { + if (mColumnNameIdx < -1) + { + const std::string errStr = "invalid column name index " + + std::to_string(mColumnNameIdx) + " < -1"; + throw std::out_of_range(errStr); + } + + if (mRowNameIdx < -1) + { + const std::string errStr = "invalid row name index " + + std::to_string(mRowNameIdx) + " < -1"; + throw std::out_of_range(errStr); + } + } + + /** + * @brief specifies the zero-based row index of the column labels. + */ + ssize_t mColumnNameIdx; + + /** + * @brief specifies the zero-based column index of the row labels. + */ + ssize_t mRowNameIdx; + }; + + /** + * @brief Datastructure holding parameters controlling how the CSV data fields are separated. + */ + struct SeparatorParams + { + /** + * @brief Constructor + * @param pSeparator specifies the column separator (default ','). + * @param pTrim specifies whether to trim leading and trailing spaces from + * cells read (default false). + * @param pHasCR specifies whether a new document (i.e. not an existing document read) + * should use CR/LF instead of only LF (default is to use standard + * behavior of underlying platforms - CR/LF for Win, and LF for others). + * @param pQuotedLinebreaks specifies whether to allow line breaks in quoted text (default false) + * @param pAutoQuote specifies whether to automatically dequote data during read, and add + * quotes during write (default true). + * @param pQuoteChar specifies the quote character (default '\"'). + */ + explicit SeparatorParams(const char pSeparator = ',', const bool pTrim = false, + const bool pHasCR = sPlatformHasCR, const bool pQuotedLinebreaks = false, + const bool pAutoQuote = true, const char pQuoteChar = '"') + : mSeparator(pSeparator) + , mTrim(pTrim) + , mHasCR(pHasCR) + , mQuotedLinebreaks(pQuotedLinebreaks) + , mAutoQuote(pAutoQuote) + , mQuoteChar(pQuoteChar) + { + } + + /** + * @brief specifies the column separator. + */ + char mSeparator; + + /** + * @brief specifies whether to trim leading and trailing spaces from cells read. + */ + bool mTrim; + + /** + * @brief specifies whether new documents should use CR/LF instead of LF. + */ + bool mHasCR; + + /** + * @brief specifies whether to allow line breaks in quoted text. + */ + bool mQuotedLinebreaks; + + /** + * @brief specifies whether to automatically dequote cell data. + */ + bool mAutoQuote; + + /** + * @brief specifies the quote character. + */ + char mQuoteChar; + }; + + /** + * @brief Datastructure holding parameters controlling how special line formats should be + * treated. + */ + struct LineReaderParams + { + /** + * @brief Constructor + * @param pSkipCommentLines specifies whether to skip lines prefixed with + * mCommentPrefix. Default: false + * @param pCommentPrefix specifies which prefix character to indicate a comment + * line. Default: # + * @param pSkipEmptyLines specifies whether to skip empty lines. Default: false + */ + explicit LineReaderParams(const bool pSkipCommentLines = false, + const char pCommentPrefix = '#', + const bool pSkipEmptyLines = false) + : mSkipCommentLines(pSkipCommentLines) + , mCommentPrefix(pCommentPrefix) + , mSkipEmptyLines(pSkipEmptyLines) + { + } + + /** + * @brief specifies whether to skip lines prefixed with mCommentPrefix. + */ + bool mSkipCommentLines; + + /** + * @brief specifies which prefix character to indicate a comment line. + */ + char mCommentPrefix; + + /** + * @brief specifies whether to skip empty lines. + */ + bool mSkipEmptyLines; + }; + + /** + * @brief Class representing a CSV document. + */ + class Document + { + public: + /** + * @brief Constructor + * @param pPath specifies the path of an existing CSV-file to populate the Document + * data with. + * @param pLabelParams specifies which row and column should be treated as labels. + * @param pSeparatorParams specifies which field and row separators should be used. + * @param pConverterParams specifies how invalid numbers (including empty strings) should be + * handled. + * @param pLineReaderParams specifies how special line formats should be treated. + */ + explicit Document(const std::string& pPath = std::string(), + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) + : mPath(pPath) + , mLabelParams(pLabelParams) + , mSeparatorParams(pSeparatorParams) + , mConverterParams(pConverterParams) + , mLineReaderParams(pLineReaderParams) + , mData() + , mColumnNames() + , mRowNames() + { + if (!mPath.empty()) + { + ReadCsv(); + } + } + + /** + * @brief Constructor + * @param pStream specifies a binary input stream to read CSV data from. + * @param pLabelParams specifies which row and column should be treated as labels. + * @param pSeparatorParams specifies which field and row separators should be used. + * @param pConverterParams specifies how invalid numbers (including empty strings) should be + * handled. + * @param pLineReaderParams specifies how special line formats should be treated. + */ + explicit Document(std::istream& pStream, + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) + : mPath() + , mLabelParams(pLabelParams) + , mSeparatorParams(pSeparatorParams) + , mConverterParams(pConverterParams) + , mLineReaderParams(pLineReaderParams) + , mData() + , mColumnNames() + , mRowNames() + { + ReadCsv(pStream); + } + + /** + * @brief Read Document data from file. + * @param pPath specifies the path of an existing CSV-file to populate the Document + * data with. + * @param pLabelParams specifies which row and column should be treated as labels. + * @param pSeparatorParams specifies which field and row separators should be used. + * @param pConverterParams specifies how invalid numbers (including empty strings) should be + * handled. + * @param pLineReaderParams specifies how special line formats should be treated. + */ + void Load(const std::string& pPath, + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) + { + mPath = pPath; + mLabelParams = pLabelParams; + mSeparatorParams = pSeparatorParams; + mConverterParams = pConverterParams; + mLineReaderParams = pLineReaderParams; + ReadCsv(); + } + + /** + * @brief Read Document data from stream. + * @param pStream specifies a binary input stream to read CSV data from. + * @param pLabelParams specifies which row and column should be treated as labels. + * @param pSeparatorParams specifies which field and row separators should be used. + * @param pConverterParams specifies how invalid numbers (including empty strings) should be + * handled. + * @param pLineReaderParams specifies how special line formats should be treated. + */ + void Load(std::istream& pStream, + const LabelParams& pLabelParams = LabelParams(), + const SeparatorParams& pSeparatorParams = SeparatorParams(), + const ConverterParams& pConverterParams = ConverterParams(), + const LineReaderParams& pLineReaderParams = LineReaderParams()) + { + mPath = ""; + mLabelParams = pLabelParams; + mSeparatorParams = pSeparatorParams; + mConverterParams = pConverterParams; + mLineReaderParams = pLineReaderParams; + ReadCsv(pStream); + } + + /** + * @brief Write Document data to file. + * @param pPath optionally specifies the path where the CSV-file will be created + * (if not specified, the original path provided when creating or + * loading the Document data will be used). + */ + void Save(const std::string& pPath = std::string()) + { + if (!pPath.empty()) + { + mPath = pPath; + } + WriteCsv(); + } + + /** + * @brief Write Document data to stream. + * @param pStream specifies a binary output stream to write the data to. + */ + void Save(std::ostream& pStream) const + { + WriteCsv(pStream); + } + + /** + * @brief Clears loaded Document data. + * + */ + void Clear() + { + mData.clear(); + mColumnNames.clear(); + mRowNames.clear(); +#ifdef HAS_CODECVT + mIsUtf16 = false; + mIsLE = false; +#endif + } + + /** + * @brief Get column index by name. + * @param pColumnName column label name. + * @returns zero-based column index. + */ + ssize_t GetColumnIdx(const std::string& pColumnName) const + { + if (mLabelParams.mColumnNameIdx >= 0) + { + if (mColumnNames.find(pColumnName) != mColumnNames.end()) + { + return static_cast<ssize_t>(mColumnNames.at(pColumnName)) - (mLabelParams.mRowNameIdx + 1); + } + } + return -1; + } + + /** + * @brief Get column by index. + * @param pColumnIdx zero-based column index. + * @returns vector of column data. + */ + template<typename T> + std::vector<T> GetColumn(const size_t pColumnIdx) const + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + std::vector<T> column; + Converter<T> converter(mConverterParams); + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) + { + if (dataColumnIdx < itRow->size()) + { + T val; + converter.ToVal(itRow->at(dataColumnIdx), val); + column.push_back(val); + } + else + { + const std::string errStr = "requested column index " + + std::to_string(pColumnIdx) + " >= " + + std::to_string(itRow->size() - GetDataColumnIndex(0)) + + " (number of columns on row index " + + std::to_string(std::distance(mData.begin(), itRow) - + (mLabelParams.mColumnNameIdx + 1)) + ")"; + throw std::out_of_range(errStr); + } + } + } + return column; + } + + /** + * @brief Get column by index. + * @param pColumnIdx zero-based column index. + * @param pToVal conversion function. + * @returns vector of column data. + */ + template<typename T> + std::vector<T> GetColumn(const size_t pColumnIdx, ConvFunc<T> pToVal) const + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + std::vector<T> column; + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) + { + T val; + pToVal(itRow->at(dataColumnIdx), val); + column.push_back(val); + } + } + return column; + } + + /** + * @brief Get column by name. + * @param pColumnName column label name. + * @returns vector of column data. + */ + template<typename T> + std::vector<T> GetColumn(const std::string& pColumnName) const + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + return GetColumn<T>(static_cast<size_t>(columnIdx)); + } + + /** + * @brief Get column by name. + * @param pColumnName column label name. + * @param pToVal conversion function. + * @returns vector of column data. + */ + template<typename T> + std::vector<T> GetColumn(const std::string& pColumnName, ConvFunc<T> pToVal) const + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + return GetColumn<T>(static_cast<size_t>(columnIdx), pToVal); + } + + /** + * @brief Set column by index. + * @param pColumnIdx zero-based column index. + * @param pColumn vector of column data. + */ + template<typename T> + void SetColumn(const size_t pColumnIdx, const std::vector<T>& pColumn) + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + + while (GetDataRowIndex(pColumn.size()) > GetDataRowCount()) + { + std::vector<std::string> row; + row.resize(GetDataColumnCount()); + mData.push_back(row); + } + + if ((dataColumnIdx + 1) > GetDataColumnCount()) + { + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + itRow->resize(GetDataColumnIndex(dataColumnIdx + 1)); + } + } + + Converter<T> converter(mConverterParams); + for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow) + { + std::string str; + converter.ToStr(*itRow, str); + mData.at(static_cast<size_t>(std::distance(pColumn.begin(), itRow) + mLabelParams.mColumnNameIdx + 1)).at( + dataColumnIdx) = str; + } + } + + /** + * @brief Set column by name. + * @param pColumnName column label name. + * @param pColumn vector of column data. + */ + template<typename T> + void SetColumn(const std::string& pColumnName, const std::vector<T>& pColumn) + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + SetColumn<T>(static_cast<size_t>(columnIdx), pColumn); + } + + /** + * @brief Remove column by index. + * @param pColumnIdx zero-based column index. + */ + void RemoveColumn(const size_t pColumnIdx) + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + itRow->erase(itRow->begin() + static_cast<ssize_t>(dataColumnIdx)); + } + + UpdateColumnNames(); + } + + /** + * @brief Remove column by name. + * @param pColumnName column label name. + */ + void RemoveColumn(const std::string& pColumnName) + { + ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + + RemoveColumn(static_cast<size_t>(columnIdx)); + } + + /** + * @brief Insert column at specified index. + * @param pColumnIdx zero-based column index. + * @param pColumn vector of column data (optional argument). + * @param pColumnName column label name (optional argument). + */ + template<typename T> + void InsertColumn(const size_t pColumnIdx, const std::vector<T>& pColumn = std::vector<T>(), + const std::string& pColumnName = std::string()) + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + + std::vector<std::string> column; + if (pColumn.empty()) + { + column.resize(GetDataRowCount()); + } + else + { + column.resize(GetDataRowIndex(pColumn.size())); + Converter<T> converter(mConverterParams); + for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow) + { + std::string str; + converter.ToStr(*itRow, str); + const size_t rowIdx = + static_cast<size_t>(std::distance(pColumn.begin(), itRow) + (mLabelParams.mColumnNameIdx + 1)); + column.at(rowIdx) = str; + } + } + + while (column.size() > GetDataRowCount()) + { + std::vector<std::string> row; + const size_t columnCount = std::max<size_t>(static_cast<size_t>(mLabelParams.mColumnNameIdx + 1), + GetDataColumnCount()); + row.resize(columnCount); + mData.push_back(row); + } + + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + const size_t rowIdx = static_cast<size_t>(std::distance(mData.begin(), itRow)); + itRow->insert(itRow->begin() + static_cast<ssize_t>(dataColumnIdx), column.at(rowIdx)); + } + + if (!pColumnName.empty()) + { + SetColumnName(pColumnIdx, pColumnName); + } + + UpdateColumnNames(); + } + + /** + * @brief Get number of data columns (excluding label columns). + * @returns column count. + */ + size_t GetColumnCount() const + { + const ssize_t count = static_cast<ssize_t>((mData.size() > 0) ? mData.at(0).size() : 0) - + (mLabelParams.mRowNameIdx + 1); + return (count >= 0) ? static_cast<size_t>(count) : 0; + } + + /** + * @brief Get row index by name. + * @param pRowName row label name. + * @returns zero-based row index. + */ + ssize_t GetRowIdx(const std::string& pRowName) const + { + if (mLabelParams.mRowNameIdx >= 0) + { + if (mRowNames.find(pRowName) != mRowNames.end()) + { + return static_cast<ssize_t>(mRowNames.at(pRowName)) - (mLabelParams.mColumnNameIdx + 1); + } + } + return -1; + } + + /** + * @brief Get row by index. + * @param pRowIdx zero-based row index. + * @returns vector of row data. + */ + template<typename T> + std::vector<T> GetRow(const size_t pRowIdx) const + { + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + std::vector<T> row; + Converter<T> converter(mConverterParams); + for (auto itCol = mData.at(dataRowIdx).begin(); itCol != mData.at(dataRowIdx).end(); ++itCol) + { + if (std::distance(mData.at(dataRowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) + { + T val; + converter.ToVal(*itCol, val); + row.push_back(val); + } + } + return row; + } + + /** + * @brief Get row by index. + * @param pRowIdx zero-based row index. + * @param pToVal conversion function. + * @returns vector of row data. + */ + template<typename T> + std::vector<T> GetRow(const size_t pRowIdx, ConvFunc<T> pToVal) const + { + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + std::vector<T> row; + Converter<T> converter(mConverterParams); + for (auto itCol = mData.at(dataRowIdx).begin(); itCol != mData.at(dataRowIdx).end(); ++itCol) + { + if (std::distance(mData.at(dataRowIdx).begin(), itCol) > mLabelParams.mRowNameIdx) + { + T val; + pToVal(*itCol, val); + row.push_back(val); + } + } + return row; + } + + /** + * @brief Get row by name. + * @param pRowName row label name. + * @returns vector of row data. + */ + template<typename T> + std::vector<T> GetRow(const std::string& pRowName) const + { + ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + return GetRow<T>(static_cast<size_t>(rowIdx)); + } + + /** + * @brief Get row by name. + * @param pRowName row label name. + * @param pToVal conversion function. + * @returns vector of row data. + */ + template<typename T> + std::vector<T> GetRow(const std::string& pRowName, ConvFunc<T> pToVal) const + { + ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + return GetRow<T>(static_cast<size_t>(rowIdx), pToVal); + } + + /** + * @brief Set row by index. + * @param pRowIdx zero-based row index. + * @param pRow vector of row data. + */ + template<typename T> + void SetRow(const size_t pRowIdx, const std::vector<T>& pRow) + { + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + + while ((dataRowIdx + 1) > GetDataRowCount()) + { + std::vector<std::string> row; + row.resize(GetDataColumnCount()); + mData.push_back(row); + } + + if (pRow.size() > GetDataColumnCount()) + { + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + itRow->resize(GetDataColumnIndex(pRow.size())); + } + } + + Converter<T> converter(mConverterParams); + for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol) + { + std::string str; + converter.ToStr(*itCol, str); + mData.at(dataRowIdx).at(static_cast<size_t>(std::distance(pRow.begin(), + itCol) + mLabelParams.mRowNameIdx + 1)) = str; + } + } + + /** + * @brief Set row by name. + * @param pRowName row label name. + * @param pRow vector of row data. + */ + template<typename T> + void SetRow(const std::string& pRowName, const std::vector<T>& pRow) + { + ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + return SetRow<T>(static_cast<size_t>(rowIdx), pRow); + } + + /** + * @brief Remove row by index. + * @param pRowIdx zero-based row index. + */ + void RemoveRow(const size_t pRowIdx) + { + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + mData.erase(mData.begin() + static_cast<ssize_t>(dataRowIdx)); + UpdateRowNames(); + } + + /** + * @brief Remove row by name. + * @param pRowName row label name. + */ + void RemoveRow(const std::string& pRowName) + { + ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + + RemoveRow(static_cast<size_t>(rowIdx)); + } + + /** + * @brief Insert row at specified index. + * @param pRowIdx zero-based row index. + * @param pRow vector of row data (optional argument). + * @param pRowName row label name (optional argument). + */ + template<typename T> + void InsertRow(const size_t pRowIdx, const std::vector<T>& pRow = std::vector<T>(), + const std::string& pRowName = std::string()) + { + const size_t rowIdx = GetDataRowIndex(pRowIdx); + + std::vector<std::string> row; + if (pRow.empty()) + { + row.resize(GetDataColumnCount()); + } + else + { + row.resize(GetDataColumnIndex(pRow.size())); + Converter<T> converter(mConverterParams); + for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol) + { + std::string str; + converter.ToStr(*itCol, str); + row.at(static_cast<size_t>(std::distance(pRow.begin(), itCol) + mLabelParams.mRowNameIdx + 1)) = str; + } + } + + while (rowIdx > GetDataRowCount()) + { + std::vector<std::string> tempRow; + tempRow.resize(GetDataColumnCount()); + mData.push_back(tempRow); + } + + mData.insert(mData.begin() + static_cast<ssize_t>(rowIdx), row); + + if (!pRowName.empty()) + { + SetRowName(pRowIdx, pRowName); + } + + UpdateRowNames(); + } + + /** + * @brief Get number of data rows (excluding label rows). + * @returns row count. + */ + size_t GetRowCount() const + { + const ssize_t count = static_cast<ssize_t>(mData.size()) - (mLabelParams.mColumnNameIdx + 1); + return (count >= 0) ? static_cast<size_t>(count) : 0; + } + + /** + * @brief Get cell by index. + * @param pColumnIdx zero-based column index. + * @param pRowIdx zero-based row index. + * @returns cell data. + */ + template<typename T> + T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + + T val; + Converter<T> converter(mConverterParams); + converter.ToVal(mData.at(dataRowIdx).at(dataColumnIdx), val); + return val; + } + + /** + * @brief Get cell by index. + * @param pColumnIdx zero-based column index. + * @param pRowIdx zero-based row index. + * @param pToVal conversion function. + * @returns cell data. + */ + template<typename T> + T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc<T> pToVal) const + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + + T val; + pToVal(mData.at(dataRowIdx).at(dataColumnIdx), val); + return val; + } + + /** + * @brief Get cell by name. + * @param pColumnName column label name. + * @param pRowName row label name. + * @returns cell data. + */ + template<typename T> + T GetCell(const std::string& pColumnName, const std::string& pRowName) const + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + + const ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + + return GetCell<T>(static_cast<size_t>(columnIdx), static_cast<size_t>(rowIdx)); + } + + /** + * @brief Get cell by name. + * @param pColumnName column label name. + * @param pRowName row label name. + * @param pToVal conversion function. + * @returns cell data. + */ + template<typename T> + T GetCell(const std::string& pColumnName, const std::string& pRowName, ConvFunc<T> pToVal) const + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + + const ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + + return GetCell<T>(static_cast<size_t>(columnIdx), static_cast<size_t>(rowIdx), pToVal); + } + + /** + * @brief Get cell by column name and row index. + * @param pColumnName column label name. + * @param pRowIdx zero-based row index. + * @returns cell data. + */ + template<typename T> + T GetCell(const std::string& pColumnName, const size_t pRowIdx) const + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + + return GetCell<T>(static_cast<size_t>(columnIdx), pRowIdx); + } + + /** + * @brief Get cell by column name and row index. + * @param pColumnName column label name. + * @param pRowIdx zero-based row index. + * @param pToVal conversion function. + * @returns cell data. + */ + template<typename T> + T GetCell(const std::string& pColumnName, const size_t pRowIdx, ConvFunc<T> pToVal) const + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + + return GetCell<T>(static_cast<size_t>(columnIdx), pRowIdx, pToVal); + } + + /** + * @brief Get cell by column index and row name. + * @param pColumnIdx zero-based column index. + * @param pRowName row label name. + * @returns cell data. + */ + template<typename T> + T GetCell(const size_t pColumnIdx, const std::string& pRowName) const + { + const ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + + return GetCell<T>(pColumnIdx, static_cast<size_t>(rowIdx)); + } + + /** + * @brief Get cell by column index and row name. + * @param pColumnIdx zero-based column index. + * @param pRowName row label name. + * @param pToVal conversion function. + * @returns cell data. + */ + template<typename T> + T GetCell(const size_t pColumnIdx, const std::string& pRowName, ConvFunc<T> pToVal) const + { + const ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + + return GetCell<T>(pColumnIdx, static_cast<size_t>(rowIdx), pToVal); + } + + /** + * @brief Set cell by index. + * @param pRowIdx zero-based row index. + * @param pColumnIdx zero-based column index. + * @param pCell cell data. + */ + template<typename T> + void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T& pCell) + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + + while ((dataRowIdx + 1) > GetDataRowCount()) + { + std::vector<std::string> row; + row.resize(GetDataColumnCount()); + mData.push_back(row); + } + + if ((dataColumnIdx + 1) > GetDataColumnCount()) + { + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + itRow->resize(dataColumnIdx + 1); + } + } + + std::string str; + Converter<T> converter(mConverterParams); + converter.ToStr(pCell, str); + mData.at(dataRowIdx).at(dataColumnIdx) = str; + } + + /** + * @brief Set cell by name. + * @param pColumnName column label name. + * @param pRowName row label name. + * @param pCell cell data. + */ + template<typename T> + void SetCell(const std::string& pColumnName, const std::string& pRowName, const T& pCell) + { + const ssize_t columnIdx = GetColumnIdx(pColumnName); + if (columnIdx < 0) + { + throw std::out_of_range("column not found: " + pColumnName); + } + + const ssize_t rowIdx = GetRowIdx(pRowName); + if (rowIdx < 0) + { + throw std::out_of_range("row not found: " + pRowName); + } + + SetCell<T>(static_cast<size_t>(columnIdx), static_cast<size_t>(rowIdx), pCell); + } + + /** + * @brief Get column name + * @param pColumnIdx zero-based column index. + * @returns column name. + */ + std::string GetColumnName(const size_t pColumnIdx) const + { + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + if (mLabelParams.mColumnNameIdx < 0) + { + throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx)); + } + + return mData.at(static_cast<size_t>(mLabelParams.mColumnNameIdx)).at(dataColumnIdx); + } + + /** + * @brief Set column name + * @param pColumnIdx zero-based column index. + * @param pColumnName column name. + */ + void SetColumnName(size_t pColumnIdx, const std::string& pColumnName) + { + if (mLabelParams.mColumnNameIdx < 0) + { + throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx)); + } + + const size_t dataColumnIdx = GetDataColumnIndex(pColumnIdx); + mColumnNames[pColumnName] = dataColumnIdx; + + // increase table size if necessary: + const size_t rowIdx = static_cast<size_t>(mLabelParams.mColumnNameIdx); + if (rowIdx >= mData.size()) + { + mData.resize(rowIdx + 1); + } + auto& row = mData[rowIdx]; + if (dataColumnIdx >= row.size()) + { + row.resize(dataColumnIdx + 1); + } + + mData.at(static_cast<size_t>(mLabelParams.mColumnNameIdx)).at(dataColumnIdx) = pColumnName; + } + + /** + * @brief Get column names + * @returns vector of column names. + */ + std::vector<std::string> GetColumnNames() const + { + if (mLabelParams.mColumnNameIdx >= 0) + { + return std::vector<std::string>(mData.at(static_cast<size_t>(mLabelParams.mColumnNameIdx)).begin() + + (mLabelParams.mRowNameIdx + 1), + mData.at(static_cast<size_t>(mLabelParams.mColumnNameIdx)).end()); + } + + return std::vector<std::string>(); + } + + /** + * @brief Get row name + * @param pRowIdx zero-based column index. + * @returns row name. + */ + std::string GetRowName(const size_t pRowIdx) const + { + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + if (mLabelParams.mRowNameIdx < 0) + { + throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx)); + } + + return mData.at(dataRowIdx).at(static_cast<size_t>(mLabelParams.mRowNameIdx)); + } + + /** + * @brief Set row name + * @param pRowIdx zero-based row index. + * @param pRowName row name. + */ + void SetRowName(size_t pRowIdx, const std::string& pRowName) + { + const size_t dataRowIdx = GetDataRowIndex(pRowIdx); + mRowNames[pRowName] = dataRowIdx; + if (mLabelParams.mRowNameIdx < 0) + { + throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx)); + } + + // increase table size if necessary: + if (dataRowIdx >= mData.size()) + { + mData.resize(dataRowIdx + 1); + } + auto& row = mData[dataRowIdx]; + if (mLabelParams.mRowNameIdx >= static_cast<ssize_t>(row.size())) + { + row.resize(static_cast<size_t>(mLabelParams.mRowNameIdx) + 1); + } + + mData.at(dataRowIdx).at(static_cast<size_t>(mLabelParams.mRowNameIdx)) = pRowName; + } + + /** + * @brief Get row names + * @returns vector of row names. + */ + std::vector<std::string> GetRowNames() const + { + std::vector<std::string> rownames; + if (mLabelParams.mRowNameIdx >= 0) + { + for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow) + { + if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx) + { + rownames.push_back(itRow->at(static_cast<size_t>(mLabelParams.mRowNameIdx))); + } + } + } + return rownames; + } + + private: + void ReadCsv() + { + std::ifstream stream; + stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + stream.open(mPath, std::ios::binary); + ReadCsv(stream); + } + + void ReadCsv(std::istream& pStream) + { + Clear(); + pStream.seekg(0, std::ios::end); + std::streamsize length = pStream.tellg(); + pStream.seekg(0, std::ios::beg); + +#ifdef HAS_CODECVT + std::vector<char> bom2b(2, '\0'); + if (length >= 2) + { + pStream.read(bom2b.data(), 2); + pStream.seekg(0, std::ios::beg); + } + + static const std::vector<char> bomU16le = { '\xff', '\xfe' }; + static const std::vector<char> bomU16be = { '\xfe', '\xff' }; + if ((bom2b == bomU16le) || (bom2b == bomU16be)) + { + mIsUtf16 = true; + mIsLE = (bom2b == bomU16le); + + std::wifstream wstream; + wstream.exceptions(std::wifstream::failbit | std::wifstream::badbit); + wstream.open(mPath, std::ios::binary); + if (mIsLE) + { + wstream.imbue(std::locale(wstream.getloc(), + new std::codecvt_utf16<wchar_t, 0x10ffff, + static_cast<std::codecvt_mode>(std::consume_header | + std::little_endian)>)); + } + else + { + wstream.imbue(std::locale(wstream.getloc(), + new std::codecvt_utf16<wchar_t, 0x10ffff, + std::consume_header>)); + } + std::wstringstream wss; + wss << wstream.rdbuf(); + std::string utf8 = ToString(wss.str()); + std::stringstream ss(utf8); + ParseCsv(ss, static_cast<std::streamsize>(utf8.size())); + } + else +#endif + { + // check for UTF-8 Byte order mark and skip it when found + if (length >= 3) + { + std::vector<char> bom3b(3, '\0'); + pStream.read(bom3b.data(), 3); + static const std::vector<char> bomU8 = { '\xef', '\xbb', '\xbf' }; + if (bom3b != bomU8) + { + // file does not start with a UTF-8 Byte order mark + pStream.seekg(0, std::ios::beg); + } + else + { + // file did start with a UTF-8 Byte order mark, simply skip it + length -= 3; + } + } + + ParseCsv(pStream, length); + } + } + + void ParseCsv(std::istream& pStream, std::streamsize p_FileLength) + { + const std::streamsize bufLength = 64 * 1024; + std::vector<char> buffer(bufLength); + std::vector<std::string> row; + std::string cell; + bool quoted = false; + int cr = 0; + int lf = 0; + + while (p_FileLength > 0) + { + const std::streamsize toReadLength = std::min<std::streamsize>(p_FileLength, bufLength); + pStream.read(buffer.data(), toReadLength); + + // With user-specified istream opened in non-binary mode on windows, we may have a + // data length mismatch, so ensure we don't parse outside actual data length read. + const std::streamsize readLength = pStream.gcount(); + if (readLength <= 0) + { + break; + } + + for (size_t i = 0; i < static_cast<size_t>(readLength); ++i) + { + if (buffer[i] == mSeparatorParams.mQuoteChar) + { + if (cell.empty() || (cell[0] == mSeparatorParams.mQuoteChar)) + { + quoted = !quoted; + } + cell += buffer[i]; + } + else if (buffer[i] == mSeparatorParams.mSeparator) + { + if (!quoted) + { + row.push_back(Unquote(Trim(cell))); + cell.clear(); + } + else + { + cell += buffer[i]; + } + } + else if (buffer[i] == '\r') + { + if (mSeparatorParams.mQuotedLinebreaks && quoted) + { + cell += buffer[i]; + } + else + { + ++cr; + } + } + else if (buffer[i] == '\n') + { + if (mSeparatorParams.mQuotedLinebreaks && quoted) + { + cell += buffer[i]; + } + else + { + ++lf; + if (mLineReaderParams.mSkipEmptyLines && row.empty() && cell.empty()) + { + // skip empty line + } + else + { + row.push_back(Unquote(Trim(cell))); + + if (mLineReaderParams.mSkipCommentLines && !row.at(0).empty() && + (row.at(0)[0] == mLineReaderParams.mCommentPrefix)) + { + // skip comment line + } + else + { + mData.push_back(row); + } + + cell.clear(); + row.clear(); + quoted = false; + } + } + } + else + { + cell += buffer[i]; + } + } + p_FileLength -= readLength; + } + + // Handle last line without linebreak + if (!cell.empty() || !row.empty()) + { + row.push_back(Unquote(Trim(cell))); + cell.clear(); + mData.push_back(row); + row.clear(); + } + + // Assume CR/LF if at least half the linebreaks have CR + mSeparatorParams.mHasCR = (cr > (lf / 2)); + + // Set up column labels + UpdateColumnNames(); + + // Set up row labels + UpdateRowNames(); + } + + void WriteCsv() const + { +#ifdef HAS_CODECVT + if (mIsUtf16) + { + std::stringstream ss; + WriteCsv(ss); + std::string utf8 = ss.str(); + std::wstring wstr = ToWString(utf8); + + std::wofstream wstream; + wstream.exceptions(std::wofstream::failbit | std::wofstream::badbit); + wstream.open(mPath, std::ios::binary | std::ios::trunc); + + if (mIsLE) + { + wstream.imbue(std::locale(wstream.getloc(), + new std::codecvt_utf16<wchar_t, 0x10ffff, + static_cast<std::codecvt_mode>(std::little_endian)>)); + } + else + { + wstream.imbue(std::locale(wstream.getloc(), + new std::codecvt_utf16<wchar_t, 0x10ffff>)); + } + + wstream << static_cast<wchar_t>(0xfeff); + wstream << wstr; + } + else +#endif + { + std::ofstream stream; + stream.exceptions(std::ofstream::failbit | std::ofstream::badbit); + stream.open(mPath, std::ios::binary | std::ios::trunc); + WriteCsv(stream); + } + } + + void WriteCsv(std::ostream& pStream) const + { + for (auto itr = mData.begin(); itr != mData.end(); ++itr) + { + for (auto itc = itr->begin(); itc != itr->end(); ++itc) + { + if (mSeparatorParams.mAutoQuote && + ((itc->find(mSeparatorParams.mSeparator) != std::string::npos) || + (itc->find(' ') != std::string::npos))) + { + // escape quotes in string + std::string str = *itc; + const std::string quoteCharStr = std::string(1, mSeparatorParams.mQuoteChar); + ReplaceString(str, quoteCharStr, quoteCharStr + quoteCharStr); + + pStream << quoteCharStr << str << quoteCharStr; + } + else + { + pStream << *itc; + } + + if (std::distance(itc, itr->end()) > 1) + { + pStream << mSeparatorParams.mSeparator; + } + } + pStream << (mSeparatorParams.mHasCR ? "\r\n" : "\n"); + } + } + + size_t GetDataRowCount() const + { + return mData.size(); + } + + size_t GetDataColumnCount() const + { + return (mData.size() > 0) ? mData.at(0).size() : 0; + } + + inline size_t GetDataRowIndex(const size_t pRowIdx) const + { + return pRowIdx + static_cast<size_t>(mLabelParams.mColumnNameIdx + 1); + } + + inline size_t GetDataColumnIndex(const size_t pColumnIdx) const + { + return pColumnIdx + static_cast<size_t>(mLabelParams.mRowNameIdx + 1); + } + + std::string Trim(const std::string& pStr) const + { + if (mSeparatorParams.mTrim) + { + std::string str = pStr; + + // ltrim + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](int ch) { return !isspace(ch); })); + + // rtrim + str.erase(std::find_if(str.rbegin(), str.rend(), [](int ch) { return !isspace(ch); }).base(), str.end()); + + return str; + } + else + { + return pStr; + } + } + + std::string Unquote(const std::string& pStr) const + { + if (mSeparatorParams.mAutoQuote && (pStr.size() >= 2) && + (pStr.front() == mSeparatorParams.mQuoteChar) && + (pStr.back() == mSeparatorParams.mQuoteChar)) + { + // remove start/end quotes + std::string str = pStr.substr(1, pStr.size() - 2); + + // unescape quotes in string + const std::string quoteCharStr = std::string(1, mSeparatorParams.mQuoteChar); + ReplaceString(str, quoteCharStr + quoteCharStr, quoteCharStr); + + return str; + } + else + { + return pStr; + } + } + + void UpdateColumnNames() + { + mColumnNames.clear(); + if ((mLabelParams.mColumnNameIdx >= 0) && + (static_cast<ssize_t>(mData.size()) > mLabelParams.mColumnNameIdx)) + { + size_t i = 0; + for (auto& columnName : mData[static_cast<size_t>(mLabelParams.mColumnNameIdx)]) + { + mColumnNames[columnName] = i++; + } + } + } + + void UpdateRowNames() + { + mRowNames.clear(); + if ((mLabelParams.mRowNameIdx >= 0) && + (static_cast<ssize_t>(mData.size()) > + (mLabelParams.mColumnNameIdx + 1))) + { + size_t i = 0; + for (auto& dataRow : mData) + { + if (static_cast<ssize_t>(dataRow.size()) > mLabelParams.mRowNameIdx) + { + mRowNames[dataRow[static_cast<size_t>(mLabelParams.mRowNameIdx)]] = i++; + } + } + } + } + +#ifdef HAS_CODECVT +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable: 4996) +#endif + static std::string ToString(const std::wstring& pWStr) + { + return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>{ }.to_bytes(pWStr); + } + + static std::wstring ToWString(const std::string& pStr) + { + return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>{ }.from_bytes(pStr); + } +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#endif + + static void ReplaceString(std::string& pStr, const std::string& pSearch, const std::string& pReplace) + { + size_t pos = 0; + + while ((pos = pStr.find(pSearch, pos)) != std::string::npos) + { + pStr.replace(pos, pSearch.size(), pReplace); + pos += pReplace.size(); + } + } + + private: + std::string mPath; + LabelParams mLabelParams; + SeparatorParams mSeparatorParams; + ConverterParams mConverterParams; + LineReaderParams mLineReaderParams; + std::vector<std::vector<std::string>> mData; + std::map<std::string, size_t> mColumnNames; + std::map<std::string, size_t> mRowNames; +#ifdef HAS_CODECVT + bool mIsUtf16 = false; + bool mIsLE = false; +#endif + }; +}