Skip to content

Commit

Permalink
Track source position on Message class, add new .pos field
Browse files Browse the repository at this point in the history
  • Loading branch information
maximecb committed Feb 8, 2024
1 parent cbfdf38 commit 22e7c5c
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 11 deletions.
65 changes: 54 additions & 11 deletions lib/protobuff/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,33 @@
module ProtoBuff
# Position in a source file
# Numbers start from 1
SrcPos = Struct.new(:line_no, :col_no)
class SrcPos
attr_reader :line_no
attr_reader :col_no

def initialize(line_no, col_no)
@line_no = line_no
@col_no = col_no
end

def to_s
@line_no.to_s + ":" + @col_no.to_s
end
end

# Whole unit of input (e.g. one source file)
Unit = Struct.new(:messages)

Message = Struct.new(:name, :fields)
Message = Struct.new(:name, :fields, :pos)

# TODO: repeated fields
# Qualifier is :optional, :required or :repeated
Field = Struct.new(:qualifier, :type, :name, :number)

Enum = Struct.new(:name, :variants)




# Parse an entire source unit (e.g. input file)
def self.parse_unit(input)
messages = []
Expand All @@ -52,6 +68,7 @@ def self.parse_unit(input)
break
end

pos = input.pos
ident = input.read_ident

# Syntax mode
Expand All @@ -67,7 +84,7 @@ def self.parse_unit(input)

# Message definition
if ident == "message"
messages << parse_message(input)
messages << parse_message(input, pos)
end
end

Expand All @@ -85,7 +102,7 @@ def self.parse_file(name)
end

# Parse a message definition
def self.parse_message(input)
def self.parse_message(input, pos)
fields = []

input.eat_ws
Expand Down Expand Up @@ -123,7 +140,7 @@ def self.parse_message(input)
fields << Field.new(qualifier, type, name, number)
end

Message.new(message_name, fields)
Message.new(message_name, fields, pos)
end

# Represents an input string/file
Expand All @@ -132,6 +149,13 @@ class Input
def initialize(src)
@src = src
@cur_idx = 0
@line_no = 1
@col_no = 1
end

# Get the current source position
def pos
SrcPos.new(@line_no, @col_no)
end

# Check if we're at the end of the input
Expand All @@ -149,7 +173,10 @@ def start_with?(str)
# Does not read whitespace first
def match_exact(str)
if start_with?(str)
@cur_idx += str.size
# Use eat_ch to maintain source position tracking
str.size.times do
eat_ch
end
true
else
false
Expand Down Expand Up @@ -180,6 +207,17 @@ def peek_ch()
def eat_ch()
ch = @src[@cur_idx]
@cur_idx += 1

# Keep track of the line and column number
if ch == '\n'
if ch != '\r'
@col_no += 1
end
else
@line_no += 1
@col_no = 1
end

ch
end

Expand Down Expand Up @@ -236,8 +274,7 @@ def read_ident
end

if name.size == 0
raise "expected identifier, cur_idx=#{@cur_idx}, ch='#{@src[@cur_idx]}' #{@src[@cur_idx].getbyte 0}"
#raise "expected identifier"
raise "expected identifier at #{pos}"
end

return name
Expand All @@ -253,7 +290,7 @@ def read_string

loop do
if eof?
raise "unexpected end of input"
raise "unexpected end of input inside string constant"
end

# End of string
Expand All @@ -276,10 +313,11 @@ def read_string
# Read an integer constant
def read_int
value = 0
num_digits = 0

loop do
if eof?
raise "unexpected end of input"
break
end

ch = peek_ch
Expand All @@ -291,9 +329,14 @@ def read_int
# Decimal digit value
digit = ch.getbyte(0) - 0x30
value = 10 * value + digit
num_digits += 1
eat_ch
end

if num_digits == 0
raise "expected integer"
end

value
end
end
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/test.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ message TestEmbeddee {

message TestEmbedder {
uint64 id = 1;
// Comment between fields
TestEmbeddee value = 2;
string message = 3;
}
Expand Down

0 comments on commit 22e7c5c

Please sign in to comment.