From d31c960d89e1fb06329a44065807817ba760c40b Mon Sep 17 00:00:00 2001 From: Marcel Greter Date: Thu, 11 Dec 2014 04:11:18 +0100 Subject: [PATCH] Implement @error function and Sass_Warning for C-API --- ast.hpp | 12 ++++++++++++ ast_factory.hpp | 1 + ast_fwd_decl.hpp | 1 + constants.cpp | 1 + constants.hpp | 1 + error_handling.cpp | 6 +++--- error_handling.hpp | 4 ++-- eval.cpp | 39 ++++++++++++++++++++++++++++++++++++++- eval.hpp | 1 + expand.cpp | 7 +++++++ expand.hpp | 1 + functions.cpp | 7 +++++-- inspect.cpp | 8 ++++++++ inspect.hpp | 1 + operation.hpp | 2 ++ output_compressed.hpp | 1 + output_nested.hpp | 1 + parser.cpp | 18 ++++++++++++++++-- parser.hpp | 1 + prelexer.cpp | 4 ++++ prelexer.hpp | 1 + sass2scss.cpp | 7 +++++-- sass_context.cpp | 2 +- sass_interface.cpp | 4 ++-- sass_values.cpp | 30 +++++++++++++++++++++++++++--- sass_values.h | 9 ++++++++- 26 files changed, 151 insertions(+), 19 deletions(-) diff --git a/ast.hpp b/ast.hpp index aab7bb11d..b714ca215 100644 --- a/ast.hpp +++ b/ast.hpp @@ -442,6 +442,18 @@ namespace Sass { ATTACH_OPERATIONS(); }; + /////////////////////////////// + // The Sass `@error` directive. + /////////////////////////////// + class Error : public Statement { + ADD_PROPERTY(Expression*, message); + public: + Error(string path, Position position, Expression* msg) + : Statement(path, position), message_(msg) + { } + ATTACH_OPERATIONS(); + }; + /////////////////////////////////////////// // CSS comments. These may be interpolated. /////////////////////////////////////////// diff --git a/ast_factory.hpp b/ast_factory.hpp index 283f16915..43b164e27 100644 --- a/ast_factory.hpp +++ b/ast_factory.hpp @@ -24,6 +24,7 @@ namespace Sass { Import* new_CSS_Import(string p, size_t l, Function_Call* loc); Import* new_SASS_Import(string p, size_t l, String* loc); Warning* new_Warning(string p, size_t l, Expression* msg); + Error* new_Error(string p, size_t l, Expression* msg); Comment* new_Comment(string p, size_t l, String* txt); If* new_If(string p, size_t l, Expression* pred, Block* con, Block* alt = 0); For* new_For(string p, size_t l, string var, Expression* lo, Expression* hi, Block* b, bool inc); diff --git a/ast_fwd_decl.hpp b/ast_fwd_decl.hpp index 2b94cf577..06de33d89 100644 --- a/ast_fwd_decl.hpp +++ b/ast_fwd_decl.hpp @@ -17,6 +17,7 @@ namespace Sass { class Import; class Import_Stub; class Warning; + class Error; class Comment; class If; class For; diff --git a/constants.cpp b/constants.cpp index 6b0add996..a2b63cb8c 100644 --- a/constants.cpp +++ b/constants.cpp @@ -26,6 +26,7 @@ namespace Sass { extern const char in_kwd[] = "in"; extern const char while_kwd[] = "@while"; extern const char warn_kwd[] = "@warn"; + extern const char error_kwd[] = "@error"; extern const char default_kwd[] = "default"; extern const char global_kwd[] = "global"; extern const char null_kwd[] = "null"; diff --git a/constants.hpp b/constants.hpp index 41b880ab0..781969eb5 100644 --- a/constants.hpp +++ b/constants.hpp @@ -26,6 +26,7 @@ namespace Sass { extern const char in_kwd[]; extern const char while_kwd[]; extern const char warn_kwd[]; + extern const char error_kwd[]; extern const char default_kwd[]; extern const char global_kwd[]; extern const char null_kwd[]; diff --git a/error_handling.cpp b/error_handling.cpp index 8f09cfc04..8378384b8 100644 --- a/error_handling.cpp +++ b/error_handling.cpp @@ -7,12 +7,12 @@ namespace Sass { - Error::Error(Type type, string path, Position position, string message) + Sass_Error::Sass_Error(Type type, string path, Position position, string message) : type(type), path(path), position(position), message(message) { } void error(string msg, string path, Position position) - { throw Error(Error::syntax, path, position, msg); } + { throw Sass_Error(Sass_Error::syntax, path, position, msg); } void error(string msg, string path, Position position, Backtrace* bt) { @@ -22,7 +22,7 @@ namespace Sass { Backtrace top(bt, path, position, ""); msg += top.to_string(); - throw Error(Error::syntax, path, position, msg); + throw Sass_Error(Sass_Error::syntax, path, position, msg); } } diff --git a/error_handling.hpp b/error_handling.hpp index 03fcc13e7..06f8061ca 100644 --- a/error_handling.hpp +++ b/error_handling.hpp @@ -10,7 +10,7 @@ namespace Sass { struct Backtrace; - struct Error { + struct Sass_Error { enum Type { read, write, syntax, evaluation }; Type type; @@ -18,7 +18,7 @@ namespace Sass { Position position; string message; - Error(Type type, string path, Position position, string message); + Sass_Error(Type type, string path, Position position, string message); }; diff --git a/eval.cpp b/eval.cpp index 03128a748..b788052ff 100644 --- a/eval.cpp +++ b/eval.cpp @@ -218,6 +218,38 @@ namespace Sass { return 0; } + Expression* Eval::operator()(Error* e) + { + Expression* message = e->message()->perform(this); + To_String to_string; + + // try to use generic function + if (env->has("@error[f]")) { + + Definition* def = static_cast((*env)["@error[f]"]); + // Block* body = def->block(); + // Native_Function func = def->native_function(); + Sass_C_Function c_func = def->c_function(); + + To_C to_c; + union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); + sass_list_set_value(c_args, 0, message->perform(&to_c)); + Sass_Value* c_val = c_func(c_args, def->cookie()); + sass_delete_value(c_args); + sass_delete_value(c_val); + return 0; + + } + + string prefix("Error: "); + string result(unquote(message->perform(&to_string))); + cerr << prefix << result; + Backtrace top(backtrace, e->path(), e->position(), ""); + cerr << top.to_string(true); + cerr << endl << endl; + return 0; + } + Expression* Eval::operator()(List* l) { if (l->is_expanded()) return l; @@ -444,6 +476,8 @@ namespace Sass { Sass_Value* c_val = c_func(c_args, def->cookie()); if (sass_value_get_tag(c_val) == SASS_ERROR) { error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->path(), c->position(), backtrace); + } else if (sass_value_get_tag(c_val) == SASS_WARNING) { + error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->path(), c->position(), backtrace); } result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->position()); @@ -1029,7 +1063,10 @@ namespace Sass { e = new (ctx.mem) Null(path, position); } break; case SASS_ERROR: { - error("error in C function: " + string(sass_error_get_message(v)), path, position, backtrace); + error("Error in C function: " + string(sass_error_get_message(v)), path, position, backtrace); + } break; + case SASS_WARNING: { + error("Warning in C function: " + string(sass_warning_get_message(v)), path, position, backtrace); } break; } return e; diff --git a/eval.hpp b/eval.hpp index 5eff892db..9f1e7d8b8 100644 --- a/eval.hpp +++ b/eval.hpp @@ -46,6 +46,7 @@ namespace Sass { Expression* operator()(While*); Expression* operator()(Return*); Expression* operator()(Warning*); + Expression* operator()(Error*); Expression* operator()(List*); Expression* operator()(Map*); diff --git a/expand.cpp b/expand.cpp index 8f108880b..c2a003e5c 100644 --- a/expand.cpp +++ b/expand.cpp @@ -174,6 +174,13 @@ namespace Sass { return 0; } + Statement* Expand::operator()(Error* e) + { + // eval handles this too, because errors may occur in functions + e->perform(eval->with(env, backtrace)); + return 0; + } + Statement* Expand::operator()(Comment* c) { // TODO: eval the text, once we're parsing/storing it as a String_Schema diff --git a/expand.hpp b/expand.hpp index f611b8286..054114909 100644 --- a/expand.hpp +++ b/expand.hpp @@ -55,6 +55,7 @@ namespace Sass { Statement* operator()(Import*); Statement* operator()(Import_Stub*); Statement* operator()(Warning*); + Statement* operator()(Error*); Statement* operator()(Comment*); Statement* operator()(If*); Statement* operator()(For*); diff --git a/functions.cpp b/functions.cpp index 1e1f4cbca..0932d7e2e 100644 --- a/functions.cpp +++ b/functions.cpp @@ -47,8 +47,11 @@ namespace Sass { Definition* make_c_function(Signature sig, Sass_C_Function f, void* cookie, Context& ctx) { Parser sig_parser = Parser::from_c_str(sig, ctx, "[c function]"); - // allow to overload generic callback and @warn with custom functions - sig_parser.lex< alternatives < identifier, exactly <'*'>, exactly > >(); + // allow to overload generic callback plus @warn and @error with custom functions + sig_parser.lex < alternatives < identifier, exactly <'*'>, + exactly < Constants::warn_kwd >, + exactly < Constants::error_kwd > + > >(); string name(Util::normalize_underscores(sig_parser.lexed)); Parameters* params = sig_parser.parse_parameters(); return new (ctx.mem) Definition("[c function]", diff --git a/inspect.cpp b/inspect.cpp index bd9b479de..aa2519303 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -138,6 +138,14 @@ namespace Sass { append_to_buffer(";"); } + void Inspect::operator()(Error* error) + { + if (ctx) ctx->source_map.add_mapping(error); + append_to_buffer("@error "); + error->message()->perform(this); + append_to_buffer(";"); + } + void Inspect::operator()(Comment* comment) { comment->text()->perform(this); diff --git a/inspect.hpp b/inspect.hpp index 628de6af1..caa0ecc7b 100644 --- a/inspect.hpp +++ b/inspect.hpp @@ -48,6 +48,7 @@ namespace Sass { virtual void operator()(Import*); virtual void operator()(Import_Stub*); virtual void operator()(Warning*); + virtual void operator()(Error*); virtual void operator()(Comment*); virtual void operator()(If*); virtual void operator()(For*); diff --git a/operation.hpp b/operation.hpp index 9d80ed012..7edcc4c1d 100644 --- a/operation.hpp +++ b/operation.hpp @@ -25,6 +25,7 @@ namespace Sass { virtual T operator()(Import* x) = 0; virtual T operator()(Import_Stub* x) = 0; virtual T operator()(Warning* x) = 0; + virtual T operator()(Error* x) = 0; virtual T operator()(Comment* x) = 0; virtual T operator()(If* x) = 0; virtual T operator()(For* x) = 0; @@ -93,6 +94,7 @@ namespace Sass { virtual T operator()(Import* x) { return static_cast(this)->fallback(x); } virtual T operator()(Import_Stub* x) { return static_cast(this)->fallback(x); } virtual T operator()(Warning* x) { return static_cast(this)->fallback(x); } + virtual T operator()(Error* x) { return static_cast(this)->fallback(x); } virtual T operator()(Comment* x) { return static_cast(this)->fallback(x); } virtual T operator()(If* x) { return static_cast(this)->fallback(x); } virtual T operator()(For* x) { return static_cast(this)->fallback(x); } diff --git a/output_compressed.hpp b/output_compressed.hpp index ab987ffed..3b92eb3a4 100644 --- a/output_compressed.hpp +++ b/output_compressed.hpp @@ -38,6 +38,7 @@ namespace Sass { virtual void operator()(Import*); // virtual void operator()(Import_Stub*); // virtual void operator()(Warning*); + // virtual void operator()(Error*); virtual void operator()(Comment*); // virtual void operator()(If*); // virtual void operator()(For*); diff --git a/output_nested.hpp b/output_nested.hpp index 20e24b610..ea08e970c 100644 --- a/output_nested.hpp +++ b/output_nested.hpp @@ -51,6 +51,7 @@ namespace Sass { virtual void operator()(Import*); // virtual void operator()(Import_Stub*); // virtual void operator()(Warning*); + // virtual void operator()(Error*); // virtual void operator()(Comment*); // virtual void operator()(If*); // virtual void operator()(For*); diff --git a/parser.cpp b/parser.cpp index 83c9d746b..e0bbc3669 100644 --- a/parser.cpp +++ b/parser.cpp @@ -96,6 +96,10 @@ namespace Sass { (*root) << parse_warning(); if (!lex< exactly<';'> >()) error("top-level @warn directive must be terminated by ';'"); } + else if (peek< err >()) { + (*root) << parse_error(); + if (!lex< exactly<';'> >()) error("top-level @error directive must be terminated by ';'"); + } // ignore the @charset directive for now else if (lex< exactly< charset_kwd > >()) { lex< string_constant >(); @@ -707,6 +711,10 @@ namespace Sass { (*block) << parse_warning(); semicolon = true; } + else if (peek< err >()) { + (*block) << parse_error(); + semicolon = true; + } else if (stack.back() == function_def) { error("only variable declarations and control directives are allowed inside functions"); } @@ -1092,7 +1100,7 @@ namespace Sass { *args << arg; return result; } - catch (Error&) { + catch (Sass_Error&) { // back up so we can try again position = here; source_position = here_p; @@ -1722,6 +1730,12 @@ namespace Sass { return new (ctx.mem) Warning(path, source_position, parse_list()); } + Error* Parser::parse_error() + { + lex< err >(); + return new (ctx.mem) Error(path, source_position, parse_list()); + } + Selector_Lookahead Parser::lookahead_for_selector(const char* start) { const char* p = start ? start : position; @@ -1939,7 +1953,7 @@ namespace Sass { void Parser::error(string msg, Position pos) { - throw Error(Error::syntax, path, pos.line ? pos : source_position, msg); + throw Sass_Error(Sass_Error::syntax, path, pos.line ? pos : source_position, msg); } } diff --git a/parser.hpp b/parser.hpp index 9df80915b..412a9920a 100644 --- a/parser.hpp +++ b/parser.hpp @@ -246,6 +246,7 @@ namespace Sass { Feature_Query_Condition* parse_supports_declaration(); At_Rule* parse_at_rule(); Warning* parse_warning(); + Error* parse_error(); Selector_Lookahead lookahead_for_selector(const char* start = 0); Selector_Lookahead lookahead_for_extension_target(const char* start = 0); diff --git a/prelexer.cpp b/prelexer.cpp index df8663499..bf468840d 100644 --- a/prelexer.cpp +++ b/prelexer.cpp @@ -302,6 +302,10 @@ namespace Sass { return exactly(src); } + const char* err(const char* src) { + return exactly(src); + } + const char* directive(const char* src) { return sequence< exactly<'@'>, identifier >(src); } diff --git a/prelexer.hpp b/prelexer.hpp index c6cf52a96..57b72c2f7 100644 --- a/prelexer.hpp +++ b/prelexer.hpp @@ -363,6 +363,7 @@ namespace Sass { const char* while_directive(const char* src); const char* warn(const char* src); + const char* err(const char* src); const char* directive(const char* src); const char* at_keyword(const char* src); diff --git a/sass2scss.cpp b/sass2scss.cpp index a3537ddb5..787aa1977 100644 --- a/sass2scss.cpp +++ b/sass2scss.cpp @@ -562,8 +562,11 @@ namespace Sass } // terminate warn and debug statements immediately - else if (sass.substr(pos_left, 5) == "@warn" || sass.substr(pos_left, 6) == "@debug") - { sass = indent + sass.substr(pos_left); } + else if ( + sass.substr(pos_left, 5) == "@warn" || + sass.substr(pos_left, 6) == "@debug" || + sass.substr(pos_left, 6) == "@error" + ) { sass = indent + sass.substr(pos_left); } // replace some specific sass shorthand directives (if not fallowed by a white space character) else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1) { sass = indent + "@mixin " + sass.substr(pos_left + 1); } diff --git a/sass_context.cpp b/sass_context.cpp index 5b47b04b0..06116e0ce 100644 --- a/sass_context.cpp +++ b/sass_context.cpp @@ -206,7 +206,7 @@ extern "C" { try { throw; } - catch (Error& e) { + catch (Sass_Error& e) { stringstream msg_stream; JsonNode* json_err = json_mkobject(); json_append_member(json_err, "status", json_mknumber(1)); diff --git a/sass_interface.cpp b/sass_interface.cpp index 6fe55fe15..e86868d12 100644 --- a/sass_interface.cpp +++ b/sass_interface.cpp @@ -138,7 +138,7 @@ extern "C" { copy_strings(cpp_ctx.get_included_files(1), &c_ctx->included_files, 1); } - catch (Error& e) { + catch (Sass_Error& e) { stringstream msg_stream; msg_stream << e.path << ":" << e.position.line << ": " << e.message << endl; c_ctx->error_message = strdup(msg_stream.str().c_str()); @@ -225,7 +225,7 @@ extern "C" { copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files); } - catch (Error& e) { + catch (Sass_Error& e) { stringstream msg_stream; msg_stream << e.path << ":" << e.position.line << ": " << e.message << endl; c_ctx->error_message = strdup(msg_stream.str().c_str()); diff --git a/sass_values.cpp b/sass_values.cpp index 1b498d4b7..5130f40d1 100644 --- a/sass_values.cpp +++ b/sass_values.cpp @@ -62,6 +62,11 @@ extern "C" { char* message; }; + struct Sass_Warning { + enum Sass_Tag tag; + char* message; + }; + union Sass_Value { struct Sass_Unknown unknown; struct Sass_Boolean boolean; @@ -72,6 +77,7 @@ extern "C" { struct Sass_Map map; struct Sass_Null null; struct Sass_Error error; + struct Sass_Warning warning; }; struct Sass_MapPair { @@ -84,13 +90,14 @@ extern "C" { // Check value for specified type bool sass_value_is_null(union Sass_Value* v) { return v->unknown.tag == SASS_NULL; } - bool sass_value_is_map(union Sass_Value* v) { return v->unknown.tag == SASS_MAP; } - bool sass_value_is_list(union Sass_Value* v) { return v->unknown.tag == SASS_LIST; } bool sass_value_is_number(union Sass_Value* v) { return v->unknown.tag == SASS_NUMBER; } bool sass_value_is_string(union Sass_Value* v) { return v->unknown.tag == SASS_STRING; } bool sass_value_is_boolean(union Sass_Value* v) { return v->unknown.tag == SASS_BOOLEAN; } - bool sass_value_is_error(union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; } bool sass_value_is_color(union Sass_Value* v) { return v->unknown.tag == SASS_COLOR; } + bool sass_value_is_list(union Sass_Value* v) { return v->unknown.tag == SASS_LIST; } + bool sass_value_is_map(union Sass_Value* v) { return v->unknown.tag == SASS_MAP; } + bool sass_value_is_error(union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; } + bool sass_value_is_warning(union Sass_Value* v) { return v->unknown.tag == SASS_WARNING; } // Getters and setters for Sass_Number double sass_number_get_value(union Sass_Value* v) { return v->number.value; } @@ -136,6 +143,10 @@ extern "C" { char* sass_error_get_message(union Sass_Value* v) { return v->error.message; }; void sass_error_set_message(union Sass_Value* v, char* msg) { v->error.message = msg; }; + // Getters and setters for Sass_Warning + char* sass_warning_get_message(union Sass_Value* v) { return v->warning.message; }; + void sass_warning_set_message(union Sass_Value* v, char* msg) { v->warning.message = msg; }; + // Creator functions for all value types union Sass_Value* sass_make_boolean(bool val) @@ -221,6 +232,16 @@ extern "C" { return v; } + union Sass_Value* sass_make_warning(const char* msg) + { + Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); + if (v == 0) return 0; + v->warning.tag = SASS_WARNING; + v->warning.message = strdup(msg); + if (v->warning.message == 0) { free(v); return 0; } + return v; + } + // will free all associated sass values void sass_delete_value(union Sass_Value* val) { @@ -255,6 +276,9 @@ extern "C" { case SASS_ERROR: { free(val->error.message); } break; + case SASS_WARNING: { + free(val->error.message); + } break; } free(val); diff --git a/sass_values.h b/sass_values.h index 3d8196c95..1b18c3b73 100644 --- a/sass_values.h +++ b/sass_values.h @@ -21,7 +21,8 @@ enum Sass_Tag { SASS_LIST, SASS_MAP, SASS_NULL, - SASS_ERROR + SASS_ERROR, + SASS_WARNING }; // Tags for denoting Sass list separators @@ -44,6 +45,7 @@ bool sass_value_is_color (union Sass_Value* v); bool sass_value_is_list (union Sass_Value* v); bool sass_value_is_map (union Sass_Value* v); bool sass_value_is_error (union Sass_Value* v); +bool sass_value_is_warning (union Sass_Value* v); // Getters and setters for Sass_Number double sass_number_get_value (union Sass_Value* v); @@ -90,6 +92,10 @@ void sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*); char* sass_error_get_message (union Sass_Value* v); void sass_error_set_message (union Sass_Value* v, char* msg); +// Getters and setters for Sass_Warning +char* sass_warning_get_message (union Sass_Value* v); +void sass_warning_set_message (union Sass_Value* v, char* msg); + // Creator functions for all value types union Sass_Value* sass_make_null (void); union Sass_Value* sass_make_boolean (bool val); @@ -99,6 +105,7 @@ union Sass_Value* sass_make_color (double r, double g, double b, double a); union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep); union Sass_Value* sass_make_map (size_t len); union Sass_Value* sass_make_error (const char* msg); +union Sass_Value* sass_make_warning (const char* msg); // Generic destructor function for all types // Will release memory of all associated Sass_Values