mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Fixed problems with property assignment.
This commit is contained in:
parent
ea9dd315ae
commit
d79d4f2437
@ -1,12 +1,9 @@
|
|||||||
|
|
||||||
PROGRAM <- _ STATEMENTS
|
PROGRAM <- _ STATEMENTS
|
||||||
|
|
||||||
STATEMENTS <- (EXPRESSION (';' _)?)*
|
STATEMENTS <- (EXPRESSION (';' _)?)*
|
||||||
|
|
||||||
EXPRESSION <- ASSIGNMENT / LOGICAL_OR
|
EXPRESSION <- ASSIGNMENT / LOGICAL_OR
|
||||||
ASSIGNMENT <- MUTABLE IDENTIFIER '=' _ EXPRESSION
|
|
||||||
WHILE <- 'while' _ EXPRESSION BLOCK
|
ASSIGNMENT <- MUTABLE PRIMARY (ARGUMENTS / INDEX / DOT)* '=' _ EXPRESSION
|
||||||
IF <- 'if' _ EXPRESSION BLOCK ('else' _ 'if' _ EXPRESSION BLOCK)* ('else' _ BLOCK)?
|
|
||||||
|
|
||||||
LOGICAL_OR <- LOGICAL_AND ('||' _ LOGICAL_AND)*
|
LOGICAL_OR <- LOGICAL_AND ('||' _ LOGICAL_AND)*
|
||||||
LOGICAL_AND <- CONDITION ('&&' _ CONDITION)*
|
LOGICAL_AND <- CONDITION ('&&' _ CONDITION)*
|
||||||
@ -22,6 +19,9 @@
|
|||||||
INDEX <- '[' _ EXPRESSION ']' _
|
INDEX <- '[' _ EXPRESSION ']' _
|
||||||
DOT <- '.' _ IDENTIFIER
|
DOT <- '.' _ IDENTIFIER
|
||||||
|
|
||||||
|
WHILE <- 'while' _ EXPRESSION BLOCK
|
||||||
|
IF <- 'if' _ EXPRESSION BLOCK ('else' _ 'if' _ EXPRESSION BLOCK)* ('else' _ BLOCK)?
|
||||||
|
|
||||||
PRIMARY <- WHILE / IF / FUNCTION / OBJECT / ARRAY / UNDEFINED / BOOLEAN / NUMBER / IDENTIFIER / STRING / INTERPOLATED_STRING / '(' _ EXPRESSION ')' _
|
PRIMARY <- WHILE / IF / FUNCTION / OBJECT / ARRAY / UNDEFINED / BOOLEAN / NUMBER / IDENTIFIER / STRING / INTERPOLATED_STRING / '(' _ EXPRESSION ')' _
|
||||||
|
|
||||||
FUNCTION <- 'fn' _ PARAMETERS BLOCK
|
FUNCTION <- 'fn' _ PARAMETERS BLOCK
|
||||||
@ -40,7 +40,7 @@
|
|||||||
IDENTIFIER <- < [a-zA-Z_][a-zA-Z0-9_]* > _
|
IDENTIFIER <- < [a-zA-Z_][a-zA-Z0-9_]* > _
|
||||||
|
|
||||||
OBJECT <- '{' _ (OBJECT_PROPERTY (',' _ OBJECT_PROPERTY)*)? '}' _
|
OBJECT <- '{' _ (OBJECT_PROPERTY (',' _ OBJECT_PROPERTY)*)? '}' _
|
||||||
OBJECT_PROPERTY <- IDENTIFIER ':' _ EXPRESSION
|
OBJECT_PROPERTY <- MUTABLE IDENTIFIER ':' _ EXPRESSION
|
||||||
|
|
||||||
ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*)? ']' _
|
ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*)? ']' _
|
||||||
|
|
||||||
|
@ -8,13 +8,10 @@ namespace culebra {
|
|||||||
const auto grammar_ = R"(
|
const auto grammar_ = R"(
|
||||||
|
|
||||||
PROGRAM <- _ STATEMENTS
|
PROGRAM <- _ STATEMENTS
|
||||||
|
|
||||||
STATEMENTS <- (EXPRESSION (';' _)?)*
|
STATEMENTS <- (EXPRESSION (';' _)?)*
|
||||||
|
|
||||||
EXPRESSION <- ASSIGNMENT / LOGICAL_OR
|
EXPRESSION <- ASSIGNMENT / LOGICAL_OR
|
||||||
ASSIGNMENT <- MUTABLE IDENTIFIER '=' _ EXPRESSION
|
|
||||||
WHILE <- 'while' _ EXPRESSION BLOCK
|
ASSIGNMENT <- MUTABLE PRIMARY (ARGUMENTS / INDEX / DOT)* '=' _ EXPRESSION
|
||||||
IF <- 'if' _ EXPRESSION BLOCK ('else' _ 'if' _ EXPRESSION BLOCK)* ('else' _ BLOCK)?
|
|
||||||
|
|
||||||
LOGICAL_OR <- LOGICAL_AND ('||' _ LOGICAL_AND)*
|
LOGICAL_OR <- LOGICAL_AND ('||' _ LOGICAL_AND)*
|
||||||
LOGICAL_AND <- CONDITION ('&&' _ CONDITION)*
|
LOGICAL_AND <- CONDITION ('&&' _ CONDITION)*
|
||||||
@ -30,6 +27,9 @@ const auto grammar_ = R"(
|
|||||||
INDEX <- '[' _ EXPRESSION ']' _
|
INDEX <- '[' _ EXPRESSION ']' _
|
||||||
DOT <- '.' _ IDENTIFIER
|
DOT <- '.' _ IDENTIFIER
|
||||||
|
|
||||||
|
WHILE <- 'while' _ EXPRESSION BLOCK
|
||||||
|
IF <- 'if' _ EXPRESSION BLOCK ('else' _ 'if' _ EXPRESSION BLOCK)* ('else' _ BLOCK)?
|
||||||
|
|
||||||
PRIMARY <- WHILE / IF / FUNCTION / OBJECT / ARRAY / UNDEFINED / BOOLEAN / NUMBER / IDENTIFIER / STRING / INTERPOLATED_STRING / '(' _ EXPRESSION ')' _
|
PRIMARY <- WHILE / IF / FUNCTION / OBJECT / ARRAY / UNDEFINED / BOOLEAN / NUMBER / IDENTIFIER / STRING / INTERPOLATED_STRING / '(' _ EXPRESSION ')' _
|
||||||
|
|
||||||
FUNCTION <- 'fn' _ PARAMETERS BLOCK
|
FUNCTION <- 'fn' _ PARAMETERS BLOCK
|
||||||
@ -48,7 +48,7 @@ const auto grammar_ = R"(
|
|||||||
IDENTIFIER <- < [a-zA-Z_][a-zA-Z0-9_]* > _
|
IDENTIFIER <- < [a-zA-Z_][a-zA-Z0-9_]* > _
|
||||||
|
|
||||||
OBJECT <- '{' _ (OBJECT_PROPERTY (',' _ OBJECT_PROPERTY)*)? '}' _
|
OBJECT <- '{' _ (OBJECT_PROPERTY (',' _ OBJECT_PROPERTY)*)? '}' _
|
||||||
OBJECT_PROPERTY <- IDENTIFIER ':' _ EXPRESSION
|
OBJECT_PROPERTY <- MUTABLE IDENTIFIER ':' _ EXPRESSION
|
||||||
|
|
||||||
ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*)? ']' _
|
ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*)? ']' _
|
||||||
|
|
||||||
@ -93,6 +93,7 @@ inline peglib::peg& get_parser()
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Value;
|
struct Value;
|
||||||
|
struct Symbol;
|
||||||
struct Environment;
|
struct Environment;
|
||||||
|
|
||||||
struct FunctionValue {
|
struct FunctionValue {
|
||||||
@ -112,12 +113,14 @@ struct FunctionValue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ObjectValue {
|
struct ObjectValue {
|
||||||
bool has_property(const std::string& name) const;
|
bool has(const std::string& name) const;
|
||||||
Value get_property(const std::string& name) const;
|
const Value& get(const std::string& name) const;
|
||||||
|
void assign(const std::string& name, const Value& val);
|
||||||
|
void initialize(const std::string& name, const Value& val, bool mut);
|
||||||
virtual std::map<std::string, Value>& builtins();
|
virtual std::map<std::string, Value>& builtins();
|
||||||
|
|
||||||
std::shared_ptr<std::map<std::string, Value>> properties =
|
std::shared_ptr<std::map<std::string, Symbol>> properties =
|
||||||
std::make_shared<std::map<std::string, Value>>();
|
std::make_shared<std::map<std::string, Symbol>>();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArrayValue : public ObjectValue {
|
struct ArrayValue : public ObjectValue {
|
||||||
@ -196,43 +199,30 @@ struct Value
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectValue to_object() const {
|
const ObjectValue& to_object() const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Object: return v.get<ObjectValue>();
|
case Object: return v.get<ObjectValue>();
|
||||||
|
case Array: return v.get<ArrayValue>();
|
||||||
default: throw std::runtime_error("type error.");
|
default: throw std::runtime_error("type error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayValue to_array() const {
|
ObjectValue& to_object() {
|
||||||
|
switch (type) {
|
||||||
|
case Object: return v.get<ObjectValue>();
|
||||||
|
case Array: return v.get<ArrayValue>();
|
||||||
|
default: throw std::runtime_error("type error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ArrayValue& to_array() const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Array: return v.get<ArrayValue>();
|
case Array: return v.get<ArrayValue>();
|
||||||
default: throw std::runtime_error("type error.");
|
default: throw std::runtime_error("type error.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value get_property(const std::string& name) const {
|
std::string str_object() const;
|
||||||
switch (type) {
|
|
||||||
case Object: return to_object().get_property(name);
|
|
||||||
case Array: return to_array().get_property(name);
|
|
||||||
default: throw std::runtime_error("type error.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string str_object() const {
|
|
||||||
const auto& properties = *to_object().properties;
|
|
||||||
std::string s = "{";
|
|
||||||
auto it = properties.begin();
|
|
||||||
for (; it != properties.end(); ++it) {
|
|
||||||
if (it != properties.begin()) {
|
|
||||||
s += ", ";
|
|
||||||
}
|
|
||||||
s += '"' + it->first + '"';
|
|
||||||
s += ": ";
|
|
||||||
s += it->second.str();
|
|
||||||
}
|
|
||||||
s += "}";
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string str_array() const {
|
std::string str_array() const {
|
||||||
const auto& values = *to_array().values;
|
const auto& values = *to_array().values;
|
||||||
@ -251,8 +241,8 @@ struct Value
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case Undefined: return "undefined";
|
case Undefined: return "undefined";
|
||||||
case Bool: return to_bool() ? "true" : "false";
|
case Bool: return to_bool() ? "true" : "false";
|
||||||
case Long: return std::to_string(to_long()); break;
|
case Long: return std::to_string(to_long());
|
||||||
case String: return to_string();
|
case String: return "'" + to_string() + "'";
|
||||||
case Object: return str_object();
|
case Object: return str_object();
|
||||||
case Array: return str_array();
|
case Array: return str_array();
|
||||||
case Function: return "[function]";
|
case Function: return "[function]";
|
||||||
@ -334,6 +324,11 @@ struct Value
|
|||||||
peglib::any v;
|
peglib::any v;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Symbol {
|
||||||
|
Value val;
|
||||||
|
bool mut;
|
||||||
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& os, const Value& val)
|
inline std::ostream& operator<<(std::ostream& os, const Value& val)
|
||||||
{
|
{
|
||||||
return val.out(os);
|
return val.out(os);
|
||||||
@ -370,6 +365,7 @@ struct Environment
|
|||||||
}
|
}
|
||||||
|
|
||||||
void assign(const std::string& s, const Value& val) {
|
void assign(const std::string& s, const Value& val) {
|
||||||
|
assert(has(s));
|
||||||
if (dic_.find(s) != dic_.end()) {
|
if (dic_.find(s) != dic_.end()) {
|
||||||
auto& sym = dic_[s];
|
auto& sym = dic_[s];
|
||||||
if (!sym.mut) {
|
if (!sym.mut) {
|
||||||
@ -380,30 +376,22 @@ struct Environment
|
|||||||
sym.val = val;
|
sym.val = val;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (outer && outer->has(s)) {
|
outer->assign(s, val);
|
||||||
outer->assign(s, val);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
// NOTREACHED
|
|
||||||
throw std::logic_error("invalid internal condition.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize(const std::string& s, const Value& val, bool mut) {
|
void initialize(const std::string& s, const Value& val, bool mut) {
|
||||||
//std::cout << "Env::initialize: " << s << std::endl;
|
//std::cout << "Env::initialize: " << s << std::endl;
|
||||||
dic_[s] = Symbol{val, mut};
|
dic_[s] = Symbol{ val, mut };
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Environment> outer;
|
std::shared_ptr<Environment> outer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Symbol {
|
|
||||||
Value val;
|
|
||||||
bool mut;
|
|
||||||
};
|
|
||||||
std::map<std::string, Symbol> dic_;
|
std::map<std::string, Symbol> dic_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool ObjectValue::has_property(const std::string& name) const {
|
inline bool ObjectValue::has(const std::string& name) const {
|
||||||
if (properties->find(name) == properties->end()) {
|
if (properties->find(name) == properties->end()) {
|
||||||
const auto& props = const_cast<ObjectValue*>(this)->builtins();
|
const auto& props = const_cast<ObjectValue*>(this)->builtins();
|
||||||
return props.find(name) != props.end();
|
return props.find(name) != props.end();
|
||||||
@ -411,12 +399,27 @@ inline bool ObjectValue::has_property(const std::string& name) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Value ObjectValue::get_property(const std::string& name) const {
|
inline const Value& ObjectValue::get(const std::string& name) const {
|
||||||
if (properties->find(name) == properties->end()) {
|
if (properties->find(name) == properties->end()) {
|
||||||
const auto& props = const_cast<ObjectValue*>(this)->builtins();
|
const auto& props = const_cast<ObjectValue*>(this)->builtins();
|
||||||
return props.at(name);
|
return props.at(name);
|
||||||
}
|
}
|
||||||
return properties->at(name);
|
return properties->at(name).val;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ObjectValue::assign(const std::string& name, const Value& val) {
|
||||||
|
assert(has(name));
|
||||||
|
auto& sym = properties->at(name);
|
||||||
|
if (!sym.mut) {
|
||||||
|
std::string msg = "immutable property '" + name + "'...";
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
sym.val = val;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ObjectValue::initialize(const std::string& name, const Value& val, bool mut) {
|
||||||
|
(*properties)[name] = Symbol{ val, mut };
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::map<std::string, Value>& ObjectValue::builtins() {
|
inline std::map<std::string, Value>& ObjectValue::builtins() {
|
||||||
@ -465,6 +468,27 @@ inline std::map<std::string, Value>& ArrayValue::builtins() {
|
|||||||
return props_;
|
return props_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string Value::str_object() const {
|
||||||
|
const auto& properties = *to_object().properties;
|
||||||
|
std::string s = "{";
|
||||||
|
auto it = properties.begin();
|
||||||
|
for (; it != properties.end(); ++it) {
|
||||||
|
if (it != properties.begin()) {
|
||||||
|
s += ", ";
|
||||||
|
}
|
||||||
|
const auto& name = it->first;
|
||||||
|
const auto& sym = it->second;
|
||||||
|
if (sym.mut) {
|
||||||
|
s += "mut ";
|
||||||
|
}
|
||||||
|
s += name;
|
||||||
|
s += ": ";
|
||||||
|
s += sym.val.str();
|
||||||
|
}
|
||||||
|
s += "}";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
inline void setup_built_in_functions(Environment& env) {
|
inline void setup_built_in_functions(Environment& env) {
|
||||||
env.initialize(
|
env.initialize(
|
||||||
"puts",
|
"puts",
|
||||||
@ -595,63 +619,73 @@ private:
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Value eval_function_call(const peglib::Ast& ast, std::shared_ptr<Environment> env, const Value& val) {
|
||||||
|
const auto& f = val.to_function();
|
||||||
|
const auto& params = *f.params;
|
||||||
|
const auto& args = ast.nodes;
|
||||||
|
|
||||||
|
if (params.size() <= args.size()) {
|
||||||
|
auto callEnv = std::make_shared<Environment>();
|
||||||
|
callEnv->initialize("self", val, false);
|
||||||
|
for (auto iprm = 0u; iprm < params.size(); iprm++) {
|
||||||
|
auto param = params[iprm];
|
||||||
|
auto arg = args[iprm];
|
||||||
|
auto val = eval(*arg, env);
|
||||||
|
callEnv->initialize(param.name, val, param.mut);
|
||||||
|
}
|
||||||
|
callEnv->initialize("__LINE__", Value((long)ast.line), false);
|
||||||
|
callEnv->initialize("__COLUMN__", Value((long)ast.column), false);
|
||||||
|
return f.eval(callEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string msg = "arguments error...";
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value eval_array_reference(const peglib::Ast& ast, std::shared_ptr<Environment> env, const Value& val) {
|
||||||
|
const auto& arr = val.to_array();
|
||||||
|
auto idx = eval(ast, env).to_long();
|
||||||
|
if (0 <= idx && idx < static_cast<long>(arr.values->size())) {
|
||||||
|
return arr.values->at(idx);
|
||||||
|
} else {
|
||||||
|
// TODO: error? or 'undefined'?
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value eval_property(const peglib::Ast& ast, std::shared_ptr<Environment> env, const Value& val) {
|
||||||
|
const auto& obj = val.to_object();
|
||||||
|
auto name = ast.token;
|
||||||
|
if (!obj.has(name)) {
|
||||||
|
return Value();
|
||||||
|
}
|
||||||
|
const auto& prop = obj.get(name);
|
||||||
|
if (prop.type == Value::Function) {
|
||||||
|
const auto& pf = prop.to_function();
|
||||||
|
return Value(FunctionValue(
|
||||||
|
*pf.params,
|
||||||
|
[=](std::shared_ptr<Environment> callEnv) {
|
||||||
|
callEnv->initialize("this", val, false);
|
||||||
|
return pf.eval(callEnv);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
static Value eval_call(const peglib::Ast& ast, std::shared_ptr<Environment> env) {
|
static Value eval_call(const peglib::Ast& ast, std::shared_ptr<Environment> env) {
|
||||||
using peglib::operator"" _;
|
using peglib::operator"" _;
|
||||||
|
|
||||||
Value val = eval(*ast.nodes[0], env);
|
Value val = eval(*ast.nodes[0], env);
|
||||||
|
|
||||||
for (auto i = 1u; i < ast.nodes.size(); i++) {
|
for (auto i = 1u; i < ast.nodes.size(); i++) {
|
||||||
const auto& n = *ast.nodes[i];
|
const auto& postfix = *ast.nodes[i];
|
||||||
if (n.original_tag == "ARGUMENTS"_) {
|
|
||||||
// Function call
|
|
||||||
const auto& f = val.to_function();
|
|
||||||
const auto& params = *f.params;
|
|
||||||
const auto& args = n.nodes;
|
|
||||||
if (params.size() <= args.size()) {
|
|
||||||
auto callEnv = std::make_shared<Environment>();
|
|
||||||
|
|
||||||
callEnv->initialize("self", val, false);
|
switch (postfix.original_tag) {
|
||||||
|
case "ARGUMENTS"_: val = eval_function_call(postfix, env, val); break;
|
||||||
for (auto iprm = 0u; iprm < params.size(); iprm++) {
|
case "INDEX"_: val = eval_array_reference(postfix, env, val); break;
|
||||||
auto param = params[iprm];
|
case "DOT"_: val = eval_property(postfix, env, val); break;
|
||||||
auto arg = args[iprm];
|
default: throw std::logic_error("invalid internal condition.");
|
||||||
auto val = eval(*arg, env);
|
|
||||||
callEnv->initialize(param.name, val, param.mut);
|
|
||||||
}
|
|
||||||
|
|
||||||
callEnv->initialize("__LINE__", Value((long)ast.line), false);
|
|
||||||
callEnv->initialize("__COLUMN__", Value((long)ast.column), false);
|
|
||||||
|
|
||||||
val = f.eval(callEnv);
|
|
||||||
} else {
|
|
||||||
std::string msg = "arguments error...";
|
|
||||||
throw std::runtime_error(msg);
|
|
||||||
}
|
|
||||||
} else if (n.original_tag == "INDEX"_) {
|
|
||||||
// Array reference
|
|
||||||
const auto& arr = val.to_array();
|
|
||||||
auto idx = eval(n, env).to_long();
|
|
||||||
if (0 <= idx && idx < static_cast<long>(arr.values->size())) {
|
|
||||||
val = arr.values->at(idx);
|
|
||||||
}
|
|
||||||
} else if (n.original_tag == "DOT"_) {
|
|
||||||
// Property
|
|
||||||
auto name = n.token;
|
|
||||||
auto prop = val.get_property(name);
|
|
||||||
if (prop.type == Value::Function) {
|
|
||||||
const auto& pf = prop.to_function();
|
|
||||||
val = Value(FunctionValue(
|
|
||||||
*pf.params,
|
|
||||||
[=](std::shared_ptr<Environment> callEnv) {
|
|
||||||
callEnv->initialize("this", val, false);
|
|
||||||
return pf.eval(callEnv);
|
|
||||||
}
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
val = prop;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw std::logic_error("invalid internal condition.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,15 +767,63 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Value eval_assignment(const peglib::Ast& ast, std::shared_ptr<Environment> env) {
|
static Value eval_assignment(const peglib::Ast& ast, std::shared_ptr<Environment> env) {
|
||||||
const auto& var = ast.nodes[1]->token;
|
auto end = ast.nodes.size() - 1;
|
||||||
auto val = eval(*ast.nodes[2], env);
|
|
||||||
if (env->has(var)) {
|
auto mut = ast.nodes[0]->token == "mut";
|
||||||
env->assign(var, val);
|
auto val = eval(*ast.nodes[end], env);
|
||||||
|
|
||||||
|
if (ast.nodes.size() == 3) {
|
||||||
|
const auto& var = ast.nodes[1]->token;
|
||||||
|
if (env->has(var)) {
|
||||||
|
env->assign(var, val);
|
||||||
|
} else {
|
||||||
|
env->initialize(var, val, mut);
|
||||||
|
}
|
||||||
|
return std::move(val);
|
||||||
} else {
|
} else {
|
||||||
const auto& mut = ast.nodes[0]->token;
|
using peglib::operator"" _;
|
||||||
env->initialize(var, val, mut == "mut");
|
|
||||||
|
Value lval = eval(*ast.nodes[1], env);
|
||||||
|
|
||||||
|
auto end = ast.nodes.size() - 2;
|
||||||
|
for (auto i = 2; i < end; i++) {
|
||||||
|
const auto& postfix = *ast.nodes[i];
|
||||||
|
|
||||||
|
switch (postfix.original_tag) {
|
||||||
|
case "ARGUMENTS"_: lval = eval_function_call(postfix, env, lval); break;
|
||||||
|
case "INDEX"_: lval = eval_array_reference(postfix, env, lval); break;
|
||||||
|
case "DOT"_: lval = eval_property(postfix, env, lval); break;
|
||||||
|
default: throw std::logic_error("invalid internal condition.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& postfix = *ast.nodes[end];
|
||||||
|
|
||||||
|
switch (postfix.original_tag) {
|
||||||
|
case "INDEX"_: {
|
||||||
|
const auto& arr = lval.to_array();
|
||||||
|
auto idx = eval(postfix, env).to_long();
|
||||||
|
if (0 <= idx && idx < static_cast<long>(arr.values->size())) {
|
||||||
|
arr.values->at(idx) = val;
|
||||||
|
} else {
|
||||||
|
// TODO: error? or set 'undefined'?
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
case "DOT"_: {
|
||||||
|
auto& obj = lval.to_object();
|
||||||
|
auto name = postfix.token;
|
||||||
|
if (obj.has(name)) {
|
||||||
|
obj.assign(name, val);
|
||||||
|
} else {
|
||||||
|
obj.initialize(name, val, mut);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::logic_error("invalid internal condition.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return std::move(val);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static Value eval_identifier(const peglib::Ast& ast, std::shared_ptr<Environment> env) {
|
static Value eval_identifier(const peglib::Ast& ast, std::shared_ptr<Environment> env) {
|
||||||
@ -752,9 +834,10 @@ private:
|
|||||||
ObjectValue obj;
|
ObjectValue obj;
|
||||||
for (auto i = 0u; i < ast.nodes.size(); i++) {
|
for (auto i = 0u; i < ast.nodes.size(); i++) {
|
||||||
const auto& prop = *ast.nodes[i];
|
const auto& prop = *ast.nodes[i];
|
||||||
const auto& name = prop.nodes[0]->token;
|
auto mut = prop.nodes[0]->token == "mut";
|
||||||
auto val = eval(*prop.nodes[1], env);
|
const auto& name = prop.nodes[1]->token;
|
||||||
obj.properties->emplace(name, val);
|
auto val = eval(*prop.nodes[2], env);
|
||||||
|
obj.properties->emplace(name, Symbol{ val, mut });
|
||||||
}
|
}
|
||||||
return Value(std::move(obj));
|
return Value(std::move(obj));
|
||||||
}
|
}
|
||||||
@ -785,7 +868,11 @@ private:
|
|||||||
std::string s;
|
std::string s;
|
||||||
for (auto node: ast.nodes) {
|
for (auto node: ast.nodes) {
|
||||||
const auto& val = eval(*node, env);
|
const auto& val = eval(*node, env);
|
||||||
s += val.str();
|
if (val.type == Value::String) {
|
||||||
|
s += val.to_string();
|
||||||
|
} else {
|
||||||
|
s += val.str();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Value(std::move(s));
|
return Value(std::move(s));
|
||||||
};
|
};
|
||||||
|
@ -79,6 +79,12 @@ test_object = fn () {
|
|||||||
assert(o.size() == 4)
|
assert(o.size() == 4)
|
||||||
assert(o.f1(10) == 133)
|
assert(o.f1(10) == 133)
|
||||||
assert(o.f2(10) == 11)
|
assert(o.f2(10) == 11)
|
||||||
|
|
||||||
|
a = {}
|
||||||
|
a.b = 1
|
||||||
|
assert(a.a == undefined)
|
||||||
|
assert(a.b == 1)
|
||||||
|
assert(a.size() == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
test_object_factory = fn () {
|
test_object_factory = fn () {
|
||||||
|
Loading…
Reference in New Issue
Block a user