Skip to content

Commit cc1640f

Browse files
author
CLR
committed
-- still working towards a functional engine --
1 parent 0c99491 commit cc1640f

22 files changed

+990
-239
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.DS_Store
2+
**~
3+
casey.time.txt

README

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The library setup is virtually identical to the HAML / SASS layout, with exceptions for obvious refactoring opportunities, such as moving the 'shared' library out of 'haml/'
2+
3+

lib/sassij.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Sassij = function(){};
2+
3+
// Sugar functions follow.
4+
// Inspired by [ http://www.crockford.com/javascript/inheritance.html ]
5+
6+
Function.prototype.method = function( name, lambda ){
7+
this.prototype[name] = lambda;
8+
return this;
9+
};
10+
11+
// To be used as in ChildClass.inherits( ParentClass )
12+
Function.method( 'inherits', function( parent ) {
13+
var d = {};
14+
var p = ( this.prototype = new parent() );
15+
16+
this.method( '_super', function _super( name ){
17+
if( !( name in d ) ){
18+
d[name] = 0;
19+
}
20+
var f, r, t = d[name];
21+
var v = parent.prototype;
22+
if( t ){
23+
while( t ){
24+
v = v.constructor.prototype;
25+
t -= 1;
26+
}
27+
f = v[name];
28+
} else {
29+
f = p[name];
30+
if( f == this[name] ){
31+
f = v[name];
32+
}
33+
}
34+
d[name] += 1;
35+
r = f.apply( this, Array.prototype.slice.apply( arguments, [1] ) );
36+
d[name] -= 1;
37+
return r;
38+
});
39+
return this;
40+
});
41+
42+
Function.method( 'swiss', function( parent ){
43+
for( var i = 1; i < arguments.length; i++ ){
44+
var name = arguments[i];
45+
this.prototype[name] = parent.prototype[name];
46+
}
47+
return this;
48+
});
49+
50+
Function.method( 'attr', function( valueName, initialValue ){
51+
var that = this;
52+
that.prototype[valueName] = initialValue;
53+
54+
// Create a getter.
55+
this.method( ( 'get' + valueName.toCamelCase() ), function(){
56+
return that.prototype[valueName];
57+
} );
58+
59+
// Create a setter.
60+
this.method( 'set' + valueName.toCamelCase(), function( newValue ){
61+
that.prototype[valueName] = newValue;
62+
return that;
63+
});
64+
65+
return this;
66+
});
67+
68+
Function.prototype.bind = function( object ){
69+
var method = this;
70+
var temp = function() {
71+
return method.apply( object, arguments );
72+
};
73+
return temp;
74+
}

lib/sassij/engine.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// This is the class where all the parsing and processing of the Sass
2+
// template is done. It can be directly used by the user by creating a
3+
// new instance and calling <tt>render</tt> to render the template. For example:
4+
//
5+
// template = File.load('stylesheets/sassy.sass')
6+
// sass_engine = Sass::Engine.new(template)
7+
// output = sass_engine.render
8+
// puts output
9+
SassijEngine = function( vars ){
10+
11+
var dslString = vars;
12+
// The character that begins a CSS attribute.
13+
var attributeChar = ':';
14+
// The character that designates that an attribute should be assigned to a SassScript,
15+
// expression.
16+
var scriptChar = '=';
17+
// The character that designates the beginning of a comment, either Sass or CSS.
18+
var commentChar = '/';
19+
// The character that follows the general COMMENT_CHAR and designates a Sass comment,
20+
// which is not output as a CSS comment.
21+
var sassCommentChar = '/';
22+
// The character that follows the general COMMENT_CHAR and designates a CSS comment,
23+
// which is embedded in the CSS document.
24+
var cssCommentChar = '*';
25+
// The character used to denote a compiler directive.
26+
var directiveChar = '@';
27+
// Designates a non-parsed rule.
28+
var escapeChar = '\\';
29+
// Designates block as mixin definition rather than CSS rules to output
30+
var mixinDefinitionChar = '=';
31+
// Includes named mixin declared using MIXIN_DEFINITION_CHAR
32+
var mixinIncludeChar = '+';
33+
// The regex that matches and extracts data from
34+
// attributes of the form <tt>:name attr</tt>.
35+
var attribute = new RegExp( "^:([^\s=:]+)\s*(=?)(?:\s+|$)(.*)" );
36+
// The regex that matches attributes of the form <tt>name: attr</tt>.
37+
var attributeAlternateMatcher = new RegExp( "^[^\s:]+\s*[=:](\s|$)" );
38+
// The regex that matches and extracts data from
39+
// attributes of the form <tt>name: attr</tt>.
40+
var attributeAltornate = new RegExp( "^([^\s=:]+)(\s*=|:)(?:\s+|$)(.*)" );
41+
42+
newOptions.style = 'nested';
43+
newOptions.loadPaths = [ '.' ];
44+
this.options = newOptions;
45+
this.template = template;
46+
}
47+
48+
SassijTreeNode.attr( 'environment', new SassijEnvironment() );
49+
SassijTreeNode.prototype.environment.important = new SassijScriptString( "!important" );
50+
51+
SassijEngine.method( 'render', function(){
52+
return 'test';
53+
});
54+
55+
SassijEngine.method( 'toSassij', function(){
56+
return dslString;
57+
});
58+
59+
// We interpret the syntax as a tree, with each indentation representing a new leaf.
60+
SassijEngine.method( 'renderToTree', function(){
61+
root = new SassijTreeNode();
62+
root.appendChildren( tree( tabulate( this.toSassij() ) );
63+
return root;
64+
});
65+
66+
67+
Sassij.prototype.engine = new SassijEngine();
68+

lib/sassij/error.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SassijError = function( comment, lineNumber ){
2+
return comment + ": " + lineNumber;
3+
}
4+

lib/sassij/tree/attr-node.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
SassijTreeAttrNode = function( newName, newValue, newOptions ){
2+
this.name = newName;
3+
this.value = newValue;
4+
this._super( newOptions );
5+
};
6+
7+
SassijTreeAttrNode.inherits( SassijTreeNode );
8+
9+
= function( newOptions ){
10+
11+
if( newOptions ){
12+
this.options = newOptions;
13+
} else {
14+
this.options = { style: 'none' };
15+
}
16+
this.style = this.options.style;
17+
this.children = [];
18+
19+
this.line = function(){
20+
21+
}
22+
this.filename = function(){
23+
24+
}
25+
};
26+
27+
module Sass::Tree
28+
class AttrNode < Node
29+
attr_accessor :name, :value
30+
31+
def initialize(name, value, options)
32+
@name = name
33+
@value = value
34+
super(options)
35+
end
36+
37+
def to_s(tabs, parent_name = nil)
38+
if value[-1] == ?;
39+
raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump} (This isn't CSS!).", @line)
40+
end
41+
real_name = name
42+
real_name = "#{parent_name}-#{real_name}" if parent_name
43+
44+
if value.empty? && children.empty?
45+
raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump}.", @line)
46+
end
47+
48+
join_string = case @style
49+
when :compact; ' '
50+
when :compressed; ''
51+
else "\n"
52+
end
53+
spaces = ' ' * (tabs - 1)
54+
to_return = ''
55+
if !value.empty?
56+
to_return << "#{spaces}#{real_name}:#{@style == :compressed ? '' : ' '}#{value};#{join_string}"
57+
end
58+
59+
children.each do |kid|
60+
to_return << "#{kid.to_s(tabs, real_name)}" << join_string
61+
end
62+
63+
(@style == :compressed && parent_name) ? to_return : to_return[0...-1]
64+
end
65+
66+
protected
67+
68+
def perform!(environment)
69+
@name = interpolate(@name, environment)
70+
@value = @value.is_a?(String) ? interpolate(@value, environment) : @value.perform(environment).to_s
71+
super
72+
end
73+
74+
private
75+
76+
def declaration
77+
":#{name} #{value}"
78+
end
79+
80+
def invalid_child?(child)
81+
if !child.is_a?(AttrNode) && !child.is_a?(CommentNode)
82+
"Illegal nesting: Only attributes may be nested beneath attributes."
83+
end
84+
end
85+
end
86+
end

lib/sassij/tree/node.js

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
SassijTreeNode = function( newOptions ){
2+
3+
if( newOptions ){
4+
this.options = newOptions;
5+
} else {
6+
this.options = { style: 'none' };
7+
}
8+
this.style = this.options.style;
9+
this.line = function(){
10+
11+
}
12+
this.filename = function(){
13+
14+
}
15+
};
16+
17+
SassijTreeNode.attr( 'children', [] );
18+
19+
SassijTreeNode.method( 'addChild', function( child ){
20+
if( error = this.isInvalidChild( child ) ) {
21+
throw( new SassijError( error, child.line ) );
22+
}
23+
this.children.push( child );
24+
return this;
25+
});
26+
27+
SassijTreeNode.method( 'lastChild', function() {
28+
return this.getChildren()[ this.getChildren().length - 1 ];
29+
});
30+
31+
SassijTreeNode.method( 'toS', function() {
32+
result = "";
33+
for( child in this.getChildren() ){
34+
if( child.isType( AttrNode ) ){
35+
throw( new SassijError( 'Attributes aren\'t allowed at the root of a document.', child.line ) );
36+
} else {
37+
// result << "#{child.to_s(1)}" + (@style == :compressed ? '' : "\n")
38+
// need to add a @style indicator at some point
39+
result += child.toS( 1 );
40+
}
41+
}
42+
// @style == :compressed ? result+"\n" : result[0...-1]
43+
return result;
44+
});
45+
46+
// This method should be overridden by subclasses to return an error message
47+
// if the given child node is invalid,
48+
// and false or nil otherwise.
49+
SassijTreeNode.method( 'isInvalidChild', function( child ){
50+
return false;
51+
});
52+
53+
54+
// def perform(environment)
55+
// _perform(environment)
56+
// rescue Sass::SyntaxError => e
57+
// e.sass_line ||= line
58+
// raise e
59+
// end
60+
// Casey says: I don't really know what this function pretends to do.
61+
// I think this is just a wrapper to catch errors on evaluating the node.
62+
SassijTreeNode.method( 'perform', function( environment ){
63+
try{
64+
_perform( environment );
65+
} catch( error ) {
66+
throw( new SassijError( error, 'unknown' ) );
67+
}
68+
});
69+
70+
// def _perform(environment)
71+
// node = dup
72+
// node.perform!(environment)
73+
// node
74+
// end
75+
SassijTreeNode.method( '_perform', function( environment ){
76+
node = this.clone();
77+
node.performBang( environment );
78+
return node;
79+
});
80+
81+
// def perform!(environment)
82+
// self.children = perform_children(Environment.new(environment))
83+
// end
84+
// This function actually evaluates the node, and then recurses to
85+
// the children for evaluation with performChildren().
86+
SassijTreeNode.method( 'performBang', function( environment ){
87+
this.setChildren( this.performChildren( new SassijEnvironment( environment ) ) );
88+
});
89+
90+
91+
// def perform_children(environment)
92+
// children.map {|c| c.perform(environment)}.flatten
93+
// end
94+
// Casey says: js function for flatten()? Not sure why that would ever be needed.
95+
SassijTreeNode.method( 'performChildren', function( environment ){
96+
childrenMap = [];
97+
for( child in this.getChildren() ){
98+
childrenMap.push( child.perform( environment ) );
99+
}
100+
return childrenMap;
101+
});
102+
103+
// def interpolate(text, environment)
104+
// res = ''
105+
// rest = Haml::Shared.handle_interpolation text do |scan|
106+
// escapes = scan[2].size
107+
// res << scan.matched[0...-2 - escapes]
108+
// if escapes % 2 == 1
109+
// res << "\\" * (escapes - 1) << '#{'
110+
// else
111+
// res << "\\" * [0, escapes - 1].max
112+
// res << Script::Parser.new(scan, line, scan.pos - scan.matchedsize, filename).
113+
// parse_interpolated.perform(environment).to_s
114+
// end
115+
// end
116+
// res + rest
117+
// end
118+
// From what I can tell, this function handles the escapes for ruby-style inline
119+
// string evaluation. We'll come back to this.
120+
// this.interpolate = function( text, environment ){
121+
// res = "";
122+
// scanner = new Shared.handle_interpolate( text );
123+
// while( var scan = scanner.scan( /(.*?)(\\*)\#\{/ ) ){
124+
// if escapes =
125+
// res +=
126+
//
127+
// }
128+
// escapes = rest[2].length;
129+
// res += rest.matched[]
130+
// //something else goes here!!!!!
131+
132+
// // Append the rest of the original string.
133+
// return res += scanner.getCurrent();
134+
// }
135+
136+
// CAN'T FIND THIS METHOD CALLED ANYWHERE
137+
// def balance(*args)
138+
// res = Haml::Shared.balance(*args)
139+
// return res if res
140+
// raise Sass::SyntaxError.new("Unbalanced brackets.", line)
141+
// end
142+
// this.balance( args ){
143+
// if( res = HamlijShared.balance( args ) ){
144+
// return res;
145+
// };
146+
// throw( new SassijError( 'Unbalanced brackets.', 'unknown' ) );
147+
// }
148+

0 commit comments

Comments
 (0)