Skip to content

Commit

Permalink
new one-step parser
Browse files Browse the repository at this point in the history
  • Loading branch information
lvivski committed Oct 3, 2012
1 parent 17a1063 commit 9674958
Show file tree
Hide file tree
Showing 14 changed files with 298 additions and 863 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
/node_modules
/lib/boss
/lib/css
/lib/parser.js
9 changes: 1 addition & 8 deletions GNUmakefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,2 @@
build:
@mkdir -p lib/{boss,css}
@node_modules/.bin/ometajs2js -b -i src/boss/parser.ometajs > lib/boss/parser.js
@node_modules/.bin/ometajs2js -b -i src/boss/transformer.ometajs > lib/boss/transformer.js
@node_modules/.bin/ometajs2js -b -i src/boss/translator.ometajs > lib/boss/translator.js

@node_modules/.bin/ometajs2js -b -i src/css/parser.ometajs > lib/css/parser.js
@node_modules/.bin/ometajs2js -b -i src/css/transformer.ometajs > lib/css/transformer.js
@node_modules/.bin/ometajs2js -b -i src/css/translator.ometajs > lib/css/translator.js
@node_modules/.bin/ometajs2js -b -i lib/parser.ometajs > lib/parser.js
12 changes: 2 additions & 10 deletions lib/boss.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
var Parser = exports.Parser = require('./boss/parser').BOSSParser

var Transformer = exports.Transformer = require('./boss/transformer').BOSSTransformer

var Translator = exports.Translator = require('./boss/translator').BOSSTranslator

var inspect = require('util').inspect
var Parser = require('./parser').BOSSParser
, q = require('q')
, fs = require('fs')
, Env = require('./env')
Expand All @@ -27,9 +21,7 @@ exports.run = function run(options) {
}

var parse = exports.parse = function parse(source) {
var ast = Parser.matchAll(source, "stylesheet")
ast = Transformer.match(ast, "stylesheet")
return Translator.match(ast, "stylesheet")
return Parser.matchAll(source, "stylesheet")
}

function mixin(filename) {
Expand Down
2 changes: 1 addition & 1 deletion lib/nodes/binop.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
var BinOp = module.exports = function BinOp(op, left, right) {
this.op = '' + op
this.op = op
this.left = left
this.right = right
}
Expand Down
6 changes: 6 additions & 0 deletions lib/nodes/dimension.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ var coercion = {
, s: {
ms: 1 / 1000
}
, rad: {
deg: Math.PI / 180
}
, deg: {
rad: 180 / Math.PI
}
}

Dimension.prototype = {
Expand Down
284 changes: 284 additions & 0 deletions lib/parser.ometajs
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
var nodes = require('./nodes');

ometa BOSSParser {

// Macros

m_comment = seq('/*') (~seq('*/') char)*:x seq('*/') -> ('/*' + x.join('') + '*/'),

m_ident = '-' (char:x m_nmstart(x) -> x):x (escape | :y m_nmchar(y) -> y)*:z -> ('-' + x + z.join(''))
| (char:x m_nmstart(x) -> x):x (escape | :y m_nmchar(y) -> y)*:z -> (x + z.join('')),

escape = '\\' char:x -> ('\\' + x),

m_name = (:x m_nmchar(x) -> x)+:xx -> xx.join(''),

m_name2 = (:x m_nmchar2(x) -> x)+:xx -> xx.join(''),

m_number = digit+:x '.' digit+:y -> (x.join('') + '.' + y.join(''))
| '.' digit+:x -> ('.' + x.join(''))
| digit+:x -> x.join(''),

m_string = '"' (m_string_nl1 | ~'"' char)*:s '"' -> ('"' + s.join('') + '"')
| '\'' (m_string_nl2 | ~'\'' char)*:s '\'' -> ('\'' + s.join('') + '\''),

m_string_nl1 = ('\n' | '\r' | seq('\\"')):x -> x,
m_string_nl2 = ('\n' | '\r' | seq('\\\'')):x -> x,

m_nmstart :x = ?BOSSParser._m_nmstart(x),
m_unicode :x = ?BOSSParser._m_unicode(x),
m_escape :x = ?BOSSParser._m_escape(x),
m_nmchar :x = ?BOSSParser._m_nmchar(x),
m_nmchar2 :x = ?BOSSParser._m_nmchar2(x),
m_nl :x = ?BOSSParser._m_nl(x),
m_w :x = ?BOSSParser._m_w(x),

// Tokens

ident = m_ident:x -> new nodes.Ident(x),

atkeyword = '@' ident:x -> '@' + x,

string = m_string:x -> new nodes.String(x.slice(1, -1), x[0]),

shash = '#' m_name:x -> '#' + x,

vhash = '#' m_name2:x -> new nodes.Color(x),

number = m_number:x -> new nodes.Dimension(x),

percentage = m_number:x '%' -> new nodes.Dimension(x, '%'),

dimension = m_number:x m_name2:y -> new nodes.Dimension(x, y),

cdo = seq('<!--') -> '<!--',
cdc = seq('-->') -> '-->',

s = (:x m_w(x) -> x)+:xx -> xx.join(''),

attrselector = (seq('=') | seq('~=') | seq('^=') | seq('$=') | seq('*=') | seq('|=')):x -> x,

delim = ',' -> ',',

comment = seq('/*') (~seq('*/') char)*:x seq('*/') -> new nodes.Comment(x.join('')),

sc = s | comment,

tset = vhash | any | sc | operator,

stylesheet = (cdo | cdc | sc | statement)*:x -> new nodes.Stylesheet( BOSSParser.filter(x) ),

definition = ident:x '(' functionBody:y ')' sc* block:z -> new nodes.Definition(x.name, y, z),

assignment = property:p '=' value:v decldelim? sc* -> new nodes.Ident(p, v),

variable = '$' ident:x -> x,

statement = definition | ruleset | atrule | assignment,

atrulerq = tset*:ap -> ap.join(''),

atruleb = atkeyword:ak atrulerq:aq block:b -> new nodes.Atrule(ak + aq, b),

atrules = atkeyword:ak atrulerq:aq ';' -> new nodes.Atrule(ak + aq),

atrule = atruleb | atrules,

blockdecl = sc* (filter | declaration):x decldelim? sc* -> x
| sc* ruleset:x sc* -> x
| sc* decldelim sc* -> ' '
| sc+:s0 -> ' ',

decldelim = ';' -> ';',

block = '{' blockdecl*:x '}' -> new nodes.Block(x),

ruleset = selector:s block:b -> new nodes.Ruleset(s, b),

combinator = ('+' | '>' | '~' | '&'):x -> x,

attrib = '[' sc* ident:x sc* attrselector:a sc* (ident | string):y sc* ']' -> '[' + [x, a, y].join('') + ']'
| '[' sc* ident:x sc* ']' -> '[' + x.join('') + ']',

clazz = '.' ident:x -> '.' + x,

pseudoe = seq('::') ident:x -> '::' + x,

pseudoc = ':' (pseudof | ident):x -> ':' + x,

pseudof = ident:x '(' pseudofBody:y ')' -> new nodes.Call(x.name, y),

pseudofBody = (simpleselector)*:x -> BOSSParser.createExpression( BOSSParser.filter(x) , true),

pseudo = pseudoe | pseudoc,

nthf = ':' seq('nth-'):x (seq('child') | seq('last-child') | seq('of-type') | seq('last-of-type')):y -> x + y,

nth = (digit | 'n')+:x -> x.join('')
| (seq('even') | seq('odd')):x -> x,

nthselector = nthf:x '(' (sc | ('+' | '-'):op -> new nodes.Literal(op) | nth)*:y ')'
-> ':' + new nodes.Ident(x) + '(' +y.join('') + ')',

namespace = '|' -> '|',

simpleselector = (nthselector | combinator | attrib | pseudo | clazz | shash | any | sc | namespace)+:x
-> new nodes.Selector(x),

selector = (simpleselector | delim)+:x -> x.filter(function (selector) { return selector !== ',' }),

declaration = property:x ':' value:y -> new nodes.Declaration(x, y),

filterp = (seq('-filter') | seq('_filter') | seq('*filter') | seq('-ms-filter') | seq('filter')):t sc*
-> new nodes.Ident(t),

progid = sc* seq('progid:DXImageTransform.Microsoft.'):x letter+:y '(' (m_string | m_comment | ~')' char)+:z ')' sc*
-> new nodes.Literal(x + y.join('') + '(' + z.join('') + ')'),

filterv = progid+:x -> x,

filter = filterp:x ':' filterv:y -> new nodes.Declaration(p, v),

property = (ident | variable):x sc* -> x.toString(),

important = '!' sc* seq('important') -> new nodes.Literal('!important'),

primary = percentage | dimension | number,

unary = ('-' | '+'):op primary:x -> x.operate('*', new nodes.Dimension(op + 1))
| primary,

multiplicative = multiplicative:x sc+ ('*' | '/' | '%'):op sc+ unary:y -> new nodes.BinOp(op, x, y)
| unary,

additive = additive:x sc+ ('+' | '-'):op sc+ multiplicative:y -> new nodes.BinOp(op, x, y)
| multiplicative,

expression = additive,

operator = ('/' | ',' | ':' | '='):x -> new nodes.Literal(x),

uri = seq('url(') sc* string:x sc* ')' -> new nodes.Call('url', new nodes.Arguments(false, [x]))
| seq('url(') sc* (~')' ~m_w char)*:x sc* ')'
-> new nodes.Call('url', new nodes.Arguments(false, [new nodes.Literal(x.join(''))])),

value = (sc | vhash | any | block | atkeyword | operator | important)+:x
-> BOSSParser.createExpression( BOSSParser.filter(x) ),

funktion = ident:x '(' functionBody:y ')' -> new nodes.Call(x.name, y),

functionBody = (tset | clazz)*:x -> BOSSParser.createExpression( BOSSParser.filter(x) , true),

braces = '(' tset*:x ')' -> '(' + x.join('') + ')'
| '[' tset*:x ']' -> '[' + x.join('') + ']',

// Don't reuse CSS-grammar elements to parse JS-grammar basics!

jsLT = '\n' | '\r',

jsComment = jsMLComment | jsSLComment,

jsMLComment = ``/*'' (~``*/'' char)*:x ``*/'' -> ('/*' + x.join('') + '*/'),

jsSLComment = ``//'' (~jsLT char)*:x -> ('//' + x.join('')),

jsString = '"' jsDSChar*:x '"' -> ('"' + x.join('') + '"')
| '\'' jsSSChar*:x '\'' -> ('\'' + x.join('') + '\''),

jsDSChar = ~'"' ~'\\' ~jsLT char
| jsEscapeChar
| jsLineContinuation,

jsSSChar = ~'\'' ~'\\' ~jsLT char
| jsEscapeChar
| jsLineContinuation,

jsLineContinuation = '\\' jsLT*:x -> ('\\' + x.join('')),

jsEscapeChar = '\\' char:x -> ('\\' + x),

jsInBraceChar = ~'(' ~')' char:x -> x,

jsBracesContent = (jsComment | jsString | jsEscapeChar | jsInBraceChar)+:x -> x.join(''),

functionExpressionBody = '(' jsBracesContent*:x (
functionExpressionBody:y jsBracesContent*:z -> (y + z.join(''))
)*:xx
')' -> ('(' + x.join('') + xx.join('') + ')')
| jsBracesContent*:x functionExpressionBody:y jsBracesContent*:z -> (x.join('') + y + z.join(''))
| jsBracesContent+:x -> x.join(''),

functionExpression = ``expression('' functionExpressionBody*:x ')' -> 'expression(' + x.join('') + ')',

any = braces | string | expression | uri | functionExpression | funktion | ident | variable
}

BOSSParser.concat = function() {
var x = [];
for (var i in arguments) {
x = x.concat(arguments[i]);
};

return x;
};

// CSS 2.1 / 4.1.1 Tokenization / macro section

// [_a-z]|{nonascii}|{escape}
BOSSParser._m_nmstart = function(x) {
return /^[_a-zA-Z\*]+$/.test(x) || this._m_escape(x);
};

// \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
BOSSParser._m_unicode = function(x) {
return /^\\[0-9a-fA-F]{1,6}(\r\n|[ \n\r\t\f])?$/.test(x);
};

// {unicode}|\\[^\n\r\f0-9a-f]
BOSSParser._m_escape = function(x) {
return this._m_unicode(x) || /^\\[^\n\r\f0-9a-fA-F]+$/.test(x);
}

// [_a-z0-9-]|{nonascii}|{escape}
BOSSParser._m_nmchar = function(x) {
return /^[_a-zA-Z0-9\-]+$/.test(x) || this._m_escape(x);
};

// [a-z0-9]|{nonascii}|{escape}
BOSSParser._m_nmchar2 = function(x) {
return /^[a-zA-Z0-9]+$/.test(x) || this._m_escape(x);
};

// [\n\r\f]
BOSSParser._m_nl = function(x) {
return /^[\n\r\f]+$/.test(x);
};

// [ \t\r\n\f]*
BOSSParser._m_w = function(x) {
return /^[ \t\r\n\f]+$/.test(x);
};

BOSSParser.createExpression = function (x, isArgs) {
var ctor = isArgs ? 'Arguments' : 'Expression',
expr = new nodes[ctor],
list = isArgs ? new nodes[ctor](true) : undefined;

x.forEach(function (node) {
if (node.value === ',') {
list = list || new nodes[ctor](true);
list.push(expr);
expr = new nodes.Expression;
return;
}
expr.push(node);
})

list && list.push(expr);

return list || expr;
}

BOSSParser.filter = function (arr) {
return arr.filter(function (any) { return !(typeof any === 'string' && /^[\n\s]+$/.test(any)) })
}
51 changes: 0 additions & 51 deletions src/boss/parser.ometajs

This file was deleted.

Loading

0 comments on commit 9674958

Please sign in to comment.