iverilog/vhdlpp/expression_emit.cc

998 lines
25 KiB
C++
Raw Normal View History

/*
* Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com)
* Copyright CERN 2012-2015 / Stephen Williams (steve@icarus.com)
2015-03-27 15:23:48 +01:00
* @author Maciej Suminski (maciej.suminski@cern.ch)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
2012-08-29 03:41:23 +02:00
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "expression.h"
# include "vtype.h"
# include "architec.h"
# include "package.h"
# include "std_funcs.h"
# include "parse_types.h"
# include <typeinfo>
# include <iostream>
# include <cstdlib>
# include <cstring>
# include "ivl_assert.h"
# include <cassert>
using namespace std;
inline static int emit_logic(char val, ostream& out, const VTypePrimitive::type_t type)
{
// TODO case 'W': case 'L': case 'H':
switch (val) {
case '-': case 'U':
val = 'x';
/* fall through */
case 'X': case 'Z':
assert(type == VTypePrimitive::STDLOGIC);
/* fall through */
case '0':
case '1':
out << (char) tolower(val);
break;
default:
assert(false);
out << "x";
return 1;
}
return 0;
}
int Expression::emit(ostream&out, Entity*, ScopeBase*)
{
out << " /* " << get_fileline() << ": internal error: "
<< "I don't know how to emit this expression! "
<< "type=" << typeid(*this).name() << " */ ";
return 1;
}
int Expression::emit_package(ostream&out)
{
out << " /* " << get_fileline() << ": internal error: "
<< "I don't know how to emit_package this expression! "
<< "type=" << typeid(*this).name() << " */ ";
return 1;
}
bool Expression::is_primary(void) const
{
return false;
}
int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
bool oper_primary = operand1_->is_primary();
if (! oper_primary) out << "(";
errors += operand1_->emit(out, ent, scope);
if (! oper_primary) out << ")";
return errors;
}
int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
bool oper_primary = operand2_->is_primary();
if (! oper_primary) out << "(";
errors += operand2_->emit(out, ent, scope);
if (! oper_primary) out << ")";
return errors;
}
int ExpUnary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
errors += operand1_->emit(out, ent, scope);
return errors;
}
int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
if (peek_type() == 0) {
out << "/* " << get_fileline() << ": internal error: "
<< "Aggregate literal needs well defined type." << endl;
return 1;
}
const VType*use_type = peek_type();
while (const VTypeDef*def = dynamic_cast<const VTypeDef*> (use_type)) {
use_type = def->peek_definition();
}
if (const VTypeArray*atype = dynamic_cast<const VTypeArray*> (use_type))
return emit_array_(out, ent, scope, atype);
else if (const VTypeRecord*arecord = dynamic_cast<const VTypeRecord*> (use_type))
return emit_record_(out, ent, scope, arecord);
out << "/* " << get_fileline() << ": internal error: "
<< "I don't know how to elab/emit aggregate in " << typeid(use_type).name()
<< " type context. */";
return 1;
}
int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*atype)
{
int errors = 0;
// Special case: The aggregate is a single "others" item.
if (aggregate_.size() == 1 && aggregate_[0].choice->others()) {
assert(atype->dimensions() == 1);
const VTypeArray::range_t&rang = atype->dimension(0);
assert(! rang.is_box());
int64_t use_msb;
int64_t use_lsb;
bool rc_msb, rc_lsb;
rc_msb = rang.msb()->evaluate(ent, scope, use_msb);
rc_lsb = rang.lsb()->evaluate(ent, scope, use_lsb);
if (rc_msb && rc_lsb) {
int asize = (use_msb >= use_lsb) ? (use_msb - use_lsb) + 1 :
(use_lsb - use_msb) + 1;
out << "{" << asize << "{";
errors += aggregate_[0].expr->emit(out, ent, scope);
out << "}}";
} else {
out << "{(";
if (rc_msb) {
out << use_msb;
} else {
out << "(";
errors += rang.msb()->emit(out, ent, scope);
out << ")";
}
if (rc_lsb && use_lsb==0) {
} else if (rc_lsb) {
out << "-" << use_lsb;
} else {
out << "-(";
errors += rang.lsb()->emit(out, ent, scope);
out << ")";
}
out << "+1){";
errors += aggregate_[0].expr->emit(out, ent, scope);
out << "}}";
}
return errors;
}
const VTypeArray::range_t&rang = atype->dimension(0);
assert(! rang.is_box());
// Fully calculate the range numbers.
int64_t use_msb, use_lsb;
bool rc;
rc = rang.msb()->evaluate(ent, scope, use_msb);
ivl_assert(*this, rc);
rc = rang.lsb()->evaluate(ent, scope, use_lsb);
ivl_assert(*this, rc);
if(use_msb < use_lsb)
swap(use_msb, use_lsb);
map<int64_t,choice_element*> element_map;
choice_element*element_other = 0;
bool positional_section = true;
int64_t positional_idx = use_msb;
for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) {
if (aggregate_[idx].choice == 0) {
// positional association!
if (!positional_section) {
cerr << get_fileline() << ": error: "
<< "All positional associations must be before"
<< " any named associations." << endl;
errors += 1;
}
element_map[positional_idx] = &aggregate_[idx];
positional_idx -= 1;
continue;
}
if (aggregate_[idx].choice->others()) {
ivl_assert(*this, element_other == 0);
element_other = &aggregate_[idx];
continue;
}
// If this is a range choice, then calculate the bounds
// of the range and scan through the values, mapping the
// value to the aggregate_[idx] element.
if (prange_t*range = aggregate_[idx].choice->range_expressions()) {
int64_t begin_val, end_val;
if (! range->msb()->evaluate(ent, scope, begin_val)) {
cerr << range->msb()->get_fileline() << ": error: "
<< "Unable to evaluate aggregate choice expression." << endl;
errors += 1;
continue;
}
if (! range->lsb()->evaluate(ent, scope, end_val)) {
cerr << range->msb()->get_fileline() << ": error: "
<< "Unable to evaluate aggregate choice expression." << endl;
errors += 1;
continue;
}
if (begin_val < end_val) {
int64_t tmp = begin_val;
begin_val = end_val;
end_val = tmp;
}
while (begin_val >= end_val) {
element_map[begin_val] = &aggregate_[idx];
begin_val -= 1;
}
continue;
}
int64_t tmp_val;
Expression*tmp = aggregate_[idx].choice->simple_expression(false);
ivl_assert(*this, tmp);
// Named aggregate element. Once we see one of
// these, we can no longer accept positional
// elements so disable further positional
// processing.
positional_section = false;
if (! tmp->evaluate(ent, scope, tmp_val)) {
cerr << tmp->get_fileline() << ": error: "
<< "Unable to evaluate aggregate choice expression." << endl;
errors += 1;
continue;
}
element_map[tmp_val] = &aggregate_[idx];
}
// Emit the elements as a concatenation. This works great for
// vectors of bits. We implement VHDL arrays as packed arrays,
// so this should be generally correct.
// TODO uncomment this once ivl supports assignments of '{}
/*if(!peek_type()->can_be_packed())
out << "'";*/
out << "{";
for (int64_t idx = use_msb ; idx >= use_lsb ; idx -= 1) {
choice_element*cur = element_map[idx];
if (cur == 0)
cur = element_other;
if (idx < use_msb)
out << ", ";
if (cur == 0) {
out << "/* Missing element " << idx << " */";
cerr << get_fileline() << ": error: "
<< "Missing element " << idx << "." << endl;
errors += 1;
} else {
errors += cur->expr->emit(out, ent, scope);
}
}
out << "}";
return errors;
}
int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*)
{
int errors = 0;
out << "{";
for (size_t idx = 0 ; idx < aggregate_.size() ; idx += 1) {
ivl_assert(*this, !aggregate_[idx].choice->others());
ivl_assert(*this, !aggregate_[idx].choice->range_expressions());
//Expression*name = aggregate_[idx].choice->simple_expression(false);
//ivl_assert(*this, name);
Expression*val = aggregate_[idx].expr;
ivl_assert(*this, val);
if(idx != 0)
out << ",";
//errors += name->emit(out, ent, scope);
//out << ": ";
errors += val->emit(out, ent, scope);
}
out << "}";
return errors;
}
int ExpAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
// Try to evaluate first
int64_t val;
if(evaluate(scope, val)) {
out << val;
return 0;
}
if (name_ == "event") {
out << "$ivlh_attribute_event(";
errors += base_->emit(out, ent, scope);
out << ")";
return errors;
}
/* Special Case: The length,left & right attributes can be calculated
all the down to a literal integer at compile time, and all it
needs is the type of the base expression. (The base
expression doesn't even need to be evaluated.) */
if (name_=="length") {
out << "$bits(";
errors += base_->emit(out, ent, scope);
out << ")";
return errors;
} else if (name_=="left" || name_=="right") {
out << "$" << name_ << "(";
errors += base_->emit(out, ent, scope);
out << ")";
return errors;
}
out << "$ivl_attribute(";
errors += base_->emit(out, ent, scope);
out << ", \"" << name_ << "\")";
return errors;
}
int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope)
2011-02-20 02:47:30 +01:00
{
int errors = 0;
errors += emit_operand1(out, ent, scope);
2011-02-20 02:47:30 +01:00
switch (fun_) {
case PLUS:
out << " + ";
break;
case MINUS:
out << " - ";
break;
case MULT:
out << " * ";
break;
case DIV:
out << " / ";
break;
case MOD:
out << " % ";
break;
case POW:
out << " ** ";
break;
case REM:
out << " /* ?remainder? */ ";
break;
case xCONCAT:
ivl_assert(*this, 0);
out << " /* ?concat? */ ";
break;
2011-02-20 02:47:30 +01:00
}
errors += emit_operand2(out, ent, scope);
2011-02-20 02:47:30 +01:00
return errors;
}
int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*)
{
int errors = 0;
out << value_.size() << "'b";
for (size_t idx = 0 ; idx < value_.size() ; idx += 1)
out << value_[value_.size()-idx-1];
return errors;
}
int ExpCharacter::emit_primitive_bit_(ostream&out, Entity*, ScopeBase*,
const VTypePrimitive*etype)
{
out << "1'b";
int res = emit_logic(value_, out, etype->type());
if(res)
cerr << get_fileline() << ": internal error: "
<< "Don't know how to handle bit " << value_
<< " with etype==" << etype->type() << endl;
return res;
}
int ExpCharacter::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
const VType*etype = peek_type();
if (const VTypePrimitive*use_type = dynamic_cast<const VTypePrimitive*>(etype)) {
return emit_primitive_bit_(out, ent, scope, use_type);
}
if (const VTypeArray*array = dynamic_cast<const VTypeArray*>(etype)) {
if (const VTypePrimitive*use_type = dynamic_cast<const VTypePrimitive*>(array->element_type())) {
return emit_primitive_bit_(out, ent, scope, use_type);
}
}
out << "\"" << value_ << "\"";
return 0;
}
bool ExpCharacter::is_primary(void) const
{
return true;
}
/*
* This is not exactly a "primary", but it is wrapped in its own
* parentheses (braces) so we return true here.
*/
bool ExpConcat::is_primary(void) const
{
return true;
}
int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
out << "{";
errors += operand1_->emit(out, ent, scope);
out << ", ";
errors += operand2_->emit(out, ent, scope);
out << "}";
return errors;
}
int ExpConditional::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
out << "(";
// Draw out any when-else expressions. These are all the else_
// clauses besides the last.
if (options_.size() > 1) {
list<case_t*>::iterator last = options_.end();
--last;
for (list<case_t*>::iterator cur = options_.begin()
; cur != last ; ++cur) {
errors += (*cur)->emit_option(out, ent, scope);
}
}
errors += options_.back()->emit_default(out, ent, scope);
out << ")";
// The emit_option() functions do not close the last
// parentheses so that the following expression can be
// nested. But that means come the end, we have some
// expressions to close.
for (size_t idx = 1 ; idx < options_.size() ; idx += 1)
out << ")";
2012-08-16 20:06:20 +02:00
return errors;
}
int ExpConditional::case_t::emit_option(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
assert(cond_ != 0);
out << "(";
errors += cond_->emit(out, ent, scope);
out << ")? (";
if (true_clause_.size() > 1) {
cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl;
errors += 1;
}
Expression*tmp = true_clause_.front();
errors += tmp->emit(out, ent, scope);
out << ") : (";
return errors;
}
int ExpConditional::case_t::emit_default(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
// Trailing else must have no condition.
assert(cond_ == 0);
if (true_clause_.size() > 1) {
cerr << get_fileline() << ": sorry: Multiple expression waveforms not supported here." << endl;
errors += 1;
}
Expression*tmp = true_clause_.front();
errors += tmp->emit(out, ent, scope);
return errors;
}
int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
switch (fun_) {
case NEGEDGE:
out << "negedge ";
break;
case POSEDGE:
out << "posedge ";
break;
case ANYEDGE:
break;
}
errors += emit_operand1(out, ent, scope);
return errors;
}
int ExpFunc::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
ivl_assert(*this, def_);
// If this function has an elaborated definition, and if
// that definition is in a package, then include the
// package name as a scope qualifier. This assures that
// the SV elaborator finds the correct VHDL elaborated
// definition.
const Package*pkg = dynamic_cast<const Package*> (def_->get_parent());
if (pkg != 0)
out << "\\" << pkg->name() << " ::";
errors += def_->emit_name(argv_, out, ent, scope);
out << " (";
def_->emit_args(argv_, out, ent, scope);
out << ")";
return errors;
}
int ExpInteger::emit(ostream&out, Entity*, ScopeBase*)
{
out << "32'd" << value_;
return 0;
}
int ExpInteger::emit_package(ostream&out)
{
out << value_;
return 0;
}
int ExpReal::emit(ostream&out, Entity*, ScopeBase*)
2014-08-06 15:00:35 +02:00
{
out << value_;
return 0;
}
int ExpReal::emit_package(ostream&out)
{
out << value_;
return 0;
}
bool ExpReal::is_primary(void) const
{
return true;
}
int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
errors += emit_operand1(out, ent, scope);
switch (fun_) {
case AND:
out << " & ";
break;
case OR:
out << " | ";
break;
case XOR:
out << " ^ ";
break;
case NAND:
out << " ~& ";
break;
case NOR:
out << " ~| ";
break;
case XNOR:
out << " ~^ ";
break;
}
errors += emit_operand2(out, ent, scope);
return errors;
}
int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
if (prefix_.get()) {
errors += prefix_->emit_as_prefix_(out, ent, scope);
}
out << "\\" << name_ << " ";
if (index_) {
out << "[";
errors += index_->emit(out, ent, scope);
out << "]";
ivl_assert(*this, lsb_ == 0);
}
out << ".";
return errors;
}
int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
int field_size = 0;
list<index_t*> indices;
if(try_workarounds_(out, ent, scope, indices, field_size)) {
emit_workaround_(out, ent, scope, indices, field_size);
2015-06-11 17:16:16 +02:00
for(list<index_t*>::iterator it = indices.begin();
it != indices.end(); ++it)
{
delete *it;
}
return 0;
}
if (prefix_.get()) {
errors += prefix_->emit_as_prefix_(out, ent, scope);
}
const GenerateStatement*gs = 0;
Architecture*arc = dynamic_cast<Architecture*>(scope);
if (arc && (gs = arc->probe_genvar_emit(name_)))
out << "\\" << gs->get_name() << ":" << name_ << " ";
else
out << "\\" << name_ << " ";
if (index_) {
out << "[";
errors += index_->emit(out, ent, scope);
if (lsb_) {
out << ":";
errors += lsb_->emit(out, ent, scope);
}
out << "]";
}
return errors;
}
bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope,
list<index_t*>& indices, int& data_size)
{
Expression*exp = NULL;
bool wrkand_required = false;
const VType*type = NULL;
if(!scope)
return false;
if(prefix_.get())
prefix_->try_workarounds_(out, ent, scope, indices, data_size);
if(index_ && !lsb_ && scope->find_constant(name_, type, exp)) {
while(const VTypeDef*type_def = dynamic_cast<const VTypeDef*>(type)) {
type = type_def->peek_definition();
}
const VTypeArray*arr = dynamic_cast<const VTypeArray*>(type);
assert(arr);
wrkand_required |= check_const_array_workaround_(arr, scope, indices, data_size);
}
if(prefix_.get() && scope->find_constant(prefix_->name_, type, exp)) {
// Handle the case of array of records
if(prefix_->index_) {
const VTypeArray*arr = dynamic_cast<const VTypeArray*>(type);
assert(arr);
type = arr->element_type();
data_size = type->get_width(scope);
}
while(const VTypeDef*type_def = dynamic_cast<const VTypeDef*>(type)) {
type = type_def->peek_definition();
}
const VTypeRecord*rec = dynamic_cast<const VTypeRecord*>(type);
assert(rec);
wrkand_required |= check_const_record_workaround_(rec, scope, indices, data_size);
}
return wrkand_required;
}
bool ExpName::check_const_array_workaround_(const VTypeArray*arr,
ScopeBase*scope, list<index_t*>&indices, int&data_size) const
{
const VType*element = arr->element_type();
data_size = element->get_width(scope);
if(data_size < 0)
return false;
2015-06-11 17:16:16 +02:00
indices.push_back(new index_t(index_->clone(), new ExpInteger(data_size)));
return true;
}
bool ExpName::check_const_record_workaround_(const VTypeRecord*rec,
ScopeBase*scope, list<index_t*>&indices, int&data_size) const
{
int tmp_offset = 0;
const vector<VTypeRecord::element_t*>& elements = rec->get_elements();
for(vector<VTypeRecord::element_t*>::const_reverse_iterator it = elements.rbegin();
it != elements.rend(); ++it) {
VTypeRecord::element_t* el = (*it);
if(el->peek_name() == name_) {
const VType*type = el->peek_type();
int tmp_field = type->get_width(scope);
if(tmp_field < 0)
return false;
data_size = tmp_field;
indices.push_back(new index_t(NULL, NULL, new ExpInteger(tmp_offset)));
if(index_) {
const VTypeArray*arr = dynamic_cast<const VTypeArray*>(type);
assert(arr);
return check_const_array_workaround_(arr, scope, indices, data_size);
}
return true;
}
int w = el->peek_type()->get_width(scope);
if(w < 0)
return false;
tmp_offset += w;
}
return false;
}
int ExpName::emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope,
const list<index_t*>& indices, int field_size)
{
int errors = 0;
out << "\\" << (prefix_.get() ? prefix_->name_ : name_) << " [";
for(list<index_t*>::const_iterator it = indices.begin();
it != indices.end(); ++it) {
errors += (*it)->emit(out, ent, scope);
out << "+";
}
out << ":" << field_size << "]";
return errors;
}
bool ExpName::is_primary(void) const
{
return true;
}
int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
errors += emit_operand1(out, ent, scope);
switch (fun_) {
case EQ:
out << " == ";
break;
case LT:
out << " < ";
break;
case GT:
out << " > ";
break;
case NEQ:
out << " != ";
break;
case LE:
out << " <= ";
break;
case GE:
out << " >= ";
break;
}
errors += emit_operand2(out, ent, scope);
return errors;
}
int ExpShift::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
errors += emit_operand1(out, ent, scope);
switch (shift_) {
case SRL:
out << " >> ";
break;
case SLL:
out << " << ";
break;
case SRA:
out << " >>> ";
break;
case SLA:
out << " <<< ";
break;
case ROR:
case ROL:
out << " /* ?ror/rol? */ ";
break;
}
errors += emit_operand2(out, ent, scope);
return errors;
}
bool ExpString::is_primary(void) const
{
return true;
}
int ExpString::emit(ostream& out, Entity*ent, ScopeBase*scope)
{
const VType*type = peek_type();
assert(type != 0);
if (const VTypeArray*arr = dynamic_cast<const VTypeArray*>(type)) {
return emit_as_array_(out, ent, scope, arr);
}
out << "\"";
for(vector<char>::const_iterator it = value_.begin()
; it != value_.end(); ++it)
out << *it;
out << "\"";
return 0;
}
int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArray*arr)
{
int errors = 0;
assert(arr->dimensions() == 1);
const VTypePrimitive*etype = dynamic_cast<const VTypePrimitive*> (arr->basic_type());
assert(etype);
// Detect the special case that this is an array of
// CHARACTER. In this case, emit at a Verilog string.
if (etype->type()==VTypePrimitive::CHARACTER) {
vector<char> tmp (value_.size() + 3);
tmp[0] = '"';
memcpy(&tmp[1], &value_[0], value_.size());
tmp[value_.size()+1] = '"';
tmp[value_.size()+2] = 0;
out << &tmp[0];
return errors;
}
assert(etype->type() != VTypePrimitive::INTEGER);
out << value_.size() << "'b";
for (size_t idx = 0 ; idx < value_.size() ; idx += 1) {
int res = emit_logic(value_[idx], out, etype->type());
errors += res;
if(res)
cerr << get_fileline() << ": internal error: "
<< "Don't know how to handle bit " << value_[idx]
<< " with etype==" << etype->type() << endl;
}
return errors;
}
int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
out << "abs(";
errors += emit_operand1(out, ent, scope);
out << ")";
return errors;
}
int ExpUNot::emit(ostream&out, Entity*ent, ScopeBase*scope)
{
int errors = 0;
out << "~(";
errors += emit_operand1(out, ent, scope);
out << ")";
return errors;
}
2015-01-22 11:26:53 +01:00
int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope)
2015-01-22 11:26:53 +01:00
{
int errors = 0;
errors += type_->emit_def(out, empty_perm_string);
out << "'(";
errors += base_->emit(out, ent, scope);
2015-01-22 11:26:53 +01:00
out << ")";
return errors;
}
2015-01-23 12:13:28 +01:00
int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope)
2015-01-23 12:13:28 +01:00
{
int errors = 0;
out << "new[";
errors += size_->emit(out, ent, scope);
2015-01-23 12:13:28 +01:00
out << "]";
return errors;
}
2015-05-20 18:53:29 +02:00
int ExpTime::emit(ostream&out, Entity*, ScopeBase*)
{
out << amount_;
switch(unit_) {
case FS: out << "fs"; break;
case PS: out << "ps"; break;
case NS: out << "ns"; break;
case US: out << "us"; break;
case MS: out << "ms"; break;
case S: out << "s"; break;
}
return 0;
}