Commit 53ac5d5f by Aaron Leung

Working on evaluating arithmetic expressions. Tough because of Sass's liberalism…

Working on evaluating arithmetic expressions. Tough because of Sass's liberalism and the fact that '/' can be both a division operator and number separator.
parent d64401f9
#include "document.hpp"
#include <iostream>
Node eval(const Node& expr)
{
switch (expr.type)
{
case expression: {
Node acc(expr.line_number, Node::expression, eval(expr.children->at(0)));
Node rhs(eval(expr.children->at(2)));
accumulate(expr.children->at(1).type, acc, rhs);
for (int i = 3; i < expr.children->size(); i += 2) {
Node rhs(eval(expr.children->at(i+1)));
accumulate(expr.children->at(i), acc, rhs);
}
return acc;
} break;
case term: {
} break;
}
}
Node accumulate(const Node::Type op, Node& acc, const Node& rhs)
{
Node lhs(acc.children->back());
if (lhs.type == Node::number && rhs.type == Node::number) {
Node result(acc.line_number, Node::number, operate(op, lhs, rhs));
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::number && rhs.type == Node::numeric_dimension) {
Node result(acc.line_number, Node::numeric_dimension, operate(op, lhs, rhs), rhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::number) {
Node result(acc.line_number, Node::numeric_dimension, operate(op, lhs, rhs), lhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::numeric_dimension) {
// CHECK FOR MISMATCHED UNITS HERE
Node result(acc.line_number, Node::numeric_dimension, operate(op, lhs, rhs), lhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else {
acc.children->push_back(rhs);
}
return acc;
}
double operate(const Node::Type op, double lhs, double rhs)
{
switch (op)
{
case Node::add: return lhs + rhs; break;
case Node::sub: return lhs - rhs; break;
case Node::mul: return lhs * rhs; break;
case Node::div: return lhs / rhs; break;
default: return 0; break;
}
}
\ No newline at end of file
......@@ -46,7 +46,12 @@ namespace Sass {
const Token key(lexed);
lex< exactly<':'> >();
// context.environment[key] = parse_values();
context.environment[key] = parse_list();
Node val(parse_list());
val.from_variable = true;
val.eval_me = true;
// context.environment[key] = eval(val);
context.environment[key] = val;
}
Node Document::parse_ruleset()
......@@ -315,7 +320,8 @@ namespace Sass {
peek< exactly<'}'> >(position) ||
peek< exactly<')'> >(position) ||
peek< exactly<','> >(position)))
{ space_list << parse_expression(); }
// { space_list << eval(parse_expression());
{ space_list << parse_expression(); }
return space_list;
}
......@@ -328,7 +334,8 @@ namespace Sass {
peek< exactly<'-'> >(position)))
{ return term1; }
Node expression(line_number, Node::expression, 2);
Node expression(line_number, Node::expression, 3);
term1.eval_me = true;
expression << term1;
while (lex< exactly<'+'> >() || lex< exactly<'-'> >()) {
......@@ -338,7 +345,9 @@ namespace Sass {
else {
expression << Node(line_number, Node::sub, lexed);
}
expression << parse_term();
Node term(parse_term());
term.eval_me = true;
expression << term;
}
return expression;
......@@ -352,17 +361,21 @@ namespace Sass {
peek< exactly<'/'> >(position)))
{ return fact1; }
Node term(line_number, Node::term, 2);
Node term(line_number, Node::term, 3);
term << fact1;
if (fact1.from_variable || fact1.eval_me) term.eval_me = true;
while (lex< exactly<'*'> >() || lex< exactly<'/'> >()) {
if (lexed.begin[0] == '*') {
term << Node(line_number, Node::mul, lexed);
term.eval_me = true;
}
else {
term << Node(line_number, Node::div, lexed);
}
term << parse_factor();
Node fact(parse_factor());
if (fact.from_variable || fact.eval_me) term.eval_me = true;
term << fact;
}
return term;
......@@ -372,7 +385,7 @@ namespace Sass {
{
if (lex< exactly<'('> >()) {
Node value(parse_list());
value.parenthesized = true;
value.eval_me = true;
lex< exactly<')'> >();
return value;
}
......
$x: 1 2 3;
$y: 3;
$z: 2;
$w: 10/2;
div {
a: b c d;
b: a $x b;
c: 1 (2 3) 4;
d: 1, 2 3, 4, (5 6, 7 8) 9, 10;
e: 1 + (2 3) + 3 + 4;
f: 1 + 2 + (3 4) + 5 + 6;
g: 2 / 3, (3/4);
h: 1 + 2/3;
i: 2/3 + 1;
j: 2/3 + 1 2;
k: 1 2 + 3/4;
l: (1 2) + 3/4;
m: 2 3/4;
n: $y / 2;
o: ($y) / 2;
p: 12 / 3 / $z;
q: 12 / 3 / 2;
r: 12 / (3 / 2);
s: (12 / 3) / 2;
t: 5 / (1 + (3 - 2) + 4 / (1 * $z));
t: 5 / (2 + 3);
t: 5 / (2);
t: 5 / 2;
u: 6 / 2 + 4 / $z;
v: 3-2;
w: (5 / #{$w});
x: "#{$w}";
// t: 12 (/ 3 / 2);
y: 5 + (4/2 4/2 4/2);
z: 2px + 2px, 2px + 2 //, 2px + 2em;
}
\ No newline at end of file
#include <iostream>
#include <string>
#include <cctype>
#include <cstdlib>
#include "node.hpp"
using std::string;
......@@ -105,8 +106,26 @@ namespace Sass {
return result;
} break;
case value: {
return string(token);
case expression:
case term: {
string result(children->at(0).to_string(prefix));
for (int i = 2; i < children->size(); i += 2) {
// result += " ";
result += children->at(i).to_string(prefix);
}
return result;
} break;
case numeric_dimension: {
stringstream ss;
ss << numeric_value << string(token);
return ss.str();
} break;
case number: {
stringstream ss;
ss << numeric_value;
return ss.str();
} break;
default: {
......
......@@ -9,11 +9,10 @@ namespace Sass {
struct Node {
enum Type {
nil,
root,
comment,
ruleset,
propset,
selector_group,
selector,
selector_combinator,
......@@ -27,20 +26,31 @@ namespace Sass {
pseudo_negation,
functional_pseudo,
attribute_selector,
block,
rule,
property,
nil,
comma_list,
space_list,
expression,
add,
sub,
term,
mul,
div,
factor,
values,
value
value,
dimension,
numeric_dimension,
number,
comment
};
static size_t fresh;
......@@ -49,12 +59,14 @@ namespace Sass {
size_t line_number;
mutable vector<Node>* children;
Token token;
double numeric_value;
Type type;
bool has_rules_or_comments;
bool has_rulesets;
bool has_propsets;
bool has_backref;
bool parenthesized;
bool from_variable;
bool eval_me;
Node() : type(nil), children(0) { ++fresh; }
......@@ -62,48 +74,56 @@ namespace Sass {
: line_number(n.line_number),
children(n.children),
token(n.token),
numeric_value(n.numeric_value),
type(n.type),
has_rules_or_comments(n.has_rules_or_comments),
has_rulesets(n.has_rulesets),
has_propsets(n.has_propsets),
has_backref(n.has_backref),
parenthesized(n.parenthesized)
from_variable(n.from_variable),
eval_me(n.eval_me)
{ /*n.release();*/ ++copied; } // No joint custody.
Node(size_t line_number, Type type, size_t length = 0)
: line_number(line_number),
children(new vector<Node>),
token(Token()),
numeric_value(0),
type(type),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_backref(false),
parenthesized(false)
from_variable(false),
eval_me(false)
{ children->reserve(length); ++fresh; }
Node(size_t line_number, Type type, const Node& n)
: line_number(line_number),
children(new vector<Node>(1, n)),
token(Token()),
numeric_value(0),
type(type),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_backref(false),
parenthesized(false)
from_variable(false),
eval_me(false)
{ ++fresh; }
Node(size_t line_number, Type type, const Node& n, const Node& m)
: line_number(line_number),
children(new vector<Node>),
token(Token()),
numeric_value(0),
type(type),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_backref(false),
parenthesized(false)
from_variable(false),
eval_me(false)
{
children->reserve(2);
children->push_back(n);
......@@ -115,12 +135,42 @@ namespace Sass {
: line_number(line_number),
children(0),
token(token),
numeric_value(0),
type(type),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_backref(false),
parenthesized(false)
from_variable(false),
eval_me(false)
{ ++fresh; }
Node(size_t line_number, double d)
: line_number(line_number),
children(0),
token(Token()),
numeric_value(d),
type(number),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_backref(false),
from_variable(false),
eval_me(false)
{ ++fresh; }
Node(size_t line_number, double d, Token& token)
: line_number(line_number),
children(0),
token(token),
numeric_value(d),
type(numeric_dimension),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_backref(false),
from_variable(false),
eval_me(false)
{ ++fresh; }
//~Node() { delete children; }
......@@ -131,12 +181,14 @@ namespace Sass {
children = n.children;
// n.release();
token = n.token;
numeric_value = n.numeric_value;
type = n.type;
has_rules_or_comments = n.has_rules_or_comments;
has_rulesets = n.has_rulesets;
has_propsets = n.has_propsets;
has_backref = n.has_backref;
parenthesized = n.parenthesized;
from_variable = n.from_variable;
eval_me = n.eval_me;
++copied;
return *this;
}
......
......@@ -78,7 +78,6 @@ namespace Sass {
}
}
using std::lexicographical_compare;
bool Token::operator<(const Token& rhs) const
{
const char* first1 = begin;
......@@ -93,4 +92,19 @@ namespace Sass {
}
return (first2!=last2);
}
bool Token::operator==(const Token& rhs) const
{
if (length() != rhs.length()) return false;
if ((begin[0] == '"' || begin[0] == '\'') &&
(rhs.begin[0] == '"' || rhs.begin[0] == '\''))
{ return unquote() == rhs.unquote(); }
const char* p = begin;
const char* q = rhs.begin;
for (; p < end; ++p, ++q) if (*p != *q) return false;
return true;
}
}
\ No newline at end of file
......@@ -11,6 +11,9 @@ namespace Sass {
Token();
Token(const char* begin, const char* end);
size_t length() const
{ return end - begin; }
inline operator string() const
{ return string(begin, end - begin); }
......@@ -19,6 +22,7 @@ namespace Sass {
void unquote_to_stream(std::stringstream& buf) const;
bool operator<(const Token& rhs) const;
bool operator==(const Token& rhs) const;
operator bool()
{ return begin && end && begin >= end; }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment