From 7616ea1b731c2050a10bceef5b07d37c9702e0ba Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 8 Jan 2018 17:25:39 +0100 Subject: [PATCH 01/21] Add some logging to SetTextureStageState --- gfx/common/d3d_common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gfx/common/d3d_common.c b/gfx/common/d3d_common.c index 51e90f377a..26de8a3f73 100644 --- a/gfx/common/d3d_common.c +++ b/gfx/common/d3d_common.c @@ -767,11 +767,14 @@ static void d3d_set_texture_stage_state(LPDIRECT3DDEVICE dev, unsigned sampler, unsigned value, unsigned type) { #if defined(HAVE_D3D9) && !defined(__cplusplus) - IDirect3DDevice9_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value); + if (IDirect3DDevice9_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) + RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); #elif defined(HAVE_D3D8) && !defined(__cplusplus) - IDirect3DDevice8_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value); + if (IDirect3DDevice8_SetTextureStageState(dev, sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) + RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); #else - dev->SetTextureStageState(sampler, (D3DTEXTURESTAGESTATETYPE)type, value); + if (dev->SetTextureStageState(sampler, (D3DTEXTURESTAGESTATETYPE)type, value) != D3D_OK) + RARCH_ERR("SetTextureStageState call failed, sampler: %d, value: %d, type: %d\n", sampler, value, type); #endif } #endif From 4415086176cc6ddadd727e95e634b0bf304d4c25 Mon Sep 17 00:00:00 2001 From: orbea Date: Mon, 8 Jan 2018 18:12:53 +0000 Subject: [PATCH 02/21] Revert "Fix building with built-in flac" --- qb/config.libs.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 64c07d20ad..52f32a37dd 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -273,13 +273,11 @@ fi if [ "$HAVE_FLAC" = 'no' ]; then HAVE_BUILTINFLAC=no -elif [ "$HAVE_BUILTINFLAC" = 'yes' ]; then - HAVE_FLAC=yes -else - check_pkgconf FLAC flac - check_val '' FLAC '-lFLAC' fi +check_pkgconf FLAC flac +check_val '' FLAC '-lFLAC' + check_pkgconf LIBUSB libusb-1.0 1.0.13 check_val '' LIBUSB -lusb-1.0 libusb-1.0 From 8f9d8073069f941125e5a86fdd92c615f32f9c9a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 8 Jan 2018 23:06:09 +0100 Subject: [PATCH 03/21] Remove some more Xbox ifdefs --- gfx/common/win32_common.c | 4 ++++ gfx/drivers/d3d.c | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index bffbec8db6..40638b6448 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1179,7 +1179,11 @@ bool win32_has_focus(void) HWND win32_get_window(void) { +#ifdef _XBOX + return NULL; +#else return main_window.hwnd; +#endif } void win32_window_reset(void) diff --git a/gfx/drivers/d3d.c b/gfx/drivers/d3d.c index 0cf7fbd79a..39690acf44 100644 --- a/gfx/drivers/d3d.c +++ b/gfx/drivers/d3d.c @@ -586,9 +586,7 @@ void d3d_make_d3dpp(void *data, d3dpp->BackBufferCount = 2; d3dpp->BackBufferFormat = d3d_get_color_format_backbuffer( info->rgb32, windowed_enable); -#ifndef _XBOX d3dpp->hDeviceWindow = win32_get_window(); -#endif #ifdef _XBOX360 d3dpp->FrontBufferFormat = d3d_get_color_format_front_buffer(); @@ -672,11 +670,7 @@ void d3d_make_d3dpp(void *data, static bool d3d_init_base(void *data, const video_info_t *info) { D3DPRESENT_PARAMETERS d3dpp; -#ifdef _XBOX - HWND focus_window = NULL; -#else HWND focus_window = win32_get_window(); -#endif d3d_video_t *d3d = (d3d_video_t*)data; memset(&d3dpp, 0, sizeof(d3dpp)); @@ -1453,9 +1447,7 @@ static bool d3d_frame(void *data, const void *frame, D3DVIEWPORT screen_vp; unsigned i = 0; d3d_video_t *d3d = (d3d_video_t*)data; -#ifndef _XBOX HWND window = win32_get_window(); -#endif unsigned width = video_info->width; unsigned height = video_info->height; @@ -1467,10 +1459,8 @@ static bool d3d_frame(void *data, const void *frame, /* We cannot recover in fullscreen. */ if (d3d->needs_restore) { -#ifndef _XBOX if (IsIconic(window)) return true; -#endif if (!d3d_restore(d3d)) { From 7fdb621481239c933d9de5119e2992b8e66a8bca Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 8 Jan 2018 23:23:37 +0100 Subject: [PATCH 04/21] Update --- gfx/common/win32_common.c | 2 +- gfx/drivers/d3d.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index 40638b6448..f36543aa8d 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -1196,9 +1196,9 @@ void win32_destroy_window(void) { #ifndef _XBOX UnregisterClass("RetroArch", GetModuleHandle(NULL)); -#endif #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x500 /* 2K */ UnregisterDeviceNotification(notification_handler); +#endif #endif main_window.hwnd = NULL; } diff --git a/gfx/drivers/d3d.c b/gfx/drivers/d3d.c index 39690acf44..e20de55124 100644 --- a/gfx/drivers/d3d.c +++ b/gfx/drivers/d3d.c @@ -1284,9 +1284,7 @@ static void d3d_free(void *data) d3d->dev = NULL; g_pD3D = NULL; -#ifndef _XBOX win32_monitor_from_window(); -#endif if (d3d) free(d3d); From aa641325cb7b7a8fbd09945a16d868e13ef18d3f Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 8 Jan 2018 23:30:07 +0100 Subject: [PATCH 05/21] Cleanups --- gfx/common/win32_common.c | 60 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/gfx/common/win32_common.c b/gfx/common/win32_common.c index f36543aa8d..d3985c1f0b 100644 --- a/gfx/common/win32_common.c +++ b/gfx/common/win32_common.c @@ -816,7 +816,8 @@ bool win32_window_create(void *data, unsigned style, SetWindowLongPtr(main_window.hwnd, GWL_EXSTYLE, GetWindowLongPtr(main_window.hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); - SetLayeredWindowAttributes(main_window.hwnd, 0, (255 * settings->uints.video_window_opacity) / 100, LWA_ALPHA); + SetLayeredWindowAttributes(main_window.hwnd, 0, (255 * + settings->uints.video_window_opacity) / 100, LWA_ALPHA); #endif #endif return true; @@ -944,7 +945,7 @@ bool win32_suppress_screensaver(void *data, bool enable) if (frontend->get_os) frontend->get_os(tmp, sizeof(tmp), &major, &minor); - if (major*100+minor >= 601) + if (major * 100 + minor >= 601) { #if _WIN32_WINNT >= 0x0601 /* Windows 7, 8, 10 codepath */ @@ -973,7 +974,7 @@ bool win32_suppress_screensaver(void *data, bool enable) } #endif } - else if (major*100+minor >= 410) + else if (major * 100 + minor >= 410) { #if _WIN32_WINDOWS >= 0x0410 || _WIN32_WINNT >= 0x0410 /* 98 / 2K / XP / Vista codepath */ @@ -981,12 +982,12 @@ bool win32_suppress_screensaver(void *data, bool enable) return true; #endif } - else - { + else + { /* 95 / NT codepath */ - /* No way to block the screensaver. */ + /* No way to block the screensaver. */ return true; - } + } } #endif @@ -998,18 +999,17 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, RECT *rect, RECT *mon_rect, DWORD *style) { #ifndef _XBOX - settings_t *settings = config_get_ptr(); - - /* Windows only reports the refresh rates for modelines as - * an integer, so video_refresh_rate needs to be rounded. Also, account - * for black frame insertion using video_refresh_rate set to half - * of the display refresh rate, as well as higher vsync swap intervals. */ - float refresh_mod = settings->bools.video_black_frame_insertion ? 2.0f : 1.0f; - unsigned refresh = roundf(settings->floats.video_refresh_rate - * refresh_mod * settings->uints.video_swap_interval); - if (fullscreen) { + settings_t *settings = config_get_ptr(); + /* Windows only reports the refresh rates for modelines as + * an integer, so video_refresh_rate needs to be rounded. Also, account + * for black frame insertion using video_refresh_rate set to half + * of the display refresh rate, as well as higher vsync swap intervals. */ + float refresh_mod = settings->bools.video_black_frame_insertion ? 2.0f : 1.0f; + unsigned refresh = roundf(settings->floats.video_refresh_rate + * refresh_mod * settings->uints.video_swap_interval); + if (windowed_full) { *style = WS_EX_TOPMOST | WS_POPUP; @@ -1022,7 +1022,7 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, if (!win32_monitor_set_fullscreen(*width, *height, refresh, current_mon->szDevice)) - {} + {} /* Display settings might have changed, get new coordinates. */ GetMonitorInfo(*hm_to_use, (LPMONITORINFO)current_mon); @@ -1031,10 +1031,12 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, } else { - *style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - rect->right = *width; - rect->bottom = *height; + *style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + rect->right = *width; + rect->bottom = *height; + AdjustWindowRect(rect, *style, FALSE); + g_resize_width = *width = rect->right - rect->left; g_resize_height = *height = rect->bottom - rect->top; } @@ -1207,7 +1209,7 @@ void win32_get_video_output_prev( unsigned *width, unsigned *height) { DEVMODE dm; - int iModeNum; + unsigned i; bool found = false; unsigned prev_width = 0; unsigned prev_height = 0; @@ -1220,9 +1222,9 @@ void win32_get_video_output_prev( win32_get_video_output_size(&curr_width, &curr_height); - for (iModeNum = 0; - EnumDisplaySettings(NULL, iModeNum, &dm) != 0; - iModeNum++) + for (i = 0; + EnumDisplaySettings(NULL, i, &dm) != 0; + i++) { if ( dm.dmPelsWidth == curr_width && dm.dmPelsHeight == curr_height) @@ -1250,7 +1252,7 @@ void win32_get_video_output_next( unsigned *width, unsigned *height) { DEVMODE dm; - int iModeNum; + int i; bool found = false; unsigned curr_width = 0; unsigned curr_height = 0; @@ -1260,9 +1262,9 @@ void win32_get_video_output_next( win32_get_video_output_size(&curr_width, &curr_height); - for (iModeNum = 0; - EnumDisplaySettings(NULL, iModeNum, &dm) != 0; - iModeNum++) + for (i = 0; + EnumDisplaySettings(NULL, i, &dm) != 0; + i++) { if (found) { From 6eb089cb823112e00b609ad1546cd6d22e33b38f Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 8 Jan 2018 20:03:29 -0500 Subject: [PATCH 06/21] Revert "Fast forward state disable hard sync" This reverts commit ea5045d50a54840ccd4ec91550d59a6c6789bf2e. --- retroarch.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/retroarch.c b/retroarch.c index af98194dc9..bb56980c44 100644 --- a/retroarch.c +++ b/retroarch.c @@ -230,7 +230,6 @@ static bool runloop_remaps_game_active = false; static bool runloop_game_options_active = false; static bool runloop_missing_bios = false; static bool runloop_autosave = false; -static bool runloop_had_hard_sync = false; static rarch_system_info_t runloop_system; static struct retro_frame_time_callback runloop_frame_time; @@ -2802,18 +2801,6 @@ static enum runloop_state runloop_check_state( runloop_msg_queue_push( msg_hash_to_str(MSG_FAST_FORWARD), 1, 1, false); - /* Disable gpu hard sync in fast forward state for speed. */ - if (runloop_fastmotion && settings->bools.video_hard_sync) - { - settings->bools.video_hard_sync = false; - runloop_had_hard_sync = true; - } - else if (!runloop_fastmotion && runloop_had_hard_sync) - { - settings->bools.video_hard_sync = true; - runloop_had_hard_sync = false; - } - old_button_state = new_button_state; old_hold_button_state = new_hold_button_state; } From 8007c3efd7272516446816f9df45e5ad142f9552 Mon Sep 17 00:00:00 2001 From: radius Date: Mon, 8 Jan 2018 20:06:24 -0500 Subject: [PATCH 07/21] Disable hard sync on FF without mangling settings --- gfx/drivers/gl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index f37d39dc8a..3dc0d88338 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -1202,7 +1202,8 @@ static bool gl_frame(void *data, const void *frame, video_info->cb_swap_buffers(video_info->context_data, video_info); - if (video_info->hard_sync && gl->have_sync) + /* check if we are fast forwarding, if we are ignore hard sync */ + if (gl->have_sync && video_info->hard_sync && !video_info->input_driver_nonblock_state) { glClear(GL_COLOR_BUFFER_BIT); From 42905d9bbbb7b79fa2a35de84adf103c0b6cbe2f Mon Sep 17 00:00:00 2001 From: aliaspider Date: Tue, 9 Jan 2018 03:05:29 +0100 Subject: [PATCH 08/21] (WIIU) add a better converter for slang shaders. --- wiiu/slang/Makefile | 82 +- wiiu/slang/convert.sh | 42 - wiiu/slang/grammar.txt | 55 + wiiu/slang/main.c | 2 + wiiu/slang/peglib.h | 2775 ++++++++++++++++++++++++++++++++++++ wiiu/slang/slang-parse.cpp | 271 ++++ 6 files changed, 3180 insertions(+), 47 deletions(-) delete mode 100644 wiiu/slang/convert.sh create mode 100644 wiiu/slang/grammar.txt create mode 100644 wiiu/slang/peglib.h create mode 100644 wiiu/slang/slang-parse.cpp diff --git a/wiiu/slang/Makefile b/wiiu/slang/Makefile index ed23d9cb37..64ad2fe4ff 100644 --- a/wiiu/slang/Makefile +++ b/wiiu/slang/Makefile @@ -1,8 +1,80 @@ -all: slang-convert +GLSLC = glslc -slang-convert: main.c - $(CC) $< -o $@ -g -O0 -Wall -Werror +GSH_COMPILER = ./compiler.exe +GSH_COMPILER_V = -v +GSH_COMPILER_P = -p +GSH_COMPILER_O = -o +GSH_COMPILER_ALIGN = -align -clean: - rm slang-convert main.o +SLANG_DIR = slang-shaders + +BLACKLIST := +BLACKLIST += $(SLANG_DIR)/dithering/shaders/bayer-matrix-dithering.slang +BLACKLIST += $(SLANG_DIR)/scalefx/shaders/old/scalefx-pass3.slang +BLACKLIST += $(SLANG_DIR)/scalefx/shaders/old/scalefx-pass7.slang +BLACKLIST += $(SLANG_DIR)/scalefx/shaders/scalefx-pass1.slang +BLACKLIST += $(SLANG_DIR)/scalefx/shaders/scalefx-pass2.slang +BLACKLIST += $(SLANG_DIR)/scalefx/shaders/scalefx-pass4.slang +BLACKLIST += $(SLANG_DIR)/scalefx/shaders/scalefx-pass4-hybrid.slang +BLACKLIST += $(SLANG_DIR)/test/decode-format.slang +BLACKLIST += $(SLANG_DIR)/test/format.slang +BLACKLIST += $(wildcard $(SLANG_DIR)/crt/shaders/crt-royale/src/*.slang) +BLACKLIST += $(SLANG_DIR)/slang-shaders/blurs/blur12x12shared.slang +BLACKLIST += $(SLANG_DIR)/blurs/blur5x5-gamma-encode-every-fbo.slang +BLACKLIST += $(SLANG_DIR)/blurs/blur12x12shared.slang +BLACKLIST += $(SLANG_DIR)/ntsc/shaders/ntsc-xot.slang +BLACKLIST += $(SLANG_DIR)/nedi/shaders/nedi-pass0.slang +BLACKLIST += $(SLANG_DIR)/nedi/shaders/nedi-pass1.slang +BLACKLIST += $(SLANG_DIR)/nedi/shaders/nedi-pass2.slang +BLACKLIST += $(wildcard $(SLANG_DIR)/anti-aliasing/shaders/smaa/*.slang) + +EXE_EXT := + +ifneq ($(findstring Linux,$(shell uname -a)),) +else ifneq ($(findstring Darwin,$(shell uname -a)),) +else +EXE_EXT := .exe +endif + +SLANG_PARSER = ./slang-parse$(EXE_EXT) + +rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) +SLANG_SHADERS := $(call rwildcard,$(SLANG_DIR)/,*.slang) +SLANG_SHADERS := $(filter-out $(BLACKLIST),$(SLANG_SHADERS)) +SLANG_SHADERS := $(SLANG_SHADERS:.slang=.gsh) + + + + +all: $(SLANG_SHADERS) +parser: $(SLANG_PARSER) + +$(SLANG_PARSER): slang-parse.cpp + $(CXX) $< -o $@ -g -O0 -Wall -I. + +shaders: $(SLANG_SHADERS) + +%.ppslang : %.slang + $(GLSLC) -E $< -o $@ + +%.vert %.frag: %.ppslang $(SLANG_PARSER) grammar.txt + $(SLANG_PARSER) --slang $< --vsh $*.vert --psh $*.frag + +%.gsh: %.vert %.frag + $(GSH_COMPILER) $(GSH_COMPILER_ALIGN) $(GSH_COMPILER_V) $*.vert $(GSH_COMPILER_P) $*.frag $(GSH_COMPILER_O) $@ + +.PRECIOUS: %.vert %.frag %.ppslang + +clean-shaders: + rm -rf $(SLANG_SHADERS) + +clean-temp: + rm -rf $(SLANG_SHADERS:.gsh=.ppslang) + rm -rf $(SLANG_SHADERS:.gsh=.vert) + rm -rf $(SLANG_SHADERS:.gsh=.frag) + +clean-parser: + rm -rf $(SLANG_PARSER) slang-parse.o + +clean: clean-parser clean-shaders clean-temp diff --git a/wiiu/slang/convert.sh b/wiiu/slang/convert.sh deleted file mode 100644 index 5837bff140..0000000000 --- a/wiiu/slang/convert.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh - -#### options #### - -slang_dir=slang-shaders -gshcompiler=$(pwd)/compiler.exe -vshflag=--vsh -pshflag=--psh -outputflag=--out -alignflag=--align - -################# - - -currentdir=$(pwd) - -die () -{ - echo error while converting $name - #mv -f tmp.vsh $currentdir - #mv -f tmp.psh $currentdir - #cp $1 $currentdir 2> /dev/null - #exit 1 -} - - -make - -cd $slang_dir -slang_dir=$(pwd) -slang_files=`find $slang_dir -name "*.slang"` - -for name in $slang_files ; do -echo $name -echo cd $(dirname $name) -cd $(dirname $name) -echo $currentdir/slang-convert --slang $name --vsh tmp.vsh --psh tmp.psh -$currentdir/slang-convert --slang $(basename $name) --vsh tmp.vsh --psh tmp.psh -echo $gshcompiler $alignflag $vshflag tmp.vsh $pshflag tmp.psh $outputflag `echo "$name" | sed "s/\.slang//"`.gsh -$gshcompiler $alignflag $vshflag tmp.vsh $pshflag tmp.psh $outputflag `echo "$name" | sed "s/\.slang//"`.gsh || die $name -rm -rf tmp.vsh tmp.psh -done diff --git a/wiiu/slang/grammar.txt b/wiiu/slang/grammar.txt new file mode 100644 index 0000000000..aab7c03a55 --- /dev/null +++ b/wiiu/slang/grammar.txt @@ -0,0 +1,55 @@ + +Program <- Version? (LineMarker / GoogleIncludeExtensionEnable / UniformBlock / Declaration / ConstArray / Stage / Indent / IgnoredKeyword / Line)* + +~LineMarker <- '#line' (!EndOfLine .)* EndOfLine +~GoogleIncludeExtensionEnable <- '#extension GL_GOOGLE_include_directive' (!EndOfLine .)* EndOfLine +Line <- <(!EndOfLine !EndOfFile .)*> (EndOfLine / EndOfFile) +~IgnoredKeyword <- !'const int' ('inline' / 'static ' / 'const ') +Version <- '#'_'version' Space+ <(!EndOfLine .)*> EndOfLine +Stage <- '#'_'pragma' Space+ 'stage' Space? <(!EndOfLine .)*> EndOfLine +#.*Include <- '#'_'include' _ '"' <(!'"' .)*> '"' _ EndOfLine + + +Declaration <- Layout? _ Qualifier Space _ Type Space _ Name _ ArraySize? _ EndOfStatement +ConstArray <- Type Space _ Name _ ArraySize? _ '=' _ Array _ EndOfStatement +UniformBlock <- Layout? _ 'uniform' _ StructName? _ '{' Member* '}' _ Name? _ EndOfStatement +Member <- (_ Type Space _ Name _ ArraySize? _ EndOfStatement? _ Comment?) +~Comment <- ('//' Line) + +Value <- (Float / Int / (!EndOfStatement .)*) +Array <- ('{' ( _ ArrayItem+ _ ',')* (_ ArrayItem+ _ )? '}') +ArrayItem <- <(!'}'!',' (groupedItem/.))+> +groupedItem <- '(' (!')' (groupedItem/.))+ ')' + +Qualifier <- 'in' / 'out' / 'uniform' +Type <- <'uint' / 'int' / 'ivec2' / 'ivec3' / 'ivec4' / + 'float' / 'vec2' / 'vec3' / 'vec4' / + 'bool' / 'bvec2' / 'bvec3' / 'bvec4' / + 'mat2x2' / 'mat2' / + 'mat3x2' / 'mat3x3' / 'mat3' / + 'mat4x2' / 'mat4x3' / 'mat4x4' / 'mat4' / + 'void' / 'sampler2D' / Name> +StructName <- +ArraySize <- '[' _ _ ']' +Name <- ([a-z]/[A-Z]/'_'/[0-9])+ +Layout <- 'layout' _ '(' ( _ (Location / Binding / Set / PushConstant / Std) _ ','?)+ ')' +Location <- 'location' _ '=' _ +Binding <- 'binding' _ '=' _ +Set <- 'set' _ '=' _ +PushConstant <- 'push_constant' +Std <- 'std' + + +#.*Block <- '{' (Directive/ Statement / Block _)* '}' + + +Float <- $< '-'? [0-9]+ '.' ([0-9]+ ('e-'[0-9]+)? )? 'f'? > +Int <- < '-'? [0-9]+ > +~EndOfStatement <- ';' +~EndOfLine <- '\r\n' / '\n' / '\r' +EndOfFile <- !. +End <- EndOfLine / EndOfFile +~_ <- [ \t\r\n]* +Indent <- [ \t] +~Space <- [ \t] + diff --git a/wiiu/slang/main.c b/wiiu/slang/main.c index d20e5f47ef..025495feaa 100644 --- a/wiiu/slang/main.c +++ b/wiiu/slang/main.c @@ -134,6 +134,8 @@ int main(int argc, const char** argv) fclose(vs_out_fp); fclose(ps_out_fp); + free(slang_buffer); + return 0; } diff --git a/wiiu/slang/peglib.h b/wiiu/slang/peglib.h new file mode 100644 index 0000000000..20b5080e1b --- /dev/null +++ b/wiiu/slang/peglib.h @@ -0,0 +1,2775 @@ +// +// peglib.h +// +// Copyright (c) 2015-17 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPPEGLIB_PEGLIB_H +#define CPPPEGLIB_PEGLIB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// guard for older versions of VC++ +#ifdef _MSC_VER +// VS2013 has no constexpr +#if (_MSC_VER == 1800) +#define PEGLIB_NO_CONSTEXPR_SUPPORT +#elif (_MSC_VER >= 1800) +// good to go +#else (_MSC_VER < 1800) +#error "Requires C+11 support" +#endif +#endif + +// define if the compiler doesn't support unicode characters reliably in the +// source code +//#define PEGLIB_NO_UNICODE_CHARS + +namespace peg { + +#if __clang__ == 1 && __clang_major__ == 5 && __clang_minor__ == 0 && __clang_patchlevel__ == 0 +static void* enabler = nullptr; // workaround for Clang 5.0.0 +#else +extern void* enabler; +#endif + +/*----------------------------------------------------------------------------- + * any + *---------------------------------------------------------------------------*/ + +class any +{ +public: + any() : content_(nullptr) {} + + any(const any& rhs) : content_(rhs.clone()) {} + + any(any&& rhs) : content_(rhs.content_) { + rhs.content_ = nullptr; + } + + template + any(const T& value) : content_(new holder(value)) {} + + any& operator=(const any& rhs) { + if (this != &rhs) { + if (content_) { + delete content_; + } + content_ = rhs.clone(); + } + return *this; + } + + any& operator=(any&& rhs) { + if (this != &rhs) { + if (content_) { + delete content_; + } + content_ = rhs.content_; + rhs.content_ = nullptr; + } + return *this; + } + + ~any() { + delete content_; + } + + bool is_undefined() const { + return content_ == nullptr; + } + + template < + typename T, + typename std::enable_if::value>::type*& = enabler + > + T& get() { + if (!content_) { + throw std::bad_cast(); + } + auto p = dynamic_cast*>(content_); + assert(p); + if (!p) { + throw std::bad_cast(); + } + return p->value_; + } + + template < + typename T, + typename std::enable_if::value>::type*& = enabler + > + T& get() { + return *this; + } + + template < + typename T, + typename std::enable_if::value>::type*& = enabler + > + const T& get() const { + assert(content_); + auto p = dynamic_cast*>(content_); + assert(p); + if (!p) { + throw std::bad_cast(); + } + return p->value_; + } + + template < + typename T, + typename std::enable_if::value>::type*& = enabler + > + const any& get() const { + return *this; + } + +private: + struct placeholder { + virtual ~placeholder() {} + virtual placeholder* clone() const = 0; + }; + + template + struct holder : placeholder { + holder(const T& value) : value_(value) {} + placeholder* clone() const override { + return new holder(value_); + } + T value_; + }; + + placeholder* clone() const { + return content_ ? content_->clone() : nullptr; + } + + placeholder* content_; +}; + +/*----------------------------------------------------------------------------- + * scope_exit + *---------------------------------------------------------------------------*/ + +// This is based on "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". + +template +struct scope_exit +{ + explicit scope_exit(EF&& f) + : exit_function(std::move(f)) + , execute_on_destruction{true} {} + + scope_exit(scope_exit&& rhs) + : exit_function(std::move(rhs.exit_function)) + , execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } + + ~scope_exit() { + if (execute_on_destruction) { + this->exit_function(); + } + } + + void release() { + this->execute_on_destruction = false; + } + +private: + scope_exit(const scope_exit&) = delete; + void operator=(const scope_exit&) = delete; + scope_exit& operator=(scope_exit&&) = delete; + + EF exit_function; + bool execute_on_destruction; +}; + +template +auto make_scope_exit(EF&& exit_function) -> scope_exit { + return scope_exit::type>(std::forward(exit_function)); +} + +/*----------------------------------------------------------------------------- + * PEG + *---------------------------------------------------------------------------*/ + +/* +* Line information utility function +*/ +inline std::pair line_info(const char* start, const char* cur) { + auto p = start; + auto col_ptr = p; + auto no = 1; + + while (p < cur) { + if (*p == '\n') { + no++; + col_ptr = p + 1; + } + p++; + } + + auto col = p - col_ptr + 1; + + return std::make_pair(no, col); +} + +/* +* Semantic values +*/ +struct SemanticValues : protected std::vector +{ + // Input text + const char* path; + const char* ss; + + // Matched string + const char* c_str() const { return s_; } + size_t length() const { return n_; } + + std::string str() const { + return std::string(s_, n_); + } + + // Line number and column at which the matched string is + std::pair line_info() const { + return peg::line_info(ss, s_); + } + + // Choice number (0 based index) + size_t choice() const { return choice_; } + + // Tokens + std::vector> tokens; + + std::string token(size_t id = 0) const { + if (!tokens.empty()) { + assert(id < tokens.size()); + const auto& tok = tokens[id]; + return std::string(tok.first, tok.second); + } + return std::string(s_, n_); + } + + // Transform the semantic value vector to another vector + template + auto transform(size_t beg = 0, size_t end = static_cast(-1)) const -> vector { + return this->transform(beg, end, [](const any& v) { return v.get(); }); + } + + SemanticValues() : s_(nullptr), n_(0), choice_(0) {} + + using std::vector::iterator; + using std::vector::const_iterator; + using std::vector::size; + using std::vector::empty; + using std::vector::assign; + using std::vector::begin; + using std::vector::end; + using std::vector::rbegin; + using std::vector::rend; + using std::vector::operator[]; + using std::vector::at; + using std::vector::resize; + using std::vector::front; + using std::vector::back; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::insert; + using std::vector::erase; + using std::vector::clear; + using std::vector::swap; + using std::vector::emplace; + using std::vector::emplace_back; + +private: + friend class Context; + friend class PrioritizedChoice; + friend class Holder; + + const char* s_; + size_t n_; + size_t choice_; + + template + auto transform(F f) const -> vector::type> { + vector::type> r; + for (const auto& v: *this) { + r.emplace_back(f(v)); + } + return r; + } + + template + auto transform(size_t beg, size_t end, F f) const -> vector::type> { + vector::type> r; + end = (std::min)(end, size()); + for (size_t i = beg; i < end; i++) { + r.emplace_back(f((*this)[i])); + } + return r; + } +}; + +/* + * Semantic action + */ +template < + typename R, typename F, + typename std::enable_if::value>::type*& = enabler, + typename... Args> +any call(F fn, Args&&... args) { + fn(std::forward(args)...); + return any(); +} + +template < + typename R, typename F, + typename std::enable_if::type, any>::value>::type*& = enabler, + typename... Args> +any call(F fn, Args&&... args) { + return fn(std::forward(args)...); +} + +template < + typename R, typename F, + typename std::enable_if< + !std::is_void::value && + !std::is_same::type, any>::value>::type*& = enabler, + typename... Args> +any call(F fn, Args&&... args) { + return any(fn(std::forward(args)...)); +} + +class Action +{ +public: + Action() = default; + + Action(const Action& rhs) : fn_(rhs.fn_) {} + + template ::value && !std::is_same::value>::type*& = enabler> + Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} + + template ::value>::type*& = enabler> + Action(F fn) : fn_(make_adaptor(fn, fn)) {} + + template ::value>::type*& = enabler> + Action(F /*fn*/) {} + + template ::value && !std::is_same::value>::type*& = enabler> + void operator=(F fn) { + fn_ = make_adaptor(fn, &F::operator()); + } + + template ::value>::type*& = enabler> + void operator=(F fn) { + fn_ = make_adaptor(fn, fn); + } + + template ::value>::type*& = enabler> + void operator=(F /*fn*/) {} + + Action& operator=(const Action& rhs) = default; + + operator bool() const { + return bool(fn_); + } + + any operator()(const SemanticValues& sv, any& dt) const { + return fn_(sv, dt); + } + +private: + template + struct TypeAdaptor { + TypeAdaptor(std::function fn) + : fn_(fn) {} + any operator()(const SemanticValues& sv, any& /*dt*/) { + return call(fn_, sv); + } + std::function fn_; + }; + + template + struct TypeAdaptor_c { + TypeAdaptor_c(std::function fn) + : fn_(fn) {} + any operator()(const SemanticValues& sv, any& dt) { + return call(fn_, sv, dt); + } + std::function fn_; + }; + + typedef std::function Fty; + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv) const) { + return TypeAdaptor(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv)) { + return TypeAdaptor(fn); + } + + template + Fty make_adaptor(F fn, R (* /*mf*/)(const SemanticValues& sv)) { + return TypeAdaptor(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt) const) { + return TypeAdaptor_c(fn); + } + + template + Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt)) { + return TypeAdaptor_c(fn); + } + + template + Fty make_adaptor(F fn, R(* /*mf*/)(const SemanticValues& sv, any& dt)) { + return TypeAdaptor_c(fn); + } + + Fty fn_; +}; + +/* + * Semantic predicate + */ +// Note: 'parse_error' exception class should be be used in sematic action handlers to reject the rule. +struct parse_error { + parse_error() = default; + parse_error(const char* s) : s_(s) {} + const char* what() const { return s_.empty() ? nullptr : s_.c_str(); } +private: + std::string s_; +}; + +/* + * Match action + */ +typedef std::function MatchAction; + +/* + * Result + */ +inline bool success(size_t len) { + return len != static_cast(-1); +} + +inline bool fail(size_t len) { + return len == static_cast(-1); +} + +/* + * Context + */ +class Ope; +class Context; +class Definition; + +typedef std::function Tracer; + +class Context +{ +public: + const char* path; + const char* s; + const size_t l; + + const char* error_pos; + const char* message_pos; + std::string message; // TODO: should be `int`. + + std::vector> value_stack; + size_t value_stack_size; + + size_t nest_level; + + bool in_token; + + std::shared_ptr whitespaceOpe; + bool in_whitespace; + + const size_t def_count; + const bool enablePackratParsing; + std::vector cache_registered; + std::vector cache_success; + + std::map, std::tuple> cache_values; + + std::function tracer; + + Context( + const char* a_path, + const char* a_s, + size_t a_l, + size_t a_def_count, + std::shared_ptr a_whitespaceOpe, + bool a_enablePackratParsing, + Tracer a_tracer) + : path(a_path) + , s(a_s) + , l(a_l) + , error_pos(nullptr) + , message_pos(nullptr) + , value_stack_size(0) + , nest_level(0) + , in_token(false) + , whitespaceOpe(a_whitespaceOpe) + , in_whitespace(false) + , def_count(a_def_count) + , enablePackratParsing(a_enablePackratParsing) + , cache_registered(enablePackratParsing ? def_count * (l + 1) : 0) + , cache_success(enablePackratParsing ? def_count * (l + 1) : 0) + , tracer(a_tracer) + { + } + + template + void packrat(const char* a_s, size_t def_id, size_t& len, any& val, T fn) { + if (!enablePackratParsing) { + fn(val); + return; + } + + auto col = a_s - s; + auto idx = def_count * static_cast(col) + def_id; + + if (cache_registered[idx]) { + if (cache_success[idx]) { + auto key = std::make_pair(col, def_id); + std::tie(len, val) = cache_values[key]; + return; + } else { + len = static_cast(-1); + return; + } + } else { + fn(val); + cache_registered[idx] = true; + cache_success[idx] = success(len); + if (success(len)) { + auto key = std::make_pair(col, def_id); + cache_values[key] = std::make_pair(len, val); + } + return; + } + } + + SemanticValues& push() { + assert(value_stack_size <= value_stack.size()); + if (value_stack_size == value_stack.size()) { + value_stack.emplace_back(std::make_shared()); + } + auto& sv = *value_stack[value_stack_size++]; + if (!sv.empty()) { + sv.clear(); + } + sv.path = path; + sv.ss = s; + sv.s_ = nullptr; + sv.n_ = 0; + sv.tokens.clear(); + return sv; + } + + void pop() { + value_stack_size--; + } + + void set_error_pos(const char* a_s) { + if (error_pos < a_s) error_pos = a_s; + } + + void trace(const char* name, const char* a_s, size_t n, SemanticValues& sv, any& dt) const { + if (tracer) tracer(name, a_s, n, sv, *this, dt); + } +}; + +/* + * Parser operators + */ +class Ope +{ +public: + struct Visitor; + + virtual ~Ope() {} + virtual size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const = 0; + virtual void accept(Visitor& v) = 0; +}; + +class Sequence : public Ope +{ +public: + Sequence(const Sequence& rhs) : opes_(rhs.opes_) {} + +#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 + // NOTE: Compiler Error C2797 on Visual Studio 2013 + // "The C++ compiler in Visual Studio does not implement list + // initialization inside either a member initializer list or a non-static + // data member initializer. Before Visual Studio 2013 Update 3, this was + // silently converted to a function call, which could lead to bad code + // generation. Visual Studio 2013 Update 3 reports this as an error." + template + Sequence(const Args& ...args) { + opes_ = std::vector>{ static_cast>(args)... }; + } +#else + template + Sequence(const Args& ...args) : opes_{ static_cast>(args)... } {} +#endif + + Sequence(const std::vector>& opes) : opes_(opes) {} + Sequence(std::vector>&& opes) : opes_(opes) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("Sequence", s, n, sv, dt); + size_t i = 0; + for (const auto& ope : opes_) { + c.nest_level++; + auto se = make_scope_exit([&]() { c.nest_level--; }); + const auto& rule = *ope; + auto len = rule.parse(s + i, n - i, sv, c, dt); + if (fail(len)) { + return static_cast(-1); + } + i += len; + } + return i; + } + + void accept(Visitor& v) override; + + std::vector> opes_; +}; + +class PrioritizedChoice : public Ope +{ +public: +#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 + // NOTE: Compiler Error C2797 on Visual Studio 2013 + // "The C++ compiler in Visual Studio does not implement list + // initialization inside either a member initializer list or a non-static + // data member initializer. Before Visual Studio 2013 Update 3, this was + // silently converted to a function call, which could lead to bad code + // generation. Visual Studio 2013 Update 3 reports this as an error." + template + PrioritizedChoice(const Args& ...args) { + opes_ = std::vector>{ static_cast>(args)... }; + } +#else + template + PrioritizedChoice(const Args& ...args) : opes_{ static_cast>(args)... } {} +#endif + + PrioritizedChoice(const std::vector>& opes) : opes_(opes) {} + PrioritizedChoice(std::vector>&& opes) : opes_(opes) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("PrioritizedChoice", s, n, sv, dt); + size_t id = 0; + for (const auto& ope : opes_) { + c.nest_level++; + auto& chldsv = c.push(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop(); + }); + const auto& rule = *ope; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + if (!chldsv.empty()) { + sv.insert(sv.end(), chldsv.begin(), chldsv.end()); + } + sv.s_ = chldsv.c_str(); + sv.n_ = chldsv.length(); + sv.choice_ = id; + sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); + return len; + } + id++; + } + return static_cast(-1); + } + + void accept(Visitor& v) override; + + size_t size() const { return opes_.size(); } + + std::vector> opes_; +}; + +class ZeroOrMore : public Ope +{ +public: + ZeroOrMore(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("ZeroOrMore", s, n, sv, dt); + auto save_error_pos = c.error_pos; + size_t i = 0; + while (n - i > 0) { + c.nest_level++; + auto se = make_scope_exit([&]() { c.nest_level--; }); + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + const auto& rule = *ope_; + auto len = rule.parse(s + i, n - i, sv, c, dt); + if (fail(len)) { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + break; + } + i += len; + } + return i; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class OneOrMore : public Ope +{ +public: + OneOrMore(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("OneOrMore", s, n, sv, dt); + size_t len = 0; + { + c.nest_level++; + auto se = make_scope_exit([&]() { c.nest_level--; }); + const auto& rule = *ope_; + len = rule.parse(s, n, sv, c, dt); + if (fail(len)) { + return static_cast(-1); + } + } + auto save_error_pos = c.error_pos; + auto i = len; + while (n - i > 0) { + c.nest_level++; + auto se = make_scope_exit([&]() { c.nest_level--; }); + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + const auto& rule = *ope_; + len = rule.parse(s + i, n - i, sv, c, dt); + if (fail(len)) { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + break; + } + i += len; + } + return i; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class Option : public Ope +{ +public: + Option(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("Option", s, n, sv, dt); + auto save_error_pos = c.error_pos; + c.nest_level++; + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + auto se = make_scope_exit([&]() { c.nest_level--; }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len)) { + return len; + } else { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + return 0; + } + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class AndPredicate : public Ope +{ +public: + AndPredicate(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("AndPredicate", s, n, sv, dt); + c.nest_level++; + auto& chldsv = c.push(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + return 0; + } else { + return static_cast(-1); + } + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class NotPredicate : public Ope +{ +public: + NotPredicate(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("NotPredicate", s, n, sv, dt); + auto save_error_pos = c.error_pos; + c.nest_level++; + auto& chldsv = c.push(); + auto se = make_scope_exit([&]() { + c.nest_level--; + c.pop(); + }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + c.set_error_pos(s); + return static_cast(-1); + } else { + c.error_pos = save_error_pos; + return 0; + } + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class LiteralString : public Ope +{ +public: + LiteralString(const std::string& s) : lit_(s) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::string lit_; +}; + +class CharacterClass : public Ope +{ +public: + CharacterClass(const std::string& chars) : chars_(chars) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("CharacterClass", s, n, sv, dt); + // TODO: UTF8 support + if (n < 1) { + c.set_error_pos(s); + return static_cast(-1); + } + auto ch = s[0]; + auto i = 0u; + while (i < chars_.size()) { + if (i + 2 < chars_.size() && chars_[i + 1] == '-') { + if (chars_[i] <= ch && ch <= chars_[i + 2]) { + return 1; + } + i += 3; + } else { + if (chars_[i] == ch) { + return 1; + } + i += 1; + } + } + c.set_error_pos(s); + return static_cast(-1); + } + + void accept(Visitor& v) override; + + std::string chars_; +}; + +class Character : public Ope +{ +public: + Character(char ch) : ch_(ch) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("Character", s, n, sv, dt); + // TODO: UTF8 support + if (n < 1 || s[0] != ch_) { + c.set_error_pos(s); + return static_cast(-1); + } + return 1; + } + + void accept(Visitor& v) override; + + char ch_; +}; + +class AnyCharacter : public Ope +{ +public: + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + c.trace("AnyCharacter", s, n, sv, dt); + // TODO: UTF8 support + if (n < 1) { + c.set_error_pos(s); + return static_cast(-1); + } + return 1; + } + + void accept(Visitor& v) override; +}; + +class Capture : public Ope +{ +public: + Capture(const std::shared_ptr& ope, MatchAction ma, size_t id, const std::string& name) + : ope_(ope), match_action_(ma), id_(id), name_(name) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len) && match_action_) { + match_action_(s, len, id_, name_); + } + return len; + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; + +private: + MatchAction match_action_; + size_t id_; + std::string name_; +}; + +class TokenBoundary : public Ope +{ +public: + TokenBoundary(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +class Ignore : public Ope +{ +public: + Ignore(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& /*sv*/, Context& c, any& dt) const override { + const auto& rule = *ope_; + auto& chldsv = c.push(); + auto se = make_scope_exit([&]() { + c.pop(); + }); + return rule.parse(s, n, chldsv, c, dt); + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +typedef std::function Parser; + +class WeakHolder : public Ope +{ +public: + WeakHolder(const std::shared_ptr& ope) : weak_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + auto ope = weak_.lock(); + assert(ope); + const auto& rule = *ope; + return rule.parse(s, n, sv, c, dt); + } + + void accept(Visitor& v) override; + + std::weak_ptr weak_; +}; + +class Holder : public Ope +{ +public: + Holder(Definition* outer) + : outer_(outer) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + any reduce(const SemanticValues& sv, any& dt) const; + + std::shared_ptr ope_; + Definition* outer_; + + friend class Definition; +}; + +class DefinitionReference : public Ope +{ +public: + DefinitionReference( + const std::unordered_map& grammar, const std::string& name, const char* s) + : grammar_(grammar) + , name_(name) + , s_(s) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + + void accept(Visitor& v) override; + + std::shared_ptr get_rule() const; + + const std::unordered_map& grammar_; + const std::string name_; + const char* s_; + +private: + mutable std::once_flag init_; + mutable std::shared_ptr rule_; +}; + +class Whitespace : public Ope +{ +public: + Whitespace(const std::shared_ptr& ope) : ope_(ope) {} + + size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + if (c.in_whitespace) { + return 0; + } + c.in_whitespace = true; + auto se = make_scope_exit([&]() { c.in_whitespace = false; }); + const auto& rule = *ope_; + return rule.parse(s, n, sv, c, dt); + } + + void accept(Visitor& v) override; + + std::shared_ptr ope_; +}; + +/* + * Visitor + */ +struct Ope::Visitor +{ + virtual ~Visitor() {} + virtual void visit(Sequence& /*ope*/) {} + virtual void visit(PrioritizedChoice& /*ope*/) {} + virtual void visit(ZeroOrMore& /*ope*/) {} + virtual void visit(OneOrMore& /*ope*/) {} + virtual void visit(Option& /*ope*/) {} + virtual void visit(AndPredicate& /*ope*/) {} + virtual void visit(NotPredicate& /*ope*/) {} + virtual void visit(LiteralString& /*ope*/) {} + virtual void visit(CharacterClass& /*ope*/) {} + virtual void visit(Character& /*ope*/) {} + virtual void visit(AnyCharacter& /*ope*/) {} + virtual void visit(Capture& /*ope*/) {} + virtual void visit(TokenBoundary& /*ope*/) {} + virtual void visit(Ignore& /*ope*/) {} + virtual void visit(WeakHolder& /*ope*/) {} + virtual void visit(Holder& /*ope*/) {} + virtual void visit(DefinitionReference& /*ope*/) {} + virtual void visit(Whitespace& /*ope*/) {} +}; + +struct AssignIDToDefinition : public Ope::Visitor +{ + using Ope::Visitor::visit; + + void visit(Sequence& ope) override { + for (auto op: ope.opes_) { + op->accept(*this); + } + } + void visit(PrioritizedChoice& ope) override { + for (auto op: ope.opes_) { + op->accept(*this); + } + } + void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); } + void visit(OneOrMore& ope) override { ope.ope_->accept(*this); } + void visit(Option& ope) override { ope.ope_->accept(*this); } + void visit(AndPredicate& ope) override { ope.ope_->accept(*this); } + void visit(NotPredicate& ope) override { ope.ope_->accept(*this); } + void visit(Capture& ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary& ope) override { ope.ope_->accept(*this); } + void visit(Ignore& ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder& ope) override; + void visit(DefinitionReference& ope) override { ope.get_rule()->accept(*this); } + + std::unordered_map ids; +}; + +struct IsToken : public Ope::Visitor +{ + IsToken() : has_token_boundary(false), has_rule(false) {} + + using Ope::Visitor::visit; + + void visit(Sequence& ope) override { + for (auto op: ope.opes_) { + op->accept(*this); + } + } + void visit(PrioritizedChoice& ope) override { + for (auto op: ope.opes_) { + op->accept(*this); + } + } + void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); } + void visit(OneOrMore& ope) override { ope.ope_->accept(*this); } + void visit(Option& ope) override { ope.ope_->accept(*this); } + void visit(Capture& ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary& /*ope*/) override { has_token_boundary = true; } + void visit(Ignore& ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } + void visit(DefinitionReference& /*ope*/) override { has_rule = true; } + + bool is_token() const { + return has_token_boundary || !has_rule; + } + + bool has_token_boundary; + bool has_rule; +}; + +static const char* WHITESPACE_DEFINITION_NAME = "%whitespace"; + +/* + * Definition + */ +class Definition +{ +public: + struct Result { + bool ret; + size_t len; + const char* error_pos; + const char* message_pos; + const std::string message; + }; + + Definition() + : ignoreSemanticValue(false) + , enablePackratParsing(false) + , is_token(false) + , has_token_boundary(false) + , holder_(std::make_shared(this)) {} + + Definition(const Definition& rhs) + : name(rhs.name) + , ignoreSemanticValue(false) + , enablePackratParsing(false) + , is_token(false) + , has_token_boundary(false) + , holder_(rhs.holder_) + { + holder_->outer_ = this; + } + + Definition(Definition&& rhs) + : name(std::move(rhs.name)) + , ignoreSemanticValue(rhs.ignoreSemanticValue) + , whitespaceOpe(rhs.whitespaceOpe) + , enablePackratParsing(rhs.enablePackratParsing) + , is_token(rhs.is_token) + , has_token_boundary(rhs.has_token_boundary) + , holder_(std::move(rhs.holder_)) + { + holder_->outer_ = this; + } + + Definition(const std::shared_ptr& ope) + : ignoreSemanticValue(false) + , enablePackratParsing(false) + , is_token(false) + , has_token_boundary(false) + , holder_(std::make_shared(this)) + { + *this <= ope; + } + + operator std::shared_ptr() { + return std::make_shared(holder_); + } + + Definition& operator<=(const std::shared_ptr& ope) { + IsToken isToken; + ope->accept(isToken); + is_token = isToken.is_token(); + has_token_boundary = isToken.has_token_boundary; + + holder_->ope_ = ope; + + return *this; + } + + Result parse(const char* s, size_t n, const char* path = nullptr) const { + SemanticValues sv; + any dt; + return parse_core(s, n, sv, dt, path); + } + + Result parse(const char* s, const char* path = nullptr) const { + auto n = strlen(s); + return parse(s, n, path); + } + + Result parse(const char* s, size_t n, any& dt, const char* path = nullptr) const { + SemanticValues sv; + return parse_core(s, n, sv, dt, path); + } + + Result parse(const char* s, any& dt, const char* path = nullptr) const { + auto n = strlen(s); + return parse(s, n, dt, path); + } + + template + Result parse_and_get_value(const char* s, size_t n, T& val, const char* path = nullptr) const { + SemanticValues sv; + any dt; + auto r = parse_core(s, n, sv, dt, path); + if (r.ret && !sv.empty() && !sv.front().is_undefined()) { + val = sv[0].get(); + } + return r; + } + + template + Result parse_and_get_value(const char* s, T& val, const char* path = nullptr) const { + auto n = strlen(s); + return parse_and_get_value(s, n, val, path); + } + + template + Result parse_and_get_value(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const { + SemanticValues sv; + auto r = parse_core(s, n, sv, dt, path); + if (r.ret && !sv.empty() && !sv.front().is_undefined()) { + val = sv[0].get(); + } + return r; + } + + template + Result parse_and_get_value(const char* s, any& dt, T& val, const char* path = nullptr) const { + auto n = strlen(s); + return parse_and_get_value(s, n, dt, val, path); + } + + Definition& operator=(Action a) { + action = a; + return *this; + } + + template + Definition& operator,(T fn) { + operator=(fn); + return *this; + } + + Definition& operator~() { + ignoreSemanticValue = true; + return *this; + } + + void accept(Ope::Visitor& v) { + holder_->accept(v); + } + + std::shared_ptr get_core_operator() { + return holder_->ope_; + } + + std::string name; + size_t id; + Action action; + std::function enter; + std::function leave; + std::function error_message; + bool ignoreSemanticValue; + std::shared_ptr whitespaceOpe; + bool enablePackratParsing; + bool is_token; + bool has_token_boundary; + Tracer tracer; + +private: + friend class DefinitionReference; + + Definition& operator=(const Definition& rhs); + Definition& operator=(Definition&& rhs); + + Result parse_core(const char* s, size_t n, SemanticValues& sv, any& dt, const char* path) const { + AssignIDToDefinition assignId; + holder_->accept(assignId); + + std::shared_ptr ope = holder_; + if (whitespaceOpe) { + ope = std::make_shared(whitespaceOpe, ope); + } + + Context cxt(path, s, n, assignId.ids.size(), whitespaceOpe, enablePackratParsing, tracer); + auto len = ope->parse(s, n, sv, cxt, dt); + return Result{ success(len), len, cxt.error_pos, cxt.message_pos, cxt.message }; + } + + std::shared_ptr holder_; +}; + +/* + * Implementations + */ + +inline size_t LiteralString::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { + c.trace("LiteralString", s, n, sv, dt); + + size_t i = 0; + for (; i < lit_.size(); i++) { + if (i >= n || s[i] != lit_[i]) { + c.set_error_pos(s); + return static_cast(-1); + } + } + + // Skip whiltespace + if (!c.in_token) { + if (c.whitespaceOpe) { + auto len = c.whitespaceOpe->parse(s + i, n - i, sv, c, dt); + if (fail(len)) { + return static_cast(-1); + } + i += len; + } + } + + return i; +} + +inline size_t TokenBoundary::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { + c.in_token = true; + auto se = make_scope_exit([&]() { c.in_token = false; }); + const auto& rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len)) { + sv.tokens.push_back(std::make_pair(s, len)); + + if (c.whitespaceOpe) { + auto l = c.whitespaceOpe->parse(s + len, n - len, sv, c, dt); + if (fail(l)) { + return static_cast(-1); + } + len += l; + } + } + return len; +} + +inline size_t Holder::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { + if (!ope_) { + throw std::logic_error("Uninitialized definition ope was used..."); + } + + c.trace(outer_->name.c_str(), s, n, sv, dt); + c.nest_level++; + auto se = make_scope_exit([&]() { c.nest_level--; }); + + size_t len; + any val; + + c.packrat(s, outer_->id, len, val, [&](any& a_val) { + auto& chldsv = c.push(); + + if (outer_->enter) { + outer_->enter(dt); + } + + auto se2 = make_scope_exit([&]() { + c.pop(); + + if (outer_->leave) { + outer_->leave(dt); + } + }); + + const auto& rule = *ope_; + len = rule.parse(s, n, chldsv, c, dt); + + // Invoke action + if (success(len)) { + chldsv.s_ = s; + chldsv.n_ = len; + + try { + a_val = reduce(chldsv, dt); + } catch (const parse_error& e) { + if (e.what()) { + if (c.message_pos < s) { + c.message_pos = s; + c.message = e.what(); + } + } + len = static_cast(-1); + } + } + }); + + if (success(len)) { + if (!outer_->ignoreSemanticValue) { + sv.emplace_back(val); + } + } else { + if (outer_->error_message) { + if (c.message_pos < s) { + c.message_pos = s; + c.message = outer_->error_message(); + } + } + } + + return len; +} + +inline any Holder::reduce(const SemanticValues& sv, any& dt) const { + if (outer_->action) { + return outer_->action(sv, dt); + } else if (sv.empty()) { + return any(); + } else { + return sv.front(); + } +} + +inline size_t DefinitionReference::parse( + const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { + const auto& rule = *get_rule(); + return rule.parse(s, n, sv, c, dt); +} + +inline std::shared_ptr DefinitionReference::get_rule() const { + if (!rule_) { + std::call_once(init_, [this]() { + rule_ = grammar_.at(name_).holder_; + }); + } + assert(rule_); + return rule_; +} + +inline void Sequence::accept(Visitor& v) { v.visit(*this); } +inline void PrioritizedChoice::accept(Visitor& v) { v.visit(*this); } +inline void ZeroOrMore::accept(Visitor& v) { v.visit(*this); } +inline void OneOrMore::accept(Visitor& v) { v.visit(*this); } +inline void Option::accept(Visitor& v) { v.visit(*this); } +inline void AndPredicate::accept(Visitor& v) { v.visit(*this); } +inline void NotPredicate::accept(Visitor& v) { v.visit(*this); } +inline void LiteralString::accept(Visitor& v) { v.visit(*this); } +inline void CharacterClass::accept(Visitor& v) { v.visit(*this); } +inline void Character::accept(Visitor& v) { v.visit(*this); } +inline void AnyCharacter::accept(Visitor& v) { v.visit(*this); } +inline void Capture::accept(Visitor& v) { v.visit(*this); } +inline void TokenBoundary::accept(Visitor& v) { v.visit(*this); } +inline void Ignore::accept(Visitor& v) { v.visit(*this); } +inline void WeakHolder::accept(Visitor& v) { v.visit(*this); } +inline void Holder::accept(Visitor& v) { v.visit(*this); } +inline void DefinitionReference::accept(Visitor& v) { v.visit(*this); } +inline void Whitespace::accept(Visitor& v) { v.visit(*this); } + +inline void AssignIDToDefinition::visit(Holder& ope) { + auto p = static_cast(ope.outer_); + if (ids.count(p)) { + return; + } + auto id = ids.size(); + ids[p] = id; + ope.outer_->id = id; + ope.ope_->accept(*this); +} + +/* + * Factories + */ +template +std::shared_ptr seq(Args&& ...args) { + return std::make_shared(static_cast>(args)...); +} + +template +std::shared_ptr cho(Args&& ...args) { + return std::make_shared(static_cast>(args)...); +} + +inline std::shared_ptr zom(const std::shared_ptr& ope) { + return std::make_shared(ope); +} + +inline std::shared_ptr oom(const std::shared_ptr& ope) { + return std::make_shared(ope); +} + +inline std::shared_ptr opt(const std::shared_ptr& ope) { + return std::make_shared