The Crystal Programming Language Forum

Error: can't define def inside def

I’m porting a Ruby project over to Crystal and ran into a strange complier error:

In src/spec_lang/v2/tokenizer.cr:28:17

 28 | private def scan(line : String) : Token
              ^
Error: can't define def inside def

Here’s a snippet of the class:

require "string_scanner"
require "./token_patterns"
require "./language_support"
require "./token"

module SpecLang
  module V2
    class Tokenizer
      def initialize(text : String)
        text.encode! if text.encoding_aware?
        @lines = text.split("\n")
      end

      def tokenize : Array(Token)
        @tokens = [] of Token
        @lines.each_with_index do |text, line|
          @scanner = StringScanner.new(text)
          until @scanner.eos? do
            @tokens << scan(line + 1)
          end
          unless line == @lines.count - 1
            @tokens << create_token({token: :new_line, value: "\n", line: line + 1, pos: text.length + 1})
          end
        end
        @tokens
      end

      private def scan(line : String) : Token
        # etc...
      end

      # etc...

    end
  end
end

If I comment out the code body in tokenizer the error goes away.

        def tokenize : Array(Token)
          #@tokens = [] of Token
          #@lines.each_with_index do |text, line|
          #  @scanner = StringScanner.new(text)
          #  until @scanner.eos? do
          #    @tokens << scan(line + 1)
          #  end
          #  unless line == @lines.count - 1
          #    @tokens << create_token({token: :new_line, value: "\n", line: line + 1, pos: text.length + 1})
          #  end
          #end
          #@tokens
        end

So, I’m wondering if Crystal doesn’t like something in the Ruby syntax? Or, is there a macro which unravels with extra unterminated code blocks?

I’m at a lose…

At this line until @scanner.eos? do you have an additional do

I only have one do but if I comment out that until .. do block, the error goes away:

def tokenize : Array(Token)
  @tokens = [] of Token
  @lines.each_with_index do |text, line|
    @scanner = StringScanner.new(text)
    # until @scanner.eos? do
    #   @tokens << scan(line + 1)
    # end
    unless line == @lines.count - 1
      @tokens << create_token({token: :new_line, value: "\n", line: line + 1, pos: text.length + 1})
    end
  end
  @tokens
end

I wonder if until .. do is a macro in Crystal?

Okay, I see until doesn’t have a do in Crystal:

https://crystal-lang.org/reference/syntax_and_semantics/until.html

The error went away. Thanks for pointing me in the right direction!