2017-05-28 02:01:43 +00:00
|
|
|
package zclsyntax
|
|
|
|
|
|
|
|
import (
|
2017-05-29 15:55:53 +00:00
|
|
|
"bytes"
|
|
|
|
|
2017-05-28 02:01:43 +00:00
|
|
|
"github.com/zclconf/go-zcl/zcl"
|
|
|
|
)
|
|
|
|
|
2017-05-28 14:11:24 +00:00
|
|
|
// This file is generated from scan_tokens.rl. DO NOT EDIT.
|
2017-05-28 02:01:43 +00:00
|
|
|
%%{
|
2017-05-28 14:11:24 +00:00
|
|
|
# (except you are actually in scan_tokens.rl here, so edit away!)
|
2017-05-28 02:01:43 +00:00
|
|
|
|
|
|
|
machine zcltok;
|
|
|
|
write data;
|
|
|
|
}%%
|
|
|
|
|
2017-05-28 22:44:22 +00:00
|
|
|
func scanTokens(data []byte, filename string, start zcl.Pos, mode scanMode) []Token {
|
2017-05-28 14:11:24 +00:00
|
|
|
f := &tokenAccum{
|
2017-05-28 02:01:43 +00:00
|
|
|
Filename: filename,
|
|
|
|
Bytes: data,
|
2017-05-28 15:38:13 +00:00
|
|
|
Pos: start,
|
2017-05-28 02:01:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
%%{
|
2017-05-28 16:16:53 +00:00
|
|
|
include UnicodeDerived "unicode_derived.rl";
|
2017-05-28 02:01:43 +00:00
|
|
|
|
|
|
|
UTF8Cont = 0x80 .. 0xBF;
|
|
|
|
AnyUTF8 = (
|
|
|
|
0x00..0x7F |
|
|
|
|
0xC0..0xDF . UTF8Cont |
|
|
|
|
0xE0..0xEF . UTF8Cont . UTF8Cont |
|
|
|
|
0xF0..0xF7 . UTF8Cont . UTF8Cont . UTF8Cont
|
|
|
|
);
|
|
|
|
BrokenUTF8 = any - AnyUTF8;
|
|
|
|
|
2017-05-28 15:56:43 +00:00
|
|
|
NumberLit = digit (digit|'.'|('e'|'E') ('+'|'-')? digit)*;
|
2017-05-28 16:16:53 +00:00
|
|
|
Ident = ID_Start ID_Continue*;
|
2017-05-28 15:56:43 +00:00
|
|
|
|
2017-05-28 16:34:20 +00:00
|
|
|
# Symbols that just represent themselves are handled as a single rule.
|
2017-05-28 22:33:01 +00:00
|
|
|
SelfToken = "[" | "]" | "(" | ")" | "." | "*" | "/" | "+" | "-" | "=" | "<" | ">" | "!" | "?" | ":" | "\n" | "&" | "|" | "~" | "^" | ";" | "`";
|
2017-05-28 16:34:20 +00:00
|
|
|
|
2017-05-28 16:40:58 +00:00
|
|
|
NotEqual = "!=";
|
|
|
|
GreaterThanEqual = ">=";
|
|
|
|
LessThanEqual = "<=";
|
|
|
|
LogicalAnd = "&&";
|
|
|
|
LogicalOr = "||";
|
|
|
|
|
2017-05-28 22:33:01 +00:00
|
|
|
Newline = '\r' ? '\n';
|
2017-05-29 16:13:35 +00:00
|
|
|
EndOfLine = Newline;
|
2017-05-28 22:33:01 +00:00
|
|
|
|
|
|
|
BeginStringTmpl = '"';
|
2017-05-29 15:55:53 +00:00
|
|
|
BeginHeredocTmpl = '<<' ('-')? Ident Newline;
|
2017-05-28 22:33:01 +00:00
|
|
|
|
2017-05-29 16:13:35 +00:00
|
|
|
Comment = (
|
|
|
|
("#" any* EndOfLine) |
|
|
|
|
("//" any* EndOfLine) |
|
|
|
|
("/*" any* "*/")
|
|
|
|
);
|
|
|
|
|
2017-05-28 02:01:43 +00:00
|
|
|
# Tabs are not valid, but we accept them in the scanner and mark them
|
|
|
|
# as tokens so that we can produce diagnostics advising the user to
|
|
|
|
# use spaces instead.
|
2017-05-28 15:38:13 +00:00
|
|
|
Tabs = 0x09+;
|
2017-05-28 02:01:43 +00:00
|
|
|
|
2017-05-28 15:38:13 +00:00
|
|
|
Spaces = ' '+;
|
2017-05-28 02:01:43 +00:00
|
|
|
|
2017-05-28 22:33:01 +00:00
|
|
|
action beginStringTemplate {
|
|
|
|
token(TokenOQuote);
|
|
|
|
fcall stringTemplate;
|
|
|
|
}
|
|
|
|
|
|
|
|
action endStringTemplate {
|
|
|
|
token(TokenCQuote);
|
|
|
|
fret;
|
|
|
|
}
|
|
|
|
|
2017-05-29 15:55:53 +00:00
|
|
|
action beginHeredocTemplate {
|
|
|
|
token(TokenOHeredoc);
|
|
|
|
// the token is currently the whole heredoc introducer, like
|
|
|
|
// <<EOT or <<-EOT, followed by a newline. We want to extract
|
|
|
|
// just the "EOT" portion that we'll use as the closing marker.
|
|
|
|
|
|
|
|
marker := data[ts+2:te-1]
|
|
|
|
if marker[0] == '-' {
|
|
|
|
marker = marker[1:]
|
|
|
|
}
|
|
|
|
if marker[len(marker)-1] == '\r' {
|
|
|
|
marker = marker[:len(marker)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
heredocs = append(heredocs, heredocInProgress{
|
|
|
|
Marker: marker,
|
|
|
|
StartOfLine: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
fcall heredocTemplate;
|
|
|
|
}
|
|
|
|
|
|
|
|
action heredocLiteralEOL {
|
|
|
|
// This action is called specificially when a heredoc literal
|
|
|
|
// ends with a newline character.
|
|
|
|
|
|
|
|
// This might actually be our end marker.
|
|
|
|
topdoc := &heredocs[len(heredocs)-1]
|
|
|
|
if topdoc.StartOfLine {
|
|
|
|
maybeMarker := bytes.TrimSpace(data[ts:te])
|
|
|
|
if bytes.Equal(maybeMarker, topdoc.Marker) {
|
|
|
|
token(TokenCHeredoc);
|
|
|
|
heredocs = heredocs[:len(heredocs)-1]
|
|
|
|
fret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
topdoc.StartOfLine = true;
|
|
|
|
token(TokenStringLit);
|
|
|
|
}
|
|
|
|
|
|
|
|
action heredocLiteralMidline {
|
|
|
|
// This action is called when a heredoc literal _doesn't_ end
|
|
|
|
// with a newline character, e.g. because we're about to enter
|
|
|
|
// an interpolation sequence.
|
|
|
|
heredocs[len(heredocs)-1].StartOfLine = false;
|
|
|
|
token(TokenStringLit);
|
|
|
|
}
|
|
|
|
|
2017-05-28 22:33:01 +00:00
|
|
|
action beginTemplateInterp {
|
|
|
|
token(TokenTemplateInterp);
|
|
|
|
braces++;
|
|
|
|
retBraces = append(retBraces, braces);
|
2017-05-29 15:55:53 +00:00
|
|
|
if len(heredocs) > 0 {
|
|
|
|
heredocs[len(heredocs)-1].StartOfLine = false;
|
|
|
|
}
|
2017-05-28 22:33:01 +00:00
|
|
|
fcall main;
|
|
|
|
}
|
|
|
|
|
|
|
|
action beginTemplateControl {
|
|
|
|
token(TokenTemplateControl);
|
|
|
|
braces++;
|
|
|
|
retBraces = append(retBraces, braces);
|
2017-05-29 15:55:53 +00:00
|
|
|
if len(heredocs) > 0 {
|
|
|
|
heredocs[len(heredocs)-1].StartOfLine = false;
|
|
|
|
}
|
2017-05-28 22:33:01 +00:00
|
|
|
fcall main;
|
|
|
|
}
|
|
|
|
|
|
|
|
action openBrace {
|
|
|
|
token(TokenOBrace);
|
|
|
|
braces++;
|
|
|
|
}
|
|
|
|
|
|
|
|
action closeBrace {
|
|
|
|
if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces {
|
|
|
|
token(TokenTemplateSeqEnd);
|
|
|
|
braces--;
|
|
|
|
retBraces = retBraces[0:len(retBraces)-1]
|
|
|
|
fret;
|
|
|
|
} else {
|
|
|
|
token(TokenCBrace);
|
|
|
|
braces--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
action closeTemplateSeqEatWhitespace {
|
|
|
|
token(TokenTemplateSeqEnd);
|
|
|
|
braces--;
|
|
|
|
|
|
|
|
// Only consume from the retBraces stack and return if we are at
|
|
|
|
// a suitable brace nesting level, otherwise things will get
|
|
|
|
// confused. (Not entering this branch indicates a syntax error,
|
|
|
|
// which we will catch in the parser.)
|
|
|
|
if len(retBraces) > 0 && retBraces[len(retBraces)-1] == braces {
|
|
|
|
retBraces = retBraces[0:len(retBraces)-1]
|
|
|
|
fret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TemplateInterp = "${" ("~")?;
|
|
|
|
TemplateControl = "!{" ("~")?;
|
|
|
|
EndStringTmpl = '"';
|
2017-05-28 22:35:55 +00:00
|
|
|
StringLiteralChars = (AnyUTF8 - ("\r"|"\n"));
|
2017-05-28 22:33:01 +00:00
|
|
|
TemplateStringLiteral = (
|
|
|
|
('$' ^'{') |
|
|
|
|
('!' ^'{') |
|
2017-05-28 22:35:55 +00:00
|
|
|
('\\' StringLiteralChars) |
|
|
|
|
(StringLiteralChars - ("$" | "!" | '"'))
|
2017-05-28 22:33:01 +00:00
|
|
|
)+;
|
2017-05-29 15:55:53 +00:00
|
|
|
HeredocStringLiteral = (
|
|
|
|
('$' ^'{') |
|
|
|
|
('!' ^'{') |
|
|
|
|
(StringLiteralChars - ("$" | "!"))
|
|
|
|
)*;
|
2017-05-28 22:33:01 +00:00
|
|
|
|
|
|
|
stringTemplate := |*
|
|
|
|
TemplateInterp => beginTemplateInterp;
|
|
|
|
TemplateControl => beginTemplateControl;
|
|
|
|
EndStringTmpl => endStringTemplate;
|
|
|
|
TemplateStringLiteral => { token(TokenStringLit); };
|
2017-05-28 22:35:55 +00:00
|
|
|
AnyUTF8 => { token(TokenInvalid); };
|
2017-05-28 22:33:01 +00:00
|
|
|
BrokenUTF8 => { token(TokenBadUTF8); };
|
|
|
|
*|;
|
|
|
|
|
2017-05-29 15:55:53 +00:00
|
|
|
heredocTemplate := |*
|
|
|
|
TemplateInterp => beginTemplateInterp;
|
|
|
|
TemplateControl => beginTemplateControl;
|
2017-05-29 16:13:35 +00:00
|
|
|
HeredocStringLiteral EndOfLine => heredocLiteralEOL;
|
2017-05-29 15:55:53 +00:00
|
|
|
HeredocStringLiteral => heredocLiteralMidline;
|
|
|
|
BrokenUTF8 => { token(TokenBadUTF8); };
|
|
|
|
*|;
|
|
|
|
|
2017-05-28 15:38:13 +00:00
|
|
|
main := |*
|
2017-05-28 15:56:43 +00:00
|
|
|
Spaces => {};
|
|
|
|
NumberLit => { token(TokenNumberLit) };
|
2017-05-28 16:16:53 +00:00
|
|
|
Ident => { token(TokenIdent) };
|
2017-05-28 16:40:58 +00:00
|
|
|
|
2017-05-29 16:13:35 +00:00
|
|
|
Comment => { token(TokenComment) };
|
|
|
|
|
2017-05-28 16:40:58 +00:00
|
|
|
NotEqual => { token(TokenNotEqual); };
|
|
|
|
GreaterThanEqual => { token(TokenGreaterThanEq); };
|
|
|
|
LessThanEqual => { token(TokenLessThanEq); };
|
|
|
|
LogicalAnd => { token(TokenAnd); };
|
|
|
|
LogicalOr => { token(TokenOr); };
|
2017-05-28 16:34:20 +00:00
|
|
|
SelfToken => { selfToken() };
|
2017-05-28 16:40:58 +00:00
|
|
|
|
2017-05-28 22:33:01 +00:00
|
|
|
"{" => openBrace;
|
|
|
|
"}" => closeBrace;
|
|
|
|
|
|
|
|
"~}" => closeTemplateSeqEatWhitespace;
|
|
|
|
|
|
|
|
BeginStringTmpl => beginStringTemplate;
|
2017-05-29 15:55:53 +00:00
|
|
|
BeginHeredocTmpl => beginHeredocTemplate;
|
2017-05-28 22:33:01 +00:00
|
|
|
|
2017-05-28 15:56:43 +00:00
|
|
|
Tabs => { token(TokenTabs) };
|
|
|
|
AnyUTF8 => { token(TokenInvalid) };
|
|
|
|
BrokenUTF8 => { token(TokenBadUTF8) };
|
2017-05-28 15:38:13 +00:00
|
|
|
*|;
|
2017-05-28 02:01:43 +00:00
|
|
|
|
|
|
|
}%%
|
|
|
|
|
|
|
|
// Ragel state
|
|
|
|
p := 0 // "Pointer" into data
|
|
|
|
pe := len(data) // End-of-data "pointer"
|
|
|
|
ts := 0
|
|
|
|
te := 0
|
|
|
|
act := 0
|
|
|
|
eof := pe
|
2017-05-28 22:33:01 +00:00
|
|
|
var stack []int
|
|
|
|
var top int
|
|
|
|
|
2017-05-28 22:44:22 +00:00
|
|
|
var cs int // current state
|
|
|
|
switch mode {
|
|
|
|
case scanNormal:
|
|
|
|
cs = zcltok_en_main
|
|
|
|
case scanTemplate:
|
|
|
|
// scanTemplate is a variant of heredoc scanning, so will
|
|
|
|
// be implemented once that is implemented.
|
|
|
|
panic("scanTemplate not yet implemented")
|
|
|
|
default:
|
|
|
|
panic("invalid scanMode")
|
|
|
|
}
|
|
|
|
|
2017-05-28 22:33:01 +00:00
|
|
|
braces := 0
|
|
|
|
var retBraces []int // stack of brace levels that cause us to use fret
|
2017-05-29 15:55:53 +00:00
|
|
|
var heredocs []heredocInProgress // stack of heredocs we're currently processing
|
2017-05-28 22:33:01 +00:00
|
|
|
|
|
|
|
%%{
|
|
|
|
prepush {
|
|
|
|
stack = append(stack, 0);
|
|
|
|
}
|
|
|
|
postpop {
|
|
|
|
stack = stack[:len(stack)-1];
|
|
|
|
}
|
|
|
|
}%%
|
2017-05-28 02:01:43 +00:00
|
|
|
|
|
|
|
// Make Go compiler happy
|
|
|
|
_ = ts
|
|
|
|
_ = te
|
|
|
|
_ = act
|
|
|
|
_ = eof
|
|
|
|
|
2017-05-28 15:38:13 +00:00
|
|
|
token := func (ty TokenType) {
|
|
|
|
f.emitToken(ty, ts, te)
|
|
|
|
}
|
2017-05-28 16:34:20 +00:00
|
|
|
selfToken := func () {
|
|
|
|
b := data[ts:te]
|
|
|
|
if len(b) != 1 {
|
|
|
|
// should never happen
|
|
|
|
panic("selfToken only works for single-character tokens")
|
|
|
|
}
|
|
|
|
f.emitToken(TokenType(b[0]), ts, te)
|
|
|
|
}
|
2017-05-28 15:38:13 +00:00
|
|
|
|
2017-05-28 02:01:43 +00:00
|
|
|
%%{
|
2017-05-28 22:44:22 +00:00
|
|
|
write init nocs;
|
2017-05-28 02:01:43 +00:00
|
|
|
write exec;
|
|
|
|
}%%
|
|
|
|
|
2017-05-28 14:11:24 +00:00
|
|
|
// If we fall out here without being in a final state then we've
|
|
|
|
// encountered something that the scanner can't match, which we'll
|
|
|
|
// deal with as an invalid.
|
|
|
|
if cs < zcltok_first_final {
|
|
|
|
f.emitToken(TokenInvalid, p, len(data))
|
|
|
|
}
|
|
|
|
|
2017-05-28 15:38:13 +00:00
|
|
|
// We always emit a synthetic EOF token at the end, since it gives the
|
|
|
|
// parser position information for an "unexpected EOF" diagnostic.
|
|
|
|
f.emitToken(TokenEOF, len(data), len(data))
|
|
|
|
|
2017-05-28 14:11:24 +00:00
|
|
|
return f.Tokens
|
2017-05-28 02:01:43 +00:00
|
|
|
}
|