3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-12 20:18:20 +00:00
yosys/passes/techmap/libparse.cc
Jannis Harder bc01468c75 read_liberty: Faster std::string construction in the liberty lexer
This extends the `LibertyInputStream` added in the previous commit to
allow arbitrary lookahead. Then this uses the lookahead to find the
total length of the token within the input buffer, instead of consuming
the token byte by byte while appending to a std::string. Constructing
the std::string with the total length is known avoids any reallocations
from growing std::string's buffer.
2025-04-01 14:12:12 +02:00

989 lines
24 KiB
C++

/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@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 "libparse.h"
#include <stdlib.h>
#include <string.h>
#include <istream>
#include <fstream>
#include <iostream>
#include <sstream>
#ifndef FILTERLIB
#include "kernel/log.h"
#endif
using namespace Yosys;
bool LibertyInputStream::extend_buffer_once()
{
if (eof)
return false;
// To support unget we leave the last already read character in the buffer
if (buf_pos > 1) {
size_t move_pos = buf_pos - 1;
memmove(buffer.data(), buffer.data() + move_pos, buf_end - move_pos);
buf_pos -= move_pos;
buf_end -= move_pos;
}
const size_t chunk_size = 4096;
if (buffer.size() < buf_end + chunk_size) {
buffer.resize(buf_end + chunk_size);
}
size_t read_size = f.rdbuf()->sgetn(buffer.data() + buf_end, chunk_size);
buf_end += read_size;
if (read_size < chunk_size)
eof = true;
return read_size != 0;
}
bool LibertyInputStream::extend_buffer_at_least(size_t size) {
while (buffered_size() < size) {
if (!extend_buffer_once())
return false;
}
return true;
}
int LibertyInputStream::get_cold()
{
if (buf_pos == buf_end) {
if (!extend_buffer_at_least())
return EOF;
}
int c = buffer[buf_pos];
buf_pos += 1;
return c;
}
int LibertyInputStream::peek_cold(size_t offset)
{
if (buf_pos + offset >= buf_end) {
if (!extend_buffer_at_least(offset + 1))
return EOF;
}
return buffer[buf_pos + offset];
}
LibertyAst::~LibertyAst()
{
for (auto child : children)
delete child;
children.clear();
}
const LibertyAst *LibertyAst::find(std::string name) const
{
for (auto child : children)
if (child->id == name)
return child;
return NULL;
}
void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string indent, std::string path, bool path_ok) const
{
if (whitelist.count(path + "/*") > 0)
path_ok = true;
path += "/" + id;
if (blacklist.count(id) > 0 || blacklist.count(path) > 0)
return;
if (whitelist.size() > 0 && whitelist.count(id) == 0 && whitelist.count(path) == 0 && !path_ok) {
fprintf(stderr, "Automatically added to blacklist: %s\n", path.c_str());
blacklist.insert(id);
return;
}
fprintf(f, "%s%s", indent.c_str(), id.c_str());
if (!args.empty() || !children.empty()) {
fprintf(f, "(");
for (size_t i = 0; i < args.size(); i++)
fprintf(f, "%s%s", i > 0 ? ", " : "", args[i].c_str());
fprintf(f, ")");
}
if (!value.empty())
fprintf(f, " : %s", value.c_str());
if (!children.empty()) {
fprintf(f, " {\n");
for (size_t i = 0; i < children.size(); i++)
children[i]->dump(f, blacklist, whitelist, indent + " ", path, path_ok);
fprintf(f, "%s}\n", indent.c_str());
} else
fprintf(f, " ;\n");
}
#ifndef FILTERLIB
// https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
if (s.empty())
return LibertyExpression{};
char c = s.peek();
auto lhs = LibertyExpression{};
while (isspace(c)) {
if (s.empty())
return lhs;
s.next();
c = s.peek();
}
if (isalpha(c)) { // pin
lhs.kind = Kind::PIN;
lhs.name = s.pin();
} else if (c == '(') { // parens
s.next();
lhs = parse(s);
if (s.peek() != ')') {
log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr().c_str());
return lhs;
}
s.next();
} else if (c == '!') { // prefix NOT
s.next();
lhs.kind = Kind::NOT;
lhs.children.push_back(parse(s, 7));
} else {
log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr().c_str());
return lhs;
}
while (true) {
if (s.empty())
break;
c = s.peek();
while (isspace(c)) {
if (s.empty())
return lhs;
s.next();
c = s.peek();
}
if (c == '\'') { // postfix NOT
if (min_prio > 7)
break;
s.next();
auto n = LibertyExpression{};
n.kind = Kind::NOT;
n.children.push_back(lhs);
lhs = std::move(n);
continue;
} else if (c == '^') { // infix XOR
if (min_prio > 5)
break;
s.next();
auto rhs = parse(s, 6);
auto n = LibertyExpression{};
n.kind = Kind::XOR;
n.children.push_back(lhs);
n.children.push_back(rhs);
lhs = std::move(n);
continue;
} else if (c == '&' || c == '*') { // infix AND
// technically space should be considered infix AND. it seems rare in practice.
if (min_prio > 3)
break;
s.next();
auto rhs = parse(s, 4);
auto n = LibertyExpression{};
n.kind = Kind::AND;
n.children.push_back(lhs);
n.children.push_back(rhs);
lhs = std::move(n);
continue;
} else if (c == '+' || c == '|') { // infix OR
if (min_prio > 1)
break;
s.next();
auto rhs = parse(s, 2);
auto n = LibertyExpression{};
n.kind = Kind::OR;
n.children.push_back(lhs);
n.children.push_back(rhs);
lhs = std::move(n);
continue;
}
break;
}
return lhs;
}
void LibertyExpression::get_pin_names(pool<std::string>& names) {
if (kind == Kind::PIN) {
names.insert(name);
} else {
for (auto& child : children)
child.get_pin_names(names);
}
}
bool LibertyExpression::eval(dict<std::string, bool>& values) {
bool result = false;
switch (kind) {
case Kind::AND:
result = true;
for (auto& child : children)
result &= child.eval(values);
return result;
case Kind::OR:
result = false;
for (auto& child : children)
result |= child.eval(values);
return result;
case Kind::NOT:
log_assert(children.size() == 1);
return !children[0].eval(values);
case Kind::XOR:
result = false;
for (auto& child : children)
result ^= child.eval(values);
return result;
case Kind::PIN:
return values.at(name);
case Kind::EMPTY:
log_assert(false);
}
return false;
}
#endif
int LibertyParser::lexer(std::string &str)
{
int c;
// eat whitespace
do {
c = f.get();
} while (c == ' ' || c == '\t' || c == '\r');
// search for identifiers, numbers, plus or minus.
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') {
f.unget();
size_t i = 1;
while (true) {
c = f.peek(i);
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
i += 1;
else
break;
}
str.clear();
str.append(f.buffered_data(), f.buffered_data() + i);
f.consume(i);
if (str == "+" || str == "-") {
/* Single operator is not an identifier */
// fprintf(stderr, "LEX: char >>%s<<\n", str.c_str());
return str[0];
}
else {
// fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
return 'v';
}
}
// if it wasn't an identifer, number of array range,
// maybe it's a string?
if (c == '"') {
size_t i = 0;
while (true) {
c = f.peek(i);
line += (c == '\n');
if (c != '"')
i += 1;
else
break;
}
str.clear();
#ifdef FILTERLIB
f.unget();
str.append(f.buffered_data(), f.buffered_data() + i + 2);
f.consume(i + 2);
#else
str.append(f.buffered_data(), f.buffered_data() + i);
f.consume(i + 1);
#endif
return 'v';
}
// if it wasn't a string, perhaps it's a comment or a forward slash?
if (c == '/') {
c = f.get();
if (c == '*') { // start of '/*' block comment
int last_c = 0;
while (c > 0 && (last_c != '*' || c != '/')) {
last_c = c;
c = f.get();
if (c == '\n')
line++;
}
return lexer(str);
} else if (c == '/') { // start of '//' line comment
while (c > 0 && c != '\n')
c = f.get();
line++;
return lexer(str);
}
f.unget();
// fprintf(stderr, "LEX: char >>/<<\n");
return '/'; // a single '/' charater.
}
// check for a backslash
if (c == '\\') {
c = f.get();
if (c == '\r')
c = f.get();
if (c == '\n') {
line++;
return lexer(str);
}
f.unget();
return '\\';
}
// check for a new line
if (c == '\n') {
line++;
return 'n';
}
// anything else, such as ';' will get passed
// through as literal items.
// if (c >= 32 && c < 255)
// fprintf(stderr, "LEX: char >>%c<<\n", c);
// else
// fprintf(stderr, "LEX: char %d\n", c);
return c;
}
LibertyAst *LibertyParser::parse()
{
std::string str;
int tok = lexer(str);
// there are liberty files in the wild that
// have superfluous ';' at the end of
// a { ... }. We simply ignore a ';' here.
// and get to the next statement.
while ((tok == 'n') || (tok == ';'))
tok = lexer(str);
if (tok == '}' || tok < 0)
return NULL;
if (tok != 'v') {
std::string eReport;
switch(tok)
{
case 'n':
error("Unexpected newline.");
break;
case '[':
case ']':
case '}':
case '{':
case '\"':
case ':':
eReport = "Unexpected '";
eReport += static_cast<char>(tok);
eReport += "'.";
error(eReport);
break;
default:
error();
}
}
LibertyAst *ast = new LibertyAst;
ast->id = str;
while (1)
{
tok = lexer(str);
// allow both ';' and new lines to
// terminate a statement.
if ((tok == ';') || (tok == 'n'))
break;
if (tok == ':' && ast->value.empty()) {
tok = lexer(ast->value);
if (tok == 'v') {
tok = lexer(str);
}
while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') {
ast->value += tok;
tok = lexer(str);
if (tok != 'v')
error();
ast->value += str;
tok = lexer(str);
}
// In a liberty file, all key : value pairs should end in ';'
// However, there are some liberty files in the wild that
// just have a newline. We'll be kind and accept a newline
// instead of the ';' too..
if ((tok == ';') || (tok == 'n'))
break;
else
error();
continue;
}
if (tok == '(') {
while (1) {
std::string arg;
tok = lexer(arg);
if (tok == ',')
continue;
if (tok == ')')
break;
// FIXME: the AST needs to be extended to store
// these vector ranges.
if (tok == '[')
{
// parse vector range [A] or [A:B]
std::string arg;
tok = lexer(arg);
if (tok != 'v')
{
// expected a vector array index
error("Expected a number.");
}
else
{
// fixme: check for number A
}
tok = lexer(arg);
// optionally check for : in case of [A:B]
// if it isn't we just expect ']'
// as we have [A]
if (tok == ':')
{
tok = lexer(arg);
if (tok != 'v')
{
// expected a vector array index
error("Expected a number.");
}
else
{
// fixme: check for number B
tok = lexer(arg);
}
}
// expect a closing bracket of array range
if (tok != ']')
{
error("Expected ']' on array range.");
}
continue;
}
if (tok != 'v') {
std::string eReport;
switch(tok)
{
case 'n':
continue;
case '[':
case ']':
case '}':
case '{':
case '\"':
case ':':
eReport = "Unexpected '";
eReport += static_cast<char>(tok);
eReport += "'.";
error(eReport);
break;
default:
error();
}
}
ast->args.push_back(arg);
}
continue;
}
if (tok == '{') {
while (1) {
LibertyAst *child = parse();
if (child == NULL)
break;
ast->children.push_back(child);
}
break;
}
error();
}
return ast;
}
#ifndef FILTERLIB
void LibertyParser::error() const
{
log_error("Syntax error in liberty file on line %d.\n", line);
}
void LibertyParser::error(const std::string &str) const
{
std::stringstream ss;
ss << "Syntax error in liberty file on line " << line << ".\n";
ss << " " << str << "\n";
log_error("%s", ss.str().c_str());
}
#else
void LibertyParser::error() const
{
fprintf(stderr, "Syntax error in liberty file on line %d.\n", line);
exit(1);
}
void LibertyParser::error(const std::string &str) const
{
std::stringstream ss;
ss << "Syntax error in liberty file on line " << line << ".\n";
ss << " " << str << "\n";
printf("%s", ss.str().c_str());
exit(1);
}
/**** BEGIN: http://svn.clairexen.net/tools/trunk/examples/check.h ****/
#define CHECK_NV(result, check) \
do { \
auto _R = (result); \
if (!(_R check)) { \
fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \
#result, (long int)_R, #check, __FILE__, __LINE__); \
abort(); \
} \
} while(0)
#define CHECK_COND(result) \
do { \
if (!(result)) { \
fprintf(stderr, "Error from '%s' in %s:%d.\n", \
#result, __FILE__, __LINE__); \
abort(); \
} \
} while(0)
/**** END: http://svn.clairexen.net/tools/trunk/examples/check.h ****/
const LibertyAst *find_non_null(const LibertyAst *node, const char *name)
{
const LibertyAst *ret = node->find(name);
if (ret == NULL)
fprintf(stderr, "Error: expected to find `%s' node.\n", name);
return ret;
}
std::string func2vl(std::string str)
{
for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
char c_left = pos > 0 ? str[pos-1] : ' ';
char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
if (std::string("\" \t*+").find(c_left) != std::string::npos)
str.erase(pos, 1);
else if (std::string("\" \t*+").find(c_right) != std::string::npos)
str.erase(pos, 1);
else
str[pos] = '*';
}
std::vector<size_t> group_start;
for (size_t pos = 0; pos < str.size(); pos++) {
if (str[pos] == '(')
group_start.push_back(pos);
if (str[pos] == ')' && group_start.size() > 0) {
if (pos+1 < str.size() && str[pos+1] == '\'') {
std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
str[group_start.back()] = '~';
str.replace(group_start.back()+1, group.size(), group);
pos++;
}
group_start.pop_back();
}
if (str[pos] == '\'' && pos > 0) {
size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
std::string group = str.substr(start, pos-start);
str[start] = '~';
str.replace(start+1, group.size(), group);
}
if (str[pos] == '*')
str[pos] = '&';
if (str[pos] == '+')
str[pos] = '|';
}
return str;
}
void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
{
edge.clear();
expr.clear();
if (ast != NULL) {
expr = func2vl(ast->value);
if (expr.size() > 0 && expr[0] == '~')
edge = "negedge " + expr.substr(1);
else
edge = "posedge " + expr;
}
}
void clear_preset_var(std::string var, std::string type)
{
if (type.find('L') != std::string::npos) {
printf(" %s <= 0;\n", var.c_str());
return;
}
if (type.find('H') != std::string::npos) {
printf(" %s <= 1;\n", var.c_str());
return;
}
if (type.find('T') != std::string::npos) {
printf(" %s <= ~%s;\n", var.c_str(), var.c_str());
return;
}
if (type.find('X') != std::string::npos) {
printf(" %s <= 'bx;\n", var.c_str());
return;
}
}
void gen_verilogsim_cell(const LibertyAst *ast)
{
if (ast->find("statetable") != NULL)
return;
CHECK_NV(ast->args.size(), == 1);
printf("module %s (", ast->args[0].c_str());
bool first = true;
for (auto child : ast->children) {
if (child->id != "pin")
continue;
CHECK_NV(child->args.size(), == 1);
printf("%s%s", first ? "" : ", ", child->args[0].c_str());
first = false;
}
printf(");\n");
for (auto child : ast->children) {
if (child->id != "ff" && child->id != "latch")
continue;
printf(" reg ");
first = true;
for (auto arg : child->args) {
printf("%s%s", first ? "" : ", ", arg.c_str());
first = false;
}
printf(";\n");
}
for (auto child : ast->children) {
if (child->id != "pin")
continue;
CHECK_NV(child->args.size(), == 1);
const LibertyAst *dir = find_non_null(child, "direction");
const LibertyAst *func = child->find("function");
printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
if (func != NULL)
printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
}
for (auto child : ast->children)
{
if (child->id != "ff" || child->args.size() != 2)
continue;
std::string iq_var = child->args[0];
std::string iqn_var = child->args[1];
std::string clock_edge, clock_expr;
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
std::string clear_edge, clear_expr;
event2vl(child->find("clear"), clear_edge, clear_expr);
std::string preset_edge, preset_expr;
event2vl(child->find("preset"), preset_edge, preset_expr);
std::string edge = "";
if (!clock_edge.empty())
edge += (edge.empty() ? "" : ", ") + clock_edge;
if (!clear_edge.empty())
edge += (edge.empty() ? "" : ", ") + clear_edge;
if (!preset_edge.empty())
edge += (edge.empty() ? "" : ", ") + preset_edge;
if (edge.empty())
continue;
printf(" always @(%s) begin\n", edge.c_str());
const char *else_prefix = "";
if (!clear_expr.empty() && !preset_expr.empty()) {
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
printf(" end\n");
else_prefix = "else ";
}
if (!clear_expr.empty()) {
printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
printf(" %s <= 0;\n", iq_var.c_str());
printf(" %s <= 1;\n", iqn_var.c_str());
printf(" end\n");
else_prefix = "else ";
}
if (!preset_expr.empty()) {
printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
printf(" %s <= 1;\n", iq_var.c_str());
printf(" %s <= 0;\n", iqn_var.c_str());
printf(" end\n");
else_prefix = "else ";
}
if (*else_prefix)
printf(" %sbegin\n", else_prefix);
std::string expr = find_non_null(child, "next_state")->value;
printf(" // %s\n", expr.c_str());
printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
if (*else_prefix)
printf(" end\n");
printf(" end\n");
}
for (auto child : ast->children)
{
if (child->id != "latch" || child->args.size() != 2)
continue;
std::string iq_var = child->args[0];
std::string iqn_var = child->args[1];
std::string enable_edge, enable_expr;
event2vl(child->find("enable"), enable_edge, enable_expr);
std::string clear_edge, clear_expr;
event2vl(child->find("clear"), clear_edge, clear_expr);
std::string preset_edge, preset_expr;
event2vl(child->find("preset"), preset_edge, preset_expr);
printf(" always @* begin\n");
const char *else_prefix = "";
if (!clear_expr.empty() && !preset_expr.empty()) {
printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str());
clear_preset_var(iq_var, find_non_null(child, "clear_preset_var1")->value);
clear_preset_var(iqn_var, find_non_null(child, "clear_preset_var2")->value);
printf(" end\n");
else_prefix = "else ";
}
if (!clear_expr.empty()) {
printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str());
printf(" %s <= 0;\n", iq_var.c_str());
printf(" %s <= 1;\n", iqn_var.c_str());
printf(" end\n");
else_prefix = "else ";
}
if (!preset_expr.empty()) {
printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str());
printf(" %s <= 1;\n", iq_var.c_str());
printf(" %s <= 0;\n", iqn_var.c_str());
printf(" end\n");
else_prefix = "else ";
}
if (!enable_expr.empty()) {
printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str());
std::string expr = find_non_null(child, "data_in")->value;
printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str());
printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str());
printf(" end\n");
else_prefix = "else ";
}
printf(" end\n");
}
printf("endmodule\n");
}
void gen_verilogsim(const LibertyAst *ast)
{
CHECK_COND(ast->id == "library");
for (auto child : ast->children)
if (child->id == "cell" && !child->find("dont_use"))
gen_verilogsim_cell(child);
}
void usage()
{
fprintf(stderr, "Usage: filterlib [rules-file [liberty-file]]\n");
fprintf(stderr, " or: filterlib -verilogsim [liberty-file]\n");
exit(1);
}
int main(int argc, char **argv)
{
bool flag_verilogsim = false;
std::set<std::string> whitelist, blacklist;
if (argc > 3)
usage();
if (argc > 1)
{
if (!strcmp(argv[1], "-verilogsim"))
flag_verilogsim = true;
if (!strcmp(argv[1], "-") || !strcmp(argv[1], "-verilogsim"))
{
whitelist.insert("/library");
whitelist.insert("/library/cell");
whitelist.insert("/library/cell/area");
whitelist.insert("/library/cell/cell_footprint");
whitelist.insert("/library/cell/dont_touch");
whitelist.insert("/library/cell/dont_use");
whitelist.insert("/library/cell/ff");
whitelist.insert("/library/cell/ff/*");
whitelist.insert("/library/cell/latch");
whitelist.insert("/library/cell/latch/*");
whitelist.insert("/library/cell/pin");
whitelist.insert("/library/cell/pin/clock");
whitelist.insert("/library/cell/pin/direction");
whitelist.insert("/library/cell/pin/driver_type");
whitelist.insert("/library/cell/pin/function");
whitelist.insert("/library/cell/pin_opposite");
whitelist.insert("/library/cell/pin/state_function");
whitelist.insert("/library/cell/pin/three_state");
whitelist.insert("/library/cell/statetable");
whitelist.insert("/library/cell/statetable/*");
}
else
{
FILE *f = fopen(argv[1], "r");
if (f == NULL) {
fprintf(stderr, "Can't open rules file `%s'.\n", argv[1]);
usage();
}
char buffer[1024];
while (fgets(buffer, 1024, f) != NULL)
{
char mode = 0;
std::string id;
for (char *p = buffer; *p; p++)
{
if (*p == '-' || *p == '+') {
if (mode != 0)
goto syntax_error;
mode = *p;
continue;
}
if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == '#') {
if (!id.empty()) {
if (mode == '-')
blacklist.insert(id);
else
if (mode == '+')
whitelist.insert(id);
else
goto syntax_error;
}
id.clear();
if (*p == '#')
break;
continue;
}
id += *p;
continue;
syntax_error:
fprintf(stderr, "Syntax error in rules file:\n%s", buffer);
exit(1);
}
}
}
}
std::istream *f = &std::cin;
if (argc == 3) {
std::ifstream *ff = new std::ifstream;
ff->open(argv[2]);
if (ff->fail()) {
delete ff;
fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]);
usage();
}
f = ff;
}
LibertyParser parser(*f);
if (parser.ast) {
if (flag_verilogsim)
gen_verilogsim(parser.ast);
else
parser.ast->dump(stdout, blacklist, whitelist);
}
if (argc == 3)
delete f;
return 0;
}
#endif