Crystal can't infer type of hash but I already have a type declared?

I’m writing a BF interpreter in Crystal. I’m getting the error

C:\home\crystal\brainfuck\src>crystal build brainfuck.cr
Showing last frame. Use --error-trace for full trace.

In interpreter.cr:39:17

 39 | @sp = @bracketMap[@pc]
            ^----------
Error: can't infer the type of instance variable '@bracketMap' of Interpreter

Could you add a type annotation like this

    struct Interpreter
      @bracketMap : Type
    end

replacing `Type` with the expected type of `@bracketMap`?

but that is strange because I do already declare a type right when I create the struct. bracketMap : Hash(UInt32, UInt32) = Hash(UInt32, UInt32).new What am I doing wrong?

struct Interpreter
  getter mem = Array(UInt8).new(30_000, 0)
  getter pc : UInt32 = 0
  getter sp : UInt32  = 0
  #old version
  #bracketMap = Hash(UInt32, UInt32).new
  bracketMap  : Hash(UInt32, UInt32) = Hash(UInt32, UInt32).new

  def createBracketMap(str : String)
    str.each_char_with_index do |char, index|
      if char == '['
        start = index
      elsif char == ']'
        bracketMap[start] = index
        bracketMap[index] = start
      end
    end
  end

  def interpret(str : String)
    str.each_char do |char|
      if char == '+'
        @mem[@sp] += 1
      elsif char == '-'
        @mem[@sp] -= 1
      elsif char == '.'
        puts mem[sp].unsafe_chr()
      elsif char == '>'
        if (@sp + 1) < @mem.size()
          @sp += 1
        end
      elsif char == '<'
        if (@sp - 1) > 0
          @sp -= 1
        end
      elsif char == '['
        if @mem[@sp] == 0
          @sp = @bracketMap[@pc]
        end
      elsif char == ']'
        if @mem[@sp] != 0
          @sp = @bracketMap[pc]
        end
      elsif char == ','
        loop do
          print "Enter a number between 0 and 255: "
          input = gets
          # check for Ctrl + d/c 
          if input.nil?
            exit
          else
            # check that user entered a number
            if input.chomp =~ /^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/
              num = input.chomp.to_u8
              @mem[@sp] = num
              break
            else
              puts "Please Enter a number between 0 and 255. #{input.chomp} is not acceptable"
            end
          end
        end
      end
    end
  end
end 

You’re missing an @ symbol. Would have to do like @bracketMap : Hash(UInt32, UInt32) = Hash(UInt32, UInt32).new, otherwise it’ll be treated at a local variable and not an instance variable. Or really you can just do @bracketMap = Hash(UInt32, UInt32).new and it’ll infer its type based on the assignment.

Thank you!
So should I always just use @ symbols when declaring member variables of structs and classes and when referring to them in functions within those objects?

If you want it to be an instance variable yes.

1 Like