@@ -505,6 +505,8 @@ protected function compileImport($rawPath, $out) {
505505
506506 // return a value to halt execution
507507 protected function compileChild ($ child , $ out ) {
508+ $ this ->sourcePos = isset ($ child [-1 ]) ? $ child [-1 ] : -1 ;
509+
508510 switch ($ child [0 ]) {
509511 case "import " :
510512 list (,$ rawPath ) = $ child ;
@@ -646,7 +648,7 @@ protected function compileChild($child, $out) {
646648 list (,$ name , $ argValues , $ content ) = $ child ;
647649 $ mixin = $ this ->get (self ::$ namespaces ["mixin " ] . $ name , false );
648650 if (!$ mixin ) {
649- throw new Exception ( sprintf ( ' Undefined mixin "%s" ' , $ name) );
651+ $ this -> throwError ( " Undefined mixin $ name" );
650652 }
651653
652654 $ callingScope = $ this ->env ;
@@ -676,7 +678,7 @@ protected function compileChild($child, $out) {
676678 case "mixin_content " :
677679 $ content = $ this ->get (self ::$ namespaces ["special " ] . "content " );
678680 if (is_null ($ content )) {
679- throw new Exception ("Unexpected @content inside of mixin " );
681+ $ this -> throwError ("Unexpected @content inside of mixin " );
680682 }
681683
682684 $ this ->storeEnv = $ content ->scope ;
@@ -694,7 +696,7 @@ protected function compileChild($child, $out) {
694696 fwrite (STDERR , "Line $ line DEBUG: $ value \n" );
695697 break ;
696698 default :
697- throw new Exception ("unknown child type: $ child [0 ]" );
699+ $ this -> throwError ("unknown child type: $ child [0 ]" );
698700 }
699701 }
700702
@@ -765,7 +767,7 @@ protected function reduce($value, $inExp = false) {
765767 $ left [0 ] == "number " && $ right [0 ] == "number " )
766768 {
767769 if ($ opName == "mod " && $ right [2 ] != "" ) {
768- throw new Exception ( sprintf ( ' Cannot modulo by a number with units: %s%s. ' , $ right [1 ], $ right [2 ]) );
770+ $ this -> throwError ( " Cannot modulo by a number with units: $ right [1 ]$ right [2 ]. " );
769771 }
770772
771773 $ unitChange = true ;
@@ -1011,12 +1013,12 @@ protected function op_color_color($op, $left, $right) {
10111013 break ;
10121014 case '/ ' :
10131015 if ($ rval == 0 ) {
1014- throw new Exception ("color: Can't divide by zero " );
1016+ $ this -> throwError ("color: Can't divide by zero " );
10151017 }
10161018 $ out [] = $ lval / $ rval ;
10171019 break ;
10181020 default :
1019- throw new Exception ("color: unknow op $ op " );
1021+ $ this -> throwError ("color: unknow op $ op " );
10201022 }
10211023 }
10221024
@@ -1152,7 +1154,7 @@ protected function compileValue($value) {
11521154 case "null " :
11531155 return "null " ;
11541156 default :
1155- throw new Exception ("unknown value type: $ type " );
1157+ $ this -> throwError ("unknown value type: $ type " );
11561158 }
11571159 }
11581160
@@ -1288,13 +1290,13 @@ protected function applyArguments($argDef, $argValues) {
12881290 foreach ((array ) $ argValues as $ arg ) {
12891291 if (!empty ($ arg [0 ])) {
12901292 if (!isset ($ args [$ arg [0 ][1 ]])) {
1291- throw new Exception ( sprintf ( ' Mixin or function doesn \ 't have an argument named $%s. ' , $ arg [0 ][1 ]) );
1293+ $ this -> throwError ( " Mixin or function doesn't have an argument named $%s. " , $ arg [0 ][1 ]);
12921294 } elseif ($ args [$ arg [0 ][1 ]][0 ] < count ($ remaining )) {
1293- throw new Exception ( sprintf ( ' The argument $%s was passed both by position and by name. ' , $ arg [0 ][1 ]) );
1295+ $ this -> throwError ( " The argument $%s was passed both by position and by name. " , $ arg [0 ][1 ]);
12941296 }
12951297 $ keywordArgs [$ arg [0 ][1 ]] = $ arg [1 ];
12961298 } elseif (count ($ keywordArgs )) {
1297- throw new Exception ('Positional arguments must come before keyword arguments. ' );
1299+ $ this -> throwError ('Positional arguments must come before keyword arguments. ' );
12981300 } elseif ($ arg [2 ] == true ) {
12991301 $ val = $ this ->reduce ($ arg [1 ], true );
13001302 if ($ val [0 ] == "list " ) {
@@ -1323,7 +1325,7 @@ protected function applyArguments($argDef, $argValues) {
13231325 } elseif (!empty ($ default )) {
13241326 $ val = $ default ;
13251327 } else {
1326- throw new Exception ( sprintf ( ' There is missing argument $%s ' , $ name) );
1328+ $ this -> throwError ( " Missing argument $$ name" );
13271329 }
13281330
13291331 $ this ->set ($ name , $ this ->reduce ($ val , true ), true );
@@ -1598,18 +1600,18 @@ protected function coerceString($value) {
15981600
15991601 protected function assertList ($ value ) {
16001602 if ($ value [0 ] != "list " )
1601- throw new Exception ("expecting list " );
1603+ $ this -> throwError ("expecting list " );
16021604 return $ value ;
16031605 }
16041606
16051607 protected function assertColor ($ value ) {
16061608 if ($ color = $ this ->coerceColor ($ value )) return $ color ;
1607- throw new Exception ("expecting color " );
1609+ $ this -> throwError ("expecting color " );
16081610 }
16091611
16101612 protected function assertNumber ($ value ) {
16111613 if ($ value [0 ] != "number " )
1612- throw new Exception ("expecting number " );
1614+ $ this -> throwError ("expecting number " );
16131615 return $ value [1 ];
16141616 }
16151617
@@ -2126,14 +2128,14 @@ protected function getNormalizedNumbers($args) {
21262128 $ numbers = array ();
21272129 foreach ($ args as $ key => $ item ) {
21282130 if ('number ' != $ item [0 ]) {
2129- throw new Exception ( sprintf ( ' %s is not a number ' , $ item [0 ]) );
2131+ $ this -> throwError ( " %s is not a number " , $ item [0 ]);
21302132 }
21312133 $ number = $ this ->normalizeNumber ($ item );
21322134
21332135 if (null === $ unit ) {
21342136 $ unit = $ number [2 ];
21352137 } elseif ($ unit !== $ number [2 ]) {
2136- throw new Exception ( sprintf ( 'Incompatible units: "%s" and "%s". ' , $ originalUnit , $ item [2 ]) );
2138+ $ this -> throwError ( 'Incompatible units: "%s" and "%s". ' , $ originalUnit , $ item [2 ]);
21372139 }
21382140
21392141 $ originalUnit = $ item [2 ];
@@ -2246,7 +2248,7 @@ protected function lib_unitless($args) {
22462248 protected function lib_comparable ($ args ) {
22472249 list ($ number1 , $ number2 ) = $ args ;
22482250 if (!isset ($ number1 [0 ]) || $ number1 [0 ] != "number " || !isset ($ number2 [0 ]) || $ number2 [0 ] != "number " ) {
2249- throw new Exception ('Invalid argument(s) for "comparable" ' );
2251+ $ this -> throwError ('Invalid argument(s) for "comparable" ' );
22502252 }
22512253
22522254 $ number1 = $ this ->normalizeNumber ($ number1 );
@@ -2255,6 +2257,18 @@ protected function lib_comparable($args) {
22552257 return $ number1 [2 ] == $ number2 [2 ] || $ number1 [2 ] == "" || $ number2 [2 ] == "" ;
22562258 }
22572259
2260+ protected function throwError ($ msg = null ) {
2261+ if (func_num_args () > 1 ) {
2262+ $ msg = call_user_func_array ("sprintf " , func_get_args ());
2263+ }
2264+
2265+ if ($ this ->sourcePos >= 0 && isset ($ this ->parser )) {
2266+ $ this ->parser ->throwParseError ($ msg , $ this ->sourcePos );
2267+ }
2268+
2269+ throw new Exception ($ msg );
2270+ }
2271+
22582272 static protected $ cssColors = array (
22592273 'aliceblue ' => '240,248,255 ' ,
22602274 'antiquewhite ' => '250,235,215 ' ,
@@ -2526,7 +2540,7 @@ protected function parseChunk() {
25262540 $ include = $ this ->pushSpecialBlock ("include " );
25272541 $ include ->child = $ child ;
25282542 } else {
2529- $ this ->append ($ child );
2543+ $ this ->append ($ child, $ s );
25302544 }
25312545
25322546 return true ;
@@ -2538,7 +2552,7 @@ protected function parseChunk() {
25382552 $ this ->valueList ($ importPath ) &&
25392553 $ this ->end ())
25402554 {
2541- $ this ->append (array ("import " , $ importPath ));
2555+ $ this ->append (array ("import " , $ importPath ), $ s );
25422556 return true ;
25432557 } else {
25442558 $ this ->seek ($ s );
@@ -2548,7 +2562,7 @@ protected function parseChunk() {
25482562 $ this ->selectors ($ selector ) &&
25492563 $ this ->end ())
25502564 {
2551- $ this ->append (array ("extend " , $ selector ));
2565+ $ this ->append (array ("extend " , $ selector ), $ s );
25522566 return true ;
25532567 } else {
25542568 $ this ->seek ($ s );
@@ -2568,7 +2582,7 @@ protected function parseChunk() {
25682582 }
25692583
25702584 if ($ this ->literal ("@return " ) && $ this ->valueList ($ retVal ) && $ this ->end ()) {
2571- $ this ->append (array ("return " , $ retVal ));
2585+ $ this ->append (array ("return " , $ retVal ), $ s );
25722586 return true ;
25732587 } else {
25742588 $ this ->seek ($ s );
@@ -2630,14 +2644,14 @@ protected function parseChunk() {
26302644 if (($ this ->literal ("@debug " ) || $ this ->literal ("@warn " )) &&
26312645 $ this ->valueList ($ value ) &&
26322646 $ this ->end ()) {
2633- $ this ->append (array ("debug " , $ value , $ s ));
2647+ $ this ->append (array ("debug " , $ value , $ s ), $ s );
26342648 return true ;
26352649 } else {
26362650 $ this ->seek ($ s );
26372651 }
26382652
26392653 if ($ this ->literal ("@content " ) && $ this ->end ()) {
2640- $ this ->append (array ("mixin_content " ));
2654+ $ this ->append (array ("mixin_content " ), $ s );
26412655 return true ;
26422656 } else {
26432657 $ this ->seek ($ s );
@@ -2667,7 +2681,7 @@ protected function parseChunk() {
26672681 if ($ this ->literal ("@charset " ) &&
26682682 $ this ->valueList ($ charset ) && $ this ->end ())
26692683 {
2670- $ this ->append (array ("charset " , $ charset ));
2684+ $ this ->append (array ("charset " , $ charset ), $ s );
26712685 return true ;
26722686 } else {
26732687 $ this ->seek ($ s );
@@ -2696,7 +2710,7 @@ protected function parseChunk() {
26962710 $ this ->end ())
26972711 {
26982712 $ name = array ("string " , "" , array ($ name ));
2699- $ this ->append (array ("assign " , $ name , $ value ));
2713+ $ this ->append (array ("assign " , $ name , $ value ), $ s );
27002714 return true ;
27012715 } else {
27022716 $ this ->seek ($ s );
@@ -2717,7 +2731,7 @@ protected function parseChunk() {
27172731 $ defaultVar = true ;
27182732 }
27192733 }
2720- $ this ->append (array ("assign " , $ name , $ value , $ defaultVar ));
2734+ $ this ->append (array ("assign " , $ name , $ value , $ defaultVar ), $ s );
27212735 return true ;
27222736 } else {
27232737 $ this ->seek ($ s );
@@ -2744,7 +2758,7 @@ protected function parseChunk() {
27442758 if ($ this ->propertyName ($ name ) && $ this ->literal (": " )) {
27452759 $ foundSomething = false ;
27462760 if ($ this ->valueList ($ value )) {
2747- $ this ->append (array ("assign " , $ name , $ value ));
2761+ $ this ->append (array ("assign " , $ name , $ value ), $ s );
27482762 $ foundSomething = true ;
27492763 }
27502764
@@ -2772,10 +2786,10 @@ protected function parseChunk() {
27722786 $ include = $ block ->child ;
27732787 unset($ block ->child );
27742788 $ include [3 ] = $ block ;
2775- $ this ->append ($ include );
2789+ $ this ->append ($ include, $ s );
27762790 } elseif (empty ($ block ->dontAppend )) {
27772791 $ type = isset ($ block ->type ) ? $ block ->type : "block " ;
2778- $ this ->append (array ($ type , $ block ));
2792+ $ this ->append (array ($ type , $ block ), $ s );
27792793 }
27802794 return true ;
27812795 }
@@ -2839,7 +2853,8 @@ protected function popBlock() {
28392853 return $ old ;
28402854 }
28412855
2842- protected function append ($ statement ) {
2856+ protected function append ($ statement , $ pos =null ) {
2857+ if ($ pos !== null ) $ statement [-1 ] = $ pos ;
28432858 $ this ->env ->children [] = $ statement ;
28442859 }
28452860
@@ -3202,7 +3217,7 @@ protected function argumentDef(&$out) {
32023217 if ($ this ->literal ("... " )) {
32033218 $ sss = $ this ->seek ();
32043219 if (!$ this ->literal (") " )) {
3205- throw new Exception ( ' ... has to be after the final argument ' );
3220+ $ this -> throwParseError ( " ... has to be after the final argument " );
32063221 }
32073222 $ arg [2 ] = true ;
32083223 $ this ->seek ($ sss );
@@ -3720,7 +3735,7 @@ protected function to($what, &$out, $until = false, $allowNewline = false) {
37203735 return true ;
37213736 }
37223737
3723- protected function throwParseError ($ msg = "parse error " , $ count = null ) {
3738+ public function throwParseError ($ msg = "parse error " , $ count = null ) {
37243739 $ count = is_null ($ count ) ? $ this ->count : $ count ;
37253740
37263741 $ line = $ this ->getLineNo ($ count );
0 commit comments