/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Claire Xenia Wolf * * 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. * * --- * * The Verilog frontend. * * This frontend is using the AST frontend library (see frontends/ast/). * Thus this frontend does not generate RTLIL code directly but creates an * AST directly from the Verilog parse tree and then passes this AST to * the AST frontend library. * * --- * * Ad-hoc implementation of a Verilog preprocessor. The directives `define, * `include, `ifdef, `ifndef, `else and `endif are handled here. All other * directives are handled by the lexer (see verilog_lexer.l). * */ #include "preproc.h" #include "verilog_frontend.h" #include "kernel/log.h" #include #include #include #include #include YOSYS_NAMESPACE_BEGIN using namespace VERILOG_FRONTEND; static std::list output_code; static std::list input_buffer; static size_t input_buffer_charp; static void return_char(char ch) { if (input_buffer_charp == 0) input_buffer.push_front(std::string() + ch); else input_buffer.front()[--input_buffer_charp] = ch; } static void insert_input(std::string str) { if (input_buffer_charp != 0) { input_buffer.front() = input_buffer.front().substr(input_buffer_charp); input_buffer_charp = 0; } input_buffer.push_front(str); } static char next_char() { if (input_buffer.empty()) return 0; log_assert(input_buffer_charp <= input_buffer.front().size()); if (input_buffer_charp == input_buffer.front().size()) { input_buffer_charp = 0; input_buffer.pop_front(); return next_char(); } char ch = input_buffer.front()[input_buffer_charp++]; return ch == '\r' ? next_char() : ch; } static std::string skip_spaces() { std::string spaces; while (1) { char ch = next_char(); if (ch == 0) break; if (ch != ' ' && ch != '\t') { return_char(ch); break; } spaces += ch; } return spaces; } static std::string next_token(bool pass_newline = false) { std::string token; char ch = next_char(); if (ch == 0) return token; token += ch; if (ch == '\n') { if (pass_newline) { output_code.push_back(token); return ""; } return token; } if (ch == ' ' || ch == '\t') { while ((ch = next_char()) != 0) { if (ch != ' ' && ch != '\t') { return_char(ch); break; } token += ch; } } else if (ch == '"') { while ((ch = next_char()) != 0) { token += ch; if (ch == '"') break; if (ch == '\\') { if ((ch = next_char()) != 0) token += ch; } } if (token == "\"\"" && (ch = next_char()) != 0) { if (ch == '"') token += ch; else return_char(ch); } } else if (ch == '\\') { while ((ch = next_char()) != 0) { if (ch < 33 || ch > 126) { return_char(ch); break; } token += ch; } } else if (ch == '/') { if ((ch = next_char()) != 0) { if (ch == '/') { token += '*'; char last_ch = 0; while ((ch = next_char()) != 0) { if (ch == '\n') { return_char(ch); break; } if (last_ch != '*' || ch != '/') { token += ch; last_ch = ch; } } token += " */"; } else if (ch == '*') { token += '*'; int newline_count = 0; char last_ch = 0; while ((ch = next_char()) != 0) { if (ch == '\n') { newline_count++; token += ' '; } else token += ch; if (last_ch == '*' && ch == '/') break; last_ch = ch; } while (newline_count-- > 0) return_char('\n'); } else return_char(ch); } } else { const char *ok = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789"; if (ch == '`' || strchr(ok, ch) != NULL) { char first = ch; ch = next_char(); if (first == '`' && (ch == '"' || ch == '`')) { token += ch; } else do { if (strchr(ok, ch) == NULL) { return_char(ch); break; } token += ch; } while ((ch = next_char()) != 0); } } return token; } struct macro_arg_t { macro_arg_t(const std::string &name_, const char *default_value_) : name(name_), has_default(default_value_
from __future__ import (absolute_import, print_function, division)

"""
    A small collection of useful user-agent header strings. These should be
    kept reasonably current to reflect common usage.
"""

# pylint: line-too-long

# A collection of (name, shortcut, string) tuples.

UASTRINGS = [
    ("android",
     "a",
     "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Nexus 7 Build/JRO03D) AFL/01.04.02"),  # noqa
    ("blackberry",
     "l",
     "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+"),  # noqa
    ("bingbot",
     "b",
     "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"),  # noqa
    ("chrome",
     "c",
     "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"),  # noqa
    ("firefox",
     "f",
     "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:14.0) Gecko/20120405 Firefox/14.0a1"),  # noqa
    ("googlebot",
     "g",
     "Googlebot/2.1 (+http://www.googlebot.com/bot.html)"),  # noqa
    ("ie9",
     "i",
     "Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US)"),  # noqa
    ("ipad",
     "p",
     "Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3"),  # noqa
    ("iphone",
     "h",
     "Mozilla/5.0 (iPhone; CPU iPhone OS 4_2_1 like Mac OS X) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5"),  # noqa
    ("safari",
     "s",
     "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10"),  # noqa
]


def get_by_shortcut(s):
    """
        Retrieve a user agent entry by shortcut.
    """
    for i in UASTRINGS:
        if s == i[1]:
            return i
ult value. args.add_arg(arg_name, nullptr); state = 0; skip_spaces(); break; } if (tok == ")") { // As with comma, but set state to 2 (end of args) args.add_arg(arg_name, nullptr); state = 2; skip_spaces(); break; } log_error("Trailing contents after identifier in macro argument `%s': " "expected '=', ',' or ')'.\n", arg_name.c_str()); default: // The only FSM states are 0-2 and we dealt with 2 at the start of the loop. log_assert(false); } } return std::make_pair(newline_count, args); } // Read a `define preprocessor directive. This is called just after reading the token containing // "`define". static void read_define(const std::string &filename, define_map_t &defines_map, define_map_t &global_defines_cache) { std::string name, value; arg_map_t args; skip_spaces(); name = next_token(true); bool here_doc_mode = false; int newline_count = 0; // The FSM state starts at 0. If it sees space (or enters here_doc_mode), it assumes this is // a macro without formal arguments and jumps to state 1. // // In state 0, if it sees an opening parenthesis, it assumes this is a macro with formal // arguments. It reads the arguments with read_define_args() and then jumps to state 2. // // In states 1 or 2, the FSM reads tokens to the end of line (or end of here_doc): this is // the body of the macro definition. int state = 0; if (skip_spaces() != "") state = 1; for (;;) { std::string tok = next_token(); if (tok.empty()) break; // printf("define-tok: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); if (tok == "\"\"\"") { here_doc_mode = !here_doc_mode; continue; } if (state == 0 && tok == "(") { auto pr = read_define_args(); newline_count += pr.first; args = pr.second; state = 2; continue; } // This token isn't an opening parenthesis immediately following the macro name, so // it's presumably at or after the start of the macro body. If state isn't already 2 // (which would mean we'd parsed an argument list), set it to 1. if (state == 0) { state = 1; } if (tok == "\n") { if (here_doc_mode) { value += " "; newline_count++; } else { return_char('\n'); break; } continue; } if (tok == "\\") { char ch = next_char(); if (ch == '\n') { value += " "; newline_count++; } else { value += std::string("\\"); return_char(ch); } continue; } // Is this token the name of a macro argument? If so, replace it with a magic symbol // that we'll replace with the argument value. int arg_pos; if (args.find(tok, &arg_pos)) { value += '`' + args.str_token(name, arg_pos); continue; } // This token is nothing special. Insert it verbatim into the macro body. value += tok; } // Append some newlines so that we don't mess up line counts in error messages. while (newline_count-- > 0) return_char('\n'); if (strchr("abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ$0123456789", name[0])) { // printf("define: >>%s<< -> >>%s<<\n", name.c_str(), value.c_str()); defines_map.add(name, value, (state == 2) ? &args : nullptr); global_defines_cache.add(name, value, (state == 2) ? &args : nullptr); } else { log_file_error(filename, 0, "Invalid name for macro definition: >>%s<<.\n", name.c_str()); } } std::string frontend_verilog_preproc(std::istream &f, std::string filename, const define_map_t &pre_defines, define_map_t &global_defines_cache, const std::list &include_dirs) { define_map_t defines; defines.merge(pre_defines); defines.merge(global_defines_cache); macro_arg_stack_t macro_arg_stack; std::vector filename_stack; // We are inside pass_level levels of satisfied ifdefs, and then within // fail_level levels of unsatisfied ifdefs. The unsatisfied ones are // always within satisfied ones — even if some condition within is true, // the parent condition failing renders it moot. int ifdef_fail_level = 0; int ifdef_pass_level = 0; // For the outermost unsatisfied ifdef, true iff that ifdef already // had a satisfied branch, and further elsif/else branches should be // considered unsatisfied even if the condition is true. // Meaningless if ifdef_fail_level == 0. bool ifdef_already_satisfied = false; output_code.clear(); input_buffer.clear(); input_buffer_charp = 0; input_file(f, filename); while (!input_buffer.empty()) { std::string tok = next_token(); // printf("token: >>%s<<\n", tok != "\n" ? tok.c_str() : "NEWLINE"); if (tok == "`endif") { if (ifdef_fail_level > 0) ifdef_fail_level--; else if (ifdef_pass_level > 0) ifdef_pass_level--; else log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); continue; } if (tok == "`else") { if (ifdef_fail_level == 0) { if (ifdef_pass_level == 0) log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); ifdef_pass_level--; ifdef_fail_level = 1; ifdef_already_satisfied = true; } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied) { ifdef_fail_level = 0; ifdef_pass_level++; ifdef_already_satisfied = true; } continue; } if (tok == "`elsif") { skip_spaces(); std::string name = next_token(true); if (ifdef_fail_level == 0) { if (ifdef_pass_level == 0) log_error("Found %s outside of macro conditional branch!\n", tok.c_str()); ifdef_pass_level--; ifdef_fail_level = 1; ifdef_already_satisfied = true; } else if (ifdef_fail_level == 1 && !ifdef_already_satisfied && defines.find(name)) { ifdef_fail_level = 0; ifdef_pass_level++; ifdef_already_satisfied = true; } continue; } if (tok == "`ifdef") { skip_spaces(); std::string name = next_token(true); if (ifdef_fail_level > 0 || !defines.find(name)) { ifdef_fail_level++; } else { ifdef_pass_level++; ifdef_already_satisfied = true; } if (ifdef_fail_level == 1) ifdef_already_satisfied = false; continue; } if (tok == "`ifndef") { skip_spaces(); std::string name = next_token(true); if (ifdef_fail_level > 0 || defines.find(name)) { ifdef_fail_level++; } else { ifdef_pass_level++; ifdef_already_satisfied = true; } if (ifdef_fail_level == 1) ifdef_already_satisfied = false; continue; } if (ifdef_fail_level > 0) { if (tok == "\n") output_code.push_back(tok); continue; } if (tok == "`include") { skip_spaces(); std::string fn = next_token(true); while (try_expand_macro(defines, macro_arg_stack, fn)) { fn = next_token(); } while (1) { size_t pos = fn.find('"'); if (pos == std::string::npos) break; if (pos == 0) fn = fn.substr(1); else fn = fn.substr(0, pos) + fn.substr(pos+1); } std::ifstream ff; ff.clear(); std::string fixed_fn = fn; ff.open(fixed_fn.c_str()); bool filename_path_sep_found; bool fn_relative; #ifdef _WIN32 // Both forward and backslash are acceptable separators on Windows. filename_path_sep_found = (filename.find_first_of("/\\") != std::string::npos); // Easier just to invert the check for an absolute path (e.g. C:\ or C:/) fn_relative = !(fn[1] == ':' && (fn[2] == '/' || fn[2] == '\\')); #else filename_path_sep_found = (filename.find('/') != std::string::npos); fn_relative = (fn[0] != '/'); #endif if (ff.fail() && fn.size() > 0 && fn_relative && filename_path_sep_found) { // if the include file was not found, it is not given with an absolute path, and the // currently read file is given with a path, then try again relative to its directory ff.clear(); #ifdef _WIN32 fixed_fn = filename.substr(0, filename.find_last_of("/\\")+1) + fn; #else fixed_fn = filename.substr(0, filename.rfind('/')+1) + fn; #endif ff.open(fixed_fn); } if (ff.fail() && fn.size() > 0 && fn_relative) { // if the include file was not found and it is not given with an absolute path, then // search it in the include path for (auto incdir : include_dirs) { ff.clear(); fixed_fn = incdir + '/' + fn; ff.open(fixed_fn); if (!ff.fail()) break; } } if (ff.fail()) { output_code.push_back("`file_notfound " + fn); } else { input_file(ff, fixed_fn); yosys_input_files.insert(fixed_fn); } continue; } if (tok == "`file_push") { skip_spaces(); std::string fn = next_token(true); if (!fn.empty() && fn.front() == '"' && fn.back() == '"') fn = fn.substr(1, fn.size()-2); output_code.push_back(tok + " \"" + fn + "\""); filename_stack.push_back(filename); filename = fn; continue; } if (tok == "`file_pop") { output_code.push_back(tok); filename = filename_stack.back(); filename_stack.pop_back(); continue; } if (tok == "`define") { read_define(filename, defines, global_defines_cache); continue; } if (tok == "`undef") { std::string name; skip_spaces(); name = next_token(true); // printf("undef: >>%s<<\n", name.c_str()); defines.erase(name); global_defines_cache.erase(name); continue; } if (tok == "`timescale") { skip_spaces(); while (!tok.empty() && tok != "\n") tok = next_token(true); if (tok == "\n") return_char('\n'); continue; } if (tok == "`resetall") { defines.clear(); global_defines_cache.clear(); continue; } if (tok == "`__restore_macro_arg") { restore_macro_arg(defines, macro_arg_stack); continue; } if (try_expand_macro(defines, macro_arg_stack, tok)) continue; output_code.push_back(tok); } if (ifdef_fail_level > 0 || ifdef_pass_level > 0) { log_error("Unterminated preprocessor conditional!\n"); } std::string output; for (auto &str : output_code) output += str; output_code.clear(); input_buffer.clear(); input_buffer_charp = 0; return output; } YOSYS_NAMESPACE_END