Merge pull request #1465 from YosysHQ/dave/ice40_timing_sim
[yosys.git] / passes / techmap / libparse.cc
index def480394aec91faee5001920c459cea1bf9edb8..349ccc11505568cc05d6881a11e8706bebb51619 100644 (file)
@@ -2,11 +2,11 @@
  *  yosys -- Yosys Open SYnthesis Suite
  *
  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
- *  
+ *
  *  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
@@ -24,6 +24,7 @@
 #include <istream>
 #include <fstream>
 #include <iostream>
+#include <sstream>
 
 #ifndef FILTERLIB
 #include "kernel/log.h"
@@ -86,12 +87,14 @@ 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 == '.') {
-               str = c;
+               str = static_cast<char>(c);
                while (1) {
                        c = f.get();
                        if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.')
@@ -100,10 +103,19 @@ int LibertyParser::lexer(std::string &str)
                                break;
                }
                f.unget();
-               // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
-               return 'v';
+               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 == '"') {
                str = "";
                while (1) {
@@ -118,9 +130,10 @@ int LibertyParser::lexer(std::string &str)
                return 'v';
        }
 
+       // if it wasn't a string, perhaps it's a comment or a forward slash?
        if (c == '/') {
                c = f.get();
-               if (c == '*') {
+               if (c == '*') {         // start of '/*' block comment
                        int last_c = 0;
                        while (c > 0 && (last_c != '*' || c != '/')) {
                                last_c = c;
@@ -129,7 +142,7 @@ int LibertyParser::lexer(std::string &str)
                                        line++;
                        }
                        return lexer(str);
-               } else if (c == '/') {
+               } else if (c == '/') {  // start of '//' line comment
                        while (c > 0 && c != '\n')
                                c = f.get();
                        line++;
@@ -137,24 +150,31 @@ int LibertyParser::lexer(std::string &str)
                }
                f.unget();
                // fprintf(stderr, "LEX: char >>/<<\n");
-               return '/';
+               return '/';             // a single '/' charater.
        }
 
+       // check for a backslash
        if (c == '\\') {
-               c = f.get();
+               c = f.get();            
                if (c == '\r')
                        c = f.get();
-               if (c == '\n')
+               if (c == '\n') {
+                       line++;
                        return lexer(str);
+               }
                f.unget();
                return '\\';
        }
 
+       // check for a new line
        if (c == '\n') {
                line++;
-               return ';';
+               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
@@ -168,14 +188,39 @@ LibertyAst *LibertyParser::parse()
 
        int tok = lexer(str);
 
-       while (tok == ';')
+       // 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')
-               error();
+
+       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;
@@ -184,13 +229,33 @@ LibertyAst *LibertyParser::parse()
        {
                tok = lexer(str);
 
-               if (tok == ';')
+               // 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')
                                error();
+                       tok = lexer(str);
+                       while (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;
                }
 
@@ -202,8 +267,70 @@ LibertyAst *LibertyParser::parse()
                                        continue;
                                if (tok == ')')
                                        break;
-                               if (tok != 'v')
-                                       error();
+                               
+                               // 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':
+                                               error("Unexpected newline.");
+                                               break;
+                                       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;
@@ -229,14 +356,31 @@ LibertyAst *LibertyParser::parse()
 
 void LibertyParser::error()
 {
-       log_error("Syntax error in line %d.\n", line);
+       log_error("Syntax error in liberty file on line %d.\n", line);
+}
+
+void LibertyParser::error(const std::string &str)
+{
+       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()
 {
-       fprintf(stderr, "Syntax error in line %d.\n", line);
+       fprintf(stderr, "Syntax error in liberty file on line %d.\n", line);
+       exit(1);
+}
+
+void LibertyParser::error(const std::string &str)
+{
+       std::stringstream ss;
+       ss << "Syntax error in liberty file on line " << line << ".\n";
+       ss << "  " << str << "\n";
+       printf("%s", ss.str().c_str());
        exit(1);
 }
 
@@ -244,21 +388,21 @@ void LibertyParser::error()
 
 #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();                                                      \
-     }                                                               \
+        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();                                                      \
-     }                                                               \
+        if (!(result)) {                                                \
+          fprintf(stderr, "Error from '%s' in %s:%d.\n",                \
+                          #result, __FILE__, __LINE__);                         \
+          abort();                                                      \
+        }                                                               \
    } while(0)
 
 /**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/