Current Version: 1.0.10
Project Name: csspp
assembler.cpp
Go to the documentation of this file.
1 // CSS Preprocessor
2 // Copyright (C) 2015-2016 Made to Order Software Corp.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 
31 #include "csspp/assembler.h"
32 
33 #include "csspp/exceptions.h"
34 #include "csspp/lexer.h"
35 #include "csspp/nth_child.h"
36 #include "csspp/unicode_range.h"
37 
38 #include <iostream>
39 
40 namespace csspp
41 {
42 
43 namespace
44 {
45 
46 typedef uint32_t flags_t;
47 
48 flags_t const g_flag_optional_operator = 0x01;
49 flags_t const g_flag_optional_spaces = 0x02;
50 flags_t const g_flag_optional_space_before = 0x04;
51 flags_t const g_flag_optional_space_after = 0x08;
55 
57 {
58  std::string const dimension(n->get_string());
59  std::string::size_type pos(dimension.find_first_of(" */"));
60  if(pos != std::string::npos)
61  {
62  error::instance() << n->get_position()
63  << "\""
64  << dimension
65  << "\" is not a valid CSS dimension."
67  }
68 }
69 
70 } // no name namespace
71 
72 // base class
74 {
75 public:
76  assembler_impl(std::ostream & out)
77  : f_out(out)
78  {
79  }
80 
81  virtual ~assembler_impl()
82  {
83  }
84 
85  virtual void output_string(std::string const & str)
86  {
87  if(!str.empty())
88  {
89  f_started = str.back() != '\n';
90  }
91  f_out << str;
92  }
93 
94  virtual void output_operator(std::string const & str, flags_t flags)
95  {
96  static_cast<void>(flags);
97 
98  // the default prints that as is
99  if((flags & g_flag_optional_operator) == 0)
100  {
101  output_string(str);
102  }
103  }
104 
105  virtual void output_token(std::string const & str)
106  {
107  // the default prints that as is
108  output_string(str);
109  }
110 
111  virtual void newline()
112  {
113  // by default do not write newlines
114  }
115 
116  virtual void newline_if_not_empty()
117  {
118  if(f_started)
119  {
120  f_started = false;
121  f_out << std::endl;
122  }
123  }
124 
125  virtual void output_identation()
126  {
127  // by default do not write identation
128  }
129 
130 protected:
131  std::ostream & f_out;
132  bool f_started = false;
133 };
134 
136 {
137 public:
138  assembler_compressed(std::ostream & out)
139  : assembler_impl(out)
140  {
141  }
142 
144  {
145  }
146 };
147 
149 {
150 public:
151  assembler_tidy(std::ostream & out)
152  : assembler_compressed(out)
153  {
154  }
155 
156  virtual ~assembler_tidy()
157  {
158  }
159 
160  virtual void newline()
161  {
162  f_started = false;
163  f_out << std::endl;
164  }
165 };
166 
168 {
169 public:
170  assembler_compact(std::ostream & out)
171  : assembler_tidy(out)
172  {
173  }
174 
176  {
177  }
178 
179  virtual void output_operator(std::string const & str, flags_t flags)
180  {
181  f_started = true;
183  {
184  f_out << " " << str << " ";
185  }
187  {
188  f_out << " " << str;
189  }
191  {
192  f_out << str << " ";
193  }
194  else
195  {
197  }
198  }
199 };
200 
202 {
203 public:
204  assembler_expanded(std::ostream & out)
205  : assembler_compact(out)
206  {
207  }
208 
210  {
211  }
212 
213  virtual void output_operator(std::string const & str, flags_t flags)
214  {
215  if((flags & g_flag_optional_spaces_or_newlines) != 0)
216  {
217  f_started = false;
218  f_out << std::endl << str << std::endl;
219  }
220  else if((flags & g_flag_optional_space_before_or_newline) != 0)
221  {
222  f_started = false;
223  f_out << std::endl << str;
224  }
225  else if((flags & g_flag_optional_space_after_or_newline) != 0)
226  {
227  f_started = false;
228  f_out << str << std::endl;
229  }
230  else if((flags & g_flag_optional_operator) != 0)
231  {
232  f_started = true;
233  f_out << str;
234  }
235  else
236  {
238  }
239  }
240 
241  virtual void output_identation()
242  {
243  f_out << " ";
244  }
245 };
246 
247 assembler::assembler(std::ostream & out)
248  : f_out(out)
249 {
250 }
251 
252 std::string assembler::escape_id(std::string const & id)
253 {
254  std::string result;
255 
256  // create a temporary lexer to apply the conversion
257  std::stringstream ss;
258  position pos("assembler.css");
259  lexer l(ss, pos);
260 
261  bool first_char(true);
262  for(char const *s(id.c_str()); *s != '\0'; )
263  {
264  char mb[5];
265  unsigned char c(static_cast<unsigned char>(*s));
266  size_t len(1);
267  if(c >= 0xF0)
268  {
269  len = 4;
270  }
271  else if(c >= 0xE0)
272  {
273  len = 3;
274  }
275  else if(c >= 0xC0)
276  {
277  len = 2;
278  }
279  //else len = 1 -- already set to 1 by default
280  for(size_t i(0); i < len; ++i, ++s)
281  {
282  if(*s == '\0')
283  {
284  // UTF-8 should be perfect when we reach the assembler
285  throw csspp_exception_logic("assembler.cpp: assembler::escape_id(): invalid UTF-8 character found."); // LCOV_EXCL_LINE
286  }
287  mb[i] = *s;
288  }
289  mb[len] = '\0';
290 
291  wide_char_t wc(l.mbtowc(mb));
292 
293  if((first_char && lexer::is_start_identifier(wc))
294  || (!first_char && lexer::is_identifier(wc)))
295  {
296  result += mb;
297  }
298  else
299  {
300  result += '\\';
301  if(wc >= '0' && wc <= '9')
302  {
303  // digits need to be defined as hexa
304  result += '3';
305  result += wc;
306  // add a space if the next character requires us to do so
307  // (by now identifier letters should all be lower case so
308  // the 'A' to 'F' should never match)
309  if((s[0] >= '0' && s[0] <= '9')
310  || (s[0] >= 'a' && s[0] <= 'f')
311  || (s[0] >= 'A' && s[0] <= 'F')) // LCOV_EXCL_LINE
312  {
313  result += ' ';
314  }
315  }
316  else
317  {
318  result += mb;
319  }
320  }
321  first_char = false;
322  }
323 
324  return result;
325 }
326 
328 {
329  f_root = n;
330 
331  f_impl.reset();
332  switch(mode)
333  {
335  f_impl.reset(new assembler_compact(f_out));
336  break;
337 
339  f_impl.reset(new assembler_compressed(f_out));
340  break;
341 
343  f_impl.reset(new assembler_expanded(f_out));
344  break;
345 
346  case output_mode_t::TIDY:
347  f_impl.reset(new assembler_tidy(f_out));
348  break;
349 
350  }
351  if(!f_impl)
352  {
353  throw csspp_exception_logic("assembler.cpp: assembler::output(): called with an invalid mode.");
354  }
355 
356  output(n);
357 }
358 
360 {
361  switch(n->get_type())
362  {
363  case node_type_t::ADD:
364  f_impl->output_operator("+", g_flag_optional_spaces);
365  break;
366 
367  case node_type_t::ARG:
368  {
369  size_t const max_children(n->size());
370  for(size_t idx(0); idx < max_children; ++idx)
371  {
372  output(n->get_child(idx));
373  }
374  }
375  break;
376 
379  break;
380 
381  case node_type_t::COLON:
382  f_impl->output_operator(":", 0);
383  break;
384 
385  case node_type_t::COLOR:
386  {
387  color c(n->get_color());
388  f_impl->output_token(c.to_string());
389  }
390  break;
391 
392  // This should have been transformed to a list (ARG for selectors
393  // and functions...)
394  //case node_type_t::COMMA:
395  // f_impl->output_operator(",", g_flag_optional_space_after);
396  // break;
397 
399  output_comment(n);
400  break;
401 
403  f_impl->output_operator("|=", g_flag_optional_spaces);
404  break;
405 
407  // this may be a dimension, if not f_string is empty anyway
408  verify_dimension(n);
409  f_out << decimal_number_to_string(n->get_decimal_number(), true) << n->get_string();
410  break;
411 
413  {
414  f_impl->output_identation();
415  f_out << n->get_string();
416  f_impl->output_operator(":", g_flag_optional_space_after);
417  size_t const max_children(n->size());
418  for(size_t idx(0); idx < max_children; ++idx)
419  {
420  node::pointer_t child(n->get_child(idx));
421  output(child);
422  if(child->is(node_type_t::ARG)
423  && idx + 1 != max_children)
424  {
425  f_impl->output_operator(",", g_flag_optional_space_after);
426  }
427  }
428  // we make sure it appears at the end
429  if(n->get_flag("important"))
430  {
431  f_impl->output_operator("!", g_flag_optional_space_before);
432  f_out << "important";
433  }
434  }
435  break;
436 
437  // We should have FONT_METRICS for fonts... so we should not need this one
438  //case node_type_t::DIVIDE:
439  // f_impl->output_operator("/", 0);
440  // break;
441 
442  case node_type_t::EQUAL:
443  f_impl->output_operator("=", g_flag_optional_spaces);
444  break;
445 
447  // this is a mouthful!
448  f_out << decimal_number_to_string(n->get_font_size() * (n->get_dim1() == "%" ? 100.0 : 1.0), true) << n->get_dim1()
449  << "/" << decimal_number_to_string(n->get_line_height() * (n->get_dim2() == "%" ? 100.0 : 1.0), true) << n->get_dim2();
450  break;
451 
453  {
454  f_out << n->get_string();
455  f_impl->output_operator("(", 0);
456  if(!n->empty())
457  {
458  if(n->get_child(0)->is(node_type_t::ARG))
459  {
460  bool first(true);
461  size_t const max_children(n->size());
462  for(size_t idx(0); idx < max_children; ++idx)
463  {
464  if(first)
465  {
466  first = false;
467  }
468  else
469  {
470  f_impl->output_operator(",", g_flag_optional_space_after);
471  }
472  output(n->get_child(idx));
473  }
474  }
475  else
476  {
477  // no ARG then no commas; this happens in :not(),
478  // :lang(), nth-child(), etc.
479  size_t const max_children(n->size());
480  for(size_t idx(0); idx < max_children; ++idx)
481  {
482  output(n->get_child(idx));
483  }
484  }
485  }
486  f_impl->output_operator(")", 0);
487  }
488  break;
489 
491  f_impl->output_operator(">", g_flag_optional_spaces);
492  break;
493 
494  case node_type_t::HASH:
495  f_out << "#" << n->get_string();
496  break;
497 
499  f_out << escape_id(n->get_string());
500  break;
501 
503  f_impl->output_operator("~=", g_flag_optional_spaces);
504  break;
505 
507  // this may be a dimension, if not f_string is empty anyway
508  verify_dimension(n);
509  f_out << n->get_integer() << n->get_string();
510  break;
511 
512  case node_type_t::LIST:
513  {
514  size_t const max_children(n->size());
515  for(size_t idx(0); idx < max_children; ++idx)
516  {
517  node::pointer_t child(n->get_child(idx));
518  output(child);
519  if(child->is(node_type_t::DECLARATION)
520  && idx + 1 != max_children)
521  {
522  f_impl->output_operator(";", g_flag_optional_space_after_or_newline);
523  }
524  }
525  }
526  break;
527 
529  f_impl->output_operator("*", 0);
530  break;
531 
533  {
534  f_impl->output_operator("{", g_flag_optional_spaces_or_newlines);
535  size_t const max_children(n->size());
536  for(size_t idx(0); idx < max_children; ++idx)
537  {
538  node::pointer_t item(n->get_child(idx));
539  output(n->get_child(idx));
540  if(item->is(node_type_t::DECLARATION)
541  && idx + 1 < max_children)
542  {
543  f_impl->output_operator(";", g_flag_optional_space_after_or_newline);
544  }
545  }
546  f_impl->output_operator(";", g_flag_optional_operator);
547  f_impl->output_operator("}", g_flag_optional_space_before_or_newline);
548  f_impl->newline();
549  }
550  break;
551 
553  output_parenthesis(n, 0);
554  break;
555 
557  {
558  f_impl->output_operator("[", 0);
559  size_t const max_children(n->size());
560  for(size_t idx(0); idx < max_children; ++idx)
561  {
562  output(n->get_child(idx));
563  }
564  f_impl->output_operator("]", 0);
565  }
566  break;
567 
569  f_out << decimal_number_to_string(n->get_decimal_number() * 100.0, true) << "%";
570  break;
571 
572  case node_type_t::PERIOD:
573  f_impl->output_operator(".", 0);
574  break;
575 
577  f_impl->output_operator("~", g_flag_optional_spaces);
578  break;
579 
581  f_impl->output_operator("^=", g_flag_optional_spaces);
582  break;
583 
584  case node_type_t::SCOPE:
585  f_impl->output_operator("|", 0);
586  break;
587 
588  case node_type_t::STRING:
589  output_string(n->get_string());
590  break;
591 
593  f_impl->output_operator("*=", g_flag_optional_spaces);
594  break;
595 
596  case node_type_t::SUBTRACT: // for calc() / expression()
597  f_impl->output_operator("-", 0);
598  break;
599 
601  f_impl->output_operator("$=", g_flag_optional_spaces);
602  break;
603 
605  {
606  unicode_range_t const range(static_cast<range_value_t>(n->get_integer()));
607  f_out << "U+" << range.to_string();
608  }
609  break;
610 
611  case node_type_t::URL:
612  // TODO: escape special characters or we won't be able to re-read this one
613  output_url(n->get_string());
614  break;
615 
617  // explicit whitespace that we still have in the tree are kept as is
618  f_out << " ";
619  break;
620 
623  break;
624 
626  {
627  // TODO: support adding around the operator?
628  nth_child const an_b(n->get_integer());
629  f_out << an_b.to_string();
630  }
631  break;
632 
634  case node_type_t::AND:
636  case node_type_t::ARRAY:
638  case node_type_t::CDC:
639  case node_type_t::CDO:
643  case node_type_t::COLUMN:
644  case node_type_t::COMMA:
646  case node_type_t::DIVIDE:
647  case node_type_t::DOLLAR:
653  case node_type_t::MAP:
654  case node_type_t::MODULO:
658  case node_type_t::POWER:
664  // many of the nodes are not expected in a valid tree being compiled
665  // all of those will generate this exception
666  {
667  std::stringstream ss;
668  ss << "assembler.cpp: unexpected token "
669  << n->get_type()
670  << " in output() call.";
671  throw csspp_exception_logic(ss.str());
672  }
673 
674  }
675 }
676 
678 {
679  bool first(true);
680  bool has_arg(false);
681  size_t const max_children(n->size());
682  for(size_t idx(0); idx < max_children; ++idx)
683  {
684  node::pointer_t c(n->get_child(idx));
686  {
687  if(has_arg)
688  {
689  output(c);
690  }
691  }
692  else if(!c->is(node_type_t::ARG))
693  {
694  // unexpected for a component value
695  std::stringstream ss; // LCOV_EXCL_LINE
696  ss << "assembler.cpp: expected all direct children of COMPONENT_VALUE to be ARG instead of " // LCOV_EXCL_LINE
697  << c->get_type() // LCOV_EXCL_LINE
698  << "."; // LCOV_EXCL_LINE
699  throw csspp_exception_logic(ss.str()); // LCOV_EXCL_LINE
700  }
701  else if(c->empty() || !c->get_last_child()->is(node_type_t::PLACEHOLDER))
702  {
703  // TODO: if we compile out PLACEHOLDER nodes in the compiler
704  // then we can remove the test here... (on the line prior)
705  has_arg = true;
706  if(first)
707  {
708  first = false;
709  }
710  else
711  {
712  f_impl->output_operator(",", g_flag_optional_space_after);
713  }
714  output(c);
715  }
716  }
717 }
718 
720 {
721  if(flags == 0)
722  {
723  // we must have a space here otherwise the '(' transforms a
724  // preceeding identifier in a function
725  //
726  // TODO: once our assembler is smarter we will know what is
727  // before and thus avoid the space if possible.
728  //
729  f_out << " ";
730  }
731  f_impl->output_operator("(", 0);
732  size_t const max_children(n->size());
733  for(size_t idx(0); idx < max_children; ++idx)
734  {
735  node::pointer_t child(n->get_child(idx));
736  if(child->is(node_type_t::OPEN_PARENTHESIS))
737  {
738  if(idx != 0)
739  {
740  f_out << " ";
741  }
742  output_parenthesis(child, 1);
743  }
744  else
745  {
746  output(child);
747  }
748  }
749  f_impl->output_operator(")", flags == 0 ? g_flag_optional_space_after : 0);
750 }
751 
753 {
754  f_out << "@" << n->get_string() << " ";
755  bool no_block(true);
756  size_t const max_children(n->size());
757  if(max_children > 0)
758  {
759  for(size_t idx(0); idx < max_children; ++idx)
760  {
761  node::pointer_t child(n->get_child(idx));
762  if(idx + 1 == max_children
763  && child->is(node_type_t::OPEN_CURLYBRACKET))
764  {
765  f_impl->newline();
766  no_block = false; // do not output ';'
767  }
768  //if(child->is(node_type_t::COMPONENT_VALUE))
769  //{
770  // f_impl->newline();
771  // f_impl->output_operator("{", 0);
772  // f_impl->newline();
773  // output(child);
774  // f_impl->output_operator("}", 0);
775  // f_impl->newline();
776  //}
777  //else
778  if(child->is(node_type_t::OPEN_CURLYBRACKET))
779  {
780  // nearly like output(child), except that we do not add
781  // a ';' before the '}'
782  f_impl->output_operator("{", 0);
783  f_impl->newline();
784  size_t const max_sub_children(child->size());
785  for(size_t j(0); j < max_sub_children; ++j)
786  {
787  output(child->get_child(j));
788  }
789  f_impl->output_operator("}", 0);
790  f_impl->newline();
791  }
792  else if(child->is(node_type_t::ARG))
793  {
794  output(child);
795  if(idx + 1 < max_children
796  && n->get_child(idx + 1)->is(node_type_t::ARG))
797  {
798  f_impl->output_operator(",", g_flag_optional_space_after);
799  }
800  }
801  else
802  {
803  output(child);
804  }
805  }
806  }
807  if(no_block)
808  {
809  f_out << ";";
810  }
811 
812  // extra newline after an @-keyword
813  f_impl->newline();
814 }
815 
817 {
818  // we take care of comments and don't give the impl's a chance to
819  // do anything about this; (1) we force a newline before if we
820  // already output something; (2) we force a newline at the end
821  //
822  f_impl->newline_if_not_empty();
823  std::string const comment(n->get_string());
824  if(n->get_integer() == 0)
825  {
826  // note: a C++ comment is not valid in a .css file, so here we
827  // convert it
828  //
829  bool first(true);
830  std::string::size_type start(0);
831  std::string::size_type end(comment.find('\n'));
832  while(end != std::string::npos)
833  {
834  if(first)
835  {
836  first = false;
837  f_out << "/* ";
838  }
839  else
840  {
841  f_out << " * ";
842  }
843  f_out << comment.substr(start, end - start) << std::endl;
844  start = end + 1;
845  end = comment.find('\n', start);
846  }
847  if(start < comment.size())
848  {
849  if(first)
850  {
851  // write the whole thing on a single line
852  f_out << "/* "
853  << comment.substr(start)
854  << " */"
855  << std::endl;
856  }
857  else
858  {
859  f_out << " * " << comment.substr(start) << std::endl
860  << " */" << std::endl;
861  }
862  }
863  }
864  else
865  {
866  // TODO: add the " * " on each line? (I don't think we remove
867  // those thus we would already have them if present in the
868  // source)
869  //
870  f_out << "/* " << comment << " */" << std::endl;
871  }
872 }
873 
874 void assembler::output_string(std::string const & str)
875 {
876  // count the single and double quotes
877  int sq(0);
878  int dq(0);
879  for(char const *s(str.c_str()); *s != '\0'; ++s)
880  {
881  if(*s == '\'')
882  {
883  ++sq;
884  }
885  else if(*s == '"')
886  {
887  ++dq;
888  }
889  }
890 
891  // more single quotes? if so use "..."
892  if(sq >= dq)
893  {
894  // use " in this case
895  f_out << '"';
896  for(char const *s(str.c_str()); *s != '\0'; ++s)
897  {
898  if(*s == '"')
899  {
900  f_out << "\\\"";
901  }
902  else
903  {
904  f_out << *s;
905  }
906  }
907  f_out << '"';
908  }
909  else
910  {
911  // use ' in this case
912  f_out << '\'';
913  for(char const *s(str.c_str()); *s != '\0'; ++s)
914  {
915  if(*s == '\'')
916  {
917  f_out << "\\'";
918  }
919  else
920  {
921  f_out << *s;
922  }
923  }
924  f_out << '\'';
925  }
926 }
927 
928 void assembler::output_url(std::string const & str)
929 {
930  f_out << "url";
931  f_impl->output_operator("(", g_flag_optional_space_after);
932 
933  //
934  // the URI can be output as is if it does not include one of:
935  // '('
936  // ')'
937  // "'"
938  // '"'
939  // non-printable character (see lexer)
940  //
941  bool direct(true);
942  for(char const *s(str.c_str()); *s != '\0'; ++s)
943  {
944  char const c(*s);
945  if(c == '('
946  || c == ')'
947  || c == '\''
948  || c == '"'
949  || lexer::is_non_printable(static_cast<wide_char_t>(c))) // this is UTF-8 compatible
950  {
951  direct = false;
952  break;
953  }
954  }
955  if(direct)
956  {
957  // we can output that one as is
958  f_out << str;
959  }
960  else
961  {
962  // output this URI as a string
963  output_string(str);
964  }
965 
966  f_impl->output_operator(")", g_flag_optional_space_before);
967 }
968 
969 } // namespace csspp
970 
971 std::ostream & operator << (std::ostream & out, csspp::output_mode_t const type)
972 {
973  switch(type)
974  {
976  out << "COMPACT";
977  break;
978 
980  out << "COMPRESSED";
981  break;
982 
984  out << "EXPANDED";
985  break;
986 
988  out << "TIDY";
989  break;
990 
991  }
992 
993  return out;
994 }
995 
996 // Local Variables:
997 // mode: cpp
998 // indent-tabs-mode: nil
999 // c-basic-offset: 4
1000 // tab-width: 4
1001 // End:
1002 
1003 // vim: ts=4 sw=4 et
virtual void output_operator(std::string const &str, flags_t flags)
Definition: assembler.cpp:94
int32_t wide_char_t
Definition: csspp.h:49
static bool constexpr is_non_printable(wide_char_t c)
Definition: lexer.h:46
std::ostream & operator<<(std::ostream &out, csspp::output_mode_t const type)
Definition: assembler.cpp:971
std::string decimal_number_to_string(decimal_number_t d, bool remove_leading_zero)
Definition: csspp.cpp:75
std::shared_ptr< node > pointer_t
Definition: node.h:122
void output_string(std::string const &str)
Definition: assembler.cpp:874
void output_url(std::string const &str)
Definition: assembler.cpp:928
void output_component_value(node::pointer_t n)
Definition: assembler.cpp:677
std::shared_ptr< assembler_impl > f_impl
Definition: assembler.h:53
std::ostream & f_out
Definition: assembler.h:54
virtual void newline()
Definition: assembler.cpp:111
assembler_tidy(std::ostream &out)
Definition: assembler.cpp:151
assembler_compressed(std::ostream &out)
Definition: assembler.cpp:138
virtual ~assembler_tidy()
Definition: assembler.cpp:156
virtual void newline_if_not_empty()
Definition: assembler.cpp:116
std::string to_string() const
std::ostream & f_out
Definition: assembler.cpp:131
static bool constexpr is_start_identifier(wide_char_t c)
Definition: lexer.h:77
wide_char_t mbtowc(char const *mb)
Definition: lexer.cpp:487
virtual ~assembler_impl()
Definition: assembler.cpp:81
virtual void newline()
Definition: assembler.cpp:160
output_mode_t
Definition: assembler.h:25
assembler(std::ostream &out)
Definition: assembler.cpp:247
void output(node::pointer_t n, output_mode_t mode)
Definition: assembler.cpp:327
virtual void output_operator(std::string const &str, flags_t flags)
Definition: assembler.cpp:179
virtual void output_identation()
Definition: assembler.cpp:125
void verify_dimension(node::pointer_t n)
Definition: assembler.cpp:56
void output_at_keyword(node::pointer_t n)
Definition: assembler.cpp:752
virtual void output_operator(std::string const &str, flags_t flags)
Definition: assembler.cpp:213
assembler_impl(std::ostream &out)
Definition: assembler.cpp:76
assembler_compact(std::ostream &out)
Definition: assembler.cpp:170
virtual void output_identation()
Definition: assembler.cpp:241
std::string escape_id(std::string const &id)
Definition: assembler.cpp:252
virtual void output_token(std::string const &str)
Definition: assembler.cpp:105
virtual void output_string(std::string const &str)
Definition: assembler.cpp:85
assembler_expanded(std::ostream &out)
Definition: assembler.cpp:204
node::pointer_t f_root
Definition: assembler.h:55
void output_parenthesis(node::pointer_t n, int flags)
Definition: assembler.cpp:719
static error & instance()
Definition: error.cpp:78
void output_comment(node::pointer_t n)
Definition: assembler.cpp:816
static bool constexpr is_identifier(wide_char_t c)
Definition: lexer.h:66

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.