diff --git a/grammar/culebra.peg b/grammar/culebra.peg index 981c41c..7d929f5 100644 --- a/grammar/culebra.peg +++ b/grammar/culebra.peg @@ -17,9 +17,10 @@ UNARY_NOT <- UNARY_NOT_OPERATOR? MULTIPLICATIVE MULTIPLICATIVE <- CALL (MULTIPLICATIVE_OPERATOR CALL)* - CALL <- PRIMARY (ARGUMENTS / INDEX)* + CALL <- PRIMARY (ARGUMENTS / INDEX / METHOD)* ARGUMENTS <- '(' _ (EXPRESSION (',' _ EXPRESSION)*)? ')' _ INDEX <- '[' _ EXPRESSION ']' _ + METHOD <- '.' _ IDENTIFIER ARGUMENTS PRIMARY <- WHILE / IF / FUNCTION / IDENTIFIER / ARRAY / NUMBER / BOOLEAN / STRING / INTERPOLATED_STRING / '(' _ EXPRESSION ')' _ @@ -38,7 +39,7 @@ IDENTIFIER <- < [a-zA-Z_][a-zA-Z0-9_]* > _ - ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*) ']' _ + ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*)? ']' _ NUMBER <- < [0-9]+ > _ BOOLEAN <- < ('true' / 'false') > _ STRING <- ['] < (!['] .)* > ['] _ diff --git a/language/interpreter.cc b/language/interpreter.cc index 51bb1da..4ab27c7 100644 --- a/language/interpreter.cc +++ b/language/interpreter.cc @@ -81,7 +81,7 @@ private: } static Value eval_function(const Ast& ast, shared_ptr env) { - vector params; + std::vector params; for (auto node: ast.nodes[0]->nodes) { auto mut = node->nodes[0]->token == "mut"; const auto& name = node->nodes[1]->token; @@ -90,13 +90,15 @@ private: auto body = ast.nodes[1]; - return Value(Value::FunctionValue { + auto f = Value::FunctionValue( params, [=](shared_ptr callEnv) { callEnv->set_outer(env); return eval(*body, callEnv); } - }); + ); + + return Value(std::move(f)); }; static Value eval_call(const Ast& ast, shared_ptr env) { @@ -107,14 +109,15 @@ private: if (n.tag == AstTag::Arguments) { // Function call const auto& f = val.to_function(); + const auto& params = f.data->params; const auto& args = n.nodes; - if (f.params.size() <= args.size()) { + if (params.size() <= args.size()) { auto callEnv = make_shared(); callEnv->initialize("self", val, false); - for (auto iprm = 0u; iprm < f.params.size(); iprm++) { - auto param = f.params[iprm]; + 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); @@ -123,17 +126,41 @@ private: callEnv->initialize("__LINE__", Value((long)ast.line), false); callEnv->initialize("__COLUMN__", Value((long)ast.column), false); - val = f.eval(callEnv); + val = f.data->eval(callEnv); } else { string msg = "arguments error..."; throw runtime_error(msg); } - } else { // n.tag == AstTag::Index + } else if (n.tag == AstTag::Index) { // Array reference - const auto& a = val.to_array(); - const auto& idx = eval(*n.nodes[0], env).to_long(); - if (0 <= idx && idx < static_cast(a.values.size())) { - val = a.values[idx]; + const auto& arr = val.to_array(); + auto idx = eval(*n.nodes[0], env).to_long(); + if (0 <= idx && idx < static_cast(arr.data->values.size())) { + val = arr.data->values.at(idx); + } + } else { // n.tag == AstTag::Property + // Property + auto name = n.nodes[0]->token; + auto prop = val.get_property(name); + + if (prop.get_type() == Value::Function) { + const auto& pf = prop.to_function(); + + auto f = Value::FunctionValue( + pf.data->params, + [=](shared_ptr callEnv) { + auto thisEnv = make_shared(); + thisEnv->set_outer(env); + thisEnv->initialize("this", val, false); + + callEnv->set_outer(thisEnv); + return pf.data->eval(callEnv); + } + ); + + val = Value(std::move(f)); + } else { + val = prop; } } } @@ -251,17 +278,15 @@ private: }; static Value eval_array(const Ast& ast, shared_ptr env) { - vector values; + Value::ArrayValue arr; for (auto i = 0u; i < ast.nodes.size(); i++) { auto expr = ast.nodes[i]; auto val = eval(*expr, env); - values.push_back(val); + arr.data->values.push_back(val); } - return Value(Value::ArrayValue { - values - }); + return Value(std::move(arr)); } static Value eval_number(const Ast& ast, shared_ptr env) { @@ -282,6 +307,32 @@ private: }; }; +std::map Value::ArrayValue::prototypes = { + { + "size", + Value(FunctionValue( + {}, + [](shared_ptr callEnv) { + const auto& val = callEnv->get("this"); + long n = val.to_array().data->values.size(); + return Value(n); + } + )) + }, + { + "push", + Value(FunctionValue { + { {"arg", false} }, + [](shared_ptr callEnv) { + const auto& val = callEnv->get("this"); + const auto& arg = callEnv->get("arg"); + val.to_array().data->values.push_back(arg); + return Value(); + } + }) + }, +}; + bool run( const string& path, shared_ptr env, diff --git a/language/interpreter.hpp b/language/interpreter.hpp index b7dc550..fdd63a2 100644 --- a/language/interpreter.hpp +++ b/language/interpreter.hpp @@ -6,19 +6,57 @@ struct Environment; struct Value { - enum Type { Undefined, Bool, Long, String, Array, Function }; - - struct ArrayValue { - std::vector values; - }; + enum Type { Undefined, Bool, Long, String, Object, Array, Function }; struct FunctionValue { struct Parameter { std::string name; bool mut; }; - std::vector params; - std::function env)> eval; + + struct Data { + std::vector params; + std::function env)> eval; + }; + + FunctionValue( + const std::vector& params, + const std::function env)>& eval) { + + data = std::make_shared(); + data->params = params; + data->eval = eval; + } + + std::shared_ptr data; + }; + + struct ObjectValue { + Value get_property(const std::string& name) const { + return data->props.at(name); + } + + struct Data { + std::map props; + }; + std::shared_ptr data = std::make_shared(); + }; + + struct ArrayValue { + Value get_property(const std::string& name) const { + if (data->props.find(name) == data->props.end()) { + return prototypes.at(name); + } + return data->props.at(name); + } + + struct Data { + std::map props; + std::vector values; + }; + std::shared_ptr data = std::make_shared(); + + static std::map prototypes; }; Value() : type(Undefined) { @@ -52,56 +90,84 @@ struct Value explicit Value(bool b) : type(Bool), v(b) {} explicit Value(long l) : type(Long), v(l) {} explicit Value(std::string&& s) : type(String), v(s) {} + explicit Value(ObjectValue&& o) : type(Object), v(0) {} explicit Value(ArrayValue&& a) : type(Array), v(a) {} explicit Value(FunctionValue&& f) : type(Function), v(f) {} - long size() const { - switch (type) { - case String: return to_string().size(); - case Array: return to_array().values.size(); - } - throw std::runtime_error("type error."); + Type get_type() const { + return type; } bool to_bool() const { switch (type) { case Bool: return v.get(); case Long: return v.get() != 0; + default: throw std::runtime_error("type error."); } - throw std::runtime_error("type error."); } long to_long() const { switch (type) { case Bool: return v.get(); case Long: return v.get(); + default: throw std::runtime_error("type error."); } - throw std::runtime_error("type error."); } std::string to_string() const { switch (type) { case String: return v.get(); + default: throw std::runtime_error("type error."); + } + } + + ObjectValue to_object() const { + switch (type) { + case Object: return v.get(); + default: throw std::runtime_error("type error."); } - throw std::runtime_error("type error."); } ArrayValue to_array() const { switch (type) { case Array: return v.get(); + default: throw std::runtime_error("type error."); } - throw std::runtime_error("type error."); } FunctionValue to_function() const { switch (type) { case Function: return v.get(); + default: throw std::runtime_error("type error."); } - throw std::runtime_error("type error."); + } + + Value get_property(const std::string& name) 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& props = to_object().data->props; + std::string s = "{"; + auto it = props.begin(); + for (; it != props.end(); ++it) { + if (it != props.begin()) { + s += ", "; + } + s += '"' + it->first + '"'; + s += ": "; + s += it->second.str(); + } + s += "}"; + return s; } std::string str_array() const { - const auto& values = to_array().values; + const auto& values = to_array().data->values; std::string s = "["; for (auto i = 0u; i < values.size(); i++) { if (i != 0) { @@ -119,8 +185,9 @@ struct Value case Bool: return to_bool() ? "true" : "false"; case Long: return std::to_string(to_long()); break; case String: return to_string(); - case Function: return "[function]"; + case Object: return str_object(); case Array: return str_array(); + case Function: return "[function]"; default: throw std::logic_error("invalid internal condition."); } // NOTREACHED @@ -206,7 +273,7 @@ struct Value private: friend std::ostream& operator<<(std::ostream&, const Value&); - int type; + Type type; peglib::any v; }; @@ -267,20 +334,19 @@ struct Environment } void setup_built_in_functions() { - initialize( - "puts", - Value(Value::FunctionValue { + { + auto f = Value::FunctionValue( { {"arg", true} }, [](std::shared_ptr env) { std::cout << env->get("arg").str() << std::endl; return Value(); } - }), - false); + ); + initialize("puts", Value(std::move(f)), false); + } - initialize( - "assert", - Value(Value::FunctionValue { + { + auto f = Value::FunctionValue( { {"arg", true} }, [](std::shared_ptr env) { auto cond = env->get("arg").to_bool(); @@ -292,19 +358,9 @@ struct Environment } return Value(); } - }), - false); - - initialize( - "size", - Value(Value::FunctionValue { - { {"arg", true} }, - [](std::shared_ptr env) { - auto size = env->get("arg").size(); - return Value(size); - } - }), - false); + ); + initialize("assert", Value(std::move(f)), false); + } } private: diff --git a/language/parser.cc b/language/parser.cc index 347401a..d55821d 100644 --- a/language/parser.cc +++ b/language/parser.cc @@ -23,9 +23,10 @@ static auto g_grammar = R"( UNARY_NOT <- UNARY_NOT_OPERATOR? MULTIPLICATIVE MULTIPLICATIVE <- CALL (MULTIPLICATIVE_OPERATOR CALL)* - CALL <- PRIMARY (ARGUMENTS / INDEX)* + CALL <- PRIMARY (ARGUMENTS / INDEX / PROPERTY)* ARGUMENTS <- '(' _ (EXPRESSION (',' _ EXPRESSION)*)? ')' _ INDEX <- '[' _ EXPRESSION ']' _ + PROPERTY <- '.' _ IDENTIFIER PRIMARY <- WHILE / IF / FUNCTION / IDENTIFIER / ARRAY / NUMBER / BOOLEAN / STRING / INTERPOLATED_STRING / '(' _ EXPRESSION ')' _ @@ -44,7 +45,7 @@ static auto g_grammar = R"( IDENTIFIER <- < [a-zA-Z_][a-zA-Z0-9_]* > _ - ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*) ']' _ + ARRAY <- '[' _ (EXPRESSION (',' _ EXPRESSION)*)? ']' _ NUMBER <- < [0-9]+ > _ BOOLEAN <- < ('true' / 'false') > _ STRING <- ['] < (!['] .)* > ['] _ @@ -91,6 +92,7 @@ peg& get_parser() { "CALL", Call, true }, { "ARGUMENTS", Arguments, false }, { "INDEX", Index, false }, + { "PROPERTY", Property, false }, { "LOGICAL_OR", LogicalOr, true }, { "LOGICAL_AND", LogicalAnd, true }, { "CONDITION", Condition, true }, diff --git a/language/parser.hpp b/language/parser.hpp index 8f7580a..7fd41bf 100644 --- a/language/parser.hpp +++ b/language/parser.hpp @@ -4,7 +4,7 @@ enum AstTag { Default = peglib::AstDefaultTag, Statements, While, If, Call, Assignment, - Arguments, Index, + Arguments, Index, Property, LogicalOr, LogicalAnd, Condition, UnaryPlus, UnaryMinus, UnaryNot, BinExpresion, Identifier, InterpolatedString, Number, Boolean, Function, Array diff --git a/language/samples/test.cul b/language/samples/test.cul index 9599746..287ae9d 100644 --- a/language/samples/test.cul +++ b/language/samples/test.cul @@ -32,6 +32,17 @@ test_sum = fn () { assert(ret == 55) } +test_array = fn () { + a = [1,2,3] + assert(a.size() == 3) + + a.push(4) + assert(a.size() == 4) + + b = [] + assert(b.size() == 0) +} + test_fib = fn () { fib = fn (x) { if x < 2 { @@ -48,5 +59,6 @@ test_fib = fn () { test_call() test_closure() +test_array() test_sum() test_fib()