relative values, types check
This commit is contained in:
parent
17426705ae
commit
b38128ef39
@ -171,6 +171,13 @@ char BasicParser::peek() {
|
|||||||
return source[pos];
|
return source[pos];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char BasicParser::peekNoJump() {
|
||||||
|
if (pos >= source.length()) {
|
||||||
|
throw error("unexpected end");
|
||||||
|
}
|
||||||
|
return source[pos];
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view BasicParser::readUntil(char c) {
|
std::string_view BasicParser::readUntil(char c) {
|
||||||
int start = pos;
|
int start = pos;
|
||||||
while (hasNext() && source[pos] != c) {
|
while (hasNext() && source[pos] != c) {
|
||||||
|
|||||||
@ -99,6 +99,7 @@ public:
|
|||||||
std::string parseName();
|
std::string parseName();
|
||||||
bool hasNext();
|
bool hasNext();
|
||||||
char peek();
|
char peek();
|
||||||
|
char peekNoJump();
|
||||||
char nextChar();
|
char nextChar();
|
||||||
|
|
||||||
BasicParser(std::string_view file, std::string_view source);
|
BasicParser(std::string_view file, std::string_view source);
|
||||||
|
|||||||
@ -242,6 +242,20 @@ size_t Map::size() const {
|
|||||||
return values.size();
|
return values.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::string TYPE_NAMES[] {
|
||||||
|
"none",
|
||||||
|
"map",
|
||||||
|
"list",
|
||||||
|
"string",
|
||||||
|
"number",
|
||||||
|
"bool",
|
||||||
|
"integer",
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string& dynamic::type_name(const Value& value) {
|
||||||
|
return TYPE_NAMES[value.index()];
|
||||||
|
}
|
||||||
|
|
||||||
List_sptr dynamic::create_list(std::initializer_list<Value> values) {
|
List_sptr dynamic::create_list(std::initializer_list<Value> values) {
|
||||||
return std::make_shared<List>(values);
|
return std::make_shared<List>(values);
|
||||||
}
|
}
|
||||||
@ -249,3 +263,19 @@ List_sptr dynamic::create_list(std::initializer_list<Value> values) {
|
|||||||
Map_sptr dynamic::create_map(std::initializer_list<std::pair<const std::string, Value>> entries) {
|
Map_sptr dynamic::create_map(std::initializer_list<std::pair<const std::string, Value>> entries) {
|
||||||
return std::make_shared<Map>(entries);
|
return std::make_shared<Map>(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
number_t dynamic::get_number(const Value& value) {
|
||||||
|
if (auto num = std::get_if<number_t>(&value)) {
|
||||||
|
return *num;
|
||||||
|
} else if (auto num = std::get_if<integer_t>(&value)) {
|
||||||
|
return *num;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("cannot cast "+type_name(value)+" to number");
|
||||||
|
}
|
||||||
|
|
||||||
|
integer_t dynamic::get_integer(const Value& value) {
|
||||||
|
if (auto num = std::get_if<integer_t>(&value)) {
|
||||||
|
return *num;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("cannot cast "+type_name(value)+" to integer");
|
||||||
|
}
|
||||||
|
|||||||
@ -35,8 +35,16 @@ namespace dynamic {
|
|||||||
integer_t
|
integer_t
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
const std::string& type_name(const Value& value);
|
||||||
List_sptr create_list(std::initializer_list<Value> values={});
|
List_sptr create_list(std::initializer_list<Value> values={});
|
||||||
Map_sptr create_map(std::initializer_list<std::pair<const std::string, Value>> entries={});
|
Map_sptr create_map(std::initializer_list<std::pair<const std::string, Value>> entries={});
|
||||||
|
number_t get_number(const Value& value);
|
||||||
|
integer_t get_integer(const Value& value);
|
||||||
|
|
||||||
|
inline bool is_numeric(const Value& value) {
|
||||||
|
return std::holds_alternative<number_t>(value) ||
|
||||||
|
std::holds_alternative<integer_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
class List {
|
class List {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -157,29 +157,126 @@ public:
|
|||||||
return Command(name, std::move(args), std::move(kwargs), executor);
|
return Command(name, std::move(args), std::move(kwargs), executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline parsing_error argumentError(
|
||||||
|
const std::string& argname,
|
||||||
|
const std::string& message
|
||||||
|
) {
|
||||||
|
return error("argument "+util::quote(argname)+": "+message);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline parsing_error typeError(
|
||||||
|
const std::string& argname,
|
||||||
|
const std::string& expected,
|
||||||
|
const dynamic::Value& value
|
||||||
|
) {
|
||||||
|
return argumentError(
|
||||||
|
argname, expected+" expected, got "+dynamic::type_name(value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool typeCheck(Argument* arg, const dynamic::Value& value, const std::string& tname) {
|
||||||
|
if (!std::holds_alternative<T>(value)) {
|
||||||
|
if (arg->optional) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
throw typeError(arg->name, tname, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool typeCheck(Argument* arg, const dynamic::Value& value) {
|
bool typeCheck(Argument* arg, const dynamic::Value& value) {
|
||||||
switch (arg->type) {
|
switch (arg->type) {
|
||||||
case ArgType::enumvalue: {
|
case ArgType::enumvalue: {
|
||||||
auto& enumname = arg->enumname;
|
|
||||||
if (auto* string = std::get_if<std::string>(&value)) {
|
if (auto* string = std::get_if<std::string>(&value)) {
|
||||||
|
auto& enumname = arg->enumname;
|
||||||
if (enumname.find("|"+*string+"|") == std::string::npos) {
|
if (enumname.find("|"+*string+"|") == std::string::npos) {
|
||||||
throw error("invalid enumeration value");
|
throw error("argument "+util::quote(arg->name)+
|
||||||
|
": invalid enumeration value");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (arg->optional) {
|
if (arg->optional) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
throw error("enumeration value expected");
|
throw typeError(arg->name, "enumeration value", value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ArgType::number: {
|
case ArgType::number:
|
||||||
// FIXME
|
if (!dynamic::is_numeric(value)) {
|
||||||
|
if (arg->optional) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
throw typeError(arg->name, "number", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ArgType::integer:
|
||||||
|
return typeCheck<integer_t>(arg, value, "integer");
|
||||||
|
case ArgType::string:
|
||||||
|
return typeCheck<std::string>(arg, value, "string");
|
||||||
|
case ArgType::selector:
|
||||||
|
return typeCheck<integer_t>(arg, value, "id");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dynamic::Value fetchOrigin(Argument* arg) {
|
||||||
|
if (dynamic::is_numeric(arg->origin)) {
|
||||||
|
return arg->origin;
|
||||||
|
}
|
||||||
|
return dynamic::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic::Value applyRelative(
|
||||||
|
Argument* arg,
|
||||||
|
dynamic::Value value,
|
||||||
|
dynamic::Value origin
|
||||||
|
) {
|
||||||
|
if (origin.index() == 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (arg->type == ArgType::number) {
|
||||||
|
return dynamic::get_number(origin) + dynamic::get_number(value);
|
||||||
|
} else {
|
||||||
|
return dynamic::get_integer(origin) + dynamic::get_integer(value);
|
||||||
|
}
|
||||||
|
} catch (std::runtime_error& err) {
|
||||||
|
throw argumentError(arg->name, err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic::Value parseRelativeValue(Argument* arg) {
|
||||||
|
if (arg->type != ArgType::number && arg->type != ArgType::integer) {
|
||||||
|
throw error("'~' operator is only allowed for numeric arguments");
|
||||||
|
}
|
||||||
|
nextChar();
|
||||||
|
auto origin = fetchOrigin(arg);
|
||||||
|
if (peekNoJump() == ' ' || !hasNext()) {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
auto value = parseValue();
|
||||||
|
if (origin.index() == 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return applyRelative(arg, value, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline dynamic::Value performKeywordArg(
|
||||||
|
Command* command, const std::string& key
|
||||||
|
) {
|
||||||
|
if (auto arg = command->getArgument(key)) {
|
||||||
|
nextChar();
|
||||||
|
auto value = peek() == '~' ? parseRelativeValue(arg) : parseValue();
|
||||||
|
typeCheck(arg, value);
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
throw error("unknown keyword "+util::quote(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Prompt parsePrompt(CommandsRepository* repo) {
|
Prompt parsePrompt(CommandsRepository* repo) {
|
||||||
std::string name = parseIdentifier();
|
std::string name = parseIdentifier();
|
||||||
auto command = repo->get(name);
|
auto command = repo->get(name);
|
||||||
@ -192,12 +289,23 @@ public:
|
|||||||
int arg_index = 0;
|
int arg_index = 0;
|
||||||
|
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
auto value = parseValue();
|
bool relative = false;
|
||||||
if (hasNext() && peek() == '=') {
|
if (peek() == '~') {
|
||||||
auto key = std::get<std::string>(value);
|
relative = true;
|
||||||
nextChar();
|
nextChar();
|
||||||
kwargs->put(key, parseValue());
|
}
|
||||||
} else {
|
dynamic::Value value = dynamic::NONE;
|
||||||
|
if (hasNext() && peekNoJump() != ' ') {
|
||||||
|
value = parseValue();
|
||||||
|
|
||||||
|
// keyword argument
|
||||||
|
if (!relative && hasNext() && peek() == '=') {
|
||||||
|
auto key = std::get<std::string>(value);
|
||||||
|
kwargs->put(key, performKeywordArg(command, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// positional argument
|
||||||
Argument* arg;
|
Argument* arg;
|
||||||
do {
|
do {
|
||||||
arg = command->getArgument(arg_index++);
|
arg = command->getArgument(arg_index++);
|
||||||
@ -205,8 +313,12 @@ public:
|
|||||||
throw error("extra positional argument");
|
throw error("extra positional argument");
|
||||||
}
|
}
|
||||||
} while (!typeCheck(arg, value));
|
} while (!typeCheck(arg, value));
|
||||||
args->put(value);
|
|
||||||
|
if (relative) {
|
||||||
|
value = applyRelative(arg, value, fetchOrigin(arg));
|
||||||
}
|
}
|
||||||
|
std::cout << "argument value: " << value << std::endl;
|
||||||
|
args->put(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (auto arg = command->getArgument(arg_index++)) {
|
while (auto arg = command->getArgument(arg_index++)) {
|
||||||
|
|||||||
@ -59,6 +59,14 @@ namespace cmd {
|
|||||||
return &args[index];
|
return &args[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Argument* getArgument(const std::string& keyword) {
|
||||||
|
auto found = kwargs.find(keyword);
|
||||||
|
if (found == kwargs.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &found->second;
|
||||||
|
}
|
||||||
|
|
||||||
dynamic::Value execute(const Prompt& prompt) {
|
dynamic::Value execute(const Prompt& prompt) {
|
||||||
return executor(prompt.args, prompt.kwargs);
|
return executor(prompt.args, prompt.kwargs);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user