IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

VBScript のメモ

だらだら続けます

文が多い
関数呼び出しが文><

statement : sub_call
	    { $$ = gb_stmt_new_call (buffer, $1) }
	  | object_list '=' expr
            { $$ = gb_stmt_new_assignment (buffer, $1, $3); }
	  | dim_statement				{ $$ = NULL; }
          | static_statement                            { $$ = NULL; }
          | GB_REDIM { gb_parse_data_inside_dim (buffer); }
		  opt_preserve var_name '(' subscripts ')'
            {
		    gb_parse_data_dim_finish (buffer);
		    $$ = gb_stmt_new_redim (buffer, $4, $6, $3);
	    }
	  | GB_ERASE var_name				{ $$ = gb_stmt_new_erase (buffer, $2); }
	  | GB_SET object_list '=' opt_new object_list	{ $$ = gb_stmt_new_set (buffer, $2, $4, $5); }
	  | GB_ON GB_ERROR on_error			{ $$ = $3; }
	  | GB_EXIT nesting				{ $$ = gb_stmt_new_exit (buffer, $2); }

	  | GB_DO opt_while_until eostmt
		statements
	    GB_LOOP opt_while_until
	    { $$ = gb_stmt_new_do_while (buffer, $2, $6, $4); }

	  | GB_SELECT GB_CASE expr eostmt
		case_stmts
	    GB_END GB_SELECT
	    { $$ = gb_stmt_new_select   (buffer, $3, $5); }

	  | GB_WHILE expr eostmt
		statements
	    GB_WEND
	    { $$ = gb_stmt_new_do_while (buffer, $2, NULL, $4); }

	  | GB_FOR NAME '=' expr GB_TO expr opt_step eostmt
		statements
	    GB_NEXT opt_name /* TODO : add check for NAME == opt_name */
	    { $$ = gb_stmt_new_forloop (buffer, $2, $4, $6, $7, $9); }

	  | GB_FOR GB_EACH NAME GB_IN object_list eostmt
		statements
	    GB_NEXT opt_name /* TODO : add check for NAME == opt_name */
	    { $$ = gb_stmt_new_foreach (buffer, $3, $5, $7); }

	  | GB_WITH object_list eostmt { gb_module_with_depth_inc (buffer, $2); }
		statements
	    GB_END GB_WITH
	    { $$ = gb_stmt_new_with (buffer, $2, $5); }

	  | GB_IF expr GB_THEN if_body
	    { $$ = gb_stmt_if_set_cond ((GBStatement *)$4, $2); }

	  | GB_BEEP					{ $$ = NULL; }
	  | GB_DATE '=' expr				{ $$ = NULL; }
	  | GB_RANDOMIZE opt_expr { $$ = gb_stmt_new_randomize (buffer, $2); }

          | GB_LOAD expr   { $$ = gb_stmt_new_load (buffer, $2); }
          | GB_UNLOAD expr { $$ = gb_stmt_new_unload (buffer, $2); }

	  /* THIS LOOKS EXTREMELY BROKEN ! */
          | object_refs '.' GB_LINE '(' expr ',' expr ')' '-' opt_line_step '(' expr ',' expr ')' ',' expr ',' expr
            { }

          | object_refs '.' GB_LINE '(' expr ',' expr ')' '-' opt_line_step '(' expr ',' expr ')' ',' expr
            { }

          | object_refs '.' GB_SCALE '(' expr ',' expr ')' '-' '(' expr ',' expr ')'
            { }

          /* File handling related statements */
          | GB_OPEN expr GB_FOR open_mode GB_AS expr opt_open_len
            { $$ = gb_stmt_new_open (buffer, $2, $4, $6, $7); }
          | GB_OPEN expr GB_FOR open_mode GB_AS '#' expr opt_open_len
            { $$ = gb_stmt_new_open (buffer, $2, $4, $7, $8); }
          | GB_INPUT '#' expr ',' expr_csv
            { $$ = gb_stmt_new_input (buffer, $3, $5); }
          | GB_LINE GB_INPUT '#' expr ',' expr
            { $$ = gb_stmt_new_line_input (buffer, $4, $6); }
          | GB_CLOSE expr_csv
            { $$ = gb_stmt_new_close (buffer, $2); }
          | GB_CLOSE handles
            { $$ = gb_stmt_new_close (buffer, $2); }
          | GB_GET '#' expr ',' opt_expr ',' expr
            { $$ = gb_stmt_new_get (buffer, $3, $5, $7); }
          | GB_PUT '#' expr ',' opt_expr ',' expr
            { $$ = gb_stmt_new_put (buffer, $3, $5, $7); }
          | GB_SEEK '#' expr ',' expr
            { $$ = gb_stmt_new_seek (buffer, $3, $5); }
          | GB_PRINT '#' expr ',' expr_csv
            { $$ = gb_stmt_new_print (buffer, $3, $5); }
          /* End of File handling related statements */

          | GB_GOTO NAME
	    { $$ = gb_stmt_new_goto  (buffer, $2); }
	  | GB_GOTO_LABEL
	    { $$ = gb_stmt_new_label (buffer, $1); }
          ;

expr : value            { $$ = gb_expr_new_value ($1); }
     | object_list	{ $$ = $1; }
     | '+' expr		{ $$ = gb_expr_new_unary (GB_EXPR_POSITIVE, $2);}
     | '-' expr		{ $$ = gb_expr_new_unary (GB_EXPR_NEGATIVE, $2);}
     | GB_NOT expr	{ $$ = gb_expr_new_unary (GB_EXPR_NOT, $2);}
     | '(' expr ')'	{ $$ = gb_expr_new_unary (GB_EXPR_PAREN, $2); }
     | expr '&' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_CONCAT, $3); }
     | expr GB_AND expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_AND, $3); }
     | expr GB_OR expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_OR, $3); }
     | expr GB_XOR expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_XOR, $3); }
     | expr '>' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_GT, $3); }
     | expr GB_GE expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_GE, $3); }
     | expr '=' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_EQ, $3); }
     | expr GB_IS expr  { $$ = gb_expr_new_binary ($1, GB_EXPR_EQ, $3); }
     | expr GB_NE expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_NE, $3); }
     | expr GB_LE expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_LE, $3); }
     | expr '<' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_LT, $3); }
     | expr '-' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_SUB, $3); }
     | expr '+' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_ADD, $3); }
     | expr '*' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_MULT, $3); }
     | expr '/' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_DIV, $3); }
     | expr '''' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_INT_DIV, $3); }
     | expr '^' expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_POW, $3); }
     | expr GB_EQV expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_EQV, $3); }
     | expr GB_IMP expr	{ $$ = gb_expr_new_binary ($1, GB_EXPR_IMP, $3); }
     ;

以下のような関数呼び出しは

func(a)
  1. method_array_ref
  2. object_ref
  3. object_refs
  4. object_list
  5. expr

という風にリデュースされるので、式の中に関数呼び出しを書ける。

method_array_ref : GB_OBJ_NAME_LPAR opt_parms ')' { $$ = gb_obj_ref_new ($1, TRUE, $2); }
                 ;

object_ref  : prop_ref             { $$ = $1; }
            | method_array_ref     { $$ = $1; }
            ;

object_refs : object_refs '.' object_ref { $$ = g_slist_prepend ($1, $3);   }
            | object_refs '!' obj_name
              {
		      $$ = g_slist_prepend ($1, gb_obj_ref_new (NULL, TRUE,
								g_slist_prepend (NULL, (gpointer) gb_expr_new_string ($3))));
	      }
            | object_ref                 { $$ = g_slist_prepend (NULL, $1); }
	    | GB_SPACE_DOT object_ref    { $$ = g_slist_prepend (NULL, $2); }
            ;
object_list : object_refs { $$ = gb_expr_new_obj_list ($1); }
            ;

型を調べる

TypeName を使います

alert(TypeName(document.createElement("div")))

http://msdn.microsoft.com/ja-jp/library/cc392344.aspx

関数宣言してすぐ代入はダメ

dim_statement の定義が以下のようになっているので、

dim_statement : GB_DIM    { gb_parse_data_inside_dim (buffer); }
                var_decls { gb_parse_data_dim_finish (buffer); }
              ;
var_decls : var_decls ',' var_decl
          | var_decl
          ;
var_decl : opt_events var_name opt_subscripts dim_type
	   { gb_parse_data_add_var (buffer, gb_var_new ($2, $4.as_new, $3.is_array,
							$3.indices, $4.name)); }
DIm elm = document.createElement("div")

などは、文法エラー

代入は文(しかもキモい)

代入は文なので、以下のようには書けません。
と、思いきや書けます。

a = b = 1
  • 最初のイコールは、代入文の一部として評価され、
  • 次のイコールは比較式のイコールとして評価されます。

つまり、 b と 1 を比較した結果を a に代入するということになります。

オブジェクトの代入だけは特殊

以下のようにオブジェクトを代入文で代入するときは注意が必要です。

Dim elm
elm = document.createElement("div")

これをすると elm に "[object]" という文字列が代入されます。
JavaScript 的に言うとオブジェクトは通常の代入時に valueOf が呼び出されてしまうのですね。
なので、オブジェクト型の値を代入する場合に限って Set が必要になります。

Dim elm
Set elm = document.createElement("div")

これを Set 文(Set ステートメント)といいます。
http://msdn.microsoft.com/ja-jp/library/cc392465.aspx

コメント

' コメントはアポストロフィ

文の区切り

文の区切りは改行

alert(0)
alert(1)
alert(2)

または、コロン

alert(0): alert(1): alert(2)

返り値

' 1 を足して返す
' 関数と同じ名前の変数に代入する
Function succ(a)
    succ = a + 1
End Function

alert(succ(succ(0))) ' 2
' return のように関数の最後じゃなくてもいい
Function succ(a)
    succ = a + 1
    alert(succ)
End Function
alert(succ(succ(0))) ' 2

Sub と Function は同じ風に使われるが、 Sub には返り値の機能はない

関数呼び出しの () は省略 OK

関数名のシンボルが式の中で評価されると関数は引数なしで呼び出される
ここらへんは Perl と同じ

Function foo: alert(0): End Function
foo ' foo() と foo は同じ
Function bar(a): alert(a): End Function
bar 1 ' bar(1) と同じ

a = foo ' foo の返り値を a に代入

関数の宣言文は、代入文や式文より先に実行される

foo
Function foo: alert(0): End Function

これは JavaScript と同じ

Eval と Execute の違い

Eval は式の評価 Execute は文の評価を行う。
しかも、 Eval は式(関数)で Execute は文(値は持たない)。

' Eval は式として評価するので a = 1 は比較式になる
Eval("a = 1")
' Eval 自身も式なので値を持つ
alert(Eval("1 = 1")) ' True

' Execute は式として評価するので a = 1 は代入文になる
Execute "a = 1"
' 複数の文も一つの Execute で実行できる
Execute "dim a: a = 1: alert(a)"
' Execute 自身も式なので文の途中には書けない
' alert(Execute("a = 1")) はエラー

Execute を使うと動的に関数を宣言できる

Execute "Function foo: alert(1): End Function"

関数を値として使う

GetRef を使う

Function foo: alert(1): End Function
Dim bar
Set bar = GetRef("foo") ' 関数名を文字列で指定する
bar() ' これは () の省略ができない。省略しても実行されない。

alert(TypeName(bar))

JavaScript から見た VBScript の関数

<script language="vbscript" type="text/vbscript">
Function foo
    foo = 1
End Function
</script>
<script language="javascript" type="text/javascript">
alert(foo()); // 呼び出せる
alert(typeof foo); // unknown
alert(typeof window.foo); // unknown

try {
    var bar = foo; // これはエラーになる
}
catch(e) {
    alert(e.message);
}
</script>

これでは不便すぎるので、 GetRef してやると普通の JavaScript の関数のように扱える

<script language="vbscript" type="text/vbscript">
Function foo_
    foo_ = 1
End Function
Dim foo
Set foo = GetRef("foo_")
</script>
<script language="javascript" type="text/javascript">
alert(foo())
alert(typeof foo); // object
alert(typeof window.foo); // object
try {
    var bar = foo; // これがエラーにならない
    alert(bar());
    alert(bar.apply); // だけど apply などの Function.prototype に依存するものは持っていない
}
catch(e) {
    alert(e.message);
}
</script>

VBScript から見た JavaScript の関数

<script language="javascript" type="text/javascript">
function foo() { return 1 }
</script>
<script language="vbscript" type="text/vbscript">
' JavaScript の関数はちょうど GetRef された VBScript の関数と同じ扱いになる
alert(foo) ' 呼び出されない
alert(foo()) ' 呼び出される
Dim bar
Set bar = foo ' 代入可能
alert(bar)
alert(bar())
</script>

クラス

なんか、めんどくなってきたので凄いところだけw

ゲッター(Get) セッター(Set/Let)
Class Foo
    Property Let foo(a): alert("Let " + TypeName(a)): End Property
    Property Set foo(a): alert("Set " + TypeName(a)): End Property
    Property Get foo: foo = 1: End Property
End Class

Let は普通の代入文、 Set は Set 文

Dim f
Set f = new Foo

f.foo = "hoge" 'Let が呼ばれる
Set f.foo = "hoge" ' Set が呼ばれる
alert(f.foo) ' Get が呼ばれる

JavaScript からも使える

alert(f.foo); // Get が呼ばれる
f.foo = "hoge"; // 文字列だと Let が呼ばれる
f.foo = {}; // オブジェクトだと Set が呼ばれる
Public Default

オブジェクトを Callable にする

<script language="vbscript" type="text/vbscript">
Class Foo
    Public Default Function item(a, b, c)
        alert(a)
        alert(b)
        alert(c)
        item = "fuga"
    End Function
End Class
alert(1)

Dim bar
Set bar = New Foo

alert(bar(1, 2, 3))
</script>
<script language="javascript" type="text/javascript">
alert(bar(1, 2, 3));

Setter や Getter と、 Callable は JScript からは出来ないので重宝しそう。