59 return "E_TAB_INDENT";
61 return "E_BAD_INDENT_UNIT";
63 return "E_BAD_DEDENT";
65 return "E_UNEXPECTED_INDENT";
67 return "E_UNEXPECTED_CHAR";
69 return "E_BAD_NUMBER";
71 return "E_EXPECTED_COLON";
73 return "E_EXPECTED_LPAREN";
75 return "E_EXPECTED_RPAREN";
77 return "E_EXPECTED_NEWLINE";
79 return "E_EXPECTED_NAME";
81 return "E_EXPECTED_EXPR";
83 return "E_EXPECTED_BLOCK";
85 return "E_UNKNOWN_CALL";
87 return "E_CHAINED_COMPARISON";
89 return "E_CALL_NOT_A_STATEMENT";
91 return "E_ASSIGN_TO_CALL";
93 return "E_PROGRAM_TOO_LARGE";
95 return "E_TOO_MANY_LINES";
99 return "E_TOO_MANY_STATEMENTS";
101 return "E_TOO_MANY_VARIABLES";
103 return "E_EXPR_TOO_DEEP";
105 return "E_DELAY_NOT_POSITIVE";
107 return "E_LOOP_WITHOUT_DELAY";
109 return "E_VAR_USED_BEFORE_ASSIGN";
111 return "E_DIVIDE_BY_ZERO_LITERAL";
113 return "E_UNKNOWN_ACTUATOR";
115 return "E_UNKNOWN_SENSOR";
117 return "E_UNKNOWN_STATE";
119 return "E_TRANSITION_NOT_ALLOWED";
121 return "E_VAR_SHADOWS_SLUG";
143 const std::vector<Token>&
toks;
158 return toks[std::min(
p,
toks.size() - 1)];
161 return cur().kind == k;
165 if (
p <
toks.size() - 1)
174 out.diagnostics.push_back({code, t.line, t.col, std::move(msg)});
182 fail(code,
cur(), std::string(
"expected ") + what +
", found " +
tok_name(
cur().kind));
187 out.program.exprs.push_back(e);
188 return static_cast<int32_t
>(
out.program.exprs.size() - 1);
191 out.program.stmts.push_back(s);
192 return static_cast<int32_t
>(
out.program.stmts.size() - 1);
195 auto& v =
out.program.variables;
196 for (
size_t k = 0; k < v.size(); k++)
198 return static_cast<int32_t
>(k);
200 return static_cast<int32_t
>(v.size() - 1);
203 out.program.slugs.push_back(
SlugRef{t.text, ns, t.line, t.col, t.len});
204 return static_cast<int32_t
>(
out.program.slugs.size() - 1);
211 std::vector<int32_t> items;
220 auto& bi =
out.program.block_items;
221 const int32_t
begin =
static_cast<int32_t
>(bi.size());
222 bi.insert(bi.end(), items.begin(), items.end());
223 return {
begin,
static_cast<int32_t
>(bi.size())};
238 out.program.max_depth = std::max(
out.program.max_depth,
depth);
241 "nested " + std::to_string(
depth) +
" deep; the limit is " +
254 if (range.first == range.second) {
256 "an indented block must contain at least one statement");
299 const int32_t nested =
parseIf();
302 auto& bi =
out.program.block_items;
303 const int32_t
begin =
static_cast<int32_t
>(bi.size());
304 bi.push_back(nested);
306 s.
else_end =
static_cast<int32_t
>(bi.size());
334 const int32_t idx =
addStmt(s);
342 "this loop's body never calls delay() — add one (e.g. delay(0.05)) so the loop "
343 "waits rather than spinning, and so its poll rate is visible where it is read");
350 for (int32_t k =
begin; k <
end; k++) {
351 const Stmt& s =
out.program.stmts[
out.program.block_items[k]];
362 if (then_ok && else_ok)
372 std::string(
"expected a statement, found ") +
tok_name(
cur().kind));
380 "'" + name.text +
"' is a built-in and cannot be assigned to");
390 std::find(
out.program.variables.begin(),
out.program.variables.end(), name.text) ==
391 out.program.variables.end()) {
393 "more than " + std::to_string(
kMaxVariables) +
" variables");
408 "expected '=' or '(' after '" + name.text +
"', found " +
tok_name(
cur().kind));
423 }
else if (name.text ==
kDelay) {
427 "'" + name.text +
"' produces a value and cannot stand alone as a statement");
431 "unknown command '" + name.text +
432 "' — the commands are open_valve, close_valve, delay and transition_to");
439 std::string(
"expected a bare ") +
ns_name(ns) +
" name here (e.g. " +
440 (ns ==
Ns::State ?
"PRESS_STANDBY" :
"FUEL_VENT") +
"), found " +
474 const Expr& ex =
out.program.exprs[e];
477 "delay(" + std::to_string(ex.
number) +
478 ") — a delay must be a positive number of "
508 "expression nested deeper than " + std::to_string(
kMaxExprDepth));
574 switch (
cur().kind) {
603 switch (
cur().kind) {
611 "two comparisons in a row — write it as a and b, e.g. "
612 "(x > 100) and (x < 200)");
688 Expr n =
out.program.exprs[
static_cast<size_t>(operand)];
689 n.number = -
n.number;
692 if (
static_cast<size_t>(operand) + 1 ==
out.program.exprs.size())
693 out.program.exprs.pop_back();
711 "expression nested deeper than " + std::to_string(
kMaxExprDepth));
739 std::string(
"expected a bare sensor name (e.g. GN2_HIGH), found ") +
757 "')' — elapsed() takes no arguments"))
768 "' does not produce a value and cannot appear in an expression");
773 "unknown function '" + t.text +
774 "' — the value-producing built-ins are pressure() and elapsed()");
779 "'" + t.text +
"' is read before it is ever assigned");
790 std::string(
"expected a value, found ") +
tok_name(
cur().kind));
802 std::string(
"unexpected ") +
tok_name(
cur().kind) +
803 " after the end of the script");
806 out.program.top_begin = range.first;
807 out.program.top_end = range.second;
822 return std::move(ps.out);
835 const Expr& e =
p.exprs[
static_cast<size_t>(idx)];
839 std::string s = std::to_string(e.
number);
840 while (s.size() > 1 && s.back() ==
'0')
842 if (!s.empty() && s.back() ==
'.')
848 out +=
p.variables[
static_cast<size_t>(e.
var)];
851 out +=
"(pressure " +
p.slugs[
static_cast<size_t>(e.
slug)].slug +
")";
857 out += std::string(
"(") +
op_name(e.
op) +
" ";
862 out += std::string(
"(") +
op_name(e.
op) +
" ";
874 const Stmt& s =
p.stmts[
static_cast<size_t>(idx)];
877 out +=
"(set " +
p.variables[
static_cast<size_t>(s.
var)] +
" ";
882 out +=
"(open " +
p.slugs[
static_cast<size_t>(s.
slug)].slug +
")";
885 out +=
"(close " +
p.slugs[
static_cast<size_t>(s.
slug)].slug +
")";
893 out +=
"(goto " +
p.slugs[
static_cast<size_t>(s.
slug)].slug +
")";
918 for (int32_t k =
begin; k <
end; k++) {
920 printStmt(
p,
p.block_items[
static_cast<size_t>(k)], out);
*brief append another JSON pointer at the end of this JSON pointer *sa ptr reference_tokens begin()
*brief create JSON pointer *sa reference_tokens end()
T & at(const key_type &key)
*brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer *sa const json_pointer & rhs
void printExpr(const ScriptProgram &p, int32_t idx, std::string &out)
bool isBuiltin(const std::string &s)
constexpr const char * kOpenValve
The builtin call names.
void printBlock(const ScriptProgram &p, int32_t begin, int32_t end, std::string &out)
constexpr const char * kDelay
void printStmt(const ScriptProgram &p, int32_t idx, std::string &out)
constexpr const char * kElapsed
constexpr const char * kCloseValve
constexpr const char * kPressure
constexpr const char * kTransitionTo
const char * diag_name(Diag d)
constexpr uint32_t kMaxExprDepth
Ns
Which table a slug is resolved against.
std::string print_canonical(const ScriptProgram &program)
A stable s-expression rendering of the parsed program.
const char * op_name(Op op)
const char * tok_name(Tok t)
constexpr uint32_t kMaxVariables
constexpr uint32_t kMaxStatements
LexResult lex(const std::string &source)
Tokenize, emitting INDENT/DEDENT for block structure.
ParseResult parse(const std::string &source)
Lex and parse, applying every check that does not need config.
constexpr uint32_t kMaxNestingDepth
const char * ns_name(Ns ns)
Motorized ball valve position: raw ADC counts -> % open.
*brief user defined string literal for JSON values *sa std::size_t n
std::vector< Diagnostic > diagnostics
std::vector< Token > tokens
A parsed script, as a flat arena.
One slug reference, with its source span so a rename can rewrite exactly this token.
int32_t body_begin
If/While body, and If's else arm: [begin, end) into ScriptProgram::block_items.
int32_t expr
Assign value, Delay seconds, If/While condition.
int32_t slug
OpenValve/CloseValve/TransitionTo target.
int32_t var
Assign: index into ScriptProgram::variables.
bool blockHasDelay(int32_t begin, int32_t end) const
std::pair< int32_t, int32_t > parseIndentedBlock()
: NEWLINE INDENT stmt+ DEDENT
int32_t parseDelay(const Token &name)
void fail(Diag code, const Token &t, std::string msg)
int32_t parseComparison()
const Token & cur() const
const std::vector< Token > & toks
bool expect(Tok k, Diag code, const char *what)
int32_t internVar(const std::string &name)
std::set< std::string > assigned
Variables assigned somewhere at or before the current point, for use-before-assign.
Parser(const std::vector< Token > &t)
std::pair< int32_t, int32_t > parseBlockBody()
Parse statements until DEDENT/EOF, appending indices to block_items; returns [begin,...
int32_t addSlug(const Token &t, Ns ns)