SassyJSON 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.md +11 -0
  2. data/README.md +96 -0
  3. data/lib/SassyJSON.rb +14 -0
  4. data/src/SassyJSON.scss +2 -0
  5. data/src/decode/api/_json.scss +26 -0
  6. data/src/decode/decode.scss +28 -0
  7. data/src/decode/helpers/all/_throw.scss +11 -0
  8. data/src/decode/helpers/all/_value.scss +49 -0
  9. data/src/decode/helpers/color/_color.scss +45 -0
  10. data/src/decode/helpers/color/_get-color-value.scss +18 -0
  11. data/src/decode/helpers/color/_hex-to-dec.scss +20 -0
  12. data/src/decode/helpers/color/_hex.scss +39 -0
  13. data/src/decode/helpers/color/_hsl.scss +46 -0
  14. data/src/decode/helpers/color/_rgb.scss +46 -0
  15. data/src/decode/helpers/map/_consume.scss +33 -0
  16. data/src/decode/helpers/number/_find-digits.scss +40 -0
  17. data/src/decode/helpers/number/_find-exponent.scss +46 -0
  18. data/src/decode/helpers/number/_find-integer.scss +38 -0
  19. data/src/decode/helpers/number/_pow.scss +22 -0
  20. data/src/decode/helpers/string/_find-ending-quote.scss +36 -0
  21. data/src/decode/helpers/string/_length.scss +39 -0
  22. data/src/decode/helpers/string/_strip-token.scss +16 -0
  23. data/src/decode/types/_bool.scss +40 -0
  24. data/src/decode/types/_list.scss +54 -0
  25. data/src/decode/types/_map.scss +78 -0
  26. data/src/decode/types/_null.scss +19 -0
  27. data/src/decode/types/_number.scss +63 -0
  28. data/src/decode/types/_string.scss +41 -0
  29. data/src/encode/api/_json.scss +17 -0
  30. data/src/encode/encode.scss +17 -0
  31. data/src/encode/helpers/_quote.scss +9 -0
  32. data/src/encode/mixins/_json.scss +34 -0
  33. data/src/encode/types/_bool.scss +9 -0
  34. data/src/encode/types/_color.scss +9 -0
  35. data/src/encode/types/_list.scss +13 -0
  36. data/src/encode/types/_map.scss +13 -0
  37. data/src/encode/types/_null.scss +9 -0
  38. data/src/encode/types/_number.scss +9 -0
  39. data/src/encode/types/_string.scss +9 -0
  40. metadata +119 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ * `1.0.6`: released a Ruby gem
4
+ * `1.0.5`: improved the encoding mixin
5
+ * `1.0.4`: fixed an error in map parsing
6
+ * `1.0.3`: slightly edited the mixin to dump JSON to CSS
7
+ * `1.0.2`: fixed an issue with string parsing
8
+ * `1.0.1`: fixed an issue with alpha color parsing
9
+ * `1.0.0`: Stable API. `json-encode` and `json-decode`
10
+ * `0.0.2`: added `json-decode` and test
11
+ * `0.0.1`: initial commit
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # SassyJSON [![NPM version](https://badge.fury.io/js/sassyjson.png)](http://badge.fury.io/js/sassyjson) [![Build Status](https://travis-ci.org/HugoGiraudel/SassyJSON.png?branch=master)](https://travis-ci.org/HugoGiraudel/SassyJSON)
2
+
3
+ SassyJSON is a Sass-powered API for JSON. It provides you the classic `json-encode` and `json-decode` directly from your Sass files. We'll leave you the only judges of the point of this.
4
+
5
+ ## Install
6
+
7
+ SassyJSON is currently available in [npm](https://npmjs.org/).
8
+
9
+ ### Git
10
+
11
+ ``` git
12
+ git clone https://github.com/HugoGiraudel/SassyJSON.git && cd SassyJSON
13
+ ```
14
+
15
+ ### npm
16
+
17
+ ``` bash
18
+ npm install sassyjson --save-dev
19
+ ```
20
+
21
+ ### Compass extension
22
+
23
+ 1. `gem install SassyJSON`
24
+ 2. Add `require 'SassyJSON'` to your `config.rb`
25
+ 3. Import it in your stylesheets with `@import 'SassyJSON'`
26
+
27
+ ### Sass
28
+
29
+ If you only want to play around the code without cloning the repo or using npm, you can find a [single file](https://github.com/HugoGiraudel/SassyJSON/blob/master/dist/_SassyJSON.scss) containing the whole API in the [dist](https://github.com/HugoGiraudel/SassyJSON/tree/master/dist) folder. You can copy its content directly into [Sassmeister](http://sassmeister.com/) to play with the code.
30
+
31
+ ## Example
32
+
33
+ ### Encoding Sass to JSON
34
+
35
+ #### Sass
36
+
37
+ ``` scss
38
+ $map: ((a: (1 2 ( b : 1 )), b: ( #444444, false, ( a: 1, b: test ) ), c: (2 3 4 string)));
39
+
40
+ @include json-encode($map);
41
+ ```
42
+
43
+ #### CSS
44
+
45
+ ``` css
46
+ /*! json-encode: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}' */
47
+
48
+ body::before {
49
+ content: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
50
+ }
51
+
52
+ head {
53
+ font-family: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
54
+ }
55
+
56
+ @media -json-encode {
57
+ json {
58
+ json: '{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}';
59
+ }
60
+ }
61
+ ```
62
+
63
+ If you want to restrict the output to only one of the three drivers (comment, media query or regular output) you can pass a flag as the second parameter with one of the four following keywords: `all`, `comment`, `media` or `regular`. Default is `all`.
64
+
65
+ ### Decoding JSON to Sass
66
+
67
+ ``` scss
68
+ $json-decode: json-decode('{"a": [1, 2, {"b": 1}], "b": ["#444444", false, {"a": 1, "b": "test"}], "c": [2, 3, 4, "string"]}');
69
+ // ("a": 1 2 ("b": 1), "b": #444444 false ("a": 1, "b": "test"), "c": 2 3 4 "string")
70
+ ```
71
+
72
+ ## Requirements
73
+
74
+ All you need is a clean version of Sass 3.3. Otherwise it's just pure Sass madness.
75
+
76
+ ## Development
77
+
78
+ ### You need
79
+
80
+ * [NodeJS](http://nodejs.org)
81
+ * [Ruby](https://www.ruby-lang.org/)
82
+ * Sass 3.3 via `gem instals sass --pre`
83
+ * `grunt-cli` via `npm install -g grunt-cli`
84
+
85
+ ### How to
86
+
87
+ 1. Fork this repository
88
+ 2. Run `npm install`
89
+ 3. `grunt dev`
90
+ 4. Make your changes + write tests
91
+ 5. Commit + Pull request
92
+
93
+ ## Credits
94
+
95
+ * [Fabrice Weinberg](http://twitter.com/fweinb)
96
+ * [Hugo Giraudel](http://twitter.com/hugogiraudel)
data/lib/SassyJSON.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'compass'
2
+ extension_path = File.expand_path(File.join(File.dirname(__FILE__), ".."))
3
+ Compass::Frameworks.register('SassyJSON', :path => extension_path)
4
+
5
+ # Version is a number. If a version contains alphas, it will be created as a prerelease version
6
+ # Date is in the form of YYYY-MM-DD
7
+ module SassyJSON
8
+ VERSION = "1.0.6"
9
+ DATE = "2014-01-19"
10
+ end
11
+
12
+ module Sass::Script::Functions
13
+
14
+ end
@@ -0,0 +1,2 @@
1
+ @import "decode/decode";
2
+ @import "encode/encode";
@@ -0,0 +1,26 @@
1
+ // Parse a JSON string
2
+ // --------------------------------------------------------------------------------
3
+ // @param $json: JSON string to parse
4
+ // --------------------------------------------------------------------------------
5
+ // @throw "Input string may not be null"
6
+ // --------------------------------------------------------------------------------
7
+ // @return [literal|false]
8
+
9
+ @function json-decode($json) {
10
+ $length: str-length($json);
11
+ $pointer: 1;
12
+ $value: null;
13
+
14
+ @if $json == null {
15
+ @return _throw("Input string may not be null.", $pointer);
16
+ }
17
+
18
+ @while $value != false // Stop if error
19
+ and $pointer <= $length {
20
+ $read: _json-decode--value($json, $pointer);
21
+ $pointer: nth($read, 1);
22
+ $value: nth($read, 2);
23
+ }
24
+
25
+ @return $value;
26
+ }
@@ -0,0 +1,28 @@
1
+ // Helpers
2
+ @import "helpers/all/throw";
3
+ @import "helpers/all/value";
4
+ @import "helpers/map/consume";
5
+ @import "helpers/number/pow";
6
+ @import "helpers/number/find-digits";
7
+ @import "helpers/number/find-integer";
8
+ @import "helpers/number/find-exponent";
9
+ @import "helpers/color/color";
10
+ @import "helpers/color/get-color-value";
11
+ @import "helpers/color/rgb";
12
+ @import "helpers/color/hsl";
13
+ @import "helpers/color/hex";
14
+ @import "helpers/color/hex-to-dec";
15
+ @import "helpers/string/length";
16
+ @import "helpers/string/strip-token";
17
+ @import "helpers/string/find-ending-quote";
18
+
19
+ // Type specific decoding functions
20
+ @import "types/bool";
21
+ @import "types/null";
22
+ @import "types/list";
23
+ @import "types/map";
24
+ @import "types/number";
25
+ @import "types/string";
26
+
27
+ // Public API
28
+ @import "api/json";
@@ -0,0 +1,11 @@
1
+ // Logs an error at `$pointer` with `$string` message
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: error message
4
+ // @param [number] $pointer: pointer position
5
+ // --------------------------------------------------------------------------------
6
+ // @return [list] (pointer, false)
7
+
8
+ @function _throw($string, $pointer) {
9
+ @warn "ERROR::#{$pointer}::#{$string}";
10
+ @return $pointer, false;
11
+ }
@@ -0,0 +1,49 @@
1
+ // Delay parsing to type-specific function
2
+ // according to found character
3
+ // --------------------------------------------------------------------------------
4
+ // @param [string] $source: JSON complete source
5
+ // @param [number] $pointer: current pointer
6
+ // --------------------------------------------------------------------------------
7
+ // @throw "Unexpected token $token."
8
+ // @throw "Empty JSON string."
9
+ // --------------------------------------------------------------------------------
10
+ // @return [list|false] (new pointer, parsed value)
11
+
12
+ @function _json-decode--value($source, $pointer) {
13
+ $length: str-length($source);
14
+
15
+ @while $pointer <= $length {
16
+ $token: str-slice($source, $pointer, $pointer);
17
+ $pointer: $pointer + 1;
18
+
19
+ @if $token == '{' {
20
+ @return _json-decode--map($source, $pointer);
21
+ }
22
+ @else if $token == '[' {
23
+ @return _json-decode--list($source, $pointer);
24
+ }
25
+ @else if $token == 't' {
26
+ @return _json-decode--true($source, $pointer);
27
+ }
28
+ @else if $token == 'f' {
29
+ @return _json-decode--false($source, $pointer);
30
+ }
31
+ @else if $token == '"' {
32
+ @return _json-decode--string($source, $pointer);
33
+ }
34
+ @else if $token == 'n' {
35
+ @return _json-decode--null($source, $pointer);
36
+ }
37
+ @else if index('1' '2' '3' '4' '5' '6' '7' '8' '9' '0' '-' '.', $token) {
38
+ @return _json-decode--number($source, $pointer);
39
+ }
40
+ @else if $token == ' ' {
41
+ // @continue;
42
+ }
43
+ @else {
44
+ @return _throw("Unexpected token `" + $token + "`.", $pointer);
45
+ }
46
+ }
47
+
48
+ @return _throw("Empty JSON string.", $pointer);
49
+ }
@@ -0,0 +1,45 @@
1
+ // Parses a JSON encoded string to see if it's a CSS color
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: JSON string
4
+ // --------------------------------------------------------------------------------
5
+ // @return [color|string] string or number, depending on the match
6
+
7
+ @function _color($string) {
8
+ @if type-of($string) == color {
9
+ @return $string;
10
+ }
11
+
12
+ $string: to-lower-case($string);
13
+ $keywords: "transparent" "black" "silver" "gray" "white" "maroon" "red" "purple" "fuchsia" "green" "lime" "olive" "yellow" "navy" "blue" "teal" "aqua" "aliceblue" "antiquewhite" "aqua" "aquamarine" "azure" "beige" "bisque" "black" "blanchedalmond" "blue" "blueviolet" "brown" "burlywood" "cadetblue" "chartreuse" "chocolate" "coral" "cornflowerblue" "cornsilk" "crimson" "cyan" "darkblue" "darkcyan" "darkgoldenrod" "darkgray" "darkgreen" "darkgrey" "darkkhaki" "darkmagenta" "darkolivegreen" "darkorange" "darkorchid" "darkred" "darksalmon" "darkseagreen" "darkslateblue" "darkslategray" "darkslategrey" "darkturquoise" "darkviolet" "deeppink" "deepskyblue" "dimgray" "dimgrey" "dodgerblue" "firebrick" "floralwhite" "forestgreen" "fuchsia" "gainsboro" "ghostwhite" "gold" "goldenrod" "gray" "green" "greenyellow" "grey" "honeydew" "hotpink" "indianred" "indigo" "ivory" "khaki" "lavender" "lavenderblush" "lawngreen" "lemonchiffon" "lightblue" "lightcoral" "lightcyan" "lightgoldenrodyellow" "lightgray" "lightgreen" "lightgrey" "lightpink" "lightsalmon" "lightseagreen" "lightskyblue" "lightslategray" "lightslategrey" "lightsteelblue" "lightyellow" "lime" "limegreen" "linen" "magenta" "maroon" "mediumaquamarine" "mediumblue" "mediumorchid" "mediumpurple" "mediumseagreen" "mediumslateblue" "mediumspringgreen" "mediumturquoise" "mediumvioletred" "midnightblue" "mintcream" "mistyrose" "moccasin" "navajowhite" "navy" "oldlace" "olive" "olivedrab" "orange" "orangered" "orchid" "palegoldenrod" "palegreen" "paleturquoise" "palevioletred" "papayawhip" "peachpuff" "peru" "pink" "plum" "powderblue" "purple" "red" "rosybrown" "royalblue" "saddlebrown" "salmon" "sandybrown" "seagreen" "seashell" "sienna" "silver" "skyblue" "slateblue" "slategray" "slategrey" "snow" "springgreen" "steelblue" "tan" "teal" "thistle" "tomato" "turquoise" "violet" "wheat" "white" "whitesmoke" "yellow" "yellowgreen";
14
+ $colors: transparent black silver gray white maroon red purple fuchsia green lime olive yellow navy blue teal aqua aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen;
15
+
16
+ // Deal with inherit keyword
17
+ @if $string == "inherit" {
18
+ @return unquote($string);
19
+ }
20
+
21
+ // Deal with color keywords
22
+ @if index($keywords, $string) {
23
+ @return nth($colors, index($keywords, $string));
24
+ }
25
+
26
+ // Deal with hexadecimal triplets
27
+ @else if str-slice($string, 1, 1) == '#' {
28
+ @return _from-hex($string);
29
+ }
30
+
31
+ // Deal with rgb(a) colors
32
+ @else if str-slice($string, 1, 3) == 'rgb' {
33
+ @return _from-rgb($string);
34
+ }
35
+
36
+ // Deal with hsl(a) colors
37
+ @else if str-slice($string, 1, 3) == 'hsl' {
38
+ @return _from-hsl($string);
39
+ }
40
+
41
+ // Return string
42
+ @else {
43
+ @return $string;
44
+ }
45
+ }
@@ -0,0 +1,18 @@
1
+ // Cast a stringified number / stringified percentage into number type
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: JSON string
4
+ // --------------------------------------------------------------------------------
5
+ // @return [number] unitless number or percentage
6
+
7
+ @function _get-color-value($string) {
8
+ $first: str-slice($string, 1, 1);
9
+ @if $first == '.' {
10
+ $string: '0' + $string;
11
+ }
12
+ $last: str-slice($string, -1, -1);
13
+ @return if(
14
+ $last == '%',
15
+ nth(_json-decode--number(str-slice($string, 1, -2), 2), 2) * 1%,
16
+ nth(_json-decode--number($string, 2), 2)
17
+ );
18
+ }
@@ -0,0 +1,20 @@
1
+ // Convert an hexadecimal number to a decimal number
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: hexadecimal value
4
+ // --------------------------------------------------------------------------------
5
+ // @return [number] decimal number
6
+
7
+ @function _hex-to-dec($string){
8
+ $hex: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" "d" "e" "f";
9
+ $string: to-lower-case($string);
10
+ $length: str-length($string);
11
+
12
+ $dec: 0;
13
+ @for $i from 1 through $length {
14
+ $factor: 1 + ( 15 * ( $length - $i ));
15
+ $index: index($hex, str-slice($string, $i, $i));
16
+ $dec: $dec + $factor * ($index - 1);
17
+ }
18
+
19
+ @return $dec;
20
+ }
@@ -0,0 +1,39 @@
1
+ // Cast a JSON encoded string into a hexadecimal color
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: JSON string
4
+ // --------------------------------------------------------------------------------
5
+ // @return [color|string] string or hex color depending on the match
6
+
7
+ @function _from-hex($string) {
8
+ $r: ""; $g: ""; $b: "";
9
+ $hex: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" "d" "e" "f";
10
+ $length: str-length($string);
11
+ $max: if($length == 4, 1, 2);
12
+
13
+ // Check for length accuracy
14
+ @if $length != 4 and $length != 7 {
15
+ @return $string;
16
+ }
17
+
18
+ // Loop from the second character (omitting #)
19
+ @for $i from 2 through $length {
20
+ $c: to-lower-case(str-slice($string, $i, $i));
21
+
22
+ // If wrong character, return
23
+ @if not index($hex, $c) {
24
+ @return $string;
25
+ }
26
+
27
+ @if str-length($r) < $max { $r: $r + $c }
28
+ @else if str-length($g) < $max { $g: $g + $c }
29
+ @else if str-length($b) < $max { $b: $b + $c }
30
+ }
31
+
32
+ @if $length == 4 {
33
+ $r: $r + $r;
34
+ $g: $g + $g;
35
+ $b: $b + $b;
36
+ }
37
+
38
+ @return rgb(_hex-to-dec($r), _hex-to-dec($g), _hex-to-dec($b));
39
+ }
@@ -0,0 +1,46 @@
1
+ // Cast a JSON encoded string into a hsl(a) color
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: JSON string
4
+ // --------------------------------------------------------------------------------
5
+ // @return [color|string] string or hsl(a) color, depending on the match
6
+
7
+ @function _from-hsl($string) {
8
+ $frags: ();
9
+ $is-alpha: str-slice($string, 4, 4) == 'a';
10
+ $start: str-index($string, "(");
11
+ $length: str-length($string);
12
+
13
+ @for $i from $start through $length {
14
+ $token: str-slice($string, $i, $i);
15
+ @if $token == ' ' {
16
+ // @continue;
17
+ }
18
+ @else if $token == '(' or $token == ',' {
19
+ $frags: append($frags, "");
20
+ }
21
+ @else if $token == ')' {
22
+ @if length($frags) != if($is-alpha, 4, 3) { @return $string; } // Parsing error
23
+ $hue: _get-color-value(nth($frags, 1));
24
+ $saturation: _get-color-value(nth($frags, 2));
25
+ $lightness: _get-color-value(nth($frags, 3));
26
+
27
+ @if not $hue or not $saturation or not $lightness {
28
+ @return $string;
29
+ }
30
+
31
+ @if $is-alpha {
32
+ @if length($frags) != 4 { @return $string; } // No alpha channel found
33
+ $alpha: _get-color-value(nth($frags, 4));
34
+ @if not $alpha { @return $string; } // Error parsing alpha channel
35
+ @return hsla($hue, $saturation, $lightness, $alpha);
36
+ }
37
+
38
+ @return hsl($hue, $saturation, $lightness);
39
+ }
40
+ @else {
41
+ $frags: set-nth($frags, length($frags), nth($frags, length($frags)) + $token);
42
+ }
43
+ }
44
+
45
+ @return $string;
46
+ }
@@ -0,0 +1,46 @@
1
+ // Cast a JSON encoded string into a rgb(a) color
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $string: JSON string
4
+ // --------------------------------------------------------------------------------
5
+ // @return [color|string] string or rgb(a) color depending on the match
6
+
7
+ @function _from-rgb($string) {
8
+ $frags: ();
9
+ $is-alpha: str-slice($string, 4, 4) == 'a';
10
+ $start: str-index($string, "(");
11
+ $length: str-length($string);
12
+
13
+ @for $i from $start through $length {
14
+ $token: str-slice($string, $i, $i);
15
+ @if $token == ' ' {
16
+ // @continue;
17
+ }
18
+ @else if $token == '(' or $token == ',' {
19
+ $frags: append($frags, "");
20
+ }
21
+ @else if $token == ')' {
22
+ @if length($frags) != if($is-alpha, 4, 3) { @return $string; } // Parsing error
23
+ $red: _get-color-value(nth($frags, 1));
24
+ $green: _get-color-value(nth($frags, 2));
25
+ $blue: _get-color-value(nth($frags, 3));
26
+
27
+ @if not $red or not $green or not $blue {
28
+ @return $string;
29
+ }
30
+
31
+ @if $is-alpha {
32
+ @if length($frags) != 4 { @return $string; } // No alpha channel found
33
+ $alpha: _get-color-value(nth($frags, 4));
34
+ @if not $alpha { @return $string; } // Error parsing alpha channel
35
+ @return rgba($red, $green, $blue, $alpha);
36
+ }
37
+
38
+ @return rgb($red, $green, $blue);
39
+ }
40
+ @else {
41
+ $frags: set-nth($frags, length($frags), nth($frags, length($frags)) + $token);
42
+ }
43
+ }
44
+
45
+ @return $string;
46
+ }
@@ -0,0 +1,33 @@
1
+ // Move pointer to position of token
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $source: JSON complete source
4
+ // @param [number] $pointer: current pointer
5
+ // @param [string] $token: token to reach
6
+ // --------------------------------------------------------------------------------
7
+ // @throw "Expected $token; found {x}."
8
+ // @throw "Expected $token but reached end of stream."
9
+ // --------------------------------------------------------------------------------
10
+ // @return [number|false] new pointer
11
+
12
+ @function _consume($source, $pointer, $token) {
13
+ $length: str-length($source);
14
+
15
+ @while $pointer <= $length {
16
+ $char: str-slice($source, $pointer, $pointer);
17
+ $pointer: $pointer + 1;
18
+
19
+ @if $char == $token {
20
+ @return $pointer;
21
+ }
22
+
23
+ @else if $char == " " {
24
+ // @continue;
25
+ }
26
+
27
+ @else {
28
+ @return _throw("Expected `" + $token + "; ` found `" + $char + "`.", $pointer);
29
+ }
30
+ }
31
+
32
+ @return _throw("Expected `" + $token + "` but reached end of stream.", $pointer);
33
+ }
@@ -0,0 +1,40 @@
1
+ // Parses a JSON encoded number to find the digits
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $source: JSON complete source
4
+ // @param [number] $pointer: current pointer
5
+ // --------------------------------------------------------------------------------
6
+ // @throw "Unexpected token $token."
7
+ // --------------------------------------------------------------------------------
8
+ // @return [list|false] (new pointer, parsed number)
9
+
10
+ @function _find-digits($source, $pointer) {
11
+ $length: str-length($source);
12
+ $strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
13
+ $numbers: 0 1 2 3 4 5 6 7 8 9;
14
+ $result: null;
15
+ $runs: 1;
16
+
17
+ @while $pointer <= $length {
18
+ $token: to-lower-case(str-slice($source, $pointer, $pointer));
19
+ $index: index($strings, $token);
20
+
21
+ @if $token == '.' {
22
+ // @continue;
23
+ }
24
+ @else if $index and $index > 0 {
25
+ $number: nth($numbers, $index);
26
+ $runs: $runs * 10;
27
+ $result: if($result == null, $number, $result * 10 + $number);
28
+ }
29
+ @else {
30
+ @if index('e' '.' ',' ']' '}' ' ', $token) {
31
+ @return $pointer, if($result != null, $result / $runs, $result);
32
+ }
33
+ @return _throw("Unexpected token `" + $token + "`.", $pointer);
34
+ }
35
+
36
+ $pointer: $pointer + 1;
37
+ }
38
+
39
+ @return $pointer, if($result != null, $result / $runs, $result);
40
+ }
@@ -0,0 +1,46 @@
1
+ // Parses a JSON encoded number to find the exponent part
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $source: JSON complete source
4
+ // @param [number] $pointer: current pointer
5
+ // --------------------------------------------------------------------------------
6
+ // @throw "Unexpected token $token."
7
+ // --------------------------------------------------------------------------------
8
+ // @return [list|false] (new pointer, parsed number)
9
+
10
+ @function _find-exponent($source, $pointer) {
11
+ $length: str-length($source);
12
+ $strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
13
+ $numbers: 0 1 2 3 4 5 6 7 8 9;
14
+ $result: null;
15
+ $minus: false;
16
+
17
+ @while $pointer <= $length {
18
+ $token: to-lower-case(str-slice($source, $pointer, $pointer));
19
+ $index: index($strings, $token);
20
+
21
+ @if $token == 'e' {
22
+ // @continue;
23
+ }
24
+ @else if $token == '-' {
25
+ $minus: true;
26
+ }
27
+ @else if $token == '+' {
28
+ $minus: false;
29
+ }
30
+ @else if $index and $index > 0 {
31
+ $number: nth($numbers, $index);
32
+ $result: if($result == null, $number, $result * 10 + $number);
33
+ }
34
+ @else {
35
+ @if not index(" ", ", ", "]", "}", $token) {
36
+ @return _throw("Unexpected token `" + $token + "`.", $pointer);
37
+ }
38
+
39
+ @return $pointer, if($minus and $result != null, $result * -1, $result);
40
+ }
41
+
42
+ $pointer: $pointer + 1;
43
+ }
44
+
45
+ @return $pointer, if($minus and $result != null, $result * -1, $result);
46
+ }
@@ -0,0 +1,38 @@
1
+ // Parses a JSON encoded number to find the integer part
2
+ // --------------------------------------------------------------------------------
3
+ // @param [string] $source: JSON complete source
4
+ // @param [number] $pointer: current pointer
5
+ // --------------------------------------------------------------------------------
6
+ // @throw "Unexpected token $token."
7
+ // --------------------------------------------------------------------------------
8
+ // @return [list|false] (new pointer, parsed number)
9
+
10
+ @function _find-integer($source, $pointer) {
11
+ $length: str-length($source);
12
+ $strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
13
+ $numbers: 0 1 2 3 4 5 6 7 8 9;
14
+ $result: 0;
15
+
16
+ @while $pointer <= $length {
17
+ $token: to-lower-case(str-slice($source, $pointer, $pointer));
18
+ $index: index($strings, $token);
19
+
20
+ @if $token == '-' {
21
+ // do nothing
22
+ }
23
+ @else if $index {
24
+ $number: nth($numbers, $index);
25
+ $result: $result * 10 + $number;
26
+ }
27
+ @else {
28
+ @if index('e' '.' ',' ']' '}' ' ', $token) {
29
+ @return $pointer, $result;
30
+ }
31
+ @return _throw("Unexpected token `" + $token + "`.", $pointer);
32
+ }
33
+
34
+ $pointer: $pointer + 1;
35
+ }
36
+
37
+ @return $pointer, $result;
38
+ }
@@ -0,0 +1,22 @@
1
+ // Power function
2
+ // --------------------------------------------------------------------------------
3
+ // @param [number] $x: number
4
+ // @param [number] $n: power
5
+ // --------------------------------------------------------------------------------
6
+ // @return [number] $x ^ $n
7
+
8
+ @function _pow($x, $n) {
9
+ $ret: 1;
10
+
11
+ @if $n >= 0 {
12
+ @for $i from 1 through $n {
13
+ $ret: $ret * $x;
14
+ }
15
+ } @else {
16
+ @for $i from $n to 0 {
17
+ $ret: $ret / $x;
18
+ }
19
+ }
20
+
21
+ @return $ret;
22
+ }