1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
# Copyright 2018 Google Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require_relative 'parse_error'
module HRX::Util # :nodoc:
class << self
# Scans a single HRX path from `scanner` and returns it.
#
# Throws an ArgumentError if no valid path is available to scan. If
# `assert_done` is `true`, throws an ArgumentError if there's any text after
# the path.
def scan_path(scanner, assert_done: true, file: nil)
start = scanner.pos
while _scan_component(scanner, file) && scanner.scan(%r{/}); end
if assert_done && !scanner.eos?
parse_error(scanner, "Paths may not contain newlines", file: file)
elsif scanner.pos == start
parse_error(scanner, "Expected a path", file: file)
end
scanner.string.byteslice(start...scanner.pos)
end
# Emits an ArgumentError with the given `message` and line and column
# information from the current position of `scanner`.
def parse_error(scanner, message, file: nil)
before = scanner.string.byteslice(0...scanner.pos)
line = before.count("\n") + 1
column = (before[/^.*\z/] || "").length + 1
raise HRX::ParseError.new(message, line, column, file: file)
end
# Returns `child` relative to `parent`.
#
# Assumes `parent` ends with `/`, and `child` is beneath `parent`.
#
# If `parent` is `nil`, returns `child` as-is.
def relative(parent, child)
return child unless parent
child[parent.length..-1]
end
private
# Scans a single HRX path component from `scanner`.
#
# Returns whether or not a component could be found, or throws an
# HRX::ParseError if an invalid component was encountered.
def _scan_component(scanner, file)
return unless component = scanner.scan(%r{[^\u0000-\u001F\u007F/:\\]+})
if component == "." || component == ".."
scanner.unscan
parse_error(scanner, "Invalid path component \"#{component}\"", file: file)
end
if char = scanner.scan(/[\u0000-\u0009\u000B-\u001F\u007F]/)
scanner.unscan
parse_error(scanner, "Invalid character U+00#{char.ord.to_s(16).rjust(2, "0").upcase}", file: file)
elsif char = scanner.scan(/[\\:]/)
scanner.unscan
parse_error(scanner, "Invalid character \"#{char}\"", file: file)
end
true
end
end
end
|