mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-26 09:24:37 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			163 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  yosys -- Yosys Open SYnthesis Suite
 | |
|  *
 | |
|  *  Copyright (C) 2024  Emily Schmidt <emily@yosyshq.com>
 | |
|  *
 | |
|  *  Permission to use, copy, modify, and/or distribute this software for any
 | |
|  *  purpose with or without fee is hereby granted, provided that the above
 | |
|  *  copyright notice and this permission notice appear in all copies.
 | |
|  *
 | |
|  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | |
|  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | |
|  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | |
|  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | |
|  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | |
|  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | |
|  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "sexpr.h"
 | |
| 
 | |
| YOSYS_NAMESPACE_BEGIN
 | |
| 
 | |
| std::ostream &operator<<(std::ostream &os, SExpr const &sexpr) {
 | |
|     if(sexpr.is_atom())
 | |
|         os << sexpr.atom();
 | |
|     else if(sexpr.is_list()){
 | |
|         os << "(";
 | |
|         auto l = sexpr.list();
 | |
|         for(size_t i = 0; i < l.size(); i++) {
 | |
|             if(i > 0) os << " ";
 | |
|             os << l[i];
 | |
|         }
 | |
|         os << ")";
 | |
|     }else
 | |
|         os << "<invalid>";
 | |
|     return os;
 | |
| }
 | |
| 
 | |
|  std::string SExpr::to_string() const {
 | |
|     std::stringstream ss;
 | |
|     ss << *this;
 | |
|     return ss.str();
 | |
| }
 | |
| 
 | |
| void SExprWriter::nl_if_pending() {
 | |
|     if(_pending_nl) {
 | |
|         os << '\n';
 | |
|         _pos = 0;
 | |
|         _pending_nl = false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SExprWriter::puts(std::string_view s) {
 | |
|     if(s.empty()) return;
 | |
|     nl_if_pending();
 | |
|     for(auto c : s) {
 | |
|         if(c == '\n') {
 | |
|             os << c;
 | |
|             _pos = 0;
 | |
|         } else {
 | |
|             if(_pos == 0) {
 | |
|                 for(int i = 0; i < _indent; i++)
 | |
|                     os << "  ";
 | |
|                 _pos = 2 * _indent;
 | |
|             }
 | |
|             os << c;
 | |
|             _pos++;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| // Calculate how much space would be left if expression was written
 | |
| // out in full horizontally. Returns any negative value if it doesn't fit.
 | |
| //
 | |
| // (Ideally we would avoid recalculating the widths of subexpression,
 | |
| // but I can't figure out how to store the widths. As an alternative,
 | |
| // we bail out of the calculation as soon as we can tell the expression
 | |
| // doesn't fit in the available space.)
 | |
| int SExprWriter::check_fit(SExpr const &sexpr, int space) {
 | |
|     if(sexpr.is_atom())
 | |
|         return space - sexpr.atom().size();
 | |
|     else if(sexpr.is_list()) {
 | |
|         space -= 2;
 | |
|         if(sexpr.list().size() > 1)
 | |
|             space -= sexpr.list().size() - 1;
 | |
|         for(auto arg : sexpr.list()) {
 | |
|             if(space < 0) break;
 | |
|             space = check_fit(arg, space);
 | |
|         }
 | |
|         return space;
 | |
|     } else
 | |
|         return -1;
 | |
| }
 | |
| 
 | |
| void SExprWriter::print(SExpr const &sexpr, bool close, bool indent_rest) {
 | |
|     if(sexpr.is_atom())
 | |
|         puts(sexpr.atom());
 | |
|     else if(sexpr.is_list()) {
 | |
|         auto args = sexpr.list();
 | |
|         puts("(");
 | |
|         // Expressions are printed horizontally if they fit on the line.
 | |
|         // We do the check *after* puts("(") to make sure that _pos is accurate.
 | |
|         // (Otherwise there could be a pending newline + indentation)
 | |
|         bool vertical = args.size() > 1 && check_fit(sexpr, _max_line_width - _pos + 1) < 0;
 | |
|         if(vertical) _indent++;
 | |
|         for(size_t i = 0; i < args.size(); i++) {
 | |
|             if(i > 0) puts(vertical ? "\n" : " ");
 | |
|             print(args[i]);
 | |
|         }
 | |
|         // Any remaining arguments are currently always printed vertically,
 | |
|         // but are not indented if indent_rest = false.
 | |
|         _indent += (!close && indent_rest) - vertical;
 | |
|         if(close)
 | |
|             puts(")");
 | |
|         else {
 | |
|             _unclosed.push_back(indent_rest);
 | |
|             _pending_nl = true;
 | |
|         }
 | |
|     }else
 | |
|         log_error("shouldn't happen: SExpr '%s' is neither an atom nor a list", sexpr.to_string().c_str());
 | |
| }
 | |
| 
 | |
| void SExprWriter::close(size_t n) {
 | |
|     log_assert(_unclosed.size() - (_unclosed_stack.empty() ? 0 : _unclosed_stack.back()) >= n);
 | |
|     while(n-- > 0) {
 | |
|         bool indented = _unclosed[_unclosed.size() - 1];
 | |
|         _unclosed.pop_back();
 | |
|         // Only print ) on the same line if it fits.
 | |
|         _pending_nl = _pos >= _max_line_width;
 | |
|         if(indented)
 | |
|             _indent--;
 | |
|         puts(")");
 | |
|         _pending_nl = true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void SExprWriter::comment(std::string const &str, bool hanging) {
 | |
|     if(hanging) {
 | |
|         if(_pending_nl) {
 | |
|             _pending_nl = false;
 | |
|             puts(" ");
 | |
|         }
 | |
|     }
 | |
|     size_t i = 0, e;
 | |
|     do{
 | |
|         e = str.find('\n', i);
 | |
|         puts("; ");
 | |
|         puts(std::string_view(str).substr(i, e - i));
 | |
|         puts("\n");
 | |
|         i = e + 1;
 | |
|     }while(e != std::string::npos);
 | |
| }
 | |
| 
 | |
| SExprWriter::~SExprWriter() {
 | |
|     while(!_unclosed_stack.empty())
 | |
|         pop();
 | |
|     close(_unclosed.size());
 | |
|     nl_if_pending();
 | |
| }
 | |
| 
 | |
| YOSYS_NAMESPACE_END
 |