mirror of
https://github.com/holub/mame
synced 2025-06-04 20:06:28 +03:00
501 lines
10 KiB
C
501 lines
10 KiB
C
// license:GPL-2.0+
|
|
// copyright-holders:Couriersud
|
|
/*
|
|
* nl_parser.c
|
|
*
|
|
*/
|
|
|
|
#include "nl_parser.h"
|
|
#include "nl_factory.h"
|
|
|
|
//#undef NL_VERBOSE_OUT
|
|
//#define NL_VERBOSE_OUT(x) printf x
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// A simple tokenizer
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
pstring ptokenizer::currentline_str()
|
|
{
|
|
char buf[300];
|
|
int bufp = 0;
|
|
const char *p = m_line_ptr;
|
|
while (*p && *p != 10)
|
|
buf[bufp++] = *p++;
|
|
buf[bufp] = 0;
|
|
return pstring(buf);
|
|
}
|
|
|
|
|
|
void ptokenizer::skipeol()
|
|
{
|
|
char c = getc();
|
|
while (c)
|
|
{
|
|
if (c == 10)
|
|
{
|
|
c = getc();
|
|
if (c != 13)
|
|
ungetc();
|
|
return;
|
|
}
|
|
c = getc();
|
|
}
|
|
}
|
|
|
|
|
|
unsigned char ptokenizer::getc()
|
|
{
|
|
if (*m_px == 10)
|
|
{
|
|
m_line++;
|
|
m_line_ptr = m_px + 1;
|
|
}
|
|
if (*m_px)
|
|
return *(m_px++);
|
|
else
|
|
return *m_px;
|
|
}
|
|
|
|
void ptokenizer::ungetc()
|
|
{
|
|
m_px--;
|
|
}
|
|
|
|
void ptokenizer::require_token(const token_id_t &token_num)
|
|
{
|
|
require_token(get_token(), token_num);
|
|
}
|
|
|
|
void ptokenizer::require_token(const token_t tok, const token_id_t &token_num)
|
|
{
|
|
if (!tok.is(token_num))
|
|
{
|
|
error("Error: expected token <%s> got <%s>\n", m_tokens[token_num.id()].cstr(), tok.str().cstr());
|
|
}
|
|
}
|
|
|
|
pstring ptokenizer::get_string()
|
|
{
|
|
token_t tok = get_token();
|
|
if (!tok.is_type(STRING))
|
|
{
|
|
error("Error: expected a string, got <%s>\n", tok.str().cstr());
|
|
}
|
|
return tok.str();
|
|
}
|
|
|
|
pstring ptokenizer::get_identifier()
|
|
{
|
|
token_t tok = get_token();
|
|
if (!tok.is_type(IDENTIFIER))
|
|
{
|
|
error("Error: expected an identifier, got <%s>\n", tok.str().cstr());
|
|
}
|
|
return tok.str();
|
|
}
|
|
|
|
ptokenizer::token_t ptokenizer::get_token()
|
|
{
|
|
while (true)
|
|
{
|
|
token_t ret = get_token_internal();
|
|
if (ret.is_type(ENDOFFILE))
|
|
return ret;
|
|
|
|
if (ret.is(m_tok_comment_start))
|
|
{
|
|
do {
|
|
ret = get_token_internal();
|
|
} while (ret.is_not(m_tok_comment_end));
|
|
}
|
|
else if (ret.is(m_tok_line_comment))
|
|
{
|
|
skipeol();
|
|
}
|
|
else if (ret.str() == "#")
|
|
{
|
|
skipeol();
|
|
}
|
|
else
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ptokenizer::token_t ptokenizer::get_token_internal()
|
|
{
|
|
/* skip ws */
|
|
char c = getc();
|
|
while (m_whitespace.find(c)>=0)
|
|
{
|
|
c = getc();
|
|
if (eof())
|
|
{
|
|
return token_t(ENDOFFILE);
|
|
}
|
|
}
|
|
if (m_identifier_chars.find(c)>=0)
|
|
{
|
|
/* read identifier till non identifier char */
|
|
pstring tokstr = "";
|
|
while (m_identifier_chars.find(c)>=0) {
|
|
tokstr += c;
|
|
c = getc();
|
|
}
|
|
ungetc();
|
|
token_id_t id = token_id_t(m_tokens.indexof(tokstr));
|
|
if (id.id() >= 0)
|
|
return token_t(id, tokstr);
|
|
else
|
|
{
|
|
return token_t(IDENTIFIER, tokstr);
|
|
}
|
|
}
|
|
else if (c == m_string)
|
|
{
|
|
pstring tokstr = "";
|
|
c = getc();
|
|
while (c != m_string)
|
|
{
|
|
tokstr += c;
|
|
c = getc();
|
|
}
|
|
return token_t(STRING, tokstr);
|
|
}
|
|
else
|
|
{
|
|
/* read identifier till first identifier char or ws */
|
|
pstring tokstr = "";
|
|
while ((m_identifier_chars.find(c)) < 0 && (m_whitespace.find(c) < 0)) {
|
|
tokstr += c;
|
|
/* expensive, check for single char tokens */
|
|
if (tokstr.len() == 1)
|
|
{
|
|
token_id_t id = token_id_t(m_tokens.indexof(tokstr));
|
|
if (id.id() >= 0)
|
|
return token_t(id, tokstr);
|
|
}
|
|
c = getc();
|
|
}
|
|
ungetc();
|
|
token_id_t id = token_id_t(m_tokens.indexof(tokstr));
|
|
if (id.id() >= 0)
|
|
return token_t(id, tokstr);
|
|
else
|
|
{
|
|
return token_t(UNKNOWN, tokstr);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
ATTR_COLD void ptokenizer::error(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
|
|
pstring errmsg1 =pstring(format).vprintf(ap);
|
|
va_end(ap);
|
|
|
|
verror(errmsg1, currentline_no(), currentline_str());
|
|
|
|
//throw error;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// A netlist parser
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
ATTR_COLD void netlist_parser::verror(pstring msg, int line_num, pstring line)
|
|
{
|
|
m_setup.netlist().error("line %d: error: %s\n\t\t%s\n", line_num,
|
|
msg.cstr(), line.cstr());
|
|
|
|
//throw error;
|
|
}
|
|
|
|
|
|
bool netlist_parser::parse(const char *buf, const pstring nlname)
|
|
{
|
|
m_buf = buf;
|
|
|
|
reset(buf);
|
|
set_identifier_chars("abcdefghijklmnopqrstuvwvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_.-");
|
|
set_number_chars("01234567890eE-."); //FIXME: processing of numbers
|
|
char ws[5];
|
|
ws[0] = ' ';
|
|
ws[1] = 9;
|
|
ws[2] = 10;
|
|
ws[3] = 13;
|
|
ws[4] = 0;
|
|
set_whitespace(ws);
|
|
set_comment("/*", "*/", "//");
|
|
m_tok_param_left = register_token("(");
|
|
m_tok_param_right = register_token(")");
|
|
m_tok_comma = register_token(",");
|
|
|
|
m_tok_ALIAS = register_token("ALIAS");
|
|
m_tok_NET_C = register_token("NET_C");
|
|
m_tok_PARAM = register_token("PARAM");
|
|
m_tok_NET_MODEL = register_token("NET_MODEL");
|
|
m_tok_INCLUDE = register_token("INCLUDE");
|
|
m_tok_SUBMODEL = register_token("SUBMODEL");
|
|
m_tok_NETLIST_START = register_token("NETLIST_START");
|
|
m_tok_NETLIST_END = register_token("NETLIST_END");
|
|
|
|
bool in_nl = false;
|
|
|
|
while (true)
|
|
{
|
|
token_t token = get_token();
|
|
|
|
if (token.is_type(ENDOFFILE))
|
|
{
|
|
return false;
|
|
//error("EOF while searching for <%s>", nlname.cstr());
|
|
}
|
|
|
|
if (token.is(m_tok_NETLIST_END))
|
|
{
|
|
require_token(m_tok_param_left);
|
|
if (!in_nl)
|
|
error("Unexpected NETLIST_END");
|
|
else
|
|
{
|
|
in_nl = false;
|
|
}
|
|
require_token(m_tok_param_right);
|
|
}
|
|
else if (token.is(m_tok_NETLIST_START))
|
|
{
|
|
if (in_nl)
|
|
error("Unexpected NETLIST_START");
|
|
require_token(m_tok_param_left);
|
|
token_t name = get_token();
|
|
require_token(m_tok_param_right);
|
|
if (name.str() == nlname || nlname == "")
|
|
{
|
|
parse_netlist(name.str());
|
|
return true;
|
|
} else
|
|
in_nl = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void netlist_parser::parse_netlist(const pstring &nlname)
|
|
{
|
|
while (true)
|
|
{
|
|
token_t token = get_token();
|
|
|
|
if (token.is_type(ENDOFFILE))
|
|
return;
|
|
|
|
require_token(m_tok_param_left);
|
|
NL_VERBOSE_OUT(("Parser: Device: %s\n", token.str().cstr()));
|
|
|
|
if (token.is(m_tok_ALIAS))
|
|
net_alias();
|
|
else if (token.is(m_tok_NET_C))
|
|
net_c();
|
|
else if (token.is(m_tok_PARAM))
|
|
netdev_param();
|
|
else if (token.is(m_tok_NET_MODEL))
|
|
net_model();
|
|
else if (token.is(m_tok_SUBMODEL))
|
|
net_submodel();
|
|
else if (token.is(m_tok_INCLUDE))
|
|
net_include();
|
|
else if (token.is(m_tok_NETLIST_END))
|
|
{
|
|
netdev_netlist_end();
|
|
return;
|
|
}
|
|
else
|
|
device(token.str());
|
|
}
|
|
}
|
|
|
|
|
|
void netlist_parser::netdev_netlist_start()
|
|
{
|
|
// don't do much
|
|
token_t name = get_token();
|
|
require_token(m_tok_param_right);
|
|
}
|
|
|
|
void netlist_parser::netdev_netlist_end()
|
|
{
|
|
// don't do much
|
|
require_token(m_tok_param_right);
|
|
}
|
|
|
|
void netlist_parser::net_model()
|
|
{
|
|
// don't do much
|
|
pstring model = get_string();
|
|
m_setup.register_model(model);
|
|
require_token(m_tok_param_right);
|
|
}
|
|
|
|
void netlist_parser::net_submodel()
|
|
{
|
|
// don't do much
|
|
pstring name = get_identifier();
|
|
require_token(m_tok_comma);
|
|
pstring model = get_identifier();
|
|
require_token(m_tok_param_right);
|
|
|
|
m_setup.namespace_push(name);
|
|
netlist_parser subparser(m_setup);
|
|
subparser.parse(m_buf, model);
|
|
m_setup.namespace_pop();
|
|
}
|
|
|
|
void netlist_parser::net_include()
|
|
{
|
|
// don't do much
|
|
pstring name = get_identifier();
|
|
require_token(m_tok_param_right);
|
|
|
|
netlist_parser subparser(m_setup);
|
|
subparser.parse(m_buf, name);
|
|
}
|
|
|
|
void netlist_parser::net_alias()
|
|
{
|
|
pstring alias = get_identifier();
|
|
|
|
require_token(m_tok_comma);
|
|
|
|
pstring out = get_identifier();
|
|
|
|
require_token(m_tok_param_right);
|
|
|
|
NL_VERBOSE_OUT(("Parser: Alias: %s %s\n", alias.cstr(), out.cstr()));
|
|
m_setup.register_alias(alias, out);
|
|
}
|
|
|
|
void netlist_parser::net_c()
|
|
{
|
|
pstring last = get_identifier();
|
|
require_token(m_tok_comma);
|
|
|
|
while (true)
|
|
{
|
|
pstring t1 = get_identifier();
|
|
m_setup.register_link(last , t1);
|
|
NL_VERBOSE_OUT(("Parser: Connect: %s %s\n", last.cstr(), t1.cstr()));
|
|
token_t n = get_token();
|
|
if (n.is(m_tok_param_right))
|
|
break;
|
|
if (!n.is(m_tok_comma))
|
|
error("expected a comma, found <%s>", n.str().cstr());
|
|
last = t1;
|
|
}
|
|
|
|
}
|
|
|
|
void netlist_parser::netdev_param()
|
|
{
|
|
pstring param;
|
|
nl_double val;
|
|
param = get_identifier();
|
|
require_token(m_tok_comma);
|
|
val = eval_param(get_token());
|
|
NL_VERBOSE_OUT(("Parser: Param: %s %f\n", param.cstr(), val));
|
|
m_setup.register_param(param, val);
|
|
require_token(m_tok_param_right);
|
|
}
|
|
|
|
void netlist_parser::device(const pstring &dev_type)
|
|
{
|
|
pstring devname;
|
|
net_device_t_base_factory *f = m_setup.factory().factory_by_name(dev_type, m_setup);
|
|
netlist_device_t *dev;
|
|
nl_util::pstring_list termlist = f->term_param_list();
|
|
nl_util::pstring_list def_params = f->def_params();
|
|
|
|
int cnt;
|
|
|
|
devname = get_identifier();
|
|
|
|
dev = f->Create();
|
|
m_setup.register_dev(dev, devname);
|
|
|
|
NL_VERBOSE_OUT(("Parser: IC: %s\n", devname.cstr()));
|
|
|
|
cnt = 0;
|
|
while (cnt < def_params.count())
|
|
{
|
|
pstring paramfq = devname + "." + def_params[cnt];
|
|
|
|
NL_VERBOSE_OUT(("Defparam: %s\n", paramfq.cstr()));
|
|
require_token(m_tok_comma);
|
|
token_t tok = get_token();
|
|
if (tok.is_type(STRING))
|
|
{
|
|
m_setup.register_param(paramfq, tok.str());
|
|
}
|
|
else
|
|
{
|
|
nl_double val = eval_param(tok);
|
|
m_setup.register_param(paramfq, val);
|
|
}
|
|
cnt++;
|
|
}
|
|
|
|
token_t tok = get_token();
|
|
cnt = 0;
|
|
while (tok.is(m_tok_comma) && cnt < termlist.count())
|
|
{
|
|
pstring output_name = get_identifier();
|
|
|
|
m_setup.register_link(devname + "." + termlist[cnt], output_name);
|
|
|
|
cnt++;
|
|
tok = get_token();
|
|
}
|
|
if (cnt != termlist.count())
|
|
m_setup.netlist().error("netlist: input count mismatch for %s - expected %d found %d\n", devname.cstr(), termlist.count(), cnt);
|
|
require_token(tok, m_tok_param_right);
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// private
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
|
|
nl_double netlist_parser::eval_param(const token_t tok)
|
|
{
|
|
static const char *macs[6] = {"", "RES_K", "RES_M", "CAP_U", "CAP_N", "CAP_P"};
|
|
static nl_double facs[6] = {1, 1e3, 1e6, 1e-6, 1e-9, 1e-12};
|
|
int i;
|
|
int f=0;
|
|
bool e;
|
|
nl_double ret;
|
|
pstring val;
|
|
|
|
//printf("param %s\n", tok.m_token.cstr());
|
|
for (i=1; i<6;i++)
|
|
if (tok.str().equals(macs[i]))
|
|
f = i;
|
|
if (f>0)
|
|
{
|
|
require_token(m_tok_param_left);
|
|
val = get_identifier();
|
|
}
|
|
else
|
|
val = tok.str();
|
|
|
|
ret = val.as_double(&e);
|
|
|
|
if (e)
|
|
error("Error with parameter ...\n");
|
|
if (f>0)
|
|
require_token(m_tok_param_right);
|
|
return ret * facs[f];
|
|
}
|