blob: 239ea5b2f75ece01e517de38ef5d460b76c2e60e [file] [log] [blame]
/* description: Parses expressions. */
%options case-insensitive
/* lexical grammar */
%lex
%%
\s+ /* skip whitespace */
(\-(webkit|moz)\-)?calc\b return 'CALC';
[a-z][a-z0-9-]*\s*\((?:(?:\"(?:\\.|[^\"\\])*\"|\'(?:\\.|[^\'\\])*\')|\([^)]*\)|[^\(\)]*)*\) return 'FUNCTION';
"*" return 'MUL';
"/" return 'DIV';
"+" return 'ADD';
"-" return 'SUB';
([0-9]+("."[0-9]+)?|"."[0-9]+)em\b return 'LENGTH'; // em
([0-9]+("."[0-9]+)?|"."[0-9]+)ex\b return 'LENGTH'; // ex
([0-9]+("."[0-9]+)?|"."[0-9]+)ch\b return 'LENGTH'; // ch
([0-9]+("."[0-9]+)?|"."[0-9]+)rem\b return 'LENGTH'; // rem
([0-9]+("."[0-9]+)?|"."[0-9]+)vw\b return 'LENGTH'; // vw
([0-9]+("."[0-9]+)?|"."[0-9]+)vh\b return 'LENGTH'; // vh
([0-9]+("."[0-9]+)?|"."[0-9]+)vmin\b return 'LENGTH'; // vmin
([0-9]+("."[0-9]+)?|"."[0-9]+)vmax\b return 'LENGTH'; // vmax
([0-9]+("."[0-9]+)?|"."[0-9]+)vm\b return 'LENGTH'; // vm (non-standard name)
([0-9]+("."[0-9]+)?|"."[0-9]+)px\b return 'LENGTH'; // px
([0-9]+("."[0-9]+)?|"."[0-9]+)mm\b return 'LENGTH'; // mm
([0-9]+("."[0-9]+)?|"."[0-9]+)cm\b return 'LENGTH'; // cm
([0-9]+("."[0-9]+)?|"."[0-9]+)in\b return 'LENGTH'; // in
([0-9]+("."[0-9]+)?|"."[0-9]+)pt\b return 'LENGTH'; // pt
([0-9]+("."[0-9]+)?|"."[0-9]+)pc\b return 'LENGTH'; // pc
([0-9]+("."[0-9]+)?|"."[0-9]+)Q\b return 'LENGTH'; // Q
([0-9]+("."[0-9]+)?|"."[0-9]+)fr\b return 'LENGTH'; // fr
([0-9]+("."[0-9]+)?|"."[0-9]+)deg\b return 'ANGLE'; // deg
([0-9]+("."[0-9]+)?|"."[0-9]+)grad\b return 'ANGLE'; // grad
([0-9]+("."[0-9]+)?|"."[0-9]+)turn\b return 'ANGLE'; // turn
([0-9]+("."[0-9]+)?|"."[0-9]+)rad\b return 'ANGLE'; // rad
([0-9]+("."[0-9]+)?|"."[0-9]+)s\b return 'TIME'; // s
([0-9]+("."[0-9]+)?|"."[0-9]+)ms\b return 'TIME'; // ms
([0-9]+("."[0-9]+)?|"."[0-9]+)Hz\b return 'FREQ'; // Hz
([0-9]+("."[0-9]+)?|"."[0-9]+)kHz\b return 'FREQ'; // kHz
([0-9]+("."[0-9]+)?|"."[0-9]+)dpi\b return 'RES'; // dpi
([0-9]+("."[0-9]+)?|"."[0-9]+)dpcm\b return 'RES'; // dpcm
([0-9]+("."[0-9]+)?|"."[0-9]+)dppx\b return 'RES'; // dppm
([0-9]+("."[0-9]+)?|"."[0-9]+)\% return 'PERCENTAGE';
([0-9]+("."[0-9]+)?|"."[0-9]+)\b return 'NUMBER';
"(" return 'LPAREN';
")" return 'RPAREN';
#\{([\s\S]*?)\} return 'UNKNOWN'; // scss interpolation
@\{([\s\S]*?)\} return 'UNKNOWN'; // less interpolation
\S[^\s()*/+-]* return 'UNKNOWN';
<<EOF>> return 'EOF';
/lex
%left ADD SUB
%left MUL DIV
%left UPREC
%start expression
%%
expression
: math_expression EOF { return $1; }
;
math_expression
: CALC LPAREN math_expression RPAREN {
$$ = $3;
$$.source.start = { index: @1.range[0] };
$$.source.end = { index: @4.range[1] };
}
| math_expression ADD math_expression {
$$ = {
type: 'MathExpression', operator: $2, left: $1, right: $3,
source: {
start: $1.source.start, end: $3.source.end,
operator: { start: { index: @2.range[0] }, end: { index: @2.range[1] } }
}
};
}
| math_expression SUB math_expression {
$$ = {
type: 'MathExpression', operator: $2, left: $1, right: $3,
source: {
start: $1.source.start, end: $3.source.end,
operator: { start: { index: @2.range[0] }, end: { index: @2.range[1] } }
}
};
}
| math_expression MUL math_expression {
$$ = {
type: 'MathExpression', operator: $2, left: $1, right: $3,
source: {
start: $1.source.start, end: $3.source.end,
operator: { start: { index: @2.range[0] }, end: { index: @2.range[1] } }
}
};
}
| math_expression DIV math_expression {
$$ = {
type: 'MathExpression', operator: $2, left: $1, right: $3,
source: {
start: $1.source.start, end: $3.source.end,
operator: { start: { index: @2.range[0] }, end: { index: @2.range[1] } }
}
};
}
| SUB math_expression %prec UPREC {
if (@1.range[1] !== $2.source.start.index) {
throw new Error('Unexpected spaces was found between sign and value');
}
if (typeof $2.value !== 'number') {
throw new Error('Unexpected sign');
}
if ($2.sign) {
throw new Error('Unexpected continuous sign');
}
$$ = $2;
$$.sign = '-'
$$.value = -$2.value;
$$.source.start.index = @1.range[0];
}
| ADD math_expression %prec UPREC {
if (@1.range[1] !== $2.source.start.index) {
throw new Error('Unexpected spaces was found between sign and value');
}
if (typeof $2.value !== 'number') {
throw new Error('Unexpected sign');
}
if ($2.sign) {
throw new Error('Unexpected continuous sign');
}
$$ = $2;
$$.sign = '+'
$$.source.start.index = @1.range[0];
}
| LPAREN math_expression RPAREN {
$$ = $2;
$$.source.start = { index: @1.range[0] };
$$.source.end = { index: @3.range[1] };
}
| function { $$ = $1; }
| css_value { $$ = $1; }
| value { $$ = $1; }
;
value
: NUMBER { $$ = { type: 'Value', value: parseFloat($1), source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
;
function
: FUNCTION { $$ = { type: 'Function', value: $1, source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
;
css_value
: LENGTH { $$ = { type: 'LengthValue', value: parseFloat($1), unit: /[a-z]+/i.exec($1)[0], source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
| ANGLE { $$ = { type: 'AngleValue', value: parseFloat($1), unit: /[a-z]+/i.exec($1)[0], source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
| TIME { $$ = { type: 'TimeValue', value: parseFloat($1), unit: /[a-z]+/i.exec($1)[0], source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
| FREQ { $$ = { type: 'FrequencyValue', value: parseFloat($1), unit: /[a-z]+/i.exec($1)[0], source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
| RES { $$ = { type: 'ResolutionValue', value: parseFloat($1), unit: /[a-z]+/i.exec($1)[0], source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
| PERCENTAGE { $$ = { type: 'PercentageValue', value: parseFloat($1), unit: '%', source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
| UNKNOWN { $$ = { type: 'UnknownValue', value: $1, unit: '', source: { start: { index: @1.range[0] }, end: { index: @1.range[1] } } }; }
;