Skip to content

Commit

Permalink
Merge pull request #8 from rofrischmann/2-expressions
Browse files Browse the repository at this point in the history
adding support for calc expressions
  • Loading branch information
Robin Frischmann authored Feb 11, 2017
2 parents d479773 + 15b8c36 commit 659f5aa
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 51 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ ast === {
* [Dimension](docs/ASTNodes.md#dimension)
* [Float](docs/ASTNodes.md#float)
* [Function](docs/ASTNodes.md#function)
* [Expression](docs/ASTNodes.md#expression)


## Support
Expand Down
97 changes: 73 additions & 24 deletions docs/ASTNodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ body: [ /* child nodes */ ]
* [Dimension](#dimension)
* [Float](#float)
* [Function](#function)
* [Expression](#expression)

## Identifier
Identifiers are all kind of words such as `solid`.
```javascript
// e.g. solid
{
type: 'Identifier',
value: 'solid'
type: 'Identifier',
value: 'solid'
}
```

Expand All @@ -37,8 +38,8 @@ Integers are simple numbers without a unit or fractional part.
```javascript
// e.g. 34
{
type: 'Integer',
value: 34
type: 'Integer',
value: 34
}
```

Expand All @@ -47,8 +48,8 @@ Keywords are special identifier that are globally valid for CSS. These are `inhe
```javascript
// e.g. inherit
{
type: 'Keyword',
value: 'inherit'
type: 'Keyword',
value: 'inherit'
}
```

Expand All @@ -58,8 +59,8 @@ Operators are basic arithmetic expression symbols for addition `+`, subtraction
```javascript
// e.g. +
{
type: 'Operator',
value: '+'
type: 'Operator',
value: '+'
}
```

Expand All @@ -69,8 +70,8 @@ HexColor represents color values given in hexadecimal notation.
```javascript
// e.g. #FF66FF
{
type: 'HexColor',
value: '#FF66FF'
type: 'HexColor',
value: '#FF66FF'
}
```

Expand All @@ -80,8 +81,8 @@ URL is used for any URI-type string. *It is not validated by the parser!*
```javascript
// e.g. https://github.com/
{
type: 'URL',
value: 'https://github.com/'
type: 'URL',
value: 'https://github.com/'
}
```

Expand All @@ -91,8 +92,8 @@ Strings are all values that are wrapped in quotes, either single `'` or double `
```javascript
// e.g. "I'm a string!!11!1"
{
type: 'String',
value: 'I\'m a string!!11!1'
type: 'String',
value: 'I\'m a string!!11!1'
}
```

Expand All @@ -113,10 +114,10 @@ Dimensions are special integers or floats that are postfixed with an extra unit.
```javascript
// e.g. 12px
{
type: 'Dimension',
value: 12,
unit: 'px',
dimension: 'absolute-length'
type: 'Dimension',
value: 12,
unit: 'px',
dimension: 'absolute-length'
}
```

Expand All @@ -132,9 +133,9 @@ Floats are floating-point numbers with a fractional part and an integer part. *(
```javascript
// e.g. 587.923
{
type: 'Float',
integer: 587,
fractional: 923
type: 'Float',
integer: 587,
fractional: 923
}
```

Expand All @@ -150,8 +151,56 @@ Functions represent CSS functions wrapped in parentheses.

// e.g. rgba(10, 20, 30, 0.55)
{
type: 'Function',
callee: 'rgba'
params: [ /* param nodes */ ]
type: 'Function',
callee: 'rgba'
params: [{
type: 'Integer',
value: 10
}, {
type: 'Integer',
value: 20
}, {
type: 'Integer',
value: 30
}, {
type: 'Float',
integer: 0,
fractional: 55
}]
}
```

## Expression
Expressions are mathematical calculations. They may only be used inside the CSS `calc`-function.

| Property | Description |
| ------ | ------ |
| body | An array of any AST nodes |

```javascript

// e.g. 100% - 30px*3
{
type: 'Expression',
body: [{
type: 'Dimension',
value: 100,
unit: '%',
dimension: 'percentage'
}, {
type: 'Operator',
value: '-'
}, {
type: 'Dimension',
value: 30,
unit: 'px',
dimension: 'absolute-length'
}, {
type: 'Operator',
value: '*'
}, {
type: 'Integer',
value: 3
}]
}
```
5 changes: 1 addition & 4 deletions modules/__tests__/tokenizer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,8 @@ describe('Tokenizing CSS values', () => {

it('should return an array of tokens', () => {
expect(tokenizeCSSValue('#FF66f6')).toEqual([{
type: 'hash',
value: '#'
}, {
type: 'hexadecimal',
value: 'FF66f6'
value: '#FF66f6'
}])
})
})
9 changes: 6 additions & 3 deletions modules/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export default class Generator {
case 'CSSValue':
return node.body.map(generateCSSValue).join(' ')

case 'Expression':
return node.body.map(generateCSSValue).join('')

case 'Function':
return `${node.callee}(${node.params.map(generateCSSValue).join(',')})`

Expand All @@ -19,9 +22,9 @@ export default class Generator {
return `${node.integer ? node.integer : ''}.${node.fractional}`

case 'Operator':
// we use spacings left and right to ensure
// correct syntax inside calc expressions
return ` ${node.value} `
// for addition and substraction we use spacings left and right
// to ensure correct syntax inside calc expressions
return node.value === '+' || node.value === '-' ? ` ${node.value} ` : node.value

case 'String':
return node.quote + node.value + node.quote
Expand Down
6 changes: 0 additions & 6 deletions modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,3 @@ export default function parse(input: string): ParsedCSSValue {
}
}
}

const input = '1px solid "helloMyName\'is\'foo" url(https://www.youtube.com/watch?v=CSvFpBOe8eY'

const parsed = parse(input)

console.log(parsed.toString())
27 changes: 17 additions & 10 deletions modules/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ export default class Parser {
return this.parseURL()
}

if (this.currentToken.value.indexOf('calc') > -1) {
const node = this.parseFunction()

const functionParams = node.params
node.params = [{
type: 'Expression',
body: functionParams
}]

return node
}

return this.parseFunction()
}

Expand Down Expand Up @@ -247,16 +259,10 @@ export default class Parser {
}

parseHexColor() {
if (this.currentToken.type === 'hash') {
const nextToken = this.getNextToken(1)

if (nextToken.type === 'hexadecimal') {
this.updateCurrentToken(1)

return {
type: 'HexColor',
value: `#${nextToken.value}`
}
if (this.currentToken.type === 'hexadecimal') {
return {
type: 'HexColor',
value: this.currentToken.value
}
}
}
Expand All @@ -283,6 +289,7 @@ export default class Parser {
this.parseString()

if (!node) {
console.log(this.currentToken)
throw new SyntaxError(('Cannot parse': node))
}

Expand Down
1 change: 1 addition & 0 deletions modules/traverser.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class Traverser {

switch (node.type) {
case 'CSSValue':
case 'Expression':
this.traverseNodeList(node.body, node)
break

Expand Down
7 changes: 3 additions & 4 deletions modules/utils/CSSValueRules.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ export default {
number: /^\d+$/,
url_chars: /^[&:=?]$/,
floating_point: /^[.]$/,
hexadecimal: /^([0-9a-f]+)$/i,
hexadecimal: /^#([0-9a-f]*)$/i,
whitespace: /^\s+$/,
paren: /^[()]+$/,
comma: /^,+$/,
hash: /^#$/
paren: /^[()]$/,
comma: /^,+$/
}

0 comments on commit 659f5aa

Please sign in to comment.