UWSCでCUI(コンソールウインドウ)を扱う

素晴らしい!
UWSCのコマンドラインインタプリタ | たっぷす庵
これですよ。
私が求めていたものは!
そうか、AllocConsoleすれば良いのか、、、。


このままで充分便利ですが、stuncloudさんに投げられた、補完のお題に挑戦してみる。
が、、、結局上手くできず。
ただ、日本語とコピペが不完全ですが、少しはできたので一応掲載。


でも、補完入れるより、「#help [uwsc]」に拡張してuwscのヘルプ起動した方が実用的。(かつそれを使っている)



まずはCLIモジュールについて

Command Line Interfaceの略かと思います。
わたしは、Character User Interfaceと言う派なので、stuncloudさんのCLIモジュールに補完サポート機能をつけて、CUIモジュールに改変しました。


CUI.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "CUI.uws" THEN
	STOPFORM(FALSE)
	CUI.Create()

	DIM loop = TRUE, cmd
	WHILE loop
		CUI.Output("> ", FALSE)
		//cmd = CUI.Input("CUISampleCompInput(vk, buf)")
		cmd = CUI.Input()
		loop = (cmd <> "exit") AND (cmd <> "quit")
	WEND

	CUI.Destroy()
	STOPFORM(TRUE)
ENDIF

FUNCTION CUISampleCompInput(vk, buf)
	RESULT = buf
	SELECT vk
	CASE VK_LEFT
		RESULT = "left"
	CASE VK_UP
		RESULT = "up"
	CASE VK_RIGHT
		RESULT = "right"
	CASE VK_DOWN
		RESULT = "down"
	CASE VK_RETURN
		// ここでbufを退避すると、履歴呼び出しが可能になる
	CASE VK_TAB
		// TAB補完
		RESULT = RESULT + "<#TAB>"
	SELEND
FEND


module CUI
	dim hInput, hOutput, hError
	dim orgTextAttr = 0
	dim FOREGROUND_ORIGINAL, BACKGROUND_ORIGINAL
	dim id = -1

	function Create(title = GET_UWSC_NAME)
		result = AllocConsole()
		ifb result then
			SetConsoleTitleA(title)
			hInput  = GetStdHandle(STD_INPUT_HANDLE)
			hOutput = GetStdHandle(STD_OUTPUT_HANDLE)
			hError  = GetStdHandle(STD_ERROR_HANDLE)
			orgTextAttr = getOriginalTextAttribute()
			BACKGROUND_ORIGINAL = orgTextAttr and $F0
			FOREGROUND_ORIGINAL = orgTextAttr and $F
			id = getid(title, "ConsoleWindowClass")
		endif
	fend

	function ID()
		result = id
	fend

	function GetColor(fgcolor = "", bgcolor = "")
		DIM f = _color(fgcolor)
		if f < 0 then f = FOREGROUND_ORIGINAL
		DIM b = _color(bgcolor)
		if b < 0 then b = BACKGROUND_ORIGINAL else b = b * $10
		result = f or b
	fend

	function _color(color = "", def = -1)
		select color
			case "blue", "b"
				result = FOREGROUND_BLUE or FOREGROUND_INTENSITY
			case "green", "g"
				result = FOREGROUND_GREEN or FOREGROUND_INTENSITY
			case "red", "r"
				result = FOREGROUND_RED or FOREGROUND_INTENSITY
			case "yellow", "y"
				result = FOREGROUND_GREEN or FOREGROUND_RED or FOREGROUND_INTENSITY
			case "yellow-", "y-"
				result = FOREGROUND_GREEN or FOREGROUND_RED
			case "pink", "p"
				result = FOREGROUND_BLUE or FOREGROUND_RED or FOREGROUND_INTENSITY
			case "lightblue", "lb"
				result = FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_INTENSITY
			case "white", "w"
				result = FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED or FOREGROUND_INTENSITY
			case "black", "bl", "bk"
				result = 0
			case "gray", "gy"
				result = FOREGROUND_INTENSITY
			default
				result = def
		selend
	fend

	function Destroy()
		result = FreeConsole()
	fend

	procedure Clear(row = -1, col = -1, len = -1)
		DIM cbi = getConsoleScreenBufferInfoArray()
		if row = -1 then row = cbi[3]
		if col = -1 then col = cbi[2]
		if len = -1 then len = cbi[0] - col
		SetConsoleCursorPosition(hOutput, col + row * $10000)
		Output(FORMAT(" ", len), FALSE)
		SetConsoleCursorPosition(hOutput, cbi[2] + cbi[3] * $10000)
	fend

	function Output(Text = "", Cr = TRUE, color = -1, hOut = -1)
		if hOut = -1 THEN hOut = hOutput
		if Cr then Text = Text + "<#CR>"
		color = _color(color, color)
		if length(Text) then
			dim size
			if color > -1 then SetConsoleTextAttribute(hOutput, color)
			result = WriteConsoleA(hOutput, Text, lengthb(Text), size, 0)
			if color > -1 then SetConsoleTextAttribute(hOutput, orgTextAttr)
		endif
	fend

	function Input(func = EMPTY, BufferSize = 128, outputFunc = EMPTY)
		DIM cbi = getConsoleScreenBufferInfoArray(), op = cbi[2] + cbi[3] * $10000
		DIM buf = "", pre = buf, ir[INPUT_RECORD_LEN * BufferSize], read, i, loop = TRUE, vk, add
		WHILE loop
			IFB ReadConsoleInputW(hInput, ir, BufferSize, read) AND read THEN
				FOR i = 0 TO read - 1
					IFB ir[INPUT_RECORD_LEN * i] = 1 AND ir[INPUT_RECORD_LEN * i + 2] THEN
						vk = ir[INPUT_RECORD_LEN * i + 5]
						// ir[INPUT_RECORD_LEN * i + 4] : RepeatCountはとりあえず無視か?
						IFB vk = VK_RETURN THEN
							Output("")
							loop = FALSE
							IFB func <> EMPTY THEN
								buf = eval(func)	// vkとbufを見るのがお勧め。確定時コール
							ENDIF
							BREAK
						ELSEIF vk = VK_TAB THEN
							IFB func = EMPTY THEN
								buf = buf + "<#TAB>"
							ELSE
								buf = eval(func)	// vkとbufを見るのがお勧め。TAB補完コール
							ENDIF
						ELSEIF vk = VK_BACK THEN
							buf = COPY(buf, 1, LENGTH(buf) - 1)
						ELSE
							// vkは小文字なので、CAPSLOCKとSHIFTで大文字にする
							add = CHR(ir[INPUT_RECORD_LEN * i + 7])
							IF vk <> VK_SPACE AND add <> " " THEN add = TRIM(add)
							IFB !LENGTH(add) THEN
								IFB func <> EMPTY THEN
									buf = eval(func)	// vkとbufを見るのがお勧め。VK_SPACE以降
								ELSE
									PRINT "vk:" + vk
								ENDIF
							ELSEIF (ir[INPUT_RECORD_LEN * i + 8] AND $90 = $80) OR (ir[INPUT_RECORD_LEN * i + 8] AND $90 = $10) THEN
								buf = buf + STRCONV(add, SC_UPPERCASE)
							ELSE
								buf = buf + add
							ENDIF
						ENDIF
					ENDIF
				NEXT
				IFB buf <> pre THEN
					IFB outputFunc = EMPTY THEN
						IFB COPYB(pre, 1, LENGTHB(buf)) = buf THEN	// TABあるとうまく消せないけど
							Clear(-1, cbi[2] + LENGTHB(buf))
							SetConsoleCursorPosition(hOutput, cbi[2] + cbi[3] * $10000 + LENGTHB(buf))
							pre = buf
						ELSEIF LENGTH(buf) < LENGTH(pre) THEN
							Clear(-1, cbi[2])
						ELSEIF COPYB(buf, 1, LENGTHB(pre)) = pre THEN
							Output(COPYB(buf, LENGTHB(pre) + 1), FALSE)
							pre = buf
						ENDIF
						IFB buf <> pre THEN
							SetConsoleCursorPosition(hOutput, op)
							Output(buf, FALSE)
							pre = buf
						ENDIF
					ELSE
						EVAL(outputFunc)	// bufとpreとopを見るのがお勧め
					ENDIF
				ELSE
					SLEEP(0.01)
				ENDIF
			ENDIF
		WEND
		RESULT = buf
	fend

	function Error(Text, Cr = TRUE, color = -2)
		if color = -2 then color = GetColor("r")
		result = Output(Text, Cr, color, hError)
	fend

	function getConsoleScreenBufferInfoArray()
		result = safearray(0, 10)
		GetConsoleScreenBufferInfo(hOutput, result)
	fend

	function getOriginalTextAttribute()
		DIM CONSOLE_SCREEN_BUFFER_INFO = getConsoleScreenBufferInfoArray()
		result = CONSOLE_SCREEN_BUFFER_INFO[4]
	fend

	function GetConsoleWidth()
		DIM CONSOLE_SCREEN_BUFFER_INFO = getConsoleScreenBufferInfoArray()
		result = CONSOLE_SCREEN_BUFFER_INFO[0]
	fend

	def_dll AllocConsole():bool:kernel32
	def_dll FreeConsole():bool:kernel32
	def_dll GetStdHandle(dword):dword:kernel32
	//def_dll ReadConsoleA(dword, var string, dword, var dword, dword):bool:kernel32
	def_dll WriteConsoleA(dword, string, dword, var dword, dword):bool:kernel32
	def_dll SetConsoleTitleA(string):bool:kernel32
	def_dll SetConsoleTextAttribute(dword, int):bool:kernel32
	def_dll GetConsoleScreenBufferInfo(hwnd, word[]):bool:kernel32
	def_dll ReadConsoleInputW(dword, var word[], dword, var dword): bool: kernel32
	def_dll SetConsoleCursorPosition(dword, dword): bool: kernel32

	const STD_INPUT_HANDLE  = -10   //標準入力ハンドルを取得
	const STD_OUTPUT_HANDLE = -11   //標準出力ハンドルを取得
	const STD_ERROR_HANDLE  = -12   //標準エラーハンドルを取得

	const FOREGROUND_BLUE		= $1  //文字色に青を加える
	const FOREGROUND_GREEN		= $2  //文字色に緑を加える
	const FOREGROUND_RED		= $4  //文字色に赤を加える
	const FOREGROUND_INTENSITY	= $8  //文字色を高輝度にする
	const BACKGROUND_BLUE		= $10 //背景色に青を加える
	const BACKGROUND_GREEN		= $20 //背景色に緑を加える
	const BACKGROUND_RED		= $40 //背景色に赤を加える
	const BACKGROUND_INTENSITY	= $80 //背景色を高輝度にする

	const INPUT_RECORD_LEN = 10

endmodule

CUI.Input関数に、補完補助用の関数と出力補助用の関数の口(EVAL)を用意しています。
主な変更は、CUI.Input関数になります。
入力を横取りして、地道に書いてますが、一部不完全だったりします。
もっと大胆に入力補助用関数に投げる、というのも手かもしれません。


日本語入力がおかしい?
、、、何回かトライすると大丈夫です。
ReadConsoleInputWでの処理が何か間違っているみたい。
それにコピペが変?
、、、それも同様。
コマンド履歴が使えなくなる?
それをやってくれているのが、ReadConsoleなので、それがこちらの責任になります。


PeekConsoleInputWで見るだけも検討したけど、UWSCのメインスレッドがReadConsole中はダメ。
ネイティブスレッドを起動すべきかしらん。

次にInterpreter

以前作った入力補完を利用して補完補助関数の作成と、CUI.Input関数呼び出しを変更しています。
上のCUIモジュールに依存しています。

OPTION EXPLICIT

CALL cui

Interpreter.Run()

module Interpreter

	// 予約済み変数
	dim a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
	dim id, ie
	hashtbl hash
	dim array[0]

	// 内部使用
	DIM __index = 0, __tabBase = EMPTY, __tabIndex = 0
	hashtbl __cmds, __sels
	DIM __base = SPLIT("DIM PUBLIC CONST IF THEN ELSE IFB ELSEIF ENDIF SELECT CASE DEFAULT SELEND FOR NEXT TO STEP WHILE WEND REPEAT UNTIL CALL BREAK CONTINUE EXIT PRINT AND OR XOR MOD PROCEDURE FUNCTION FEND RESULT VAR DEF_DLL OPTION THREAD CLASS ENDCLASS THIS GLOBAL WITH ENDWITH TEXTBLOCK ENDTEXTBLOCK HASHTBL TRY ENDTRY EXCEPT FINALLY GETID CLKITEM CHKBTN CTRLWIN SENDSTR GETSTR GETITEM GETSLCTLST SETSLIDER GETSLIDER SCKEY GETALLWIN STATUS MOUSEORG PEEKCOLOR CHKIMG SAVEIMG MUSCUR POSACC INPUT MSGBOX SLCTBOX POPUPMENU FUKIDASI STOPFORM LOGPRINT MONITOR EXEC SLEEP DOSCMD POWERSHELL SOUND GETTIME POFF KINDOFOS CPUUSERATE GETKEYSTATE SETHOTKEY LOCKHARD EVAL GETCTLHND IDTOHND HNDTOID VARTYPE MMV BTN KBD ACW COPY POS LENGTH CHKNUM VAL REPLACE TRIM FORMAT CHR ASC ISUNICODE STRCONV TOKEN BETWEENSTR COPYB LENGTHB POSB CHRB ASCB RESIZE SETCLEAR SHIFTARRAY CALCARRAY SPLIT JOIN SLICE QSORT FOPEN FGET FPUT FDELLINE FCLOSE GETDIR DROPFILE READINI WRITEINI DELETEINI CREATEOLEOBJ GETACTIVEOLEOBJ GETOLEITEM OLEEVENT COM_ERR_IGN COM_ERR_RET SAFEARRAY SPEAK RECOSTATE DICTATE IEGETDATA IESETDATA IEGETSRC IESETSRC IELINK ENCODE DECODE CREATEFORM GETFORMDATA SETFORMDATA XLOPEN XLCLOSE XLACTIVATE XLSHEET XLGETDATA XLSETDATA RANDOM ABS ZCUT INT CEIL ROUND SQRT POWER EXP LN LOGN SIN COS TAN ARCSIN ARCCOS ARCTAN PARAM_STR[] ALL_WIN_ID[] ALL_ITEM_LIST[] GETDIR_FILES[] ALL_OLE_ITEM[] EVENT_PRM[] GET_WIN_DIR GET_SYS_DIR GET_CUR_DIR GET_APPDATA_DIR GET_UWSC_DIR GET_UWSC_VER GET_UWSC_NAME COM_ERR_FLG G_IMG_X G_IMG_Y G_SCREEN_W G_SCREEN_H G_SCREEN_C G_MOUSE_X G_MOUSE_Y TRY_ERRMSG TRY_ERRLINE HOTKEY_VK HOTKEY_MOD G_TIME_YY G_TIME_MM G_TIME_DD G_TIME_HH G_TIME_NN G_TIME_SS G_TIME_ZZ G_TIME_WW G_TIME_YY2 G_TIME_MM2 G_TIME_DD2 G_TIME_HH2 G_TIME_NN2 G_TIME_SS2 G_TIME_ZZ2 G_TIME_YY4 TRUE FALSE NULL EMPTY NOTHING ERR_VALUE GET_SLIDER GET_MENU_HND GET_SYSMENU_HND ACTIVATE CLOSE CLOSE2 HIDE SHOW MIN MAX NORMAL TOPMOST NOTOPMOST CLICK DOWN UP LEFT RIGHT MIDDLE WHEEL F_READ F_WRITE F_EXISTS F_LINECOUNT F_ALLTEXT CLK_BTN CLK_LIST CLK_TAB CLK_MENU CLK_TREEVIEW CLK_LISTVIEW CLK_TOOLBAR CLK_ACC CLK_SHORT CLK_BACK CLK_MOUSEMOVE CLK_RIGHTCLK CLK_LEFTCLK CLK_DBLCLK CLK_FROMLAST CLK_CONTINUE STR_EDIT STR_STATIC STR_STATUS STR_ACC_EDIT STR_ACC_STATIC BTN_YES BTN_NO BTN_OK BTN_CANCEL BTN_ABORT BTN_RETRY BTN_IGNORE SLCT_BTN SLCT_CHK SLCT_RDO SLCT_CMB SLCT_LST SLCT_STR SLCT_NUM SLCT_1〜31 ITM_BTN ITM_LIST ITM_TAB ITM_MENU ITM_TREEVIEW ITM_LISTVIEW ITM_EDIT ITM_STATIC ITM_STATUSBAR ITM_TOOLBAR ITM_ACCCLK ITM_ACCCLK2 ITM_ACCTXT ITM_BACK ST_TITLE ST_CLASS ST_X ST_Y ST_WIDTH ST_HEIGHT ST_CLX ST_CLY ST_CLWIDTH ST_CLHEIGHT ST_PARENT ST_ICON ST_MAXIMIZED ST_VISIBLE ST_ACTIVE ST_BUSY ST_PATH ST_PROCESS ST_WIN64 ST_MONITOR CUR_APPSTARTING CUR_ARROW CUR_CROSS CUR_HAND CUR_HELP CUR_IBEAM CUR_NO CUR_SIZEALL CUR_SIZENESW CUR_NS CUR_NWSE CUR_WE CUR_UPARROW CUR_WAIT ACC_ACC ACC_API ACC_NAME ACC_VALUE ACC_ROLE ACC_STATE ACC_DESCRIPTION ACC_LOCATION ACC_BACK P_POWEROFF P_LOGOFF P_REBOOT P_SUSPEND P_SUSPEND2 P_MONIPOWER P_MONIPOWER2 P_MONIPOWER3 P_SCREENSAVE P_UWSC_REEXEC P_FORCE TGL_IME TGL_NUMLOCK TGL_CAPSLOCK TGL_SCROLLLOCK GET_ACTIVE_WIN GET_FROMPOINT_WIN GET_FROMPOINT_OBJ GET_THISUWSC_WIN GET_LOGPRINT_WIN GET_FUKIDASI_WIN GET_FORM_WIN GET_SCHEDULE_WIN SC_LOWERCASE SC_UPPERCASE SC_HIRAGANA SC_KATAKANA SC_HALFWIDTH SC_FULLWIDTH FOM_NOICON FOM_MINIMIZE FOM_MAXIMIZE FOM_NOHIDE FOM_NOSUBMIT FOM_NORESIZE FOM_NOLUNA FOM_BROWSER CODE_URL CODE_UTF8 CODE_BYTEARRAY CODE_BYTEARRAYW CALC_ADD CALC_MIN CALC_MAX CALC_AVR IMG_MSK_BGR1〜4 IMG_MSK_B1〜4 IMG_MSK_G1〜4 IMG_MSK_R1〜4 MOD_ALT MOD_CONTROL MOD_SHIFT MOD_WIN SLD_POS SLD_MIN SLD_MAX SLD_PAGE SLD_BAR SLD_X SLD_Y MON_X MON_Y MON_WIDTH MON_HEIGHT COL_RGB COL_R COL_G COL_B MORG_CLIENT MORG_DIRECT MORG_FORE MORG_BACK VK_START VK_WIN VK_ALT VK_CTRL VK_RCTRL VK_ESC VK_BACK // BackSpace VK_TAB VK_CLEAR VK_RETURN // Enter VK_RRETURN // 右Enter VK_SHIFT VK_RSHIFT VK_CONTROL VK_MENU VK_PAUSE VK_CAPITAL // CapsLock VK_KANA VK_FINAL VK_KANJI VK_CONVERT // 変換 VK_NONCONVERT // 無変換 VK_ACCEPT VK_MODECHANGE VK_ESCAPE VK_SPACE VK_PRIOR // PageUp VK_NEXT // PageDown VK_END VK_HOME VK_LEFT // カーソル VK_UP VK_RIGHT VK_DOWN VK_SELECT VK_PRINT VK_EXECUTE VK_SNAPSHOT // PrintScreen VK_INSERT VK_DELETE VK_HELP VK_APPS // コンテキストメニュ VK_MULTIPLY // * VK_ADD // + VK_SEPARATOR VK_SUBTRACT // - VK_DECIMAL VK_DIVIDE // / VK_NUMPAD0〜9 VK_F1〜12 VK_NUMLOCK VK_SCROLL // ScrollLock VK_SLEEP VK_BROWSER_BACK VK_BROWSER_FORWARD VK_BROWSER_REFRESH VK_BROWSER_STOP VK_BROWSER_SEARCH VK_BROWSER_FAVORITES VK_BROWSER_HOME VK_VOLUME_MUTE VK_VOLUME_DOWN VK_VOLUME_UP VK_MEDIA_NEXT_TRACK VK_MEDIA_PREV_TRACK VK_MEDIA_STOP VK_MEDIA_PLAY_PAUSE VK_LAUNCH_MEDIA_SELECT VK_LAUNCH_MAIL VK_LAUNCH_APP1 VK_LAUNCH_APP2 VK_OEM_PLUS VK_OEM_COMMA VK_OEM_MINUS VK_OEM_PERIOD VK_OEM_1〜8 VK_OEM_RESET VK_OEM_JUMP VK_OEM_PA1〜3 #help #window #enum #exit #exit silent", " ")

	procedure Run()
		dim __ret, __cmd
		CUI.Create()
		while TRUE
			CUI.Output("> ", FALSE)
			__cmd = CUI.Input("Interpreter.CompInput(vk, buf, ir[INPUT_RECORD_LEN * i + 8])")
			select TRUE
				case pos("#", __cmd) = 1
					if Command(__cmd) then break
				default
					try
						__ret = eval(__cmd)
						select __ret
							case EMPTY
								CUI.Output("  EMPTY", TRUE, "lb")
							case NULL
								CUI.Output("  NULL", TRUE, "lb")
							case NOTHING
								CUI.Output("  NOTHING", TRUE, "lb")
							case "", "__cmd"
								// do nothing
							default
								CUI.Output( "  " + __ret, TRUE, "w")
						selend
						__ret = ""
					except
						CUI.Error("  " + TRY_ERRMSG)
					endtry
			selend
			ctrlwin(CUI.ID(), ACTIVATE)
		wend
		CUI.Destroy()
	fend

	function Command(cmd)
		dim i, id, cmds
		result = FALSE
		cmds = split(cmd, " ", TRUE)
		select TRUE
			case cmds[0] = "#exit"
				if length(cmds) > 1 then
					result = TRUE
				else
					CUI.Output("終了しますか? (y/n): ", FALSE)
					result = CUI.Input() = "y"
				endif
			case cmds[0] = "#window"
				DIM listAllWindows
				if length(cmds) > 1 then
					listAllWindows = val(cmds[1], 1)
				else
					listAllWindows = FALSE
				endif
				CUI.Output("ID	Title / Class", TRUE)
				CUI.Output(format("-", CUI.GetConsoleWidth() - 1), TRUE)
				for i = 0 to getallwin() - 1
					id = ALL_WIN_ID[i]
					if ! listAllWindows and ! status(id, ST_VISIBLE) then continue
					CUI.Output(format(id, 4), FALSE)
					CUI.Output(format(" ", 2) + status(id, ST_TITLE), TRUE)
					CUI.Output(format(" ", 6) + status(id, ST_CLASS), TRUE)
				next
			case cmds[0] = "#enum"
				if length(cmds) > 1 then
					try
						for i = 0 to eval("length(" + cmds[1] + ")") - 1
							if cmds[1] = "hash"
								CUI.Output( "hash[<#DBL>" + hash[i, HASH_KEY] + "<#DBL>] = " + hash[i, HASH_VAL] )
							else
								CUI.Output(cmds[1] + "[" + i + "] = " + eval(cmds[1] + "[" + i + "]"))
							endif
						next
					except
						CUI.Error("  " + TRY_ERRMSG)
					endtry
				endif
			case cmds[0] = "#help"
				Help()
			default
				CUI.Output("不正なコマンド: ", FALSE, "p")
				CUI.Output(cmd)
				CUI.Output()
				CUI.Output("ヘルプを参照しますか? (y/n): ", FALSE)
				if CUI.Input() = "y" then
					Help()
				endif
		selend
	fend

	FUNCTION CompInput(vk, buf, op)
		RESULT = buf
		SELECT vk
		CASE VK_LEFT
			RESULT = COPY(buf, 1, LENGTH(buf) - 1)
		CASE VK_UP
			__index = __index - 1
			IF __index < 0 THEN __index = 0
			IF __cmds[__index, HASH_EXISTS] THEN RESULT = __cmds[__index]
		CASE VK_RIGHT
			IFB __cmds[__index, HASH_EXISTS] THEN
				IF buf = COPY(__cmds[__index], 1, LENGTH(buf)) THEN RESULT = COPY(__cmds[__index], 1, LENGTH(buf) + 1)
			ENDIF
		CASE VK_DOWN
			__index = __index + 1
			IF __index >= LENGTH(__cmds) THEN __index = LENGTH(__cmds) - 1
			IF __cmds[__index, HASH_EXISTS] THEN RESULT = __cmds[__index]
		CASE VK_RETURN
			// __cmdsがあふれるケースを想定する必要はあるか?
			DIM add = TRUE
			IFB __cmds[__index, HASH_EXISTS] THEN
				add = (__cmds[__index] <> buf)
			ENDIF
			IFB add THEN
				__index = LENGTH(__cmds)
				__cmds[__index] = buf
				__index = __index + 1
			ENDIF
		CASE VK_ESC
			RESULT = ""
		CASE VK_TAB
			// TAB補完
			IFB LENGTH(buf) THEN
				IFB COPY(buf, 1, LENGTH(__tabBase)) <> __tabBase OR LENGTH(__tabBase) = 0 THEN
					__tabBase = buf
					__tabIndex = 0
					__sels = HASH_REMOVEALL
					DIM n
					FOR n = 0 TO LENGTH(__base) - 1
						IF POS(__tabBase, __base[n]) = 1 THEN __sels[n] = __base[n]
					NEXT
				ELSEIF (op AND $10) = $10 THEN
					__tabIndex = __tabIndex - 1
					IF __tabIndex < 0 THEN __tabIndex = 0
				ELSE
					__tabIndex = __tabIndex + 1
					IF __tabIndex >= LENGTH(__sels) THEN __tabIndex = 0
				ENDIF
				IF LENGTH(__sels) THEN RESULT = __sels[__tabIndex, HASH_VAL]
			ENDIF
		SELEND
	FEND

	procedure Help()
		CUI.Output()
		CUI.Output("※使い方", TRUE, "lb")
		CUI.Output("  式を入力すると実行します")
		CUI.Output("  式の戻り値はコンソールに表示されます")
		CUI.Output()
		CUI.Output("※変数について", TRUE, "lb")
		CUI.Output("  連想配列は ", FALSE)
		CUI.Output("hash", FALSE, "y")
		CUI.Output(" を使ってください")
		CUI.Output("  配列は SAFEARRAY関数で作成してください")
		CUI.Output("  変数への代入は ", FALSE)
		CUI.Output(":=", FALSE, "y")
		CUI.Output(" を使用してください ")
		CUI.Output()
		CUI.Output("※特殊コマンド一覧", TRUE, "lb")
		CUI.Output("#help", TRUE, "y")
		CUI.Output("   このヘルプを表示します", TRUE)
		CUI.Output("#window [all]", TRUE, "y")
		CUI.Output("   ウィンドウIDを列挙、オプション指定で非表示ウィンドウも列挙します", TRUE)
		CUI.Output("#enum 配列名", TRUE, "y")
		CUI.Output("   配列の内容を列挙します", TRUE)
		CUI.Output("   アルファベット一文字 array hash", FALSE, "g")
		CUI.Output(" 変数のみ使用できます", TRUE)
		CUI.Output("#exit [silent]", TRUE, "y")
		CUI.Output("   " + GET_UWSC_NAME + "を終了します、オプション指定で確認なしで終了", TRUE)
		CUI.Output()
	fend

endmodule

Interpreter.Command関数をRun関数に統合すれば、予約済み変数はhashくらいでOK。
コマンド履歴の件数がメモリーの許す限り増えるので、それが嫌なら要修正。


あとは、COMやJScriptオブジェクトのメンバー列挙ができれば、UWSCスクリプト開発にPowershellはいらなくなる、、、。
あ、ScriptControl使えば余裕か、、、いや、COMオブジェクトはできないみたい。


stuncloudさんの使っている画像、うっすら写っているのは私のブログのようですね。