Fixed problems with property assignment.

This commit is contained in:
yhirose 2015-07-26 01:44:02 -04:00
parent ea9dd315ae
commit d79d4f2437
3 changed files with 211 additions and 118 deletions

View File

@ -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)*)? ']' _

View File

@ -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));
}; };

View File

@ -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 () {