| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166 |
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_INJA_HPP_
- #define INCLUDE_INJA_INJA_HPP_
- #include <nlohmann/json.hpp>
- // #include "environment.hpp"
- // Copyright (c) 2019 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
- #define INCLUDE_INJA_ENVIRONMENT_HPP_
- #include <fstream>
- #include <iostream>
- #include <memory>
- #include <sstream>
- #include <string>
- #include <nlohmann/json.hpp>
- // #include "config.hpp"
- // Copyright (c) 2019 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_CONFIG_HPP_
- #define INCLUDE_INJA_CONFIG_HPP_
- #include <functional>
- #include <string>
- // #include "string_view.hpp"
- // Copyright 2017-2019 by Martin Moene
- //
- // string-view lite, a C++17-like string_view for C++98 and later.
- // For more information see https://github.com/martinmoene/string-view-lite
- //
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef NONSTD_SV_LITE_H_INCLUDED
- #define NONSTD_SV_LITE_H_INCLUDED
- #define string_view_lite_MAJOR 1
- #define string_view_lite_MINOR 4
- #define string_view_lite_PATCH 0
- #define string_view_lite_VERSION \
- nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY( \
- string_view_lite_PATCH)
- #define nssv_STRINGIFY(x) nssv_STRINGIFY_(x)
- #define nssv_STRINGIFY_(x) #x
- // string-view lite configuration:
- #define nssv_STRING_VIEW_DEFAULT 0
- #define nssv_STRING_VIEW_NONSTD 1
- #define nssv_STRING_VIEW_STD 2
- #if !defined(nssv_CONFIG_SELECT_STRING_VIEW)
- #define nssv_CONFIG_SELECT_STRING_VIEW (nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD)
- #endif
- #if defined(nssv_CONFIG_SELECT_STD_STRING_VIEW) || defined(nssv_CONFIG_SELECT_NONSTD_STRING_VIEW)
- #error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
- #endif
- #ifndef nssv_CONFIG_STD_SV_OPERATOR
- #define nssv_CONFIG_STD_SV_OPERATOR 0
- #endif
- #ifndef nssv_CONFIG_USR_SV_OPERATOR
- #define nssv_CONFIG_USR_SV_OPERATOR 1
- #endif
- #ifdef nssv_CONFIG_CONVERSION_STD_STRING
- #define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
- #define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
- #endif
- #ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
- #define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
- #endif
- #ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
- #define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
- #endif
- // Control presence of exception handling (try and auto discover):
- #ifndef nssv_CONFIG_NO_EXCEPTIONS
- #if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
- #define nssv_CONFIG_NO_EXCEPTIONS 0
- #else
- #define nssv_CONFIG_NO_EXCEPTIONS 1
- #endif
- #endif
- // C++ language version detection (C++20 is speculative):
- // Note: VC14.0/1900 (VS2015) lacks too much from C++14.
- #ifndef nssv_CPLUSPLUS
- #if defined(_MSVC_LANG) && !defined(__clang__)
- #define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
- #else
- #define nssv_CPLUSPLUS __cplusplus
- #endif
- #endif
- #define nssv_CPP98_OR_GREATER (nssv_CPLUSPLUS >= 199711L)
- #define nssv_CPP11_OR_GREATER (nssv_CPLUSPLUS >= 201103L)
- #define nssv_CPP11_OR_GREATER_ (nssv_CPLUSPLUS >= 201103L)
- #define nssv_CPP14_OR_GREATER (nssv_CPLUSPLUS >= 201402L)
- #define nssv_CPP17_OR_GREATER (nssv_CPLUSPLUS >= 201703L)
- #define nssv_CPP20_OR_GREATER (nssv_CPLUSPLUS >= 202000L)
- // use C++17 std::string_view if available and requested:
- #if nssv_CPP17_OR_GREATER && defined(__has_include)
- #if __has_include(<string_view> )
- #define nssv_HAVE_STD_STRING_VIEW 1
- #else
- #define nssv_HAVE_STD_STRING_VIEW 0
- #endif
- #else
- #define nssv_HAVE_STD_STRING_VIEW 0
- #endif
- #define nssv_USES_STD_STRING_VIEW \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || \
- ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW))
- #define nssv_HAVE_STARTS_WITH (nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW)
- #define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
- //
- // Use C++17 std::string_view:
- //
- #if nssv_USES_STD_STRING_VIEW
- #include <string_view>
- // Extensions for std::string:
- #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
- namespace nonstd {
- template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
- std::basic_string<CharT, Traits, Allocator> to_string(std::basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
- }
- template <class CharT, class Traits, class Allocator>
- std::basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return std::basic_string_view<CharT, Traits>(s.data(), s.size());
- }
- // Literal operators sv and _sv:
- #if nssv_CONFIG_STD_SV_OPERATOR
- using namespace std::literals::string_view_literals;
- #endif
- #if nssv_CONFIG_USR_SV_OPERATOR
- inline namespace literals {
- inline namespace string_view_literals {
- constexpr std::string_view operator"" _sv(const char *str, size_t len) noexcept // (1)
- {
- return std::string_view {str, len};
- }
- constexpr std::u16string_view operator"" _sv(const char16_t *str, size_t len) noexcept // (2)
- {
- return std::u16string_view {str, len};
- }
- constexpr std::u32string_view operator"" _sv(const char32_t *str, size_t len) noexcept // (3)
- {
- return std::u32string_view {str, len};
- }
- constexpr std::wstring_view operator"" _sv(const wchar_t *str, size_t len) noexcept // (4)
- {
- return std::wstring_view {str, len};
- }
- } // namespace string_view_literals
- } // namespace literals
- #endif // nssv_CONFIG_USR_SV_OPERATOR
- } // namespace nonstd
- #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
- namespace nonstd {
- using std::basic_string_view;
- using std::string_view;
- using std::u16string_view;
- using std::u32string_view;
- using std::wstring_view;
- // literal "sv" and "_sv", see above
- using std::operator==;
- using std::operator!=;
- using std::operator<;
- using std::operator<=;
- using std::operator>;
- using std::operator>=;
- using std::operator<<;
- } // namespace nonstd
- #else // nssv_HAVE_STD_STRING_VIEW
- //
- // Before C++17: use string_view lite:
- //
- // Compiler versions:
- //
- // MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0)
- // MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002)
- // MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003)
- // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
- // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
- // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
- // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
- // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
- // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
- // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
- #if defined(_MSC_VER) && !defined(__clang__)
- #define nssv_COMPILER_MSVC_VER (_MSC_VER)
- #define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900)))
- #else
- #define nssv_COMPILER_MSVC_VER 0
- #define nssv_COMPILER_MSVC_VERSION 0
- #endif
- #define nssv_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch))
- #if defined(__clang__)
- #define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
- #else
- #define nssv_COMPILER_CLANG_VERSION 0
- #endif
- #if defined(__GNUC__) && !defined(__clang__)
- #define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
- #else
- #define nssv_COMPILER_GNUC_VERSION 0
- #endif
- // half-open range [lo..hi):
- #define nssv_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi))
- // Presence of language and library features:
- #ifdef _HAS_CPP0X
- #define nssv_HAS_CPP0X _HAS_CPP0X
- #else
- #define nssv_HAS_CPP0X 0
- #endif
- // Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
- #if nssv_COMPILER_MSVC_VER >= 1900
- #undef nssv_CPP11_OR_GREATER
- #define nssv_CPP11_OR_GREATER 1
- #endif
- #define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
- #define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
- #define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
- #define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
- #define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
- #define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
- #define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
- #define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
- // Presence of C++11 language features:
- #define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
- #define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
- #define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
- #define nssv_HAVE_NOEXCEPT nssv_CPP11_140
- #define nssv_HAVE_NULLPTR nssv_CPP11_100
- #define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
- #define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
- #define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
- #define nssv_HAVE_WCHAR16_T nssv_CPP11_100
- #define nssv_HAVE_WCHAR32_T nssv_CPP11_100
- #if !((nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION) || nssv_BETWEEN(nssv_COMPILER_CLANG_VERSION, 300, 400))
- #define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
- #else
- #define nssv_HAVE_STD_DEFINED_LITERALS 0
- #endif
- // Presence of C++14 language features:
- #define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
- // Presence of C++17 language features:
- #define nssv_HAVE_NODISCARD nssv_CPP17_000
- // Presence of C++ library features:
- #define nssv_HAVE_STD_HASH nssv_CPP11_120
- // C++ feature usage:
- #if nssv_HAVE_CONSTEXPR_11
- #define nssv_constexpr constexpr
- #else
- #define nssv_constexpr /*constexpr*/
- #endif
- #if nssv_HAVE_CONSTEXPR_14
- #define nssv_constexpr14 constexpr
- #else
- #define nssv_constexpr14 /*constexpr*/
- #endif
- #if nssv_HAVE_EXPLICIT_CONVERSION
- #define nssv_explicit explicit
- #else
- #define nssv_explicit /*explicit*/
- #endif
- #if nssv_HAVE_INLINE_NAMESPACE
- #define nssv_inline_ns inline
- #else
- #define nssv_inline_ns /*inline*/
- #endif
- #if nssv_HAVE_NOEXCEPT
- #define nssv_noexcept noexcept
- #else
- #define nssv_noexcept /*noexcept*/
- #endif
- //#if nssv_HAVE_REF_QUALIFIER
- //# define nssv_ref_qual &
- //# define nssv_refref_qual &&
- //#else
- //# define nssv_ref_qual /*&*/
- //# define nssv_refref_qual /*&&*/
- //#endif
- #if nssv_HAVE_NULLPTR
- #define nssv_nullptr nullptr
- #else
- #define nssv_nullptr NULL
- #endif
- #if nssv_HAVE_NODISCARD
- #define nssv_nodiscard [[nodiscard]]
- #else
- #define nssv_nodiscard /*[[nodiscard]]*/
- #endif
- // Additional includes:
- #include <algorithm>
- #include <cassert>
- #include <iterator>
- #include <limits>
- #include <ostream>
- #include <string> // std::char_traits<>
- #if !nssv_CONFIG_NO_EXCEPTIONS
- #include <stdexcept>
- #endif
- #if nssv_CPP11_OR_GREATER
- #include <type_traits>
- #endif
- // Clang, GNUC, MSVC warning suppression macros:
- #if defined(__clang__)
- #pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wuser-defined-literals"
- #elif defined(__GNUC__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wliteral-suffix"
- #endif // __clang__
- #if nssv_COMPILER_MSVC_VERSION >= 140
- #define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
- #define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress : code))
- #define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable : codes))
- #else
- #define nssv_SUPPRESS_MSGSL_WARNING(expr)
- #define nssv_SUPPRESS_MSVC_WARNING(code, descr)
- #define nssv_DISABLE_MSVC_WARNINGS(codes)
- #endif
- #if defined(__clang__)
- #define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
- #elif defined(__GNUC__)
- #define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
- #elif nssv_COMPILER_MSVC_VERSION >= 140
- #define nssv_RESTORE_WARNINGS() __pragma(warning(pop))
- #else
- #define nssv_RESTORE_WARNINGS()
- #endif
- // Suppress the following MSVC (GSL) warnings:
- // - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
- // start with an underscore are reserved
- // - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
- // use brace initialization, gsl::narrow_cast or gsl::narow
- // - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
- nssv_DISABLE_MSVC_WARNINGS(4455 26481 26472)
- // nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
- // nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
- namespace nonstd {
- namespace sv_lite {
- #if nssv_CPP11_OR_GREATER
- namespace detail {
- // Expect tail call optimization to make length() non-recursive:
- template <typename CharT> inline constexpr std::size_t length(CharT *s, std::size_t result = 0) {
- return *s == '\0' ? result : length(s + 1, result + 1);
- }
- } // namespace detail
- #endif // nssv_CPP11_OR_GREATER
- template <class CharT, class Traits = std::char_traits<CharT>> class basic_string_view;
- //
- // basic_string_view:
- //
- template <class CharT, class Traits /* = std::char_traits<CharT> */
- >
- class basic_string_view {
- public:
- // Member types:
- typedef Traits traits_type;
- typedef CharT value_type;
- typedef CharT *pointer;
- typedef CharT const *const_pointer;
- typedef CharT &reference;
- typedef CharT const &const_reference;
- typedef const_pointer iterator;
- typedef const_pointer const_iterator;
- typedef std::reverse_iterator<const_iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
- // 24.4.2.1 Construction and assignment:
- nssv_constexpr basic_string_view() nssv_noexcept : data_(nssv_nullptr), size_(0) {}
- #if nssv_CPP11_OR_GREATER
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept = default;
- #else
- nssv_constexpr basic_string_view(basic_string_view const &other) nssv_noexcept : data_(other.data_),
- size_(other.size_) {}
- #endif
- nssv_constexpr basic_string_view(CharT const *s, size_type count) nssv_noexcept // non-standard noexcept
- : data_(s),
- size_(count) {}
- nssv_constexpr basic_string_view(CharT const *s) nssv_noexcept // non-standard noexcept
- : data_(s)
- #if nssv_CPP17_OR_GREATER
- ,
- size_(Traits::length(s))
- #elif nssv_CPP11_OR_GREATER
- ,
- size_(detail::length(s))
- #else
- ,
- size_(Traits::length(s))
- #endif
- {
- }
- // Assignment:
- #if nssv_CPP11_OR_GREATER
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept = default;
- #else
- nssv_constexpr14 basic_string_view &operator=(basic_string_view const &other) nssv_noexcept {
- data_ = other.data_;
- size_ = other.size_;
- return *this;
- }
- #endif
- // 24.4.2.2 Iterator support:
- nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
- nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
- nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
- nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
- nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator(end()); }
- nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator(begin()); }
- nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
- nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
- // 24.4.2.3 Capacity:
- nssv_constexpr size_type size() const nssv_noexcept { return size_; }
- nssv_constexpr size_type length() const nssv_noexcept { return size_; }
- nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits<size_type>::max)(); }
- // since C++20
- nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept { return 0 == size_; }
- // 24.4.2.4 Element access:
- nssv_constexpr const_reference operator[](size_type pos) const { return data_at(pos); }
- nssv_constexpr14 const_reference at(size_type pos) const {
- #if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos < size());
- #else
- if (pos >= size()) {
- throw std::out_of_range("nonstd::string_view::at()");
- }
- #endif
- return data_at(pos);
- }
- nssv_constexpr const_reference front() const { return data_at(0); }
- nssv_constexpr const_reference back() const { return data_at(size() - 1); }
- nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
- // 24.4.2.5 Modifiers:
- nssv_constexpr14 void remove_prefix(size_type n) {
- assert(n <= size());
- data_ += n;
- size_ -= n;
- }
- nssv_constexpr14 void remove_suffix(size_type n) {
- assert(n <= size());
- size_ -= n;
- }
- nssv_constexpr14 void swap(basic_string_view &other) nssv_noexcept {
- using std::swap;
- swap(data_, other.data_);
- swap(size_, other.size_);
- }
- // 24.4.2.6 String operations:
- size_type copy(CharT *dest, size_type n, size_type pos = 0) const {
- #if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
- #else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::copy()");
- }
- #endif
- const size_type rlen = (std::min)(n, size() - pos);
- (void)Traits::copy(dest, data() + pos, rlen);
- return rlen;
- }
- nssv_constexpr14 basic_string_view substr(size_type pos = 0, size_type n = npos) const {
- #if nssv_CONFIG_NO_EXCEPTIONS
- assert(pos <= size());
- #else
- if (pos > size()) {
- throw std::out_of_range("nonstd::string_view::substr()");
- }
- #endif
- return basic_string_view(data() + pos, (std::min)(n, size() - pos));
- }
- // compare(), 6x:
- nssv_constexpr14 int compare(basic_string_view other) const nssv_noexcept // (1)
- {
- if (const int result = Traits::compare(data(), other.data(), (std::min)(size(), other.size()))) {
- return result;
- }
- return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
- }
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other) const // (2)
- {
- return substr(pos1, n1).compare(other);
- }
- nssv_constexpr int compare(size_type pos1, size_type n1, basic_string_view other, size_type pos2,
- size_type n2) const // (3)
- {
- return substr(pos1, n1).compare(other.substr(pos2, n2));
- }
- nssv_constexpr int compare(CharT const *s) const // (4)
- {
- return compare(basic_string_view(s));
- }
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s) const // (5)
- {
- return substr(pos1, n1).compare(basic_string_view(s));
- }
- nssv_constexpr int compare(size_type pos1, size_type n1, CharT const *s, size_type n2) const // (6)
- {
- return substr(pos1, n1).compare(basic_string_view(s, n2));
- }
- // 24.4.2.7 Searching:
- // starts_with(), 3x, since C++20:
- nssv_constexpr bool starts_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(0, v.size(), v) == 0;
- }
- nssv_constexpr bool starts_with(CharT c) const nssv_noexcept // (2)
- {
- return starts_with(basic_string_view(&c, 1));
- }
- nssv_constexpr bool starts_with(CharT const *s) const // (3)
- {
- return starts_with(basic_string_view(s));
- }
- // ends_with(), 3x, since C++20:
- nssv_constexpr bool ends_with(basic_string_view v) const nssv_noexcept // (1)
- {
- return size() >= v.size() && compare(size() - v.size(), npos, v) == 0;
- }
- nssv_constexpr bool ends_with(CharT c) const nssv_noexcept // (2)
- {
- return ends_with(basic_string_view(&c, 1));
- }
- nssv_constexpr bool ends_with(CharT const *s) const // (3)
- {
- return ends_with(basic_string_view(s));
- }
- // find(), 4x:
- nssv_constexpr14 size_type find(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return assert(v.size() == 0 || v.data() != nssv_nullptr),
- pos >= size() ? npos : to_pos(std::search(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
- nssv_constexpr14 size_type find(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find(basic_string_view(&c, 1), pos);
- }
- nssv_constexpr14 size_type find(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find(basic_string_view(s, n), pos);
- }
- nssv_constexpr14 size_type find(CharT const *s, size_type pos = 0) const // (4)
- {
- return find(basic_string_view(s), pos);
- }
- // rfind(), 4x:
- nssv_constexpr14 size_type rfind(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- if (size() < v.size()) {
- return npos;
- }
- if (v.empty()) {
- return (std::min)(size(), pos);
- }
- const_iterator last = cbegin() + (std::min)(size() - v.size(), pos) + v.size();
- const_iterator result = std::find_end(cbegin(), last, v.cbegin(), v.cend(), Traits::eq);
- return result != last ? size_type(result - cbegin()) : npos;
- }
- nssv_constexpr14 size_type rfind(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return rfind(basic_string_view(&c, 1), pos);
- }
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return rfind(basic_string_view(s, n), pos);
- }
- nssv_constexpr14 size_type rfind(CharT const *s, size_type pos = npos) const // (4)
- {
- return rfind(basic_string_view(s), pos);
- }
- // find_first_of(), 4x:
- nssv_constexpr size_type find_first_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos
- : to_pos(std::find_first_of(cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq));
- }
- nssv_constexpr size_type find_first_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_of(basic_string_view(&c, 1), pos);
- }
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos, size_type n) const // (3)
- {
- return find_first_of(basic_string_view(s, n), pos);
- }
- nssv_constexpr size_type find_first_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_of(basic_string_view(s), pos);
- }
- // find_last_of(), 4x:
- nssv_constexpr size_type find_last_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size() ? find_last_of(v, size() - 1)
- : to_pos(std::find_first_of(const_reverse_iterator(cbegin() + pos + 1), crend(),
- v.cbegin(), v.cend(), Traits::eq));
- }
- nssv_constexpr size_type find_last_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_of(basic_string_view(&c, 1), pos);
- }
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_of(basic_string_view(s, count), pos);
- }
- nssv_constexpr size_type find_last_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_of(basic_string_view(s), pos);
- }
- // find_first_not_of(), 4x:
- nssv_constexpr size_type find_first_not_of(basic_string_view v, size_type pos = 0) const nssv_noexcept // (1)
- {
- return pos >= size() ? npos : to_pos(std::find_if(cbegin() + pos, cend(), not_in_view(v)));
- }
- nssv_constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const nssv_noexcept // (2)
- {
- return find_first_not_of(basic_string_view(&c, 1), pos);
- }
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_first_not_of(basic_string_view(s, count), pos);
- }
- nssv_constexpr size_type find_first_not_of(CharT const *s, size_type pos = 0) const // (4)
- {
- return find_first_not_of(basic_string_view(s), pos);
- }
- // find_last_not_of(), 4x:
- nssv_constexpr size_type find_last_not_of(basic_string_view v, size_type pos = npos) const nssv_noexcept // (1)
- {
- return empty() ? npos
- : pos >= size()
- ? find_last_not_of(v, size() - 1)
- : to_pos(std::find_if(const_reverse_iterator(cbegin() + pos + 1), crend(), not_in_view(v)));
- }
- nssv_constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const nssv_noexcept // (2)
- {
- return find_last_not_of(basic_string_view(&c, 1), pos);
- }
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos, size_type count) const // (3)
- {
- return find_last_not_of(basic_string_view(s, count), pos);
- }
- nssv_constexpr size_type find_last_not_of(CharT const *s, size_type pos = npos) const // (4)
- {
- return find_last_not_of(basic_string_view(s), pos);
- }
- // Constants:
- #if nssv_CPP17_OR_GREATER
- static nssv_constexpr size_type npos = size_type(-1);
- #elif nssv_CPP11_OR_GREATER
- enum : size_type { npos = size_type(-1) };
- #else
- enum { npos = size_type(-1) };
- #endif
- private:
- struct not_in_view {
- const basic_string_view v;
- nssv_constexpr explicit not_in_view(basic_string_view v) : v(v) {}
- nssv_constexpr bool operator()(CharT c) const { return npos == v.find_first_of(c); }
- };
- nssv_constexpr size_type to_pos(const_iterator it) const { return it == cend() ? npos : size_type(it - cbegin()); }
- nssv_constexpr size_type to_pos(const_reverse_iterator it) const {
- return it == crend() ? npos : size_type(crend() - it - 1);
- }
- nssv_constexpr const_reference data_at(size_type pos) const {
- #if nssv_BETWEEN(nssv_COMPILER_GNUC_VERSION, 1, 500)
- return data_[pos];
- #else
- return assert(pos < size()), data_[pos];
- #endif
- }
- private:
- const_pointer data_;
- size_type size_;
- public:
- #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
- template <class Allocator>
- basic_string_view(std::basic_string<CharT, Traits, Allocator> const &s) nssv_noexcept : data_(s.data()),
- size_(s.size()) {}
- #if nssv_HAVE_EXPLICIT_CONVERSION
- template <class Allocator> explicit operator std::basic_string<CharT, Traits, Allocator>() const {
- return to_string(Allocator());
- }
- #endif // nssv_HAVE_EXPLICIT_CONVERSION
- #if nssv_CPP11_OR_GREATER
- template <class Allocator = std::allocator<CharT>>
- std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a = Allocator()) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
- #else
- std::basic_string<CharT, Traits> to_string() const { return std::basic_string<CharT, Traits>(begin(), end()); }
- template <class Allocator> std::basic_string<CharT, Traits, Allocator> to_string(Allocator const &a) const {
- return std::basic_string<CharT, Traits, Allocator>(begin(), end(), a);
- }
- #endif // nssv_CPP11_OR_GREATER
- #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
- };
- //
- // Non-member functions:
- //
- // 24.4.3 Non-member comparison functions:
- // lexicographically compare two string views (function template):
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
- // Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
- // Implementations shall provide sufficient additional overloads marked
- // constexpr and noexcept so that an object t with an implicit conversion
- // to S can be compared according to Table 67.
- #if !nssv_CPP11_OR_GREATER || nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 100, 141)
- // accomodate for older compilers:
- // ==
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) == 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator==(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
- // !=
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) != 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() != rhs.size() && lhs.compare(rhs) != 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator!=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return lhs.size() != rhs.size() || rhs.compare(lhs) != 0;
- }
- // <
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) > 0;
- }
- // <=
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator<=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) >= 0;
- }
- // >
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) < 0;
- }
- // >=
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs, char const *rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(char const *lhs, basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- std::basic_string<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
- template <class CharT, class Traits>
- nssv_constexpr bool operator>=(std::basic_string<CharT, Traits> rhs,
- basic_string_view<CharT, Traits> lhs) nssv_noexcept {
- return rhs.compare(lhs) <= 0;
- }
- #else // newer compilers:
- #define nssv_BASIC_STRING_VIEW_I(T, U) typename std::decay<basic_string_view<T, U>>::type
- #if nssv_BETWEEN(nssv_COMPILER_MSVC_VERSION, 140, 150)
- #define nssv_MSVC_ORDER(x) , int = x
- #else
- #define nssv_MSVC_ORDER(x) /*, int=x*/
- #endif
- // ==
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator==(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) == 0;
- }
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator==(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
- }
- // !=
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator!=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.size() != rhs.size() || lhs.compare(rhs) != 0;
- }
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator!=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) != 0;
- }
- // <
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) < 0;
- }
- // <=
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator<=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator<=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) <= 0;
- }
- // >
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) > 0;
- }
- // >=
- template <class CharT, class Traits nssv_MSVC_ORDER(1)>
- nssv_constexpr bool operator>=(basic_string_view<CharT, Traits> lhs,
- nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
- template <class CharT, class Traits nssv_MSVC_ORDER(2)>
- nssv_constexpr bool operator>=(nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
- basic_string_view<CharT, Traits> rhs) nssv_noexcept {
- return lhs.compare(rhs) >= 0;
- }
- #undef nssv_MSVC_ORDER
- #undef nssv_BASIC_STRING_VIEW_I
- #endif // compiler-dependent approach to comparisons
- // 24.4.4 Inserters and extractors:
- namespace detail {
- template <class Stream> void write_padding(Stream &os, std::streamsize n) {
- for (std::streamsize i = 0; i < n; ++i)
- os.rdbuf()->sputc(os.fill());
- }
- template <class Stream, class View> Stream &write_to_stream(Stream &os, View const &sv) {
- typename Stream::sentry sentry(os);
- if (!os)
- return os;
- const std::streamsize length = static_cast<std::streamsize>(sv.length());
- // Whether, and how, to pad:
- const bool pad = (length < os.width());
- const bool left_pad = pad && (os.flags() & std::ios_base::adjustfield) == std::ios_base::right;
- if (left_pad)
- write_padding(os, os.width() - length);
- // Write span characters:
- os.rdbuf()->sputn(sv.begin(), length);
- if (pad && !left_pad)
- write_padding(os, os.width() - length);
- // Reset output stream width:
- os.width(0);
- return os;
- }
- } // namespace detail
- template <class CharT, class Traits>
- std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
- basic_string_view<CharT, Traits> sv) {
- return detail::write_to_stream(os, sv);
- }
- // Several typedefs for common character types are provided:
- typedef basic_string_view<char> string_view;
- typedef basic_string_view<wchar_t> wstring_view;
- #if nssv_HAVE_WCHAR16_T
- typedef basic_string_view<char16_t> u16string_view;
- typedef basic_string_view<char32_t> u32string_view;
- #endif
- } // namespace sv_lite
- } // namespace nonstd::sv_lite
- //
- // 24.4.6 Suffix for basic_string_view literals:
- //
- #if nssv_HAVE_USER_DEFINED_LITERALS
- namespace nonstd {
- nssv_inline_ns namespace literals {
- nssv_inline_ns namespace string_view_literals {
- #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
- nssv_constexpr nonstd::sv_lite::string_view operator"" sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
- #endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
- #if nssv_CONFIG_USR_SV_OPERATOR
- nssv_constexpr nonstd::sv_lite::string_view operator"" _sv(const char *str, size_t len) nssv_noexcept // (1)
- {
- return nonstd::sv_lite::string_view {str, len};
- }
- nssv_constexpr nonstd::sv_lite::u16string_view operator"" _sv(const char16_t *str, size_t len) nssv_noexcept // (2)
- {
- return nonstd::sv_lite::u16string_view {str, len};
- }
- nssv_constexpr nonstd::sv_lite::u32string_view operator"" _sv(const char32_t *str, size_t len) nssv_noexcept // (3)
- {
- return nonstd::sv_lite::u32string_view {str, len};
- }
- nssv_constexpr nonstd::sv_lite::wstring_view operator"" _sv(const wchar_t *str, size_t len) nssv_noexcept // (4)
- {
- return nonstd::sv_lite::wstring_view {str, len};
- }
- #endif // nssv_CONFIG_USR_SV_OPERATOR
- }
- }
- } // namespace nonstd
- #endif
- //
- // Extensions for std::string:
- //
- #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
- namespace nonstd {
- namespace sv_lite {
- // Exclude MSVC 14 (19.00): it yields ambiguous to_string():
- #if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
- template <class CharT, class Traits, class Allocator = std::allocator<CharT>>
- std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v,
- Allocator const &a = Allocator()) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
- }
- #else
- template <class CharT, class Traits> std::basic_string<CharT, Traits> to_string(basic_string_view<CharT, Traits> v) {
- return std::basic_string<CharT, Traits>(v.begin(), v.end());
- }
- template <class CharT, class Traits, class Allocator>
- std::basic_string<CharT, Traits, Allocator> to_string(basic_string_view<CharT, Traits> v, Allocator const &a) {
- return std::basic_string<CharT, Traits, Allocator>(v.begin(), v.end(), a);
- }
- #endif // nssv_CPP11_OR_GREATER
- template <class CharT, class Traits, class Allocator>
- basic_string_view<CharT, Traits> to_string_view(std::basic_string<CharT, Traits, Allocator> const &s) {
- return basic_string_view<CharT, Traits>(s.data(), s.size());
- }
- } // namespace sv_lite
- } // namespace nonstd
- #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
- //
- // make types and algorithms available in namespace nonstd:
- //
- namespace nonstd {
- using sv_lite::basic_string_view;
- using sv_lite::string_view;
- using sv_lite::wstring_view;
- #if nssv_HAVE_WCHAR16_T
- using sv_lite::u16string_view;
- #endif
- #if nssv_HAVE_WCHAR32_T
- using sv_lite::u32string_view;
- #endif
- // literal "sv"
- using sv_lite::operator==;
- using sv_lite::operator!=;
- using sv_lite::operator<;
- using sv_lite::operator<=;
- using sv_lite::operator>;
- using sv_lite::operator>=;
- using sv_lite::operator<<;
- #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
- using sv_lite::to_string;
- using sv_lite::to_string_view;
- #endif
- } // namespace nonstd
- // 24.4.5 Hash support (C++11):
- // Note: The hash value of a string view object is equal to the hash value of
- // the corresponding string object.
- #if nssv_HAVE_STD_HASH
- #include <functional>
- namespace std {
- template <> struct hash<nonstd::string_view> {
- public:
- std::size_t operator()(nonstd::string_view v) const nssv_noexcept {
- return std::hash<std::string>()(std::string(v.data(), v.size()));
- }
- };
- template <> struct hash<nonstd::wstring_view> {
- public:
- std::size_t operator()(nonstd::wstring_view v) const nssv_noexcept {
- return std::hash<std::wstring>()(std::wstring(v.data(), v.size()));
- }
- };
- template <> struct hash<nonstd::u16string_view> {
- public:
- std::size_t operator()(nonstd::u16string_view v) const nssv_noexcept {
- return std::hash<std::u16string>()(std::u16string(v.data(), v.size()));
- }
- };
- template <> struct hash<nonstd::u32string_view> {
- public:
- std::size_t operator()(nonstd::u32string_view v) const nssv_noexcept {
- return std::hash<std::u32string>()(std::u32string(v.data(), v.size()));
- }
- };
- } // namespace std
- #endif // nssv_HAVE_STD_HASH
- nssv_RESTORE_WARNINGS()
- #endif // nssv_HAVE_STD_STRING_VIEW
- #endif // NONSTD_SV_LITE_H_INCLUDED
- namespace inja {
- /*!
- * \brief Class for lexer configuration.
- */
- struct LexerConfig {
- std::string statement_open {"{%"};
- std::string statement_open_no_lstrip {"{%+"};
- std::string statement_open_force_lstrip {"{%-"};
- std::string statement_close {"%}"};
- std::string statement_close_force_rstrip {"-%}"};
- std::string line_statement {"##"};
- std::string expression_open {"{{"};
- std::string expression_open_force_lstrip {"{{-"};
- std::string expression_close {"}}"};
- std::string expression_close_force_rstrip {"-}}"};
- std::string comment_open {"{#"};
- std::string comment_close {"#}"};
- std::string open_chars {"#{"};
- bool trim_blocks {false};
- bool lstrip_blocks {false};
- void update_open_chars() {
- open_chars = "";
- if (open_chars.find(line_statement[0]) == std::string::npos) {
- open_chars += line_statement[0];
- }
- if (open_chars.find(statement_open[0]) == std::string::npos) {
- open_chars += statement_open[0];
- }
- if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) {
- open_chars += statement_open_no_lstrip[0];
- }
- if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) {
- open_chars += statement_open_force_lstrip[0];
- }
- if (open_chars.find(expression_open[0]) == std::string::npos) {
- open_chars += expression_open[0];
- }
- if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) {
- open_chars += expression_open_force_lstrip[0];
- }
- if (open_chars.find(comment_open[0]) == std::string::npos) {
- open_chars += comment_open[0];
- }
- }
- };
- /*!
- * \brief Class for parser configuration.
- */
- struct ParserConfig {
- bool search_included_templates_in_files {true};
- /// Extra options for limiting included files' scope
- bool include_scope_limit = false;
- std::string include_scope = "templates";
- };
- /*!
- * \brief Class for render configuration.
- */
- struct RenderConfig {
- bool throw_at_missing_includes {true};
- };
- } // namespace inja
- #endif // INCLUDE_INJA_CONFIG_HPP_
- // #include "function_storage.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
- #define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
- #include <vector>
- // #include "string_view.hpp"
- namespace inja {
- using json = nlohmann::json;
- using Arguments = std::vector<const json *>;
- using CallbackFunction = std::function<json(Arguments &args)>;
- using VoidCallbackFunction = std::function<void(Arguments &args)>;
- /*!
- * \brief Class for builtin functions and user-defined callbacks.
- */
- class FunctionStorage {
- public:
- enum class Operation {
- Not,
- And,
- Or,
- In,
- Equal,
- NotEqual,
- Greater,
- GreaterEqual,
- Less,
- LessEqual,
- Add,
- Subtract,
- Multiplication,
- Division,
- Power,
- Modulo,
- AtId,
- At,
- Default,
- DivisibleBy,
- Even,
- Exists,
- ExistsInObject,
- First,
- Float,
- Int,
- IsArray,
- IsBoolean,
- IsFloat,
- IsInteger,
- IsNumber,
- IsObject,
- IsString,
- Last,
- Length,
- Lower,
- Max,
- Min,
- Odd,
- Range,
- Round,
- Sort,
- Upper,
- Callback,
- ParenLeft,
- ParenRight,
- None,
- };
- struct FunctionData {
- explicit FunctionData(const Operation &op, const CallbackFunction &cb = CallbackFunction{}) : operation(op), callback(cb) {}
- const Operation operation;
- const CallbackFunction callback;
- };
- private:
- const int VARIADIC {-1};
- std::map<std::pair<std::string, int>, FunctionData> function_storage = {
- {std::make_pair("at", 2), FunctionData { Operation::At }},
- {std::make_pair("default", 2), FunctionData { Operation::Default }},
- {std::make_pair("divisibleBy", 2), FunctionData { Operation::DivisibleBy }},
- {std::make_pair("even", 1), FunctionData { Operation::Even }},
- {std::make_pair("exists", 1), FunctionData { Operation::Exists }},
- {std::make_pair("existsIn", 2), FunctionData { Operation::ExistsInObject }},
- {std::make_pair("first", 1), FunctionData { Operation::First }},
- {std::make_pair("float", 1), FunctionData { Operation::Float }},
- {std::make_pair("int", 1), FunctionData { Operation::Int }},
- {std::make_pair("isArray", 1), FunctionData { Operation::IsArray }},
- {std::make_pair("isBoolean", 1), FunctionData { Operation::IsBoolean }},
- {std::make_pair("isFloat", 1), FunctionData { Operation::IsFloat }},
- {std::make_pair("isInteger", 1), FunctionData { Operation::IsInteger }},
- {std::make_pair("isNumber", 1), FunctionData { Operation::IsNumber }},
- {std::make_pair("isObject", 1), FunctionData { Operation::IsObject }},
- {std::make_pair("isString", 1), FunctionData { Operation::IsString }},
- {std::make_pair("last", 1), FunctionData { Operation::Last }},
- {std::make_pair("length", 1), FunctionData { Operation::Length }},
- {std::make_pair("lower", 1), FunctionData { Operation::Lower }},
- {std::make_pair("max", 1), FunctionData { Operation::Max }},
- {std::make_pair("min", 1), FunctionData { Operation::Min }},
- {std::make_pair("odd", 1), FunctionData { Operation::Odd }},
- {std::make_pair("range", 1), FunctionData { Operation::Range }},
- {std::make_pair("round", 2), FunctionData { Operation::Round }},
- {std::make_pair("sort", 1), FunctionData { Operation::Sort }},
- {std::make_pair("upper", 1), FunctionData { Operation::Upper }},
- };
- public:
- void add_builtin(nonstd::string_view name, int num_args, Operation op) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { op });
- }
- void add_callback(nonstd::string_view name, int num_args, const CallbackFunction &callback) {
- function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData { Operation::Callback, callback });
- }
- FunctionData find_function(nonstd::string_view name, int num_args) const {
- auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
- if (it != function_storage.end()) {
- return it->second;
- // Find variadic function
- } else if (num_args > 0) {
- it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
- if (it != function_storage.end()) {
- return it->second;
- }
- }
- return FunctionData { Operation::None };
- }
- };
- } // namespace inja
- #endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
- // #include "parser.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_PARSER_HPP_
- #define INCLUDE_INJA_PARSER_HPP_
- #include <limits>
- #include <stack>
- #include <string>
- #include <utility>
- #include <queue>
- #include <vector>
- // #include "config.hpp"
- // #include "exceptions.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
- #define INCLUDE_INJA_EXCEPTIONS_HPP_
- #include <stdexcept>
- #include <string>
- namespace inja {
- struct SourceLocation {
- size_t line;
- size_t column;
- };
- struct InjaError : public std::runtime_error {
- const std::string type;
- const std::string message;
- const SourceLocation location;
- explicit InjaError(const std::string &type, const std::string &message)
- : std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
- explicit InjaError(const std::string &type, const std::string &message, SourceLocation location)
- : std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" +
- std::to_string(location.column) + ") " + message),
- type(type), message(message), location(location) {}
- };
- struct ParserError : public InjaError {
- explicit ParserError(const std::string &message, SourceLocation location) : InjaError("parser_error", message, location) {}
- };
- struct RenderError : public InjaError {
- explicit RenderError(const std::string &message, SourceLocation location) : InjaError("render_error", message, location) {}
- };
- struct FileError : public InjaError {
- explicit FileError(const std::string &message) : InjaError("file_error", message) {}
- explicit FileError(const std::string &message, SourceLocation location) : InjaError("file_error", message, location) {}
- };
- struct JsonError : public InjaError {
- explicit JsonError(const std::string &message, SourceLocation location) : InjaError("json_error", message, location) {}
- };
- } // namespace inja
- #endif // INCLUDE_INJA_EXCEPTIONS_HPP_
- // #include "function_storage.hpp"
- // #include "lexer.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_LEXER_HPP_
- #define INCLUDE_INJA_LEXER_HPP_
- #include <cctype>
- #include <locale>
- // #include "config.hpp"
- // #include "token.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_TOKEN_HPP_
- #define INCLUDE_INJA_TOKEN_HPP_
- #include <string>
- // #include "string_view.hpp"
- namespace inja {
- /*!
- * \brief Helper-class for the inja Lexer.
- */
- struct Token {
- enum class Kind {
- Text,
- ExpressionOpen, // {{
- ExpressionClose, // }}
- LineStatementOpen, // ##
- LineStatementClose, // \n
- StatementOpen, // {%
- StatementClose, // %}
- CommentOpen, // {#
- CommentClose, // #}
- Id, // this, this.foo
- Number, // 1, 2, -1, 5.2, -5.3
- String, // "this"
- Plus, // +
- Minus, // -
- Times, // *
- Slash, // /
- Percent, // %
- Power, // ^
- Comma, // ,
- Dot, // .
- Colon, // :
- LeftParen, // (
- RightParen, // )
- LeftBracket, // [
- RightBracket, // ]
- LeftBrace, // {
- RightBrace, // }
- Equal, // ==
- NotEqual, // !=
- GreaterThan, // >
- GreaterEqual, // >=
- LessThan, // <
- LessEqual, // <=
- Unknown,
- Eof,
- };
- Kind kind {Kind::Unknown};
- nonstd::string_view text;
- explicit constexpr Token() = default;
- explicit constexpr Token(Kind kind, nonstd::string_view text) : kind(kind), text(text) {}
- std::string describe() const {
- switch (kind) {
- case Kind::Text:
- return "<text>";
- case Kind::LineStatementClose:
- return "<eol>";
- case Kind::Eof:
- return "<eof>";
- default:
- return static_cast<std::string>(text);
- }
- }
- };
- } // namespace inja
- #endif // INCLUDE_INJA_TOKEN_HPP_
- // #include "utils.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_UTILS_HPP_
- #define INCLUDE_INJA_UTILS_HPP_
- #include <algorithm>
- #include <fstream>
- #include <string>
- #include <utility>
- // #include "exceptions.hpp"
- // #include "string_view.hpp"
- namespace inja {
- inline void open_file_or_throw(const std::string &path, std::ifstream &file) {
- file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- try {
- file.open(path);
- } catch (const std::ios_base::failure & /*e*/) {
- throw FileError("failed accessing file at '" + path + "'");
- }
- }
- namespace string_view {
- inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
- start = std::min(start, view.size());
- end = std::min(std::max(start, end), view.size());
- return view.substr(start, end - start);
- }
- inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
- size_t idx = view.find(Separator);
- if (idx == nonstd::string_view::npos) {
- return std::make_pair(view, nonstd::string_view());
- }
- return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos));
- }
- inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
- return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
- }
- } // namespace string_view
- inline SourceLocation get_source_location(nonstd::string_view content, size_t pos) {
- // Get line and offset position (starts at 1:1)
- auto sliced = string_view::slice(content, 0, pos);
- std::size_t last_newline = sliced.rfind("\n");
- if (last_newline == nonstd::string_view::npos) {
- return {1, sliced.length() + 1};
- }
- // Count newlines
- size_t count_lines = 0;
- size_t search_start = 0;
- while (search_start <= sliced.size()) {
- search_start = sliced.find("\n", search_start) + 1;
- if (search_start == 0) {
- break;
- }
- count_lines += 1;
- }
- return {count_lines + 1, sliced.length() - last_newline};
- }
- } // namespace inja
- #endif // INCLUDE_INJA_UTILS_HPP_
- namespace inja {
- /*!
- * \brief Class for lexing an inja Template.
- */
- class Lexer {
- enum class State {
- Text,
- ExpressionStart,
- ExpressionStartForceLstrip,
- ExpressionBody,
- LineStart,
- LineBody,
- StatementStart,
- StatementStartNoLstrip,
- StatementStartForceLstrip,
- StatementBody,
- CommentStart,
- CommentBody,
- };
- enum class MinusState {
- Operator,
- Number,
- };
- const LexerConfig &config;
- State state;
- MinusState minus_state;
- nonstd::string_view m_in;
- size_t tok_start;
- size_t pos;
- Token scan_body(nonstd::string_view close, Token::Kind closeKind, nonstd::string_view close_trim = nonstd::string_view(), bool trim = false) {
- again:
- // skip whitespace (except for \n as it might be a close)
- if (tok_start >= m_in.size()) {
- return make_token(Token::Kind::Eof);
- }
- char ch = m_in[tok_start];
- if (ch == ' ' || ch == '\t' || ch == '\r') {
- tok_start += 1;
- goto again;
- }
- // check for close
- if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
- state = State::Text;
- pos = tok_start + close_trim.size();
- Token tok = make_token(closeKind);
- skip_whitespaces_and_newlines();
- return tok;
- }
- if (inja::string_view::starts_with(m_in.substr(tok_start), close)) {
- state = State::Text;
- pos = tok_start + close.size();
- Token tok = make_token(closeKind);
- if (trim) {
- skip_whitespaces_and_first_newline();
- }
- return tok;
- }
- // skip \n
- if (ch == '\n') {
- tok_start += 1;
- goto again;
- }
- pos = tok_start + 1;
- if (std::isalpha(ch)) {
- minus_state = MinusState::Operator;
- return scan_id();
- }
- MinusState current_minus_state = minus_state;
- if (minus_state == MinusState::Operator) {
- minus_state = MinusState::Number;
- }
- switch (ch) {
- case '+':
- return make_token(Token::Kind::Plus);
- case '-':
- if (current_minus_state == MinusState::Operator) {
- return make_token(Token::Kind::Minus);
- }
- return scan_number();
- case '*':
- return make_token(Token::Kind::Times);
- case '/':
- return make_token(Token::Kind::Slash);
- case '^':
- return make_token(Token::Kind::Power);
- case '%':
- return make_token(Token::Kind::Percent);
- case '.':
- return make_token(Token::Kind::Dot);
- case ',':
- return make_token(Token::Kind::Comma);
- case ':':
- return make_token(Token::Kind::Colon);
- case '(':
- return make_token(Token::Kind::LeftParen);
- case ')':
- minus_state = MinusState::Operator;
- return make_token(Token::Kind::RightParen);
- case '[':
- return make_token(Token::Kind::LeftBracket);
- case ']':
- minus_state = MinusState::Operator;
- return make_token(Token::Kind::RightBracket);
- case '{':
- return make_token(Token::Kind::LeftBrace);
- case '}':
- minus_state = MinusState::Operator;
- return make_token(Token::Kind::RightBrace);
- case '>':
- if (pos < m_in.size() && m_in[pos] == '=') {
- pos += 1;
- return make_token(Token::Kind::GreaterEqual);
- }
- return make_token(Token::Kind::GreaterThan);
- case '<':
- if (pos < m_in.size() && m_in[pos] == '=') {
- pos += 1;
- return make_token(Token::Kind::LessEqual);
- }
- return make_token(Token::Kind::LessThan);
- case '=':
- if (pos < m_in.size() && m_in[pos] == '=') {
- pos += 1;
- return make_token(Token::Kind::Equal);
- }
- return make_token(Token::Kind::Unknown);
- case '!':
- if (pos < m_in.size() && m_in[pos] == '=') {
- pos += 1;
- return make_token(Token::Kind::NotEqual);
- }
- return make_token(Token::Kind::Unknown);
- case '\"':
- return scan_string();
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- minus_state = MinusState::Operator;
- return scan_number();
- case '_':
- minus_state = MinusState::Operator;
- return scan_id();
- default:
- return make_token(Token::Kind::Unknown);
- }
- }
- Token scan_id() {
- for (;;) {
- if (pos >= m_in.size()) {
- break;
- }
- char ch = m_in[pos];
- if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
- break;
- }
- pos += 1;
- }
- return make_token(Token::Kind::Id);
- }
- Token scan_number() {
- for (;;) {
- if (pos >= m_in.size()) {
- break;
- }
- char ch = m_in[pos];
- // be very permissive in lexer (we'll catch errors when conversion happens)
- if (!std::isdigit(ch) && ch != '.' && ch != 'e' && ch != 'E' && ch != '+' && ch != '-') {
- break;
- }
- pos += 1;
- }
- return make_token(Token::Kind::Number);
- }
- Token scan_string() {
- bool escape {false};
- for (;;) {
- if (pos >= m_in.size()) {
- break;
- }
- char ch = m_in[pos++];
- if (ch == '\\') {
- escape = true;
- } else if (!escape && ch == m_in[tok_start]) {
- break;
- } else {
- escape = false;
- }
- }
- return make_token(Token::Kind::String);
- }
- Token make_token(Token::Kind kind) const { return Token(kind, string_view::slice(m_in, tok_start, pos)); }
- void skip_whitespaces_and_newlines() {
- if (pos < m_in.size()) {
- while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) {
- pos += 1;
- }
- }
- }
- void skip_whitespaces_and_first_newline() {
- if (pos < m_in.size()) {
- while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) {
- pos += 1;
- }
- }
- if (pos < m_in.size()) {
- char ch = m_in[pos];
- if (ch == '\n') {
- pos += 1;
- } else if (ch == '\r') {
- pos += 1;
- if (pos < m_in.size() && m_in[pos] == '\n') {
- pos += 1;
- }
- }
- }
- }
- static nonstd::string_view clear_final_line_if_whitespace(nonstd::string_view text) {
- nonstd::string_view result = text;
- while (!result.empty()) {
- char ch = result.back();
- if (ch == ' ' || ch == '\t') {
- result.remove_suffix(1);
- } else if (ch == '\n' || ch == '\r') {
- break;
- } else {
- return text;
- }
- }
- return result;
- }
- public:
- explicit Lexer(const LexerConfig &config) : config(config), state(State::Text), minus_state(MinusState::Number) {}
- SourceLocation current_position() const {
- return get_source_location(m_in, tok_start);
- }
- void start(nonstd::string_view input) {
- m_in = input;
- tok_start = 0;
- pos = 0;
- state = State::Text;
- minus_state = MinusState::Number;
- // Consume byte order mark (BOM) for UTF-8
- if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) {
- m_in = m_in.substr(3);
- }
- }
- Token scan() {
- tok_start = pos;
- again:
- if (tok_start >= m_in.size()) {
- return make_token(Token::Kind::Eof);
- }
- switch (state) {
- default:
- case State::Text: {
- // fast-scan to first open character
- size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
- if (open_start == nonstd::string_view::npos) {
- // didn't find open, return remaining text as text token
- pos = m_in.size();
- return make_token(Token::Kind::Text);
- }
- pos += open_start;
- // try to match one of the opening sequences, and get the close
- nonstd::string_view open_str = m_in.substr(pos);
- bool must_lstrip = false;
- if (inja::string_view::starts_with(open_str, config.expression_open)) {
- if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) {
- state = State::ExpressionStartForceLstrip;
- must_lstrip = true;
- } else {
- state = State::ExpressionStart;
- }
- } else if (inja::string_view::starts_with(open_str, config.statement_open)) {
- if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) {
- state = State::StatementStartNoLstrip;
- } else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip )) {
- state = State::StatementStartForceLstrip;
- must_lstrip = true;
- } else {
- state = State::StatementStart;
- must_lstrip = config.lstrip_blocks;
- }
- } else if (inja::string_view::starts_with(open_str, config.comment_open)) {
- state = State::CommentStart;
- must_lstrip = config.lstrip_blocks;
- } else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
- state = State::LineStart;
- } else {
- pos += 1; // wasn't actually an opening sequence
- goto again;
- }
- nonstd::string_view text = string_view::slice(m_in, tok_start, pos);
- if (must_lstrip) {
- text = clear_final_line_if_whitespace(text);
- }
- if (text.empty()) {
- goto again; // don't generate empty token
- }
- return Token(Token::Kind::Text, text);
- }
- case State::ExpressionStart: {
- state = State::ExpressionBody;
- pos += config.expression_open.size();
- return make_token(Token::Kind::ExpressionOpen);
- }
- case State::ExpressionStartForceLstrip: {
- state = State::ExpressionBody;
- pos += config.expression_open_force_lstrip.size();
- return make_token(Token::Kind::ExpressionOpen);
- }
- case State::LineStart: {
- state = State::LineBody;
- pos += config.line_statement.size();
- return make_token(Token::Kind::LineStatementOpen);
- }
- case State::StatementStart: {
- state = State::StatementBody;
- pos += config.statement_open.size();
- return make_token(Token::Kind::StatementOpen);
- }
- case State::StatementStartNoLstrip: {
- state = State::StatementBody;
- pos += config.statement_open_no_lstrip.size();
- return make_token(Token::Kind::StatementOpen);
- }
- case State::StatementStartForceLstrip: {
- state = State::StatementBody;
- pos += config.statement_open_force_lstrip.size();
- return make_token(Token::Kind::StatementOpen);
- }
- case State::CommentStart: {
- state = State::CommentBody;
- pos += config.comment_open.size();
- return make_token(Token::Kind::CommentOpen);
- }
- case State::ExpressionBody:
- return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
- case State::LineBody:
- return scan_body("\n", Token::Kind::LineStatementClose);
- case State::StatementBody:
- return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
- case State::CommentBody: {
- // fast-scan to comment close
- size_t end = m_in.substr(pos).find(config.comment_close);
- if (end == nonstd::string_view::npos) {
- pos = m_in.size();
- return make_token(Token::Kind::Eof);
- }
- // return the entire comment in the close token
- state = State::Text;
- pos += end + config.comment_close.size();
- Token tok = make_token(Token::Kind::CommentClose);
- if (config.trim_blocks) {
- skip_whitespaces_and_first_newline();
- }
- return tok;
- }
- }
- }
- const LexerConfig &get_config() const {
- return config;
- }
- };
- } // namespace inja
- #endif // INCLUDE_INJA_LEXER_HPP_
- // #include "node.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_NODE_HPP_
- #define INCLUDE_INJA_NODE_HPP_
- #include <string>
- #include <utility>
- #include <nlohmann/json.hpp>
- // #include "function_storage.hpp"
- // #include "string_view.hpp"
- namespace inja {
- class NodeVisitor;
- class BlockNode;
- class TextNode;
- class ExpressionNode;
- class LiteralNode;
- class JsonNode;
- class FunctionNode;
- class ExpressionListNode;
- class StatementNode;
- class ForStatementNode;
- class ForArrayStatementNode;
- class ForObjectStatementNode;
- class IfStatementNode;
- class IncludeStatementNode;
- class SetStatementNode;
- class NodeVisitor {
- public:
- virtual void visit(const BlockNode& node) = 0;
- virtual void visit(const TextNode& node) = 0;
- virtual void visit(const ExpressionNode& node) = 0;
- virtual void visit(const LiteralNode& node) = 0;
- virtual void visit(const JsonNode& node) = 0;
- virtual void visit(const FunctionNode& node) = 0;
- virtual void visit(const ExpressionListNode& node) = 0;
- virtual void visit(const StatementNode& node) = 0;
- virtual void visit(const ForStatementNode& node) = 0;
- virtual void visit(const ForArrayStatementNode& node) = 0;
- virtual void visit(const ForObjectStatementNode& node) = 0;
- virtual void visit(const IfStatementNode& node) = 0;
- virtual void visit(const IncludeStatementNode& node) = 0;
- virtual void visit(const SetStatementNode& node) = 0;
- };
- /*!
- * \brief Base node class for the abstract syntax tree (AST).
- */
- class AstNode {
- public:
- virtual void accept(NodeVisitor& v) const = 0;
- size_t pos;
- AstNode(size_t pos) : pos(pos) { }
- virtual ~AstNode() { };
- };
- class BlockNode : public AstNode {
- public:
- std::vector<std::shared_ptr<AstNode>> nodes;
- explicit BlockNode() : AstNode(0) {}
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class TextNode : public AstNode {
- public:
- const size_t length;
- explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class ExpressionNode : public AstNode {
- public:
- explicit ExpressionNode(size_t pos) : AstNode(pos) {}
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class LiteralNode : public ExpressionNode {
- public:
- const nlohmann::json value;
- explicit LiteralNode(const nlohmann::json& value, size_t pos) : ExpressionNode(pos), value(value) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class JsonNode : public ExpressionNode {
- public:
- const std::string name;
- const json::json_pointer ptr;
- static std::string convert_dot_to_json_ptr(nonstd::string_view ptr_name) {
- std::string result;
- do {
- nonstd::string_view part;
- std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
- result.push_back('/');
- result.append(part.begin(), part.end());
- } while (!ptr_name.empty());
- return result;
- }
- explicit JsonNode(nonstd::string_view ptr_name, size_t pos) : ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_json_ptr(ptr_name))) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class FunctionNode : public ExpressionNode {
- using Op = FunctionStorage::Operation;
- public:
- enum class Associativity {
- Left,
- Right,
- };
- unsigned int precedence;
- Associativity associativity;
- Op operation;
- std::string name;
- int number_args; // Should also be negative -> -1 for unknown number
- CallbackFunction callback;
- explicit FunctionNode(nonstd::string_view name, size_t pos) : ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) { }
- explicit FunctionNode(Op operation, size_t pos) : ExpressionNode(pos), operation(operation), number_args(1) {
- switch (operation) {
- case Op::Not: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::And: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::Or: {
- precedence = 1;
- associativity = Associativity::Left;
- } break;
- case Op::In: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Equal: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::NotEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Greater: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::GreaterEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Less: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::LessEqual: {
- precedence = 2;
- associativity = Associativity::Left;
- } break;
- case Op::Add: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Subtract: {
- precedence = 3;
- associativity = Associativity::Left;
- } break;
- case Op::Multiplication: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Division: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::Power: {
- precedence = 5;
- associativity = Associativity::Right;
- } break;
- case Op::Modulo: {
- precedence = 4;
- associativity = Associativity::Left;
- } break;
- case Op::AtId: {
- precedence = 8;
- associativity = Associativity::Left;
- } break;
- default: {
- precedence = 1;
- associativity = Associativity::Left;
- }
- }
- }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class ExpressionListNode : public AstNode {
- public:
- std::vector<std::shared_ptr<ExpressionNode>> rpn_output;
- explicit ExpressionListNode() : AstNode(0) { }
- explicit ExpressionListNode(size_t pos) : AstNode(pos) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class StatementNode : public AstNode {
- public:
- StatementNode(size_t pos) : AstNode(pos) { }
- virtual void accept(NodeVisitor& v) const = 0;
- };
- class ForStatementNode : public StatementNode {
- public:
- ExpressionListNode condition;
- BlockNode body;
- BlockNode *const parent;
- ForStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent) { }
- virtual void accept(NodeVisitor& v) const = 0;
- };
- class ForArrayStatementNode : public ForStatementNode {
- public:
- const std::string value;
- explicit ForArrayStatementNode(const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), value(value) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class ForObjectStatementNode : public ForStatementNode {
- public:
- const std::string key;
- const std::string value;
- explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode *const parent, size_t pos) : ForStatementNode(parent, pos), key(key), value(value) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class IfStatementNode : public StatementNode {
- public:
- ExpressionListNode condition;
- BlockNode true_statement;
- BlockNode false_statement;
- BlockNode *const parent;
- const bool is_nested;
- bool has_false_statement {false};
- explicit IfStatementNode(BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(false) { }
- explicit IfStatementNode(bool is_nested, BlockNode *const parent, size_t pos) : StatementNode(pos), parent(parent), is_nested(is_nested) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- }
- };
- class IncludeStatementNode : public StatementNode {
- public:
- const std::string file;
- explicit IncludeStatementNode(const std::string& file, size_t pos) : StatementNode(pos), file(file) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- };
- };
- class SetStatementNode : public StatementNode {
- public:
- const std::string key;
- ExpressionListNode expression;
- explicit SetStatementNode(const std::string& key, size_t pos) : StatementNode(pos), key(key) { }
- void accept(NodeVisitor& v) const {
- v.visit(*this);
- };
- };
- } // namespace inja
- #endif // INCLUDE_INJA_NODE_HPP_
- // #include "template.hpp"
- // Copyright (c) 2019 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_TEMPLATE_HPP_
- #define INCLUDE_INJA_TEMPLATE_HPP_
- #include <map>
- #include <memory>
- #include <string>
- #include <vector>
- // #include "node.hpp"
- // #include "statistics.hpp"
- // Copyright (c) 2019 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_STATISTICS_HPP_
- #define INCLUDE_INJA_STATISTICS_HPP_
- // #include "node.hpp"
- namespace inja {
- /*!
- * \brief A class for counting statistics on a Template.
- */
- class StatisticsVisitor : public NodeVisitor {
- void visit(const BlockNode& node) {
- for (auto& n : node.nodes) {
- n->accept(*this);
- }
- }
- void visit(const TextNode&) { }
- void visit(const ExpressionNode&) { }
- void visit(const LiteralNode&) { }
- void visit(const JsonNode&) {
- variable_counter += 1;
- }
- void visit(const FunctionNode&) { }
- void visit(const ExpressionListNode& node) {
- for (auto& n : node.rpn_output) {
- n->accept(*this);
- }
- }
- void visit(const StatementNode&) { }
- void visit(const ForStatementNode&) { }
- void visit(const ForArrayStatementNode& node) {
- node.condition.accept(*this);
- node.body.accept(*this);
- }
- void visit(const ForObjectStatementNode& node) {
- node.condition.accept(*this);
- node.body.accept(*this);
- }
- void visit(const IfStatementNode& node) {
- node.condition.accept(*this);
- node.true_statement.accept(*this);
- node.false_statement.accept(*this);
- }
- void visit(const IncludeStatementNode&) { }
- void visit(const SetStatementNode&) { }
- public:
- unsigned int variable_counter;
- explicit StatisticsVisitor() : variable_counter(0) { }
- };
- } // namespace inja
- #endif // INCLUDE_INJA_STATISTICS_HPP_
- namespace inja {
- /*!
- * \brief The main inja Template.
- */
- struct Template {
- BlockNode root;
- std::string content;
- explicit Template() { }
- explicit Template(const std::string& content): content(content) { }
- /// Return number of variables (total number, not distinct ones) in the template
- int count_variables() {
- auto statistic_visitor = StatisticsVisitor();
- root.accept(statistic_visitor);
- return statistic_visitor.variable_counter;
- }
- };
- using TemplateStorage = std::map<std::string, Template>;
- } // namespace inja
- #endif // INCLUDE_INJA_TEMPLATE_HPP_
- // #include "token.hpp"
- // #include "utils.hpp"
- #include <nlohmann/json.hpp>
- namespace inja {
- /*!
- * \brief Class for parsing an inja Template.
- */
- class Parser {
- const ParserConfig &config;
- Lexer lexer;
- TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
- Token tok, peek_tok;
- bool have_peek_tok {false};
- size_t current_paren_level {0};
- size_t current_bracket_level {0};
- size_t current_brace_level {0};
- nonstd::string_view json_literal_start;
- BlockNode *current_block {nullptr};
- ExpressionListNode *current_expression_list {nullptr};
- std::stack<std::pair<FunctionNode*, size_t>> function_stack;
- std::stack<std::shared_ptr<FunctionNode>> operator_stack;
- std::stack<IfStatementNode*> if_statement_stack;
- std::stack<ForStatementNode*> for_statement_stack;
- void throw_parser_error(const std::string &message) {
- throw ParserError(message, lexer.current_position());
- }
- void get_next_token() {
- if (have_peek_tok) {
- tok = peek_tok;
- have_peek_tok = false;
- } else {
- tok = lexer.scan();
- }
- }
- void get_peek_token() {
- if (!have_peek_tok) {
- peek_tok = lexer.scan();
- have_peek_tok = true;
- }
- }
- void add_json_literal(const char* content_ptr) {
- nonstd::string_view json_text(json_literal_start.data(), tok.text.data() - json_literal_start.data() + tok.text.size());
- current_expression_list->rpn_output.emplace_back(std::make_shared<LiteralNode>(json::parse(json_text), json_text.data() - content_ptr));
- }
- bool parse_expression(Template &tmpl, Token::Kind closing) {
- while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
- // Literals
- switch (tok.kind) {
- case Token::Kind::String: {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
- }
- } break;
- case Token::Kind::Number: {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
- }
- } break;
- case Token::Kind::LeftBracket: {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- }
- current_bracket_level += 1;
- } break;
- case Token::Kind::LeftBrace: {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- }
- current_brace_level += 1;
- } break;
- case Token::Kind::RightBracket: {
- if (current_bracket_level == 0) {
- throw_parser_error("unexpected ']'");
- }
- current_bracket_level -= 1;
- if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
- }
- } break;
- case Token::Kind::RightBrace: {
- if (current_brace_level == 0) {
- throw_parser_error("unexpected '}'");
- }
- current_brace_level -= 1;
- if (current_brace_level == 0 && current_bracket_level == 0) {
- add_json_literal(tmpl.content.c_str());
- }
- } break;
- case Token::Kind::Id: {
- get_peek_token();
- // Json Literal
- if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") || tok.text == static_cast<decltype(tok.text)>("null")) {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- json_literal_start = tok.text;
- add_json_literal(tmpl.content.c_str());
- }
- // Operator
- } else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
- goto parse_operator;
- // Functions
- } else if (peek_tok.kind == Token::Kind::LeftParen) {
- operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
- function_stack.emplace(operator_stack.top().get(), current_paren_level);
- // Variables
- } else {
- current_expression_list->rpn_output.emplace_back(std::make_shared<JsonNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
- }
- // Operators
- } break;
- case Token::Kind::Equal:
- case Token::Kind::NotEqual:
- case Token::Kind::GreaterThan:
- case Token::Kind::GreaterEqual:
- case Token::Kind::LessThan:
- case Token::Kind::LessEqual:
- case Token::Kind::Plus:
- case Token::Kind::Minus:
- case Token::Kind::Times:
- case Token::Kind::Slash:
- case Token::Kind::Power:
- case Token::Kind::Percent:
- case Token::Kind::Dot: {
- parse_operator:
- FunctionStorage::Operation operation;
- switch (tok.kind) {
- case Token::Kind::Id: {
- if (tok.text == "and") {
- operation = FunctionStorage::Operation::And;
- } else if (tok.text == "or") {
- operation = FunctionStorage::Operation::Or;
- } else if (tok.text == "in") {
- operation = FunctionStorage::Operation::In;
- } else if (tok.text == "not") {
- operation = FunctionStorage::Operation::Not;
- } else {
- throw_parser_error("unknown operator in parser.");
- }
- } break;
- case Token::Kind::Equal: {
- operation = FunctionStorage::Operation::Equal;
- } break;
- case Token::Kind::NotEqual: {
- operation = FunctionStorage::Operation::NotEqual;
- } break;
- case Token::Kind::GreaterThan: {
- operation = FunctionStorage::Operation::Greater;
- } break;
- case Token::Kind::GreaterEqual: {
- operation = FunctionStorage::Operation::GreaterEqual;
- } break;
- case Token::Kind::LessThan: {
- operation = FunctionStorage::Operation::Less;
- } break;
- case Token::Kind::LessEqual: {
- operation = FunctionStorage::Operation::LessEqual;
- } break;
- case Token::Kind::Plus: {
- operation = FunctionStorage::Operation::Add;
- } break;
- case Token::Kind::Minus: {
- operation = FunctionStorage::Operation::Subtract;
- } break;
- case Token::Kind::Times: {
- operation = FunctionStorage::Operation::Multiplication;
- } break;
- case Token::Kind::Slash: {
- operation = FunctionStorage::Operation::Division;
- } break;
- case Token::Kind::Power: {
- operation = FunctionStorage::Operation::Power;
- } break;
- case Token::Kind::Percent: {
- operation = FunctionStorage::Operation::Modulo;
- } break;
- case Token::Kind::Dot: {
- operation = FunctionStorage::Operation::AtId;
- } break;
- default: {
- throw_parser_error("unknown operator in parser.");
- }
- }
- auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
- while (!operator_stack.empty() && ((operator_stack.top()->precedence > function_node->precedence) || (operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) && (operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
- }
- operator_stack.emplace(function_node);
- } break;
- case Token::Kind::Comma: {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- if (function_stack.empty()) {
- throw_parser_error("unexpected ','");
- }
- function_stack.top().first->number_args += 1;
- }
- } break;
- case Token::Kind::Colon: {
- if (current_brace_level == 0 && current_bracket_level == 0) {
- throw_parser_error("unexpected ':'");
- }
- } break;
- case Token::Kind::LeftParen: {
- current_paren_level += 1;
- operator_stack.emplace(std::make_shared<FunctionNode>(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str()));
- get_peek_token();
- if (peek_tok.kind == Token::Kind::RightParen) {
- if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) {
- function_stack.top().first->number_args = 0;
- }
- }
- } break;
- case Token::Kind::RightParen: {
- current_paren_level -= 1;
- while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
- }
- if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
- operator_stack.pop();
- }
- if (!function_stack.empty() && function_stack.top().second == current_paren_level) {
- auto func = function_stack.top().first;
- auto function_data = function_storage.find_function(func->name, func->number_args);
- if (function_data.operation == FunctionStorage::Operation::None) {
- throw_parser_error("unknown function " + func->name);
- }
- func->operation = function_data.operation;
- if (function_data.operation == FunctionStorage::Operation::Callback) {
- func->callback = function_data.callback;
- }
- if (operator_stack.empty()) {
- throw_parser_error("internal error at function " + func->name);
- }
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
- function_stack.pop();
- }
- }
- default:
- break;
- }
- get_next_token();
- }
- while (!operator_stack.empty()) {
- current_expression_list->rpn_output.emplace_back(operator_stack.top());
- operator_stack.pop();
- }
- return true;
- }
- bool parse_statement(Template &tmpl, Token::Kind closing, nonstd::string_view path) {
- if (tok.kind != Token::Kind::Id) {
- return false;
- }
- if (tok.text == static_cast<decltype(tok.text)>("if")) {
- get_next_token();
- auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.text.data() - tmpl.content.c_str());
- current_block->nodes.emplace_back(if_statement_node);
- if_statement_stack.emplace(if_statement_node.get());
- current_block = &if_statement_node->true_statement;
- current_expression_list = &if_statement_node->condition;
- if (!parse_expression(tmpl, closing)) {
- return false;
- }
- } else if (tok.text == static_cast<decltype(tok.text)>("else")) {
- if (if_statement_stack.empty()) {
- throw_parser_error("else without matching if");
- }
- auto &if_statement_data = if_statement_stack.top();
- get_next_token();
- if_statement_data->has_false_statement = true;
- current_block = &if_statement_data->false_statement;
- // Chained else if
- if (tok.kind == Token::Kind::Id && tok.text == static_cast<decltype(tok.text)>("if")) {
- get_next_token();
- auto if_statement_node = std::make_shared<IfStatementNode>(true, current_block, tok.text.data() - tmpl.content.c_str());
- current_block->nodes.emplace_back(if_statement_node);
- if_statement_stack.emplace(if_statement_node.get());
- current_block = &if_statement_node->true_statement;
- current_expression_list = &if_statement_node->condition;
- if (!parse_expression(tmpl, closing)) {
- return false;
- }
- }
- } else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
- if (if_statement_stack.empty()) {
- throw_parser_error("endif without matching if");
- }
- // Nested if statements
- while (if_statement_stack.top()->is_nested) {
- if_statement_stack.pop();
- }
- auto &if_statement_data = if_statement_stack.top();
- get_next_token();
- current_block = if_statement_data->parent;
- if_statement_stack.pop();
- } else if (tok.text == static_cast<decltype(tok.text)>("for")) {
- get_next_token();
- // options: for a in arr; for a, b in obj
- if (tok.kind != Token::Kind::Id) {
- throw_parser_error("expected id, got '" + tok.describe() + "'");
- }
- Token value_token = tok;
- get_next_token();
- // Object type
- std::shared_ptr<ForStatementNode> for_statement_node;
- if (tok.kind == Token::Kind::Comma) {
- get_next_token();
- if (tok.kind != Token::Kind::Id) {
- throw_parser_error("expected id, got '" + tok.describe() + "'");
- }
- Token key_token = std::move(value_token);
- value_token = tok;
- get_next_token();
- for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
- // Array type
- } else {
- for_statement_node = std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
- }
- current_block->nodes.emplace_back(for_statement_node);
- for_statement_stack.emplace(for_statement_node.get());
- current_block = &for_statement_node->body;
- current_expression_list = &for_statement_node->condition;
- if (tok.kind != Token::Kind::Id || tok.text != static_cast<decltype(tok.text)>("in")) {
- throw_parser_error("expected 'in', got '" + tok.describe() + "'");
- }
- get_next_token();
- if (!parse_expression(tmpl, closing)) {
- return false;
- }
- } else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
- if (for_statement_stack.empty()) {
- throw_parser_error("endfor without matching for");
- }
- auto &for_statement_data = for_statement_stack.top();
- get_next_token();
- current_block = for_statement_data->parent;
- for_statement_stack.pop();
- } else if (tok.text == static_cast<decltype(tok.text)>("include")) {
- get_next_token();
- if (tok.kind != Token::Kind::String) {
- throw_parser_error("expected string, got '" + tok.describe() + "'");
- }
- // Build the relative path
- json json_name = json::parse(tok.text);
- std::string pathname = static_cast<std::string>(path);
- pathname += json_name.get_ref<const std::string &>();
- if (pathname.compare(0, 2, "./") == 0) {
- pathname.erase(0, 2);
- }
- // sys::path::remove_dots(pathname, true, sys::path::Style::posix);
- if(config.include_scope_limit)
- {
- #ifdef _WIN32
- if(pathname.find(":\\") != pathname.npos || pathname.find("..") != pathname.npos)
- throw FileError("access denied when trying to include '" + pathname + "': out of scope");
- #else
- if(pathname.find("/") == 0 || pathname.find("..") != pathname.npos)
- throw FileError("access denied when trying to include '" + pathname + "': out of scope");
- #endif // _WIN32
- if(config.include_scope.size() && pathname.find(config.include_scope) != 0)
- throw FileError("access denied when trying to include '" + pathname + "': out of scope");
- }
- if (config.search_included_templates_in_files && template_storage.find(pathname) == template_storage.end()) {
- auto include_template = Template(load_file(pathname));
- template_storage.emplace(pathname, include_template);
- parse_into_template(template_storage[pathname], pathname);
- }
- current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(pathname, tok.text.data() - tmpl.content.c_str()));
- get_next_token();
- } else if (tok.text == static_cast<decltype(tok.text)>("set")) {
- get_next_token();
- if (tok.kind != Token::Kind::Id) {
- throw_parser_error("expected variable name, got '" + tok.describe() + "'");
- }
- std::string key = static_cast<std::string>(tok.text);
- get_next_token();
- auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.text.data() - tmpl.content.c_str());
- current_block->nodes.emplace_back(set_statement_node);
- current_expression_list = &set_statement_node->expression;
- if (tok.text != static_cast<decltype(tok.text)>("=")) {
- throw_parser_error("expected '=', got '" + tok.describe() + "'");
- }
- get_next_token();
- if (!parse_expression(tmpl, closing)) {
- return false;
- }
- } else {
- return false;
- }
- return true;
- }
- void parse_into(Template &tmpl, nonstd::string_view path) {
- lexer.start(tmpl.content);
- current_block = &tmpl.root;
- for (;;) {
- get_next_token();
- switch (tok.kind) {
- case Token::Kind::Eof: {
- if (!if_statement_stack.empty()) {
- throw_parser_error("unmatched if");
- }
- if (!for_statement_stack.empty()) {
- throw_parser_error("unmatched for");
- }
- } return;
- case Token::Kind::Text: {
- current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
- } break;
- case Token::Kind::StatementOpen: {
- get_next_token();
- if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) {
- throw_parser_error("expected statement, got '" + tok.describe() + "'");
- }
- if (tok.kind != Token::Kind::StatementClose) {
- throw_parser_error("expected statement close, got '" + tok.describe() + "'");
- }
- } break;
- case Token::Kind::LineStatementOpen: {
- get_next_token();
- if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) {
- throw_parser_error("expected statement, got '" + tok.describe() + "'");
- }
- if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) {
- throw_parser_error("expected line statement close, got '" + tok.describe() + "'");
- }
- } break;
- case Token::Kind::ExpressionOpen: {
- get_next_token();
- auto expression_list_node = std::make_shared<ExpressionListNode>(tok.text.data() - tmpl.content.c_str());
- current_block->nodes.emplace_back(expression_list_node);
- current_expression_list = expression_list_node.get();
- if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) {
- throw_parser_error("expected expression, got '" + tok.describe() + "'");
- }
- if (tok.kind != Token::Kind::ExpressionClose) {
- throw_parser_error("expected expression close, got '" + tok.describe() + "'");
- }
- } break;
- case Token::Kind::CommentOpen: {
- get_next_token();
- if (tok.kind != Token::Kind::CommentClose) {
- throw_parser_error("expected comment close, got '" + tok.describe() + "'");
- }
- } break;
- default: {
- throw_parser_error("unexpected token '" + tok.describe() + "'");
- } break;
- }
- }
- }
- public:
- explicit Parser(const ParserConfig &parser_config, const LexerConfig &lexer_config,
- TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) { }
- Template parse(nonstd::string_view input, nonstd::string_view path) {
- auto result = Template(static_cast<std::string>(input));
- parse_into(result, path);
- return result;
- }
- Template parse(nonstd::string_view input) {
- return parse(input, "./");
- }
- void parse_into_template(Template& tmpl, nonstd::string_view filename) {
- nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
- // StringRef path = sys::path::parent_path(filename);
- auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
- sub_parser.parse_into(tmpl, path);
- }
- std::string load_file(nonstd::string_view filename) {
- std::ifstream file;
- open_file_or_throw(static_cast<std::string>(filename), file);
- std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
- return text;
- }
- };
- } // namespace inja
- #endif // INCLUDE_INJA_PARSER_HPP_
- // #include "renderer.hpp"
- // Copyright (c) 2020 Pantor. All rights reserved.
- #ifndef INCLUDE_INJA_RENDERER_HPP_
- #define INCLUDE_INJA_RENDERER_HPP_
- #include <algorithm>
- #include <numeric>
- #include <string>
- #include <utility>
- #include <vector>
- #include <nlohmann/json.hpp>
- // #include "config.hpp"
- // #include "exceptions.hpp"
- // #include "node.hpp"
- // #include "template.hpp"
- // #include "utils.hpp"
- namespace inja {
- /*!
- * \brief Class for rendering a Template with data.
- */
- class Renderer : public NodeVisitor {
- using Op = FunctionStorage::Operation;
- const RenderConfig config;
- const Template *current_template;
- const TemplateStorage &template_storage;
- const FunctionStorage &function_storage;
- const json *json_input;
- std::ostream *output_stream;
- json json_additional_data;
- json* current_loop_data = &json_additional_data["loop"];
- std::vector<std::shared_ptr<json>> json_tmp_stack;
- std::stack<const json*> json_eval_stack;
- std::stack<const JsonNode*> not_found_stack;
- bool truthy(const json* data) const {
- if (data->is_boolean()) {
- return data->get<bool>();
- } else if (data->is_number()) {
- return (*data != 0);
- } else if (data->is_null()) {
- return false;
- }
- return !data->empty();
- }
- void print_json(const std::shared_ptr<json> value) {
- if (value->is_string()) {
- *output_stream << value->get_ref<const json::string_t&>();
- } else if (value->is_number_integer()) {
- *output_stream << value->get<const json::number_integer_t>();
- } else if (value->is_null()) {
- } else {
- *output_stream << value->dump();
- }
- }
- const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
- for (auto& expression : expression_list.rpn_output) {
- expression->accept(*this);
- }
- if (json_eval_stack.empty()) {
- throw_renderer_error("empty expression", expression_list);
- } else if (json_eval_stack.size() != 1) {
- throw_renderer_error("malformed expression", expression_list);
- }
- auto result = json_eval_stack.top();
- json_eval_stack.pop();
- if (!result) {
- if (not_found_stack.empty()) {
- throw_renderer_error("expression could not be evaluated", expression_list);
- }
- auto node = not_found_stack.top();
- not_found_stack.pop();
- throw_renderer_error("variable '" + static_cast<std::string>(node->name) + "' not found", *node);
- }
- return std::make_shared<json>(*result);
- }
- void throw_renderer_error(const std::string &message, const AstNode& node) {
- SourceLocation loc = get_source_location(current_template->content, node.pos);
- throw RenderError(message, loc);
- }
- template<size_t N, bool throw_not_found=true>
- std::array<const json*, N> get_arguments(const AstNode& node) {
- if (json_eval_stack.size() < N) {
- throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(json_eval_stack.size()), node);
- }
- std::array<const json*, N> result;
- for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
- if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
- not_found_stack.pop();
- if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
- }
- }
- }
- return result;
- }
- template<bool throw_not_found=true>
- Arguments get_argument_vector(size_t N, const AstNode& node) {
- Arguments result {N};
- for (size_t i = 0; i < N; i += 1) {
- result[N - i - 1] = json_eval_stack.top();
- json_eval_stack.pop();
- if (!result[N - i - 1]) {
- auto json_node = not_found_stack.top();
- not_found_stack.pop();
- if (throw_not_found) {
- throw_renderer_error("variable '" + static_cast<std::string>(json_node->name) + "' not found", *json_node);
- }
- }
- }
- return result;
- }
- void visit(const BlockNode& node) {
- for (auto& n : node.nodes) {
- n->accept(*this);
- }
- }
- void visit(const TextNode& node) {
- output_stream->write(current_template->content.c_str() + node.pos, node.length);
- }
- void visit(const ExpressionNode&) { }
- void visit(const LiteralNode& node) {
- json_eval_stack.push(&node.value);
- }
- void visit(const JsonNode& node) {
- if (json_additional_data.contains(node.ptr)) {
- json_eval_stack.push(&(json_additional_data[node.ptr]));
-
- } else if (json_input->contains(node.ptr)) {
- json_eval_stack.push(&(*json_input)[node.ptr]);
-
- } else {
- // Try to evaluate as a no-argument callback
- auto function_data = function_storage.find_function(node.name, 0);
- if (function_data.operation == FunctionStorage::Operation::Callback) {
- Arguments empty_args {};
- auto value = std::make_shared<json>(function_data.callback(empty_args));
- json_tmp_stack.push_back(value);
- json_eval_stack.push(value.get());
- } else {
- json_eval_stack.push(nullptr);
- not_found_stack.emplace(&node);
- }
- }
- }
- void visit(const FunctionNode& node) {
- std::shared_ptr<json> result_ptr;
- switch (node.operation) {
- case Op::Not: {
- auto args = get_arguments<1>(node);
- result_ptr = std::make_shared<json>(!truthy(args[0]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::And: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) && truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Or: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(truthy(args[0]) || truthy(args[1]));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::In: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Equal: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] == *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::NotEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] != *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Greater: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] > *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::GreaterEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] >= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Less: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] < *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::LessEqual: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(*args[0] <= *args[1]);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Add: {
- auto args = get_arguments<2>(node);
- if (args[0]->is_string() && args[1]->is_string()) {
- result_ptr = std::make_shared<json>(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
- json_tmp_stack.push_back(result_ptr);
- } else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() + args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- } else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() + args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- }
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Subtract: {
- auto args = get_arguments<2>(node);
- if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() - args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- } else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() - args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- }
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Multiplication: {
- auto args = get_arguments<2>(node);
- if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
- result_ptr = std::make_shared<json>(args[0]->get<int>() * args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- } else {
- result_ptr = std::make_shared<json>(args[0]->get<double>() * args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- }
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Division: {
- auto args = get_arguments<2>(node);
- if (args[1]->get<double>() == 0) {
- throw_renderer_error("division by zero", node);
- }
- result_ptr = std::make_shared<json>(args[0]->get<double>() / args[1]->get<double>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Power: {
- auto args = get_arguments<2>(node);
- if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
- int result = std::pow(args[0]->get<int>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- } else {
- double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- }
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Modulo: {
- auto args = get_arguments<2>(node);
- result_ptr = std::make_shared<json>(args[0]->get<int>() % args[1]->get<int>());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::AtId: {
- json_eval_stack.pop(); // Pop id nullptr
- auto container = get_arguments<1, false>(node)[0];
- if (not_found_stack.empty()) {
- throw_renderer_error("could not find element with given name", node);
- }
- auto id_node = not_found_stack.top();
- not_found_stack.pop();
- json_eval_stack.push(&container->at(id_node->name));
- } break;
- case Op::At: {
- auto args = get_arguments<2>(node);
- json_eval_stack.push(&args[0]->at(args[1]->get<int>()));
- } break;
- case Op::Default: {
- auto default_arg = get_arguments<1>(node)[0];
- auto test_arg = get_arguments<1, false>(node)[0];
- json_eval_stack.push(test_arg ? test_arg : default_arg);
- } break;
- case Op::DivisibleBy: {
- auto args = get_arguments<2>(node);
- int divisor = args[1]->get<int>();
- result_ptr = std::make_shared<json>((divisor != 0) && (args[0]->get<int>() % divisor == 0));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Even: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Exists: {
- auto &&name = get_arguments<1>(node)[0]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(json_input->contains(json::json_pointer(JsonNode::convert_dot_to_json_ptr(name))));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::ExistsInObject: {
- auto args = get_arguments<2>(node);
- auto &&name = args[1]->get_ref<const std::string &>();
- result_ptr = std::make_shared<json>(args[0]->find(name) != args[0]->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::First: {
- auto result = &get_arguments<1>(node)[0]->front();
- json_eval_stack.push(result);
- } break;
- case Op::Float: {
- result_ptr = std::make_shared<json>(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Int: {
- result_ptr = std::make_shared<json>(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string &>()));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Last: {
- auto result = &get_arguments<1>(node)[0]->back();
- json_eval_stack.push(result);
- } break;
- case Op::Length: {
- auto val = get_arguments<1>(node)[0];
- if (val->is_string()) {
- result_ptr = std::make_shared<json>(val->get_ref<const std::string &>().length());
- } else {
- result_ptr = std::make_shared<json>(val->size());
- }
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Lower: {
- std::string result = get_arguments<1>(node)[0]->get<std::string>();
- std::transform(result.begin(), result.end(), result.begin(), ::tolower);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Max: {
- auto args = get_arguments<1>(node);
- auto result = std::max_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
- } break;
- case Op::Min: {
- auto args = get_arguments<1>(node);
- auto result = std::min_element(args[0]->begin(), args[0]->end());
- json_eval_stack.push(&(*result));
- } break;
- case Op::Odd: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Range: {
- std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
- std::iota(result.begin(), result.end(), 0);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Round: {
- auto args = get_arguments<2>(node);
- int precision = args[1]->get<int>();
- double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Sort: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
- std::sort(result_ptr->begin(), result_ptr->end());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Upper: {
- std::string result = get_arguments<1>(node)[0]->get<std::string>();
- std::transform(result.begin(), result.end(), result.begin(), ::toupper);
- result_ptr = std::make_shared<json>(std::move(result));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsBoolean: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_boolean());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsNumber: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsInteger: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_integer());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsFloat: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_number_float());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsObject: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_object());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsArray: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_array());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::IsString: {
- result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->is_string());
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::Callback: {
- auto args = get_argument_vector(node.number_args, node);
- result_ptr = std::make_shared<json>(node.callback(args));
- json_tmp_stack.push_back(result_ptr);
- json_eval_stack.push(result_ptr.get());
- } break;
- case Op::ParenLeft:
- case Op::ParenRight:
- case Op::None:
- break;
- }
- }
- void visit(const ExpressionListNode& node) {
- print_json(eval_expression_list(node));
- }
- void visit(const StatementNode&) { }
- void visit(const ForStatementNode&) { }
- void visit(const ForArrayStatementNode& node) {
- auto result = eval_expression_list(node.condition);
- if (!result->is_array()) {
- throw_renderer_error("object must be an array", node);
- }
- if (!current_loop_data->empty()) {
- auto tmp = *current_loop_data; // Because of clang-3
- (*current_loop_data)["parent"] = std::move(tmp);
- }
- size_t index = 0;
- (*current_loop_data)["is_first"] = true;
- (*current_loop_data)["is_last"] = (result->size() <= 1);
- for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.value)] = *it;
- (*current_loop_data)["index"] = index;
- (*current_loop_data)["index1"] = index + 1;
- if (index == 1) {
- (*current_loop_data)["is_first"] = false;
- }
- if (index == result->size() - 1) {
- (*current_loop_data)["is_last"] = true;
- }
- node.body.accept(*this);
- ++index;
- }
- json_additional_data[static_cast<std::string>(node.value)].clear();
- if (!(*current_loop_data)["parent"].empty()) {
- auto tmp = (*current_loop_data)["parent"];
- *current_loop_data = std::move(tmp);
- } else {
- current_loop_data = &json_additional_data["loop"];
- }
- }
- void visit(const ForObjectStatementNode& node) {
- auto result = eval_expression_list(node.condition);
- if (!result->is_object()) {
- throw_renderer_error("object must be an object", node);
- }
- if (!current_loop_data->empty()) {
- (*current_loop_data)["parent"] = std::move(*current_loop_data);
- }
- size_t index = 0;
- (*current_loop_data)["is_first"] = true;
- (*current_loop_data)["is_last"] = (result->size() <= 1);
- for (auto it = result->begin(); it != result->end(); ++it) {
- json_additional_data[static_cast<std::string>(node.key)] = it.key();
- json_additional_data[static_cast<std::string>(node.value)] = it.value();
- (*current_loop_data)["index"] = index;
- (*current_loop_data)["index1"] = index + 1;
- if (index == 1) {
- (*current_loop_data)["is_first"] = false;
- }
- if (index == result->size() - 1) {
- (*current_loop_data)["is_last"] = true;
- }
- node.body.accept(*this);
- ++index;
- }
- json_additional_data[static_cast<std::string>(node.key)].clear();
- json_additional_data[static_cast<std::string>(node.value)].clear();
- if (!(*current_loop_data)["parent"].empty()) {
- *current_loop_data = std::move((*current_loop_data)["parent"]);
- } else {
- current_loop_data = &json_additional_data["loop"];
- }
- }
- void visit(const IfStatementNode& node) {
- auto result = eval_expression_list(node.condition);
- if (truthy(result.get())) {
- node.true_statement.accept(*this);
- } else if (node.has_false_statement) {
- node.false_statement.accept(*this);
- }
- }
- void visit(const IncludeStatementNode& node) {
- auto sub_renderer = Renderer(config, template_storage, function_storage);
- auto included_template_it = template_storage.find(node.file);
- if (included_template_it != template_storage.end()) {
- sub_renderer.render_to(*output_stream, included_template_it->second, *json_input, &json_additional_data);
- } else if (config.throw_at_missing_includes) {
- throw_renderer_error("include '" + node.file + "' not found", node);
- }
- }
- void visit(const SetStatementNode& node) {
- json_additional_data[node.key] = *eval_expression_list(node.expression);
- }
- public:
- Renderer(const RenderConfig& config, const TemplateStorage &template_storage, const FunctionStorage &function_storage)
- : config(config), template_storage(template_storage), function_storage(function_storage) { }
- void render_to(std::ostream &os, const Template &tmpl, const json &data, json *loop_data = nullptr) {
- output_stream = &os;
- current_template = &tmpl;
- json_input = &data;
- if (loop_data) {
- json_additional_data = *loop_data;
- current_loop_data = &json_additional_data["loop"];
- }
- current_template->root.accept(*this);
- json_tmp_stack.clear();
- }
- };
- } // namespace inja
- #endif // INCLUDE_INJA_RENDERER_HPP_
- // #include "string_view.hpp"
- // #include "template.hpp"
- // #include "utils.hpp"
- namespace inja {
- using json = nlohmann::json;
- /*!
- * \brief Class for changing the configuration.
- */
- class Environment {
- std::string input_path;
- std::string output_path;
- LexerConfig lexer_config;
- ParserConfig parser_config;
- RenderConfig render_config;
- FunctionStorage function_storage;
- TemplateStorage template_storage;
- public:
- Environment() : Environment("") {}
- explicit Environment(const std::string &global_path) : input_path(global_path), output_path(global_path) {}
- Environment(const std::string &input_path, const std::string &output_path)
- : input_path(input_path), output_path(output_path) {}
- /// Sets the opener and closer for template statements
- void set_statement(const std::string &open, const std::string &close) {
- lexer_config.statement_open = open;
- lexer_config.statement_open_no_lstrip = open + "+";
- lexer_config.statement_open_force_lstrip = open + "-";
- lexer_config.statement_close = close;
- lexer_config.statement_close_force_rstrip = "-" + close;
- lexer_config.update_open_chars();
- }
- /// Sets the opener for template line statements
- void set_line_statement(const std::string &open) {
- lexer_config.line_statement = open;
- lexer_config.update_open_chars();
- }
- /// Sets the opener and closer for template expressions
- void set_expression(const std::string &open, const std::string &close) {
- lexer_config.expression_open = open;
- lexer_config.expression_open_force_lstrip = open + "-";
- lexer_config.expression_close = close;
- lexer_config.expression_close_force_rstrip = "-" + close;
- lexer_config.update_open_chars();
- }
- /// Sets the opener and closer for template comments
- void set_comment(const std::string &open, const std::string &close) {
- lexer_config.comment_open = open;
- lexer_config.comment_close = close;
- lexer_config.update_open_chars();
- }
- /// Sets whether to remove the first newline after a block
- void set_trim_blocks(bool trim_blocks) {
- lexer_config.trim_blocks = trim_blocks;
- }
- /// Sets whether to strip the spaces and tabs from the start of a line to a block
- void set_lstrip_blocks(bool lstrip_blocks) {
- lexer_config.lstrip_blocks = lstrip_blocks;
- }
- /// Sets the element notation syntax
- void set_search_included_templates_in_files(bool search_in_files) {
- parser_config.search_included_templates_in_files = search_in_files;
- }
- /// Sets whether a missing include will throw an error
- void set_throw_at_missing_includes(bool will_throw) {
- render_config.throw_at_missing_includes = will_throw;
- }
- Template parse(nonstd::string_view input) {
- Parser parser(parser_config, lexer_config, template_storage, function_storage);
- return parser.parse(input);
- }
- Template parse_template(const std::string &filename) {
- Parser parser(parser_config, lexer_config, template_storage, function_storage);
- auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
- parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
- return result;
- }
- Template parse_file(const std::string &filename) {
- return parse_template(filename);
- }
- std::string render(nonstd::string_view input, const json &data) { return render(parse(input), data); }
- std::string render(const Template &tmpl, const json &data) {
- std::stringstream os;
- render_to(os, tmpl, data);
- return os.str();
- }
- std::string render_file(const std::string &filename, const json &data) {
- return render(parse_template(filename), data);
- }
- std::string render_file_with_json_file(const std::string &filename, const std::string &filename_data) {
- const json data = load_json(filename_data);
- return render_file(filename, data);
- }
- void write(const std::string &filename, const json &data, const std::string &filename_out) {
- std::ofstream file(output_path + filename_out);
- file << render_file(filename, data);
- file.close();
- }
- void write(const Template &temp, const json &data, const std::string &filename_out) {
- std::ofstream file(output_path + filename_out);
- file << render(temp, data);
- file.close();
- }
- void write_with_json_file(const std::string &filename, const std::string &filename_data,
- const std::string &filename_out) {
- const json data = load_json(filename_data);
- write(filename, data, filename_out);
- }
- void write_with_json_file(const Template &temp, const std::string &filename_data, const std::string &filename_out) {
- const json data = load_json(filename_data);
- write(temp, data, filename_out);
- }
- std::ostream &render_to(std::ostream &os, const Template &tmpl, const json &data) {
- Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
- return os;
- }
- std::string load_file(const std::string &filename) {
- Parser parser(parser_config, lexer_config, template_storage, function_storage);
- return parser.load_file(input_path + filename);
- }
- json load_json(const std::string &filename) {
- std::ifstream file;
- open_file_or_throw(input_path + filename, file);
- json j;
- file >> j;
- return j;
- }
- /*!
- @brief Adds a variadic callback
- */
- void add_callback(const std::string &name, const CallbackFunction &callback) {
- add_callback(name, -1, callback);
- }
- /*!
- @brief Adds a variadic void callback
- */
- void add_void_callback(const std::string &name, const VoidCallbackFunction &callback) {
- add_void_callback(name, -1, callback);
- }
- /*!
- @brief Adds a callback with given number or arguments
- */
- void add_callback(const std::string &name, int num_args, const CallbackFunction &callback) {
- function_storage.add_callback(name, num_args, callback);
- }
- /*!
- @brief Adds a void callback with given number or arguments
- */
- void add_void_callback(const std::string &name, int num_args, const VoidCallbackFunction &callback) {
- function_storage.add_callback(name, num_args, [callback](Arguments& args) { callback(args); return json(); });
- }
- /** Includes a template with a given name into the environment.
- * Then, a template can be rendered in another template using the
- * include "<name>" syntax.
- */
- void include_template(const std::string &name, const Template &tmpl) {
- template_storage[name] = tmpl;
- }
- };
- /*!
- @brief render with default settings to a string
- */
- inline std::string render(nonstd::string_view input, const json &data) {
- return Environment().render(input, data);
- }
- /*!
- @brief render with default settings to the given output stream
- */
- inline void render_to(std::ostream &os, nonstd::string_view input, const json &data) {
- Environment env;
- env.render_to(os, env.parse(input), data);
- }
- } // namespace inja
- #endif // INCLUDE_INJA_ENVIRONMENT_HPP_
- // #include "exceptions.hpp"
- // #include "parser.hpp"
- // #include "renderer.hpp"
- // #include "string_view.hpp"
- // #include "template.hpp"
- #endif // INCLUDE_INJA_INJA_HPP_
|