12 return "end of script";
74 return (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z') || c ==
'_';
77 return identStart(c) || (c >=
'0' && c <=
'9');
99 const std::string&
src;
113 void fail(
Diag code, uint32_t l, uint32_t c, std::string msg) {
117 out.diagnostics.push_back({code, l, c, std::move(msg)});
120 void push(
Tok k, uint32_t l, uint32_t c, uint32_t len, std::string text = {},
127 t.text = std::move(text);
129 out.tokens.push_back(std::move(t));
132 char peek(
size_t off = 0)
const {
133 return (
i + off <
src.size()) ?
src[
i + off] :
'\0';
137 bool at_line_start =
true;
150 at_line_start =
true;
163 while (
i <
src.size() &&
src[
i] !=
'\n')
189 while (j <
src.size()) {
194 }
else if (c ==
'\t') {
196 "tab in indentation — use spaces (a tab is invisible in the editor, and "
197 "mixed indentation silently changes which branch a valve command is in)");
199 }
else if (c ==
'\r') {
206 if (j >=
src.size() ||
src[j] ==
'\n' ||
src[j] ==
'#') {
207 while (j <
src.size() &&
src[j] !=
'\n')
209 if (j <
src.size()) {
215 at_line_start =
true;
221 at_line_start =
false;
223 const uint32_t cur =
indents.back();
226 const uint32_t u = width - cur;
229 "indent of " + std::to_string(u) +
" spaces is wider than the " +
235 if (width != cur +
unit) {
237 "indented " + std::to_string(width - cur) +
" spaces; this script indents " +
238 std::to_string(
unit) +
" at a time, so " + std::to_string(cur +
unit) +
244 }
else if (width < cur) {
251 "unindent to column " + std::to_string(width + 1) +
252 " does not line up with any enclosing block");
260 const uint32_t l =
line, c =
col;
270 if (
ch ==
'\'' ||
ch ==
'"') {
272 std::string(
"quote character — names are bare slugs here, e.g. ") +
273 "open_valve(FUEL_VENT), not open_valve(\"Fuel Vent\")");
285 push(k, l, c,
static_cast<uint32_t
>(text.size()), text);
289 if (
ch >=
'0' &&
ch <=
'9') {
291 while (
i <
src.size() &&
src[
i] >=
'0' &&
src[
i] <=
'9') {
295 if (
i <
src.size() &&
src[
i] ==
'.') {
298 while (
i <
src.size() &&
src[
i] >=
'0' &&
src[
i] <=
'9') {
309 "malformed number — digits and at most one '.', with no exponent and no "
315 std::atof(text.c_str()));
319 auto two = [&](
char a,
char b) {
328 }
else if (two(
'>',
'=')) {
331 }
else if (two(
'=',
'=')) {
334 }
else if (two(
'!',
'=')) {
373 std::string what =
"'";
394 "script is " + std::to_string(source.size()) +
395 " bytes; the limit is " +
400 for (
char c : source)
405 "script is " + std::to_string(lines) +
406 " lines; the limit is " + std::to_string(
kMaxLines)});
static void start(gnss_frame_parser_t *p, uint8_t byte)
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
*brief create JSON pointer *sa reference_tokens const string_t & a
Tok keywordOf(const std::string &s)
constexpr uint32_t kMaxLines
const char * tok_name(Tok t)
LexResult lex(const std::string &source)
Tokenize, emitting INDENT/DEDENT for block structure.
constexpr uint32_t kMaxIndentUnit
Widest indent unit accepted.
constexpr size_t kMaxSourceBytes
Motorized ball valve position: raw ADC counts -> % open.
void push(Tok k, uint32_t l, uint32_t c, uint32_t len, std::string text={}, double num=0.0)
Lexer(const std::string &s)
char peek(size_t off=0) const
uint32_t unit
Set by the first indented line; every later indent must be a multiple of it.
void fail(Diag code, uint32_t l, uint32_t c, std::string msg)
std::vector< uint32_t > indents
Column stack for open blocks; [0] is always 0.
bool handleLineStart(bool &at_line_start)
Measure one line's indentation and emit INDENT/DEDENT.
static std::string ch(int ain, const std::string &kind, const std::string &extra="", const std::string &board="lj")