mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	There are some leftovers, but this is an easy regex-based approach that removes most of them.
		
			
				
	
	
		
			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());
 | 
						|
}
 | 
						|
 | 
						|
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
 |