#include "sass.hpp"
#include "parser.hpp"
#include "file.hpp"
#include "inspect.hpp"
#include "constants.hpp"
#include "util.hpp"
#include "prelexer.hpp"
#include "color_maps.hpp"
#include "sass/functions.h"
#include "error_handling.hpp"
// Notes about delayed: some ast nodes can have delayed evaluation so
// they can preserve their original semantics if needed. This is most
// prominently exhibited by the division operation, since it is not
// only a valid operation, but also a valid css statement (i.e. for
// fonts, as in `16px/24px`). When parsing lists and expression we
// unwrap single items from lists and other operations. A nested list
// must not be delayed, only the items of the first level sometimes
// are delayed (as with argument lists). To achieve this we need to
// pass status to the list parser, so this can be set correctly.
// Another case with delayed values are colors. In compressed mode
// only processed values get compressed (other are left as written).
#include <cstdlib>
#include <iostream>
#include <vector>
#include <typeinfo>
namespace Sass {
using namespace Constants;
using namespace Prelexer;
Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
pstate.offset.column = 0;
pstate.offset.line = 0;
Parser p(ctx, pstate, traces);
p.source = source ? source : beg;
p.position = beg ? beg : p.source;
p.end = p.position + strlen(p.position);
Block_Obj root = SASS_MEMORY_NEW(Block, pstate);
p.block_stack.push_back(root);
root->is_root(true);
return p;
}
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
pstate.offset.column = 0;
pstate.offset.line = 0;
Parser p(ctx, pstate, traces);
p.source = source ? source : beg;
p.position = beg ? beg : p.source;
p.end = end ? end : p.position + strlen(p.position);
Block_Obj root = SASS_MEMORY_NEW(Block, pstate);
p.block_stack.push_back(root);
root->is_root(true);
return p;
}
void Parser::advanceToNextToken() {
lex < css_comments >(false);
// advance to position
pstate += pstate.offset;
pstate.offset.column = 0;
pstate.offset.line = 0;
}
Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source);
// ToDo: ruby sass errors on parent references
// ToDo: remap the source-map entries somehow
return p.parse_selector_list(false);
}
bool Parser::peek_newline(const char* start)
{
return peek_linefeed(start ? start : position)
&& ! peek_css<exactly<'{'>>(start);
}
Parser Parser::from_token(Token t, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
Parser p(ctx, pstate, traces);
p.source = source ? source : t.begin;
p.position = t.begin ? t.begin : p.source;
p.end = t.end ? t.end : p.position + strlen(p.position);
Block_Obj root = SASS_MEMORY_NEW(Block, pstate);
p.block_stack.push_back(root);
root->is_root(true);
return p;
}
/* main entry point to parse root block */
Block_Obj Parser::parse()
{
// consume unicode BOM
read_bom();
// scan the input to find invalid utf8 sequences
const char* it = utf8::find_invalid(position, end);
// report invalid utf8
if (it != end) {
pstate += Offset::init(position, it);
traces.push_back(Backtrace(pstate));
throw Exception::InvalidSass(pstate, traces, "Invalid UTF-8 sequence");
}
// create a block AST node to hold children
Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true);
// check seems a bit esoteric but works
if (ctx.resources.size() == 1) {
// apply headers only on very first include
ctx.apply_custom_headers(root, path, pstate);
}
// parse children nodes
block_stack.push_back(root);
parse_block_nodes(true);
block_stack.pop_back();
// update final position
root->update_pstate(pstate);
if (position != end) {
css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");
}
return root;
}
// convenience function for block parsing
// will create a new block ad-hoc for you
// this is the base block parsing function
Block_Obj Parser::parse_css_block(bool is_root)
{
// parse comments before block
// lex < optional_css_comments >();
// lex mandatory opener or error out
if (!lex_css < exactly<'{'> >()) {
css_error("Invalid CSS", " after ", ": expected \"{\", was ");
}
// create new block and push to the selector stack
Block_Obj block = SASS_MEMORY_NEW(Block, pstate, 0, is_root);
block_stack.push_back(block);
if (!parse_block_nodes(is_root)) css_error("Invalid CSS", " after ", ": expected \"}\", was ");
if (!lex_css < exactly<'}'> >()) {
css_error("Invalid CSS", " after ", ": expected \"}\", was ");
}
// update for end position
// this seems to be done somewhere else
// but that fixed selector schema issue
// block->update_pstate(pstate);
// parse comments after block
// lex < optional_css_comments >();
block_stack.pop_back();
return block;
}
// convenience function for block parsing
// will create a new block ad-hoc for you
// also updates the `in_at_root` flag
Block_Obj Parser::parse_block(bool is_root)
{
return parse_css_block(is_root);
}
// the main block parsing function
// parses stuff between `{` and `}`
bool Parser::parse_block_nodes(bool is_root)
{
// loop until end of string
while (position < end) {
// we should be able to refactor this
parse_block_comments();
lex < css_whitespace >();
if (lex < exactly<';'> >()) continue;
if (peek < end_of_file >()) return true;
if (peek < exactly<'}'> >()) return true;
if (parse_block_node(is_root)) continue;
parse_block_comments();
if (lex_css < exactly<';'> >()) continue;
if (peek_css < end_of_file >()) return true;
if (peek_css < exactly<'}'> >()) return true;
// illegal sass
return false;
}
// return success
return true;
}
// parser for a single node in a block
// semicolons must be lexed beforehand
bool Parser::parse_block_node(bool is_root) {
Block_Obj block = block_stack.back();
parse_block_comments();
// throw away white-space
// includes line comments
lex < css_whitespace >();
Lookahead lookahead_result;
// also parse block comments
// first parse everything that is allowed in functions
if (lex < variable >(true)) { block->append(parse_assignment()); }
else if (lex < kwd_err >(true)) { block->append(parse_error()); }
else if (lex < kwd_dbg >(true)) { block->append(parse_debug()); }
else if (lex < kwd_warn >(true)) { block->append(parse_warning()); }
else if (lex < kwd_if_directive >(true)) { block->append(parse_if_directive()); }
else if (lex < kwd_for_directive >(true)) { block->append(parse_for_directive()); }
else if (lex < kwd_each_directive >(true)) { block->append(parse_each_directive()); }
else if (lex < kwd_while_directive >(true)) { block->append(parse_while_directive()); }
else if (lex < kwd_return_directive >(true)) { block->append(parse_return_directive()); }
// parse imports to process later
else if (lex < kwd_import >(true)) {
Scope parent = stack.empty() ? Scope::Rules : stack.back();
if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) {
if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20
error("Import directives may not be used within control directives or mixins.");
评论0