339 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Ragel
		
	
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Ragel
		
	
	
	
package influx
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	ErrNameParse = errors.New("expected measurement name")
 | 
						|
	ErrFieldParse = errors.New("expected field")
 | 
						|
	ErrTagParse = errors.New("expected tag")
 | 
						|
	ErrTimestampParse = errors.New("expected timestamp")
 | 
						|
	ErrParse = errors.New("parse error")
 | 
						|
)
 | 
						|
 | 
						|
%%{
 | 
						|
machine LineProtocol;
 | 
						|
 | 
						|
action begin {
 | 
						|
	m.pb = m.p
 | 
						|
}
 | 
						|
 | 
						|
action yield {
 | 
						|
	yield = true
 | 
						|
	fnext align;
 | 
						|
	fbreak;
 | 
						|
}
 | 
						|
 | 
						|
action name_error {
 | 
						|
	m.err = ErrNameParse
 | 
						|
	fhold;
 | 
						|
	fnext discard_line;
 | 
						|
	fbreak;
 | 
						|
}
 | 
						|
 | 
						|
action field_error {
 | 
						|
	m.err = ErrFieldParse
 | 
						|
	fhold;
 | 
						|
	fnext discard_line;
 | 
						|
	fbreak;
 | 
						|
}
 | 
						|
 | 
						|
action tagset_error {
 | 
						|
	m.err = ErrTagParse
 | 
						|
	fhold;
 | 
						|
	fnext discard_line;
 | 
						|
	fbreak;
 | 
						|
}
 | 
						|
 | 
						|
action timestamp_error {
 | 
						|
	m.err = ErrTimestampParse
 | 
						|
	fhold;
 | 
						|
	fnext discard_line;
 | 
						|
	fbreak;
 | 
						|
}
 | 
						|
 | 
						|
action parse_error {
 | 
						|
	m.err = ErrParse
 | 
						|
	fhold;
 | 
						|
	fnext discard_line;
 | 
						|
	fbreak;
 | 
						|
}
 | 
						|
 | 
						|
action hold_recover {
 | 
						|
	fhold;
 | 
						|
	fgoto main;
 | 
						|
}
 | 
						|
 | 
						|
action discard {
 | 
						|
	fgoto align;
 | 
						|
}
 | 
						|
 | 
						|
action name {
 | 
						|
	m.handler.SetMeasurement(m.text())
 | 
						|
}
 | 
						|
 | 
						|
action tagkey {
 | 
						|
	key = m.text()
 | 
						|
}
 | 
						|
 | 
						|
action tagvalue {
 | 
						|
	m.handler.AddTag(key, m.text())
 | 
						|
}
 | 
						|
 | 
						|
action fieldkey {
 | 
						|
	key = m.text()
 | 
						|
}
 | 
						|
 | 
						|
action integer {
 | 
						|
	m.handler.AddInt(key, m.text())
 | 
						|
}
 | 
						|
 | 
						|
action unsigned {
 | 
						|
	m.handler.AddUint(key, m.text())
 | 
						|
}
 | 
						|
 | 
						|
action float {
 | 
						|
	m.handler.AddFloat(key, m.text())
 | 
						|
}
 | 
						|
 | 
						|
action bool {
 | 
						|
	m.handler.AddBool(key, m.text())
 | 
						|
}
 | 
						|
 | 
						|
action string {
 | 
						|
	m.handler.AddString(key, m.text())
 | 
						|
}
 | 
						|
 | 
						|
action timestamp {
 | 
						|
	m.handler.SetTimestamp(m.text())
 | 
						|
}
 | 
						|
 | 
						|
ws =
 | 
						|
	[\t\v\f ];
 | 
						|
 | 
						|
non_zero_digit =
 | 
						|
	[1-9];
 | 
						|
 | 
						|
integer =
 | 
						|
	'-'? ( digit | ( non_zero_digit digit* ) );
 | 
						|
 | 
						|
unsigned =
 | 
						|
	( digit | ( non_zero_digit digit* ) );
 | 
						|
 | 
						|
number =
 | 
						|
	'-'? (digit+ ('.' digit*)? | '.' digit+);
 | 
						|
 | 
						|
scientific =
 | 
						|
	number 'e'i ["\-+"]? digit+;
 | 
						|
 | 
						|
timestamp =
 | 
						|
	('-'? digit{1,19}) >begin %timestamp;
 | 
						|
 | 
						|
fieldkeychar =
 | 
						|
	[^\t\n\f\r ,=\\] | ( '\\' [^\t\n\f\r] );
 | 
						|
 | 
						|
fieldkey =
 | 
						|
	fieldkeychar+ >begin %fieldkey;
 | 
						|
 | 
						|
fieldfloat =
 | 
						|
	(scientific | number) >begin %float;
 | 
						|
 | 
						|
fieldinteger =
 | 
						|
	(integer 'i') >begin %integer;
 | 
						|
 | 
						|
fieldunsigned =
 | 
						|
	(unsigned 'u') >begin %unsigned;
 | 
						|
 | 
						|
false =
 | 
						|
	"false" | "FALSE" | "False" | "F" | "f";
 | 
						|
 | 
						|
true =
 | 
						|
	"true" | "TRUE" | "True" | "T" | "t";
 | 
						|
 | 
						|
fieldbool =
 | 
						|
	(true | false) >begin %bool;
 | 
						|
 | 
						|
fieldstringchar =
 | 
						|
	[^\n\f\r\\"] | '\\' [\\"];
 | 
						|
 | 
						|
fieldstring =
 | 
						|
	fieldstringchar* >begin %string;
 | 
						|
 | 
						|
fieldstringquoted =
 | 
						|
	'"' fieldstring '"';
 | 
						|
 | 
						|
fieldvalue = fieldinteger | fieldunsigned | fieldfloat | fieldstringquoted | fieldbool;
 | 
						|
 | 
						|
field =
 | 
						|
	fieldkey '=' fieldvalue;
 | 
						|
 | 
						|
fieldset =
 | 
						|
	field ( ',' field )*;
 | 
						|
 | 
						|
tagchar =
 | 
						|
	[^\t\n\f\r ,=\\] | ( '\\' [^\t\n\f\r] );
 | 
						|
 | 
						|
tagkey =
 | 
						|
	tagchar+ >begin %tagkey;
 | 
						|
 | 
						|
tagvalue =
 | 
						|
	tagchar+ >begin %tagvalue;
 | 
						|
 | 
						|
tagset =
 | 
						|
	(',' (tagkey '=' tagvalue) $err(tagset_error))*;
 | 
						|
 | 
						|
measurement_chars =
 | 
						|
	[^\t\n\f\r ,\\] | ( '\\' [^\t\n\f\r] );
 | 
						|
 | 
						|
measurement_start =
 | 
						|
	measurement_chars - '#';
 | 
						|
 | 
						|
measurement =
 | 
						|
	(measurement_start measurement_chars*) >begin %name;
 | 
						|
 | 
						|
newline =
 | 
						|
	[\r\n];
 | 
						|
 | 
						|
comment =
 | 
						|
	'#' (any -- newline)* newline;
 | 
						|
 | 
						|
eol =
 | 
						|
	ws* newline? >yield %eof(yield);
 | 
						|
 | 
						|
line =
 | 
						|
	measurement
 | 
						|
	tagset
 | 
						|
	(ws+ fieldset) $err(field_error)
 | 
						|
	(ws+ timestamp)? $err(timestamp_error)
 | 
						|
	eol;
 | 
						|
 | 
						|
# The main machine parses a single line of line protocol.
 | 
						|
main := line $err(parse_error);
 | 
						|
 | 
						|
# The discard_line machine discards the current line.  Useful for recovering
 | 
						|
# on the next line when an error occurs.
 | 
						|
discard_line :=
 | 
						|
	(any - newline)* newline @discard;
 | 
						|
 | 
						|
# The align machine scans forward to the start of the next line.  This machine
 | 
						|
# is used to skip over whitespace and comments, keeping this logic out of the
 | 
						|
# main machine.
 | 
						|
align :=
 | 
						|
	(space* comment)* space* measurement_start @hold_recover %eof(yield);
 | 
						|
 | 
						|
series := measurement tagset $err(parse_error) eol;
 | 
						|
}%%
 | 
						|
 | 
						|
%% write data;
 | 
						|
 | 
						|
type Handler interface {
 | 
						|
	SetMeasurement(name []byte)
 | 
						|
	AddTag(key []byte, value []byte)
 | 
						|
	AddInt(key []byte, value []byte)
 | 
						|
	AddUint(key []byte, value []byte)
 | 
						|
	AddFloat(key []byte, value []byte)
 | 
						|
	AddString(key []byte, value []byte)
 | 
						|
	AddBool(key []byte, value []byte)
 | 
						|
	SetTimestamp(tm []byte)
 | 
						|
}
 | 
						|
 | 
						|
type machine struct {
 | 
						|
	data       []byte
 | 
						|
	cs         int
 | 
						|
	p, pe, eof int
 | 
						|
	pb         int
 | 
						|
	handler    Handler
 | 
						|
	initState  int
 | 
						|
	err        error
 | 
						|
}
 | 
						|
 | 
						|
func NewMachine(handler Handler) *machine {
 | 
						|
	m := &machine{
 | 
						|
		handler: handler,
 | 
						|
		initState: LineProtocol_en_align,
 | 
						|
	}
 | 
						|
 | 
						|
	%% access m.;
 | 
						|
	%% variable p m.p;
 | 
						|
	%% variable pe m.pe;
 | 
						|
	%% variable eof m.eof;
 | 
						|
	%% variable data m.data;
 | 
						|
	%% write init;
 | 
						|
 | 
						|
	return m
 | 
						|
}
 | 
						|
 | 
						|
func NewSeriesMachine(handler Handler) *machine {
 | 
						|
	m := &machine{
 | 
						|
		handler: handler,
 | 
						|
		initState: LineProtocol_en_series,
 | 
						|
	}
 | 
						|
 | 
						|
	%% access m.;
 | 
						|
	%% variable p m.p;
 | 
						|
	%% variable pe m.pe;
 | 
						|
	%% variable eof m.eof;
 | 
						|
	%% variable data m.data;
 | 
						|
	%% write init;
 | 
						|
 | 
						|
	return m
 | 
						|
}
 | 
						|
 | 
						|
func (m *machine) SetData(data []byte) {
 | 
						|
	m.data = data
 | 
						|
	m.p = 0
 | 
						|
	m.pb = 0
 | 
						|
	m.pe = len(data)
 | 
						|
	m.eof = len(data)
 | 
						|
	m.err = nil
 | 
						|
 | 
						|
	%% write init;
 | 
						|
	m.cs = m.initState
 | 
						|
}
 | 
						|
 | 
						|
// ParseLine parses a line of input and returns true if more data can be
 | 
						|
// parsed.
 | 
						|
func (m *machine) ParseLine() bool {
 | 
						|
	if m.data == nil || m.p >= m.pe {
 | 
						|
		m.err = nil
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	m.err = nil
 | 
						|
	var key []byte
 | 
						|
	var yield bool
 | 
						|
 | 
						|
	%% write exec;
 | 
						|
 | 
						|
	// Even if there was an error, return true. On the next call to this
 | 
						|
	// function we will attempt to scan to the next line of input and recover.
 | 
						|
	if m.err != nil {
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	// Don't check the error state in the case that we just yielded, because
 | 
						|
	// the yield indicates we just completed parsing a line.
 | 
						|
	if !yield && m.cs == LineProtocol_error {
 | 
						|
		m.err = ErrParse
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// Err returns the error that occurred on the last call to ParseLine.  If the
 | 
						|
// result is nil, then the line was parsed successfully.
 | 
						|
func (m *machine) Err() error {
 | 
						|
	return m.err
 | 
						|
}
 | 
						|
 | 
						|
// Position returns the current position into the input.
 | 
						|
func (m *machine) Position() int {
 | 
						|
	return m.p
 | 
						|
}
 | 
						|
 | 
						|
func (m *machine) text() []byte {
 | 
						|
	return m.data[m.pb:m.p]
 | 
						|
}
 |