lib/yoga/parser/helpers.rb in yoga-0.3.2 vs lib/yoga/parser/helpers.rb in yoga-0.4.1

- old
+ new

@@ -110,42 +110,56 @@ # Peeks to the next token. If peeking would cause a `StopIteration` # error, it instead returns the last value that was peeked. # # @return [Yoga::Token] def peek - if next? - @_last = @tokens.peek - else - @_last - end + @_last = (@buffer.any? ? @buffer.first : @tokens.peek) + rescue ::StopIteration + @_last end - # Checks if the next token exists. It does this by checking for a - # `StopIteration` error. This is actually really slow, but there's - # not much else I can do. + # Peeks out to a given token. If peeking would cause a `StopIteration` + # error, it instead returns the last value that was peeked. This is + # an expensive operation, and should be used sparingly. All other + # methods work as normal, even after this. # - # @return [::Boolean] - def next? - @tokens.peek - true - rescue StopIteration - false + # @param to [::Numeric] The distance to peek to. + # @return [Yoga::Token] + def peek_out(to) + return @buffer[to] if to <= @buffer.size + (to - @buffer.size + 1).times { @buffer << @tokens.next } + @buffer.last + rescue ::StopIteration + @buffer.last end + # Pushes back tokens onto the buffer. These, in reverse order, are + # the tokens that are pulled out by `peek/1`, `peek_to/1`, `expect/1`, + # and `shift/1`. + # + # @param tokens [<Yoga::Token>] + # @return [self] + def push(*tokens) + tokens.each { |t| @buffer.push(t) } + self + end + + alias_method :<<, :push + # Switches the function executed based on the given nodes. If the # peeked node matches one of the mappings, it calls the resulting # block or method. # # If the peeked token is not in the map, then {#error} is called. # # @see ClassMethods#switch # @param name [::Symbol] The name of the switch to use. # @return [::Object] The result of calling the block. - def switch(name, *param) + def switch(name, *param, token: peek) switch = self.class.switch(name) block = switch - .fetch(peek.kind) { switch.fetch(:$else) { error(switch.keys) } } + .fetch(token.kind) { switch.fetch(:$else) { error(switch.keys) } } instance_exec(*param, &block) end # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity @@ -163,14 +177,17 @@ ending = Utils.flatten_into_set([ending]) if ending return [] if (ending && peek?(ending)) || (!ending && !join) children << yield - while (join && expect(join)) && !(ending && peek?(ending)) - children << yield + + cond = proc do + (join ? peek?(join) && expect(join) : true) && + !(ending && peek?(ending)) end + (children << yield) while cond.call children end # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Metrics/PerceivedComplexity @@ -182,14 +199,27 @@ def peek?(*tokens) tokens = Utils.flatten_into_set(tokens) tokens.include?(peek.kind) end + # Peeks out to a specific point in the future to match. This is an + # expensive operation. + # + # @see #peek? + # @see peek_out + # @param to [::Numeric] The distance to peek to. + # @param tokens [<::Symbol>] The kinds of tokens to check for. + # @return [::Boolean] + def peek_out?(to, tokens) + tokens = Utils.flatten_into_set([tokens]) + tokens.include?(peek_out(to).kind) + end + # Shifts to the next token, and returns the old token. # # @return [Yoga::Token] def shift - @tokens.next + @buffer.any? ? @buffer.shift : @tokens.next rescue ::StopIteration fail InvalidShiftError, location: peek.location end # Sets up an expectation for a given token. If the next token is