diff --git a/spec/compiler/lexer/lexer_spec.cr b/spec/compiler/lexer/lexer_spec.cr index cc23045224c24655e701cecb7db246d7b77fea5f..af0851db0233057db96733692727b6aae5cc46d3 100755 --- a/spec/compiler/lexer/lexer_spec.cr +++ b/spec/compiler/lexer/lexer_spec.cr @@ -185,6 +185,8 @@ describe "Lexer" do it_lexes_i64 ["2147483648", "-2147483649", "-9223372036854775808"] it_lexes_u64 ["9223372036854775808", "-9223372036854775809"] it_lexes_u64 ["18446744073709551615", "18446744073709551615", "14146167139683460000"] + it_lexes_i64 [["0x3fffffffffffffff", "4611686018427387903"]] + it_lexes_u64 [["0xffffffffffffffff", "18446744073709551615"]] it_lexes_char "'a'", 'a' it_lexes_char "'\\b'", 8.chr @@ -235,6 +237,10 @@ describe "Lexer" do assert_syntax_error "18446744073709551616", "18446744073709551616 doesn't fit in an UInt64" + assert_syntax_error "0xFF_i8", "255 doesn't fit in an Int8" + assert_syntax_error "0200_i8", "128 doesn't fit in an Int8" + assert_syntax_error "0b10000000_i8", "128 doesn't fit in an Int8" + it "lexes not instance var" do lexer = Lexer.new "!@foo" token = lexer.next_token @@ -362,6 +368,15 @@ describe "Lexer" do (token.value as Char).ord.should eq(0x10FFFF) end + it "lexes float then zero (bug)" do + lexer = Lexer.new "2.5 0" + lexer.next_token.number_kind.should eq(:f64) + lexer.next_token.type.should eq(:SPACE) + token = lexer.next_token + token.type.should eq(:NUMBER) + token.number_kind.should eq(:i32) + end + assert_syntax_error "'\\uFEDZ'", "expected hexadecimal character in unicode escape" assert_syntax_error "'\\u{}'", "expected hexadecimal character in unicode escape" assert_syntax_error "'\\u{110000}'", "invalid unicode codepoint (too large)" diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index 7ac78b3d513bd4499e23bb8d772578d66da4af31..d5344471f96072ae49cde7054a21ad77222deeac 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -191,7 +191,7 @@ module Crystal when '>' next_char :"->" when '0' - scan_zero_number start, multiplier: -1, negative: true + scan_zero_number start, negative: true when '1', '2', '3', '4', '5', '6', '7', '8', '9' scan_number start, negative: true else @@ -1153,36 +1153,12 @@ module Crystal raise "#{string_value} doesn't fit in an UInt64", @token, (current_pos - start) end - def scan_hex_number(multiplier = 1) - @token.type = :NUMBER - num = 0 - next_char - - while true - char = next_char - if char == '_' - else - hex_value = char_to_hex(char) { nil } - if hex_value - num = 16 * num + hex_value - else - break - end - end - end - - num *= multiplier - @token.value = num.to_s - - consume_optional_int_suffix - end - - def scan_zero_number(start, multiplier = 1, negative = false) + def scan_zero_number(start, negative = false) case peek_next_char when 'x' - scan_hex_number(multiplier) + scan_hex_number(start, negative) when 'b' - scan_bin_number(multiplier) + scan_bin_number(start, negative) when '.' scan_number(start) when 'i' @@ -1221,13 +1197,32 @@ module Crystal scan_number(start) end else - scan_octal_number(multiplier) + scan_octal_number(start, negative) end end - def scan_octal_number(multiplier = 1) - @token.type = :NUMBER - num = 0 + def scan_bin_number(start, negative) + next_char + + num = 0_u64 + while true + case next_char + when '0' + num *= 2 + when '1' + num = num * 2 + 1 + when '_' + # Nothing + else + break + end + end + + finish_scan_prefixed_number num, negative, start + end + + def scan_octal_number(start, negative) + num = 0_u64 while true char = next_char @@ -1239,45 +1234,53 @@ module Crystal end end - num *= multiplier - @token.value = num.to_s - - consume_optional_int_suffix + finish_scan_prefixed_number num, negative, start end - def scan_bin_number(multiplier = 1) - @token.type = :NUMBER - num = 0 + def scan_hex_number(start, negative = false) next_char + num = 0_u64 while true - case next_char - when '0' - num *= 2 - when '1' - num = num * 2 + 1 - when '_' - # Nothing + char = next_char + if char == '_' else - break + hex_value = char_to_hex(char) { nil } + if hex_value + num = num * 16 + hex_value + else + break + end end end - num *= multiplier - @token.value = num.to_s - - consume_optional_int_suffix + finish_scan_prefixed_number num, negative, start end - def consume_optional_int_suffix + def finish_scan_prefixed_number(num, negative, start) + if negative + string_value = (-1 * num.to_i64).to_s + else + string_value = num.to_s + end + + name_length = string_value.length + name_length -= 1 if negative + case current_char when 'i' consume_int_suffix + check_integer_literal_fits_in_size string_value, name_length, negative, start when 'u' consume_uint_suffix + check_integer_literal_fits_in_size string_value, name_length, negative, start else @token.number_kind = :i32 + deduce_integer_kind string_value, name_length, negative, start end + + @token.type = :NUMBER + @token.value = string_value end def consume_int_suffix