Class: Tcl::Ruby::Interpreter

Inherits:
Object
  • Object
show all
Defined in:
lib/tcl/ruby/util.rb,
lib/tcl/ruby/parser.rb,
lib/tcl/ruby/command.rb,
lib/tcl/ruby/commands/list.rb,
lib/tcl/ruby/commands/array.rb,
lib/tcl/ruby/commands/basic.rb

Instance Method Summary collapse

Constructor Details

#initializeInterpreter

Returns a new instance of Interpreter.



4
5
6
7
8
9
10
# File 'lib/tcl/ruby/util.rb', line 4

def initialize
  @variables = {}
  @global = @variables
  @v_stack = []
  @hooks = {}
  @proc = {}
end

Instance Method Details

#add_hook(name, &block) ⇒ Object

Raises:

  • (ArgumentError)


17
18
19
20
# File 'lib/tcl/ruby/util.rb', line 17

def add_hook(name, &block)
  raise(ArgumentError, 'block is not given') unless block_given?
  @hooks[name.to_s] = block
end

#command(arg) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/tcl/ruby/command.rb', line 4

def command(arg)
  return @prev if arg[0][0] == '#'
  # return previous command result when comment statement executed
  arg.map! { |e| replace(e) }
  arg.to_string
  name = arg[0]
  if @proc.key?(name)
    exec_proc(arg[1..-1], @proc[name])
  elsif @hooks.key?(name)
    @hooks[name].call(arg[1..-1])
  elsif respond_to?("___#{name}", true)
    @prev = send("___#{name}", arg[1..-1])
  else
    raise(CommandError, "command not found, #{name}")
  end
end

#delete_hook(name) ⇒ Object



22
23
24
# File 'lib/tcl/ruby/util.rb', line 22

def delete_hook(name)
  @hooks.delete(name.to_s)
end

#exec_proc(arg, proc_info) ⇒ Object

Raises:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/tcl/ruby/command.rb', line 46

def exec_proc(arg, proc_info)
  proc_arg = parse(proc_info[0], true)
  raise(TclArgumentError, proc_arg.to_s) if proc_arg.size != arg.size
  @variables[:___global].each do |v| # FIXME: Buggy
    @global[v] = @variables[v]
  end if @variables.key?(:___global)
  @v_stack.push(@variables)
  @variables = {}
  arg.zip(proc_arg).each do |v|
    @variables[v[1]] = v[0]
  end
  ret = catch(:return) do
    parse(proc_info[1])
  end
  @variables[:___global].each do |v| # FIXME: Buggy
    @global[v] = @variables[v]
  end if @variables.key?(:___global)
  @variables = @v_stack.pop
  ret
end

#parse(str, to_list = false) ⇒ Object

Raises:



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/tcl/ruby/parser.rb', line 6

def parse(str, to_list = false)
  s = StringScanner.new(str)
  r = ListArray.new
  pdepth = ddepth = bdepth = 0
  buffer = ''
  ret = nil
  until s.empty?
    if s.scan(/\\./m)
      buffer << s[0] unless s[0][1] =~ /\s/
    elsif !to_list && s.scan(/\r\n|\r|\n|;/)
      if pdepth == 0 && ddepth == 0 && bdepth == 0
        r << buffer
        ret = command(r) unless r.empty?
        r = ListArray.new
      else
        buffer << s[0]
      end
    elsif s.scan(/\s+/)
      if pdepth == 0 && ddepth == 0 && bdepth == 0
        r << buffer
      else
        buffer << s[0]
      end
    else
      buffer <<
        if s.scan(/{/)
          # pdepth += 1 if ddepth == 0
          pdepth += 1 if buffer == '' || pdepth != 0
          s[0]
        elsif s.scan(/}/)
          ret = s[0] # pdepth -= 1 if ddepth == 0
          pdepth -= 1 if pdepth != 0
          raise(ParseError, 'extra characters after close-brace') if
            buffer[0] == '{' && pdepth == 0 && !s.check(/\s|\z/)
          ret
        elsif !to_list && s.scan(/\[/)
          bdepth += 1 if pdepth == 0
          s[0]
        elsif !to_list && s.scan(/\]/)
          bdepth -= 1 if pdepth == 0
          s[0]
        elsif s.scan(/"/)
          ret = s[0]
          ddepth = 1 - ddepth if buffer == '' || buffer[0] == '"'
          raise(ParseError, 'extra characters after close-quote') if
            buffer[0] == '"' && ddepth == 0 && !s.check(/\s|\z/)
          ret
        elsif s.scan(/\S/)
          s[0]
        else
          raise(ParseError, "parse error #{s.rest}")
        end
    end
  end
  r << buffer
  raise(ParseError, 'unmatched parenthesises') if
  ddepth != 0 || pdepth != 0 || bdepth != 0
  if to_list
    r.to_string
  else
    ret = command(r) unless r.empty?
    ret
  end
end

#replace(list) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/tcl/ruby/command.rb', line 21

def replace(list)
  return list if list[0] == '{'
  # replace variable
  l = list.gsub(/\$\{(.+?)\}|\$([\w()]+)/) do
    v = Regexp.last_match(Regexp.last_match(1) ? 1 : 2)
    h = vv = nil
    if (m = v.match(/\((\w+)\)\z/))
      h = m[1]
      vv = v
      v = vv.sub(/\((\w+)\)\z/, '')
    end
    raise(TclVariableNotFoundError, v.to_s, 'no such variable') unless
      @variables.key?(v)
    if h
      raise(TclVariableNotFoundError, vv.to_s, "variable isn't array") unless @variables[v].is_a?(Hash)
      @variables[v][h].to_s
    else
      raise(TclVariableNotFoundError, v.to_s, 'variable is array') if @variables[v].is_a?(Hash)
      @variables[v]
    end
  end
  # replace commands
  l = l.gsub(/\[(.+)\]/) { parse(Regexp.last_match(1)) }
end

#variables(arg) ⇒ Object



12
13
14
15
# File 'lib/tcl/ruby/util.rb', line 12

def variables(arg)
  raise(TclVariableNotFoundError, "can't read $#{arg}, no such variables") unless @variables.key?(arg)
  @variables[arg]
end