前回まででVBScriptの変数宣言機能・変数代入機能・演算機能を作りました。
今回は制御構文(If, For)を作ってみます。
まずは、制御構文に対応するように構文解析器を改良します。
VBScriptの文法を知っていればそんなに難しくない構文解析ルールですね。
一部TODOがありますが、そこは見なかったことにして下さい。
block_statement : var_declartion
| if_statement
| loop_statement
| for_statement
| select_statement
| inline_statement nl
# If Statement
if_statement : 'If' expr 'Then' nl block_statement_list else_statement_list 'End' 'If' nl { result = AST::IfStatement.new(val[1], val[4], val[5]) }
| 'If' expr 'Then' inline_statement else_opt end_if_opt nl { result = AST::IfStatement.new(val[1], val[3], val[4]) }
else_statement_list : 'Elseif' expr 'Then' nl block_statement_list else_statement_list { result = AST::IfStatement.new(val[1], val[4], val[5]) }
| 'Elseif' expr 'Then' inline_statement nl else_statement_list { result = AST::IfStatement.new(val[1], val[3], val[5]) }
| 'Else' nl block_statement_list { result = AST::ElseStatementList.new(val[2]) }
| 'Else' inline_statement nl { result = AST::ElseStatementList.new(val[1]) }
| { result = AST::Nop.new }
else_opt : 'Else' inline_statement { result = val[1] }
| { result = AST::Nop.new }
end_if_opt : 'End' 'If'
|
loop_statement : 'Do' 'While' expr nl block_statement_list 'Loop' nl { result = AST::WhileStatement.new(val[2], val[4]) }
| 'Do' 'Until' expr nl block_statement_list 'Loop' nl { result = AST::UntilStatement.new(val[2], val[4]) }
| 'Do' nl block_statement_list 'Loop' 'While' expr nl { result = AST::DoWhileStatement.new(val[5], val[2]) }
| 'Do' nl block_statement_list 'Loop' 'Until' expr nl { result = AST::DoUntilStatement.new(val[5], val[2]) }
| 'Do' nl block_statement_list 'Loop' nl { result = AST::Nop.new } # TODO: Exit文が実装されたらLoop文も実装する
| 'While' expr nl block_statement_list 'Wend' nl { result = AST::WhileStatement.new(val[1], val[3]) }
for_statement : 'For' extended_id '=' expr 'To' expr step_opt nl block_statement_list 'Next' nl { result = AST::ForStatement.new(val[1], val[3], val[5], val[6], val[>
| 'For' 'Each' extended_id 'In' expr nl block_statement_list 'Next' nl { result = AST::Nop.new } # TODO: 配列が実装されたらForEach文を実装する
step_opt : 'Step' expr { result = val[1] }
| { result = AST::NumberLiteral.new(1) }
# Select Statement
select_statement : 'Select' 'Case' expr nl case_statement_list 'End' 'Select' nl { result = AST::SelectStatement.new(val[2], val[4]) }
case_statement_list : 'Case' expr nl_opt block_statement_list case_statement_list { result = val[4].unshift(AST::CaseStatement.new(val[1], val[3])) }
| 'Case' 'Else' nl_opt block_statement_list { result = [AST::CaseElseStatement.new(val[3])] }
| { result = [] }
次に各制御構文の実装を紹介します。
まずは、If文です。
条件式をevalし、その結果がTrueかFalseかに応じてThen節もしくはElse節の実行(eval)を行います。
class IfStatement < List
def initialize(expr, then_statement_list, else_statement_list)
super([expr, then_statement_list, else_statement_list])
end
def eval(environment)
if child(0).eval(environment)
child(1).eval(environment)
else
child(2).eval(environment)
end
end
end
While文は特に説明をしなくても自明かと思います。
class WhileStatement < List
def initialize(expr, block_statement_list)
@expr = expr
super([block_statement_list])
end
def eval(environment)
while @expr.eval(environment) do
child(0).eval(environment)
end
end
end
最後にFor文の紹介です。
For文は引数が多いために実装がやや面倒くさいです。
To句やStep句には即値だけでなく式も書けることの考慮が必要です。
class ForStatement < List
def initialize(id, from, to, step, block_statement_list)
@id = id
@from = from
@to = to
@step = step
super([block_statement_list])
end
def eval(environment)
environment[@id.identifier] = @from.eval(environment)
to = @to.eval(environment)
step = @step.eval(environment)
if step >= 0
while environment[@id.identifier] <= to
child(0).eval(environment)
environment[@id.identifier] += step
end
else
while environment[@id.identifier] >= to
child(0).eval(environment)
environment[@id.identifier] += step
end
end
end
end
ここまでの機能を使うことで、フィボナッチ数のN項目を計算できるようになりました。
' フィボナッチ数のn項目を計算する
Dim n, tmp1, tmp2, answer
n = 10
tmp1 = 1
tmp2 = 1
If n = 1 Or n = 2 Then
answer = 1
Else
For i = 1 To n - 2 Step 1
answer = tmp1 + tmp2
tmp1 = tmp2
tmp2 = answer
Next
End If
' この時点でanswerに答えがセットされている
怒涛のVBScript記事連投は、VBScriptっぽい文法を持ったチューリング完全な言語が完成したあたりで一旦中断します。
気が向いたら、また来年この続きから言語実装を始めるかもしれません。
明日のZOZOテクノロジーズアドベントカレンダー#3 @zt_takumi_ito さんです。
是非そちらもご覧ください!