Skip to content
Snippets Groups Projects
Unverified Commit c5d3b303 authored by Yorick Peterse's avatar Yorick Peterse
Browse files

Remove support for binary newline sends

This removes syntax support for sending messages to the result of a
binary expression without using parentheses. In other words, this:

    foo == bar
      .something

Is no longer parsed as `(foo == bar).something`, instead it is not
parsed as `foo == bar.something`. To send a message to the result you
need to use parentheses:

    (foo == bar).something

This might be a bit more verbose, but it makes the syntax easier to
parse for both humans and computers. It also makes it possible to spread
the right-hand side over multiple lines when necessary. For example,
prior to this MR this would not work as expected:

    foo ==
      bar
        .baz
        .quix

This would be parsed as `(foo == bar).baz.quix`, and not `(foo ==
bar.baz.quix)`.
parent c94fb713
No related branches found
No related tags found
No related merge requests found
Showing
with 404 additions and 540 deletions
Loading
Loading
@@ -280,27 +280,10 @@ module Inkoc
 
while BINARY_OPERATORS.include?(@lexer.peek.type)
operator = @lexer.advance
rhs = send_chain(@lexer.advance, require_same_line: true)
rhs = send_chain(@lexer.advance)
node = AST::Send.new(operator.value, node, [], [rhs], operator.location)
end
 
# This allows us to parse code such as this:
#
# x == y
# .if_true {
#
# }
#
# Into this:
#
# (x == y).if_true {
#
# }
if @lexer.next_type_is?(:dot)
skip_one
node = send_chain_with_receiver(node)
end
node
end
 
Loading
Loading
@@ -395,14 +378,12 @@ module Inkoc
end
 
# Parses a chain of messages being sent to a receiver.
def send_chain(start, require_same_line: false)
def send_chain(start)
node = value(start)
 
loop do
case @lexer.peek.type
when :dot
break if require_same_line && @lexer.peek.line != start.line
skip_one
node = send_chain_with_receiver(node)
when :bracket_open
Loading
Loading
Loading
Loading
@@ -196,10 +196,9 @@ impl Array!(T) {
## Array.new(10, 20, 30).contains?(10) # => True
def contains?(value: T) -> Boolean where T: Equal {
each do (val) {
val == value
.if_true {
return True
}
(val == value).if_true {
return True
}
}
 
False
Loading
Loading
@@ -316,14 +315,16 @@ impl Equal for Array!(T) {
##
## Array.new(10, 20) == Array.new(20, 10) # => False
def ==(other: Self) -> Boolean where T: Equal {
length == other.length
.if_false { return False }
(length == other.length).if_false {
return False
}
 
each_with_index do (ours: T, index: Integer) {
let theirs = other[index]!
 
ours == theirs
.if_false { return False }
(ours == theirs).if_false {
return False
}
}
 
True
Loading
Loading
Loading
Loading
@@ -58,8 +58,7 @@ impl Equal for Boolean {
 
## Returns `True` if the given `Boolean` does not equal `self`.
def !=(other: Self) -> Boolean {
self == other
.not
(self == other).not
}
}
 
Loading
Loading
Loading
Loading
@@ -232,20 +232,17 @@ impl ByteArray {
## sliced[1] # => 3
def slice(start: Integer, length: Integer) -> ByteArray {
let new_array = ByteArray.new
let mut index =
start >= 0
.if true: {
start
}, false: {
start % self.length
}
let mut index = (start >= 0).if true: {
start
}, false: {
start % self.length
}
 
let mut until = index + length
 
until >= self.length
.if_true {
until = self.length
}
(until >= self.length).if_true {
until = self.length
}
 
{ index < until }.while_true {
new_array.push(self[index]!)
Loading
Loading
@@ -278,12 +275,11 @@ impl Index!(Integer, Integer) for ByteArray {
##
## bytes[5] # => Nil
def [](index: Integer) -> ?Integer {
index >= length
.if true: {
Nil
}, false: {
_INKOC.byte_array_at(self, index)
}
(index >= length).if true: {
Nil
}, false: {
_INKOC.byte_array_at(self, index)
}
}
}
 
Loading
Loading
Loading
Loading
@@ -83,10 +83,9 @@ def keyword?(bytes: ByteArray) -> Boolean {
let hash =
length + ASSOCIATED_VALUES[bytes[1]!]! + ASSOCIATED_VALUES[bytes[0]!]!
 
hash > MAX_HASH_VALUE
.if_true {
return False
}
(hash > MAX_HASH_VALUE).if_true {
return False
}
 
WORD_LIST[hash] == bytes
}
Loading
Loading
@@ -125,141 +125,115 @@ object Lexer {
return number
}
 
current == AT_SIGN
.if_true {
return attribute
}
(current == AT_SIGN).if_true {
return attribute
}
 
current == HASH
.if_true {
return comment
}
(current == HASH).if_true {
return comment
}
 
current == CURLY_OPEN
.if_true {
return single_character_token('curly_open')
}
(current == CURLY_OPEN).if_true {
return single_character_token('curly_open')
}
 
current == CURLY_CLOSE
.if_true {
return single_character_token('curly_close')
}
(current == CURLY_CLOSE).if_true {
return single_character_token('curly_close')
}
 
current == PAREN_OPEN
.if_true {
return single_character_token('paren_open')
}
(current == PAREN_OPEN).if_true {
return single_character_token('paren_open')
}
 
current == PAREN_CLOSE
.if_true {
return single_character_token('paren_close')
}
(current == PAREN_CLOSE).if_true {
return single_character_token('paren_close')
}
 
current == SINGLE_QUOTE
.if_true {
return string(
quote: SINGLE_QUOTE,
replacements: SINGLE_QUOTED_STRING_ESCAPE_SEQUENCES
)
}
(current == SINGLE_QUOTE).if_true {
return string(
quote: SINGLE_QUOTE,
replacements: SINGLE_QUOTED_STRING_ESCAPE_SEQUENCES
)
}
 
current == DOUBLE_QUOTE
.if_true {
return string(
quote: DOUBLE_QUOTE,
replacements: DOUBLE_QUOTED_STRING_ESCAPE_SEQUENCES
)
}
(current == DOUBLE_QUOTE).if_true {
return string(
quote: DOUBLE_QUOTE,
replacements: DOUBLE_QUOTED_STRING_ESCAPE_SEQUENCES
)
}
 
current == COLON
.if_true {
return colon
}
(current == COLON).if_true {
return colon
}
 
current == SLASH
.if_true {
return operator(type: 'div', assign_type: 'div_assign')
}
(current == SLASH).if_true {
return operator(type: 'div', assign_type: 'div_assign')
}
 
current == PERCENT
.if_true {
return percent
}
(current == PERCENT).if_true {
return percent
}
 
current == CARET
.if_true {
return operator(type: 'xor', assign_type: 'xor_assign')
}
(current == CARET).if_true {
return operator(type: 'xor', assign_type: 'xor_assign')
}
 
current == AMPERSAND
.if_true {
return operator(type: 'and', assign_type: 'and_assign')
}
(current == AMPERSAND).if_true {
return operator(type: 'and', assign_type: 'and_assign')
}
 
current == PIPE
.if_true {
return operator(type: 'or', assign_type: 'or_assign')
}
(current == PIPE).if_true {
return operator(type: 'or', assign_type: 'or_assign')
}
 
current == STAR
.if_true {
return operator(type: 'mul', assign_type: 'mul_assign')
}
(current == STAR).if_true {
return operator(type: 'mul', assign_type: 'mul_assign')
}
 
current == MINUS
.if_true {
return minus
}
(current == MINUS).if_true {
return minus
}
 
current == PLUS
.if_true {
return operator(type: 'add', assign_type: 'add_assign')
}
(current == PLUS).if_true {
return operator(type: 'add', assign_type: 'add_assign')
}
 
current == EQUAL
.if_true {
return operator(type: 'assign', assign_type: 'equal')
}
(current == EQUAL).if_true {
return operator(type: 'assign', assign_type: 'equal')
}
 
current == LOWER
.if_true {
return lower
}
(current == LOWER).if_true {
return lower
}
 
current == GREATER
.if_true {
return greater
}
(current == GREATER).if_true {
return greater
}
 
current == BRACKET_OPEN
.if_true {
return single_character_token('bracket_open')
}
(current == BRACKET_OPEN).if_true {
return single_character_token('bracket_open')
}
 
current == BRACKET_CLOSE
.if_true {
return single_character_token('bracket_close')
}
(current == BRACKET_CLOSE).if_true {
return single_character_token('bracket_close')
}
 
current == EXCLAMATION
.if_true {
return exclamation
}
(current == EXCLAMATION).if_true {
return exclamation
}
 
current == DOT
.if_true {
return dot
}
(current == DOT).if_true {
return dot
}
 
current == COMMA
.if_true {
return single_character_token('comma')
}
(current == COMMA).if_true {
return single_character_token('comma')
}
 
current == QUESTION
.if_true {
return single_character_token('question')
}
(current == QUESTION).if_true {
return single_character_token('question')
}
 
lowercase?.if_true {
return identifier_or_keyword
Loading
Loading
@@ -269,10 +243,9 @@ object Lexer {
return constant
}
 
current == UNDERSCORE
.if_true {
return underscore
}
(current == UNDERSCORE).if_true {
return underscore
}
 
next?.if true: {
invalid
Loading
Loading
@@ -294,11 +267,8 @@ object Lexer {
}
 
# "0x" is only valid at the start of an integer.
INTEGER_DIGIT_RANGE.start == current
.and {
next == LOWER_X
.or { next == UPPER_X }
}
(INTEGER_DIGIT_RANGE.start == current)
.and { (next == LOWER_X).or { next == UPPER_X } }
.if true: {
hexadecimal_integer(skip_after_start)
}, false: {
Loading
Loading
@@ -321,24 +291,19 @@ object Lexer {
INTEGER_DIGIT_RANGE.cover?(byte)
.or { byte == UNDERSCORE }
.or {
let float_digit =
byte == LOWER_E
.or { byte == UPPER_E }
.if true: {
let next = next_byte
# '-' and '+' are only allowed directly after the exponent sign
# (e/E).
next == PLUS
.or { next == MINUS }
.if_true {
@position += 1
}
True
}, false: {
byte == DOT
}
let float_digit = (byte == LOWER_E).or { byte == UPPER_E }.if true: {
let next = next_byte
# '-' and '+' are only allowed directly after the exponent sign
# (e/E).
(next == PLUS).or { next == MINUS }.if_true {
@position += 1
}
True
}, false: {
byte == DOT
}
 
float_digit.if_true {
type = 'float'
Loading
Loading
@@ -379,15 +344,13 @@ object Lexer {
}
 
def comment -> Token {
next_byte == HASH
.if_true {
return documentation_comment
}
(next_byte == HASH).if_true {
return documentation_comment
}
 
next_byte == EXCLAMATION
.if_true {
return module_comment
}
(next_byte == EXCLAMATION).if_true {
return module_comment
}
 
regular_comment
}
Loading
Loading
@@ -470,17 +433,15 @@ object Lexer {
@position += 1
}
 
let token_type =
next_byte == EQUAL
.if true: {
@position += 2
let token_type = (next_byte == EQUAL).if true: {
@position += 2
 
assign_type
}, false: {
@position += 1
assign_type
}, false: {
@position += 1
 
type
}
type
}
 
token(type: token_type, start: start, line: @line)
}
Loading
Loading
@@ -524,15 +485,13 @@ object Lexer {
#
# To prevent allocating an additional String, we only use the line buffer is
# needed.
let advance_column_by =
@line > start_line
.if true: {
# We subtract 1 for the opening quote padding, since that only applies
# to the first line.
line_buffer.drain_to_string.length - 1
}, false: {
value.length
}
let advance_column_by = (@line > start_line).if true: {
# We subtract 1 for the opening quote padding, since that only applies to
# the first line.
line_buffer.drain_to_string.length - 1
}, false: {
value.length
}
 
@column += advance_column_by + padding
 
Loading
Loading
@@ -546,19 +505,18 @@ object Lexer {
) -> Boolean {
let current = current_byte
 
current == BACKSLASH
.if_true {
let replace_with = replacements[next_byte]
(current == BACKSLASH).if_true {
let replace_with = replacements[next_byte]
 
replace_with.if_true {
buffer.push(replace_with!)
line_buffer.push(replace_with!)
replace_with.if_true {
buffer.push(replace_with!)
line_buffer.push(replace_with!)
 
@position += 2
@position += 2
 
return True
}
return True
}
}
 
buffer.push(current)
line_buffer.push(current)
Loading
Loading
@@ -575,18 +533,15 @@ object Lexer {
 
def colon -> Token {
let start = @position
let type = (next_byte == COLON).if true: {
@position += 2
 
let type =
next_byte == COLON
.if true: {
@position += 2
'colon_colon'
}, false: {
@position += 1
'colon_colon'
}, false: {
@position += 1
 
'colon'
}
'colon'
}
 
token(type: type, start: start, @line)
}
Loading
Loading
@@ -602,38 +557,35 @@ object Lexer {
return number(skip_after_start: True)
}
 
next == GREATER
.if true: {
arrow
}, false: {
operator(type: 'sub', assign_type: 'sub_assign')
}
(next == GREATER).if true: {
arrow
}, false: {
operator(type: 'sub', assign_type: 'sub_assign')
}
}
 
def lower -> Token {
next_byte == LOWER
.if true: {
operator(
type: 'shift_left',
assign_type: 'shift_left_assign',
skip_after_start: True
)
}, false: {
operator(type: 'lower', assign_type: 'lower_equal')
}
(next_byte == LOWER).if true: {
operator(
type: 'shift_left',
assign_type: 'shift_left_assign',
skip_after_start: True
)
}, false: {
operator(type: 'lower', assign_type: 'lower_equal')
}
}
 
def greater -> Token {
next_byte == GREATER
.if true: {
operator(
type: 'shift_right',
assign_type: 'shift_right_assign',
skip_after_start: True
)
}, false: {
operator(type: 'greater', assign_type: 'greater_equal')
}
(next_byte == GREATER).if true: {
operator(
type: 'shift_right',
assign_type: 'shift_right_assign',
skip_after_start: True
)
}, false: {
operator(type: 'greater', assign_type: 'greater_equal')
}
}
 
def arrow -> Token {
Loading
Loading
@@ -647,20 +599,17 @@ object Lexer {
def exclamation -> Token {
let next = next_byte
 
next == EQUAL
.if_true {
return two_character_token('not_equal')
}
(next == EQUAL).if_true {
return two_character_token('not_equal')
}
 
next == PAREN_OPEN
.if_true {
return two_character_token('type_args_open')
}
(next == PAREN_OPEN).if_true {
return two_character_token('type_args_open')
}
 
next == EXCLAMATION
.if_true {
return two_character_token('throws')
}
(next == EXCLAMATION).if_true {
return two_character_token('throws')
}
 
single_character_token('exclamation')
}
Loading
Loading
@@ -670,20 +619,17 @@ object Lexer {
 
@position += 1
 
current_byte == DOT
.and { next_byte == DOT }
.if_true {
@position += 2
(current_byte == DOT).and { next_byte == DOT }.if_true {
@position += 2
 
return token(type: 'exclusive_range', start: start, line: @line)
}
return token(type: 'exclusive_range', start: start, line: @line)
}
 
current_byte == DOT
.if_true {
@position += 1
(current_byte == DOT).if_true {
@position += 1
 
return token(type: 'inclusive_range', start: start, line: @line)
}
return token(type: 'inclusive_range', start: start, line: @line)
}
 
token(type: 'dot', start: start, line: @line)
}
Loading
Loading
@@ -839,7 +785,7 @@ object Lexer {
def whitespace? -> Boolean {
let current = current_byte
 
current == NEWLINE
(current == NEWLINE)
.or { current == SPACE }
.or { current == TAB }
.or { current == CARRIAGE_RETURN }
Loading
Loading
Loading
Loading
@@ -128,10 +128,9 @@ def last_call_frame_in(file: ToPath) -> CallFrame {
{ index >= 0 }.while_true {
let frame = frames[index]!
 
frame.path == path
.if_true {
return frame
}
(frame.path == path).if_true {
return frame
}
 
index -= 1
}
Loading
Loading
Loading
Loading
@@ -452,15 +452,14 @@ object LayoutBuilder {
 
# If the value is too great to fit into the current hole, we need to add
# padding to the current hole, then start over at the next hole.
type_align > remaining_in_hole
.if_true {
let padding = @alignment - remaining_in_hole
(type_align > remaining_in_hole).if_true {
let padding = @alignment - remaining_in_hole
 
size += padding
offset += padding
size += padding
offset += padding
 
remaining_in_hole = @alignment
}
remaining_in_hole = @alignment
}
 
members[name] = Member.new(name: name, type: type, offset: offset)
 
Loading
Loading
Loading
Loading
@@ -277,15 +277,13 @@ impl Float {
##
## try! Float.parse('1.2e1') # => 12.0
static def parse(string: String) !! StandardError -> Float {
string == INFINITY_LABEL
.if_true {
return INFINITY
}
(string == INFINITY_LABEL).if_true {
return INFINITY
}
 
string == NEGATIVE_INFINITY_LABEL
.if_true {
return NEGATIVE_INFINITY
}
(string == NEGATIVE_INFINITY_LABEL).if_true {
return NEGATIVE_INFINITY
}
 
try {
_INKOC.string_to_float(string)
Loading
Loading
Loading
Loading
@@ -56,16 +56,15 @@ impl Formatter for DefaultFormatter {
## If nesting _is_ too great, a placeholder value is added to the buffer, and
## the supplied block is not executed.
def descend(block: do) {
@nesting >= MAX_DEPTH
.if true: {
push(PLACEHOLDER)
}, false: {
@nesting += 1
(@nesting >= MAX_DEPTH).if true: {
push(PLACEHOLDER)
}, false: {
@nesting += 1
 
block.call
block.call
 
@nesting -= 1
}
@nesting -= 1
}
}
}
 
Loading
Loading
Loading
Loading
@@ -60,10 +60,9 @@ impl Inspect for Object {
value.format_for_inspect(formatter)
}
 
index < last_index
.if_true {
formatter.push(',')
}
(index < last_index).if_true {
formatter.push(',')
}
}
 
formatter.push(' }')
Loading
Loading
@@ -175,10 +174,9 @@ impl Inspect for Array!(T) {
value.format_for_inspect(formatter)
}
 
index < last
.if_true {
formatter.push(', ')
}
(index < last).if_true {
formatter.push(', ')
}
}
 
formatter.push(' }')
Loading
Loading
@@ -226,10 +224,9 @@ impl Inspect for Map!(K, V) {
value.format_for_inspect(formatter)
}
 
index < last
.if_true {
formatter.push(', ')
}
(index < last).if_true {
formatter.push(', ')
}
 
index += 1
}
Loading
Loading
Loading
Loading
@@ -27,11 +27,9 @@ impl Integer {
##
## 0x2ff.format(radix: 16) # => '2ff'
def format(radix = 10) -> String {
radix < 2
.or { radix > 36 }
.if_true {
process.panic('The radix argument must be between 2 and 36')
}
(radix < 2).or { radix > 36 }.if_true {
process.panic('The radix argument must be between 2 and 36')
}
 
zero?.if_true {
return '0'
Loading
Loading
Loading
Loading
@@ -184,16 +184,14 @@ object Map!(K: Hash + Equal, V) {
let mut map = Map.new
let max_index = values.length - 1
 
keys.length == values.length
.if_false {
process.panic('An equal number of keys and values must be specified')
}
(keys.length == values.length).if_false {
process.panic('An equal number of keys and values must be specified')
}
 
keys.each_with_index do (key: K, index: Integer) {
index > max_index
.if_true {
return map
}
(index > max_index).if_true {
return map
}
 
map[key] = values[index]!
}
Loading
Loading
@@ -297,8 +295,7 @@ object Map!(K: Hash + Equal, V) {
let mut found: ?Pair!(K, V) = Nil
 
{
index < max
.and { found == Nil }
(index < max).and { found == Nil }
}.while_true {
found = @buckets[index]
index += 1
Loading
Loading
@@ -374,10 +371,9 @@ object Map!(K: Hash + Equal, V) {
##
## let map = Map.new.set('a', 10).set('b', 20)
def set(key: K, value: V) -> Self {
@length >= @resize_threshold
.if_true {
rehash
}
(@length >= @resize_threshold).if_true {
rehash
}
 
_insert_pair(Pair.new(key: key, value: value, hash: _hash_key(key)))
 
Loading
Loading
@@ -421,12 +417,11 @@ object Map!(K: Hash + Equal, V) {
let existing = @buckets[index]
 
existing.if true: {
existing.key == pair.key
.if_true {
@buckets[index] = pair
(existing.key == pair.key).if_true {
@buckets[index] = pair
 
return
}
return
}
 
existing.replace_with?(pair).if_true {
@buckets[index] = pair
Loading
Loading
@@ -490,18 +485,16 @@ object Map!(K: Hash + Equal, V) {
#
# This early return ensures we don't iterate over all buckets if we are
# certain we won't be able to find the key.
pair == Nil
.if_true {
return
}
(pair == Nil).if_true {
return
}
 
index = _desired_bucket(index + 1)
 
index == desired
.if_true {
# We cycled through all buckets but didn't find a matching pair.
return
}
(index == desired).if_true {
# We cycled through all buckets but didn't find a matching pair.
return
}
 
pair = @buckets[index]
}
Loading
Loading
@@ -541,20 +534,18 @@ impl Equal for Map!(K, V) {
##
## map1 == map2 # => True
def ==(other: Self) -> Boolean where V: Equal {
length == other.length
.if_false {
return False
}
(length == other.length).if_false {
return False
}
 
each do (key, value) {
other.key?(key).if_false {
return False
}
 
value == other[key]!
.if_false {
return False
}
(value == other[key]!).if_false {
return False
}
}
 
True
Loading
Loading
Loading
Loading
@@ -167,17 +167,13 @@ impl ToString for Ipv6Address {
 
let ipv4_address = StringBuffer
.new(
@segments[6] >> _IPV4_TO_IPV6_SHIFT
.to_string,
(@segments[6] >> _IPV4_TO_IPV6_SHIFT).to_string,
'.',
@segments[6] % hextet_to_octet_modulo
.to_string,
(@segments[6] % hextet_to_octet_modulo).to_string,
'.',
@segments[7] >> _IPV4_TO_IPV6_SHIFT
.to_string,
(@segments[7] >> _IPV4_TO_IPV6_SHIFT).to_string,
'.',
@segments[7] % hextet_to_octet_modulo
.to_string
(@segments[7] % hextet_to_octet_modulo).to_string
)
.to_string
 
Loading
Loading
@@ -203,11 +199,10 @@ impl ToString for Ipv6Address {
 
current_len += 1
 
current_len > compression_len
.if_true {
compression_len = current_len
compression_start = current_at
}
(current_len > compression_len).if_true {
compression_len = current_len
compression_start = current_at
}
}, false: {
current_at = 0
current_len = 0
Loading
Loading
@@ -220,20 +215,17 @@ impl ToString for Ipv6Address {
let compression_end = compression_start + compression_len
 
@segments.each_with_index do (hextet, index) {
index == compression_start
.if_true {
(index == compression_start).if_true {
buffer.push(':')
}
(index < compression_start).or { index >= compression_end }.if_true {
index.positive?.if_true {
buffer.push(':')
}
 
index < compression_start
.or { index >= compression_end }
.if_true {
index.positive?.if_true {
buffer.push(':')
}
buffer.push(hextet.format(radix: 16))
}
buffer.push(hextet.format(radix: 16))
}
}
 
buffer.to_string
Loading
Loading
@@ -241,10 +233,9 @@ impl ToString for Ipv6Address {
@segments.each_with_index do (hextet, index) {
buffer.push(hextet.format(radix: 16))
 
index < 7
.if_true {
buffer.push(':')
}
(index < 7).if_true {
buffer.push(':')
}
}
}
 
Loading
Loading
@@ -287,10 +278,9 @@ impl IpAddress for Ipv6Address {
let mut radix = 16
 
# No point in parsing the input if we're certain it's not a valid address.
max > _IPV6_STRING_MAXIMUM_LENGTH
.if_true {
throw AddressParseError.new(bytes)
}
(max > _IPV6_STRING_MAXIMUM_LENGTH).if_true {
throw AddressParseError.new(bytes)
}
 
{ cursor < max }.while_true {
{
Loading
Loading
@@ -298,7 +288,7 @@ impl IpAddress for Ipv6Address {
 
# IPv6 addresses can embed IPv4 addresses, so instead of reading until
# we encounter a ":" we will also stop reading when running into a ".".
cursor < max
(cursor < max)
.and { byte != _COLON_BYTE }
.and { byte != _DOT_BYTE }
}.while_true {
Loading
Loading
@@ -327,11 +317,9 @@ impl IpAddress for Ipv6Address {
throw AddressParseError.new(bytes)
}
 
int < _IP_MINIMUM_VALUE
.or { int > max_hextet_value }
.if_true {
throw AddressParseError.new(bytes)
}
(int < _IP_MINIMUM_VALUE).or { int > max_hextet_value }.if_true {
throw AddressParseError.new(bytes)
}
 
ipv4_mode.if true: {
ipv4_segments.push(int)
Loading
Loading
@@ -344,69 +332,65 @@ impl IpAddress for Ipv6Address {
 
# We have reached another ":", which is used to compress one or more empty
# groups together.
bytes[cursor] == _COLON_BYTE
.if_true {
# Zero compression can only be applied once.
compressed.if true: {
throw AddressParseError.new(bytes)
}, false: {
compressed = True
}
let mut pad = _IPV6_HEXTETS - segments.length
let mut pad_cursor = cursor
let mut ipv4_padded = False
let look_ahead = cursor + 1 < max
# Scan ahead in the input to determine how many empty hextets we need
# to add, based on the remaining number of hextets.
#
# When the compression is at the end of the input (e.g. "1::") there
# is no point in looking ahead, so we don't.
{
look_ahead.and { pad_cursor < max }
}.while_true {
let byte = bytes[pad_cursor]
byte == _COLON_BYTE
.if_true {
pad -= 1
}
# Two IPv4 octets can be stored in a single IPv6 hextet, meaning
# we'd have to reduce padding by two. Since we already skip padding
# for the ":" that preceeds the IPv4 address, we only reduce the
# padding by one.
ipv4_padded
.not
.and { byte == _DOT_BYTE }
.if_true {
ipv4_padded = True
pad -= 1
}
pad_cursor += 1
}
(bytes[cursor] == _COLON_BYTE).if_true {
# Zero compression can only be applied once.
compressed.if true: {
throw AddressParseError.new(bytes)
}, false: {
compressed = True
}
 
{ pad.positive? }.while_true {
segments.push(0)
let mut pad = _IPV6_HEXTETS - segments.length
let mut pad_cursor = cursor
let mut ipv4_padded = False
let look_ahead = cursor + 1 < max
# Scan ahead in the input to determine how many empty hextets we need to
# add, based on the remaining number of hextets.
#
# When the compression is at the end of the input (e.g. "1::") there is
# no point in looking ahead, so we don't.
{
look_ahead.and { pad_cursor < max }
}.while_true {
let byte = bytes[pad_cursor]
(byte == _COLON_BYTE).if_true {
pad -= 1
}
 
cursor += 1
# Two IPv4 octets can be stored in a single IPv6 hextet, meaning we'd
# have to reduce padding by two. Since we already skip padding for the
# ":" that preceeds the IPv4 address, we only reduce the padding by
# one.
ipv4_padded
.not
.and { byte == _DOT_BYTE }
.if_true {
ipv4_padded = True
pad -= 1
}
pad_cursor += 1
}
}
 
ipv4_segments.length == _IPV4_OCTETS
.if_true {
segments[6] = _octets_to_hextet(ipv4_segments[0]!, ipv4_segments[1]!)
segments[7] = _octets_to_hextet(ipv4_segments[2]!, ipv4_segments[3]!)
}
{ pad.positive? }.while_true {
segments.push(0)
pad -= 1
}
 
segments.length == _IPV6_HEXTETS
.if_false {
throw AddressParseError.new(bytes)
cursor += 1
}
}
(ipv4_segments.length == _IPV4_OCTETS).if_true {
segments[6] = _octets_to_hextet(ipv4_segments[0]!, ipv4_segments[1]!)
segments[7] = _octets_to_hextet(ipv4_segments[2]!, ipv4_segments[3]!)
}
(segments.length == _IPV6_HEXTETS).if_false {
throw AddressParseError.new(bytes)
}
 
Ipv6Address.new(
segments[0]!,
Loading
Loading
@@ -447,8 +431,7 @@ impl IpAddress for Ipv6Address {
##
## Ipv6Address.new(0x2001, 0xdb8).documentation # => True
def documentation? -> Boolean {
@segments[0] == 0x2001
.and { @segments[1] == 0xdb8 }
(@segments[0] == 0x2001).and { @segments[1] == 0xdb8 }
}
 
## Returns `True` if `self` is a loopback address (::1).
Loading
Loading
@@ -462,7 +445,7 @@ impl IpAddress for Ipv6Address {
## Ipv6Address.new(0, 0, 0, 0, 0, 0, 0, 1).loopback? # => True
## Ipv6Address.new(0, 0, 0, 0, 0, 0, 0, 2).loopback? # => False
def loopback? -> Boolean {
@segments[0] == 0
(@segments[0] == 0)
.and { @segments[1] == 0 }
.and { @segments[2] == 0 }
.and { @segments[3] == 0 }
Loading
Loading
@@ -496,7 +479,7 @@ impl IpAddress for Ipv6Address {
## Ipv6Address.new(0, 0, 0, 0, 0, 0, 0, 0).unspecified? # => True
## Ipv6Address.new(0, 0, 0, 0, 0, 0, 0, 1).unspecified? # => False
def unspecified? -> Boolean {
@segments[0] == 0
(@segments[0] == 0)
.and { @segments[1] == 0 }
.and { @segments[2] == 0 }
.and { @segments[3] == 0 }
Loading
Loading
@@ -516,7 +499,7 @@ impl IpAddress for Ipv6Address {
##
## Ipv6Address.new(0, 0, 0, 0, 0, 0, 1, 1).ipv4_compatible? # => True
def ipv4_compatible? -> Boolean {
@segments[0] == 0
(@segments[0] == 0)
.and { @segments[1] == 0 }
.and { @segments[2] == 0 }
.and { @segments[3] == 0 }
Loading
Loading
@@ -534,7 +517,7 @@ impl IpAddress for Ipv6Address {
##
## Ipv6Address.new(0, 0, 0, 0, 0, 0xffff, 1, 1).ipv4_compatible? # => True
def ipv4_mapped? -> Boolean {
@segments[0] == 0
(@segments[0] == 0)
.and { @segments[1] == 0 }
.and { @segments[2] == 0 }
.and { @segments[3] == 0 }
Loading
Loading
@@ -625,15 +608,13 @@ impl IpAddress for Ipv4Address {
let segment_bytes = ByteArray.new
 
# No IPv4 address can be longer than 15 characters (255.255.255.255).
max > 15
.if_true {
throw AddressParseError.new(bytes)
}
(max > 15).if_true {
throw AddressParseError.new(bytes)
}
 
{ cursor < max }.while_true {
{
cursor < max
.and { bytes[cursor] != _DOT_BYTE }
(cursor < max).and { bytes[cursor] != _DOT_BYTE }
}.while_true {
segment_bytes.push(bytes[cursor]!)
cursor += 1
Loading
Loading
@@ -645,21 +626,18 @@ impl IpAddress for Ipv4Address {
throw AddressParseError.new(bytes)
}
 
int < _IP_MINIMUM_VALUE
.or { int > _IPV4_OCTET_MAXIMUM }
.if_true {
throw AddressParseError.new(bytes)
}
(int < _IP_MINIMUM_VALUE).or { int > _IPV4_OCTET_MAXIMUM }.if_true {
throw AddressParseError.new(bytes)
}
 
segments.push(int)
 
cursor += 1
}
 
segments.length == _IPV4_OCTETS
.if_false {
throw AddressParseError.new(bytes)
}
(segments.length == _IPV4_OCTETS).if_false {
throw AddressParseError.new(bytes)
}
 
Ipv4Address.new(segments[0]!, segments[1]!, segments[2]!, segments[3]!)
}
Loading
Loading
@@ -689,7 +667,7 @@ impl IpAddress for Ipv4Address {
## Ipv4Address.new(127, 0, 0, 1).broadcast? # => False
## Ipv4Address.new(255, 255, 255, 255).broadcast? # => True
def broadcast? -> Boolean {
@segments[0] == _IPV4_OCTET_MAXIMUM
(@segments[0] == _IPV4_OCTET_MAXIMUM)
.and { @segments[1] == _IPV4_OCTET_MAXIMUM }
.and { @segments[2] == _IPV4_OCTET_MAXIMUM }
.and { @segments[3] == _IPV4_OCTET_MAXIMUM }
Loading
Loading
@@ -712,21 +690,21 @@ impl IpAddress for Ipv4Address {
## Ipv4Address.new(192, 0, 2, 0).documentation? # => True
## Ipv4Address.new(192, 1, 2, 0).documentation? # => False
def documentation? -> Boolean {
@segments[0] == 192
(@segments[0] == 192)
.and { @segments[1] == 0 }
.and { @segments[2] == 2 }
.if_true {
return True
}
 
@segments[0] == 198
(@segments[0] == 198)
.and { @segments[1] == 51 }
.and { @segments[2] == 100 }
.if_true {
return True
}
 
@segments[0] == 203
(@segments[0] == 203)
.and { @segments[1] == 0 }
.and { @segments[2] == 113 }
}
Loading
Loading
@@ -743,8 +721,7 @@ impl IpAddress for Ipv4Address {
## Ipv4Address.new(169, 254, 1, 0).link_local? # => True
## Ipv4Address.new(169, 255, 1, 0).link_local? # => False
def link_local? -> Boolean {
@segments[0] == 169
.and { @segments[1] == 254 }
(@segments[0] == 169).and { @segments[1] == 254 }
}
 
## Returns `True` if `self` is a loopback address (127.0.0.0/8).
Loading
Loading
@@ -775,8 +752,7 @@ impl IpAddress for Ipv4Address {
def multicast? -> Boolean {
let first = @segments[0]!
 
first >= 224
.and { first <= 239 }
(first >= 224).and { first <= 239 }
}
 
## Returns `True` if `self` is a private address.
Loading
Loading
@@ -796,20 +772,18 @@ impl IpAddress for Ipv4Address {
## Ipv4Address.new(10, 0, 0, 1).private? # => True
## Ipv4Address.new(127, 0, 0, 1).private? # => False
def private? -> Boolean {
@segments[0] == 10
.if_true {
return True
}
(@segments[0] == 10).if_true {
return True
}
 
@segments[0] == 172
(@segments[0] == 172)
.and { @segments[1]! >= 16 }
.and { @segments[1]! <= 31 }
.if_true {
return True
}
 
@segments[0] == 192
.and { @segments[1] == 168 }
(@segments[0] == 192).and { @segments[1] == 168 }
}
 
## Returns `True` if `self` is the special "unspecified" address (0.0.0.0).
Loading
Loading
@@ -821,7 +795,7 @@ impl IpAddress for Ipv4Address {
## Ipv4Address.new(0, 0, 0, 0).unspecified? # => True
## Ipv4Address.new(0, 0, 0, 1).unspecified? # => False
def unspecified? -> Boolean {
@segments[0] == 0
(@segments[0] == 0)
.and { @segments[1] == 0 }
.and { @segments[2] == 0 }
.and { @segments[3] == 0 }
Loading
Loading
@@ -895,21 +869,18 @@ def parse(address: String) !! AddressParseError -> IpAddress {
let bytes = address.to_byte_array
 
# The address is definetely not an IPv4 or IPv6 address.
bytes.length > _IPV6_STRING_MAXIMUM_LENGTH
.if_true {
throw AddressParseError.new(bytes)
}
(bytes.length > _IPV6_STRING_MAXIMUM_LENGTH).if_true {
throw AddressParseError.new(bytes)
}
 
bytes.each do (byte) {
byte == _DOT_BYTE
.if_true {
return try Ipv4Address.parse(bytes)
}
(byte == _DOT_BYTE).if_true {
return try Ipv4Address.parse(bytes)
}
 
byte == _COLON_BYTE
.if_true {
return try Ipv6Address.parse(bytes)
}
(byte == _COLON_BYTE).if_true {
return try Ipv6Address.parse(bytes)
}
}
 
throw AddressParseError.new(address)
Loading
Loading
Loading
Loading
@@ -58,8 +58,7 @@ object SocketAddress {
impl Equal for SocketAddress {
## Returns `True` if `self` and `other` are the same.
def ==(other: Self) -> Boolean {
@ip == other.ip
.and { @port == other.port }
(@ip == other.ip).and { @port == other.port }
}
}
 
Loading
Loading
Loading
Loading
@@ -69,7 +69,6 @@ impl Equal for Object {
}
 
def !=(other: Self) -> Boolean {
self == other
.not
(self == other).not
}
}
Loading
Loading
@@ -46,8 +46,7 @@ object Range!(T: Successor + SmallerOrEqual) {
## (1..10).cover?(5) # => True
## (1..10).cover?(15) # => False
def cover?(value: T) -> Boolean {
@start <= value
.and { value <= @end }
(@start <= value).and { value <= @end }
}
}
 
Loading
Loading
@@ -64,8 +63,7 @@ impl Equal for Range!(T) {
##
## 1..10 == 1..5 # => False
def ==(other: Self) -> Boolean {
start == other.start
.and { end == other.end }
(start == other.start).and { end == other.end }
}
}
 
Loading
Loading
Loading
Loading
@@ -91,10 +91,9 @@ impl String {
## 'test_starts_with'.starts_with?('test_') # => True
## 'hello'.starts_with?('test_') # => False
def starts_with?(prefix: String) -> Boolean {
prefix.length > length
.if_true {
return False
}
(prefix.length > length).if_true {
return False
}
 
slice(0, prefix.length) == prefix
}
Loading
Loading
Loading
Loading
@@ -65,10 +65,9 @@ object PanicTest {
 
## Asserts that the given arguments are equal to each other.
def equal!(T: Inspect + Equal)(given: T, expected: T) {
given == expected
.if_true {
return
}
(given == expected).if_true {
return
}
 
let error = StringBuffer.new(
'Expected ',
Loading
Loading
@@ -82,10 +81,9 @@ def equal!(T: Inspect + Equal)(given: T, expected: T) {
 
## Asserts that the given arguments are not equal to each other.
def not_equal!(T: Inspect + Equal)(given: T, expected: T) {
given == expected
.if_false {
return
}
(given == expected).if_false {
return
}
 
let error = StringBuffer.new(
'Expected ',
Loading
Loading
@@ -99,10 +97,9 @@ def not_equal!(T: Inspect + Equal)(given: T, expected: T) {
 
## Asserts that the first argument is greater than the second argument.
def greater!(T: Inspect + Greater)(given: T, minimum: T) {
given > minimum
.if_true {
return
}
(given > minimum).if_true {
return
}
 
let error = StringBuffer.new(
'Expected ',
Loading
Loading
Loading
Loading
@@ -77,17 +77,15 @@ impl SetIndex!(String, Dynamic) for Configuration {
##
## config['concurrency'] = 64 # => 64
def []=(key: String, value: Dynamic) -> Dynamic {
key == 'concurrency'
.if_true {
@concurrency = value as Integer
return
}
(key == 'concurrency').if_true {
@concurrency = value as Integer
return
}
 
key == 'formatter'
.if_true {
@formatter = value as Formatter
return
}
(key == 'formatter').if_true {
@formatter = value as Formatter
return
}
 
process.panic(key.inspect + ' is not a valid configuration option')
}
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment