Skip to content

Instantly share code, notes, and snippets.

@andreypopp
Created July 28, 2013 21:24
Show Gist options
  • Save andreypopp/6100311 to your computer and use it in GitHub Desktop.
Save andreypopp/6100311 to your computer and use it in GitHub Desktop.
#!/usr/bin/env coffee
fs = require 'fs'
{Parser, tree} = require 'less'
{extend} = require 'underscore'
filename = process.argv[2]
parser = new Parser(filename: filename)
str = fs.readFileSync(filename).toString()
defineVisitor = (base, props) ->
extend Object.create(base), props
renderValue = (node) ->
impl = Object.create(expressionVisitor)
visitor = new tree.visitor impl
visitor.visit node
value = impl.value.trim()
value
renderTree = (node, indent = '') ->
impl = Object.create(treeVisitor)
impl.indent = indent
new tree.visitor(impl).visit(node)
renderMixinParam = (node) ->
param = node.name.slice(1)
if node.value
param = "#{param}=#{renderValue(node.value)}"
param
renderMixinArg = (node) ->
param = renderValue(node.value)
if node.name
param = "#{node.name.slice(1)}=#{param}"
param
baseVisitor =
visitAlpha: (node) ->
throw new Error('not implemented')
visitAnonymous: (node) ->
throw new Error('not implemented')
visitAssigment: (node) ->
throw new Error('not implemented')
visitCall: (node) ->
throw new Error('not implemented')
visitColor: (node) ->
throw new Error('not implemented')
visitComment: (node) ->
throw new Error('not implemented')
visitCondition: (node) ->
throw new Error('not implemented')
visitDimension: (node) ->
throw new Error('not implemented')
visitDirective: (node) ->
throw new Error('not implemented')
visitElement: (node) ->
throw new Error('not implemented')
visitExpression: (node) ->
throw new Error('not implemented')
visitExtend: (node) ->
throw new Error('not implemented')
visitImport: (node) ->
throw new Error('not implemented')
visitJavaScript: (node) ->
throw new Error('not implemented')
visitKeyword: (node) ->
throw new Error('not implemented')
visitMedia: (node) ->
throw new Error('not implemented')
visitMixin: (node) ->
throw new Error('not implemented')
visitMixinCall: (node) ->
throw new Error('not implemented')
visitMixinDefinition: (node) ->
throw new Error('not implemented')
visitNegative: (node) ->
throw new Error('not implemented')
visitOperation: (node) ->
throw new Error('not implemented')
visitParen: (node) ->
throw new Error('not implemented')
visitQuoted: (node) ->
throw new Error('not implemented')
visitRule: (node, options) ->
throw new Error('not implemented')
visitRuleset: (node, options) ->
throw new Error('not implemented')
visitSelector: (node, options) ->
throw new Error('not implemented')
visitValue: (node) ->
throw new Error('not implemented')
visitVariable: (node) ->
throw new Error('not implemented')
visitURL: (node) ->
throw new Error('not implemented')
visitUnicodeDescriptor: (node) ->
throw new Error('not implemented')
expressionVisitor = defineVisitor baseVisitor,
acc: (v) ->
@value = '' unless @value
@value += ' ' + v
visitAnonymous: (node) ->
@acc node.value
visitDimension: (node) ->
@acc "#{node.value}#{node.unit.numerator.join('')}"
visitVariable: (node) ->
@acc node.name.slice(1)
visitCall: (node, options) ->
options.visitDeeper = false
args = node.args.map(renderValue).join(', ')
@acc "#{node.name}(#{args})"
visitSelector: (node, options) ->
options.visitDeeper = false
@acc node.elements.map(renderValue).join(' ')
visitElement: (node) ->
@acc "#{node.combinator.value} #{node.value}"
visitKeyword: (node) ->
@acc node.value
visitQuoted: (node) ->
@acc "#{node.quote}#{node.value}#{node.quote}"
visitParen: (node, options) ->
options.visitDeeper = false
@acc renderValue(node.value)
visitRule: (node, options) ->
options.visitDeeper = false
@acc "#{node.name} #{renderValue(node.value)}"
visitOperation: (node, options) ->
options.visitDeeper = false
if node.operands.length != 2
throw new Error('assertion')
[left, right] = node.operands
@acc "#{renderValue(left)}#{node.op}#{renderValue(right)}"
visitValue: (node, options) ->
options.visitDeeper = false
@acc node.value.map(renderValue).join(', ')
visitExpression: (node, options) ->
options.visitDeeper = false
@acc node.value.map(renderValue).join(' ')
visitColor: (node) ->
if node.rgb
c = "rgb(#{node.rgb.join(', ')}"
if node.alpha
c += ", #{node.alpha}"
c += ")"
@acc c
else
throw new Error("unknow color #{node}")
treeVisitor = defineVisitor baseVisitor,
indent: ''
increaseIndent: ->
@indent + ' '
decreaseIndent: ->
@indent.slice(0, -2)
p: (m) ->
console.log "#{@indent}#{m.trim()}"
visitRuleset: (node, options) ->
unless node.root
options.visitDeeper = false
@p node.selectors.map(renderValue).join(', ')
for rule in node.rules
renderTree(rule, @increaseIndent())
visitRulesetOut: (node) ->
unless node.root
@decreaseIndent()
visitRule: (node, options) ->
options.visitDeeper = false
name = node.name
if name[0] == '@'
@p "#{name.slice(1)} = #{renderValue(node.value)}"
else
@p "#{name} #{renderValue(node.value)}"
visitComment: (node) ->
@p node.value
visitMedia: (node, options) ->
options.visitDeeper = false
@p "@media #{renderValue(node.features)}"
renderTree(node.ruleset, @increaseIndent())
visitSelector: (node, options) ->
options.visitDeeper = false
@p node.elements.map(renderValue).join('')
visitMixinDefinition: (node, options) ->
options.visitDeeper = false
@p "#{node.name.slice(1)}(#{node.params.map(renderMixinParam).join(', ')})"
for rule in node.rules
renderTree(rule, @increaseIndent())
visitMixinCall: (node, options) ->
options.visitDeeper = false
v = "#{renderValue(node.selector).slice(1)}"
if node.arguments.length > 0
v += "(#{node.arguments.map(renderMixinArg).join(', ')})"
@p v
parser.parse str, (err, node) ->
process.exit(1) if err
renderTree node
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment