/*
casm - Simple, portable assembler
Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 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, see .
-------------------------------------------------------------------------
Tokeniser.
*/
#include
#include
#include
#include
#include "global.h"
#include "codepage.h"
#include "parse.h"
/* ---------------------------------------- MACROS/TYPES
*/
typedef enum
{
CONSUME_WS,
CONSUME_TOKEN,
FINISHED
} State;
/* ---------------------------------------- GLOBALS
*/
static char error[1024];
static const ValueTable bool_table[] =
{
YES_NO_ENTRIES(TRUE, FALSE),
{NULL}
};
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
static void AddToken(const char *start, const char *end, Line *line, int quoted)
{
char *tok = Malloc(end - start + 2);
char *p;
p = tok;
while(start != end + 1)
{
*p++ = *start++;
}
*p = 0;
Trim(tok);
/* Special case to convert single characters in quotes to their character
code.
*/
if (*tok && *(tok+1) == 0 && (quoted == '\'' || quoted == '"'))
{
char b[64];
snprintf(b, sizeof b, "%d", CodepageConvert(*tok));
free(tok);
tok = DupStr(b);
quoted = 0;
}
line->no_tokens++;
line->token = Realloc(line->token, (sizeof *line->token) * line->no_tokens);
line->quoted = Realloc(line->quoted,
(sizeof *line->quoted) * line->no_tokens);
line->token[line->no_tokens - 1] = tok;
line->quoted[line->no_tokens - 1] = quoted;
}
static int IsClosure(const char *p, const char *sep)
{
while(*p && isspace(*p))
{
p++;
}
return !*p || strchr(sep, *p);
}
/* ---------------------------------------- INTERFACES
*/
int ParseLine(Line *line, const char *source)
{
static const char *sep_chars[2] =
{
" \t:",
","
};
static const char *quote_start_chars[2] =
{
"",
"\"'(["
};
static const char *quote_end_chars[2] =
{
"",
"\"')]"
};
const char *p = NULL;
const char *start = NULL;
State state = CONSUME_WS;
int stage = 0;
char open_quote = 0;
char quote = 0;
int status = TRUE;
p = source;
line->first_column = FALSE;
line->token = NULL;
line->comment = NULL;
line->quoted = NULL;
line->no_tokens = 0;
while(state != FINISHED)
{
switch(state)
{
case CONSUME_WS:
if (!*p)
{
state = FINISHED;
}
else if (*p == ';')
{
state = FINISHED;
line->comment = DupStr(p + 1);
Trim(line->comment);
}
else
{
const char *ptr;
if ((ptr = strchr(quote_start_chars[stage], *p)))
{
open_quote = *p;
quote = quote_end_chars
[stage]
[ptr - quote_start_chars[stage]];
state = CONSUME_TOKEN;
start = ++p;
}
else
{
if (isspace((unsigned char)*p) ||
strchr(sep_chars[stage], *p))
{
p++;
}
else
{
state = CONSUME_TOKEN;
start = p;
open_quote = 0;
quote = 0;
}
}
if (source == start && state == CONSUME_TOKEN)
{
line->first_column = TRUE;
}
}
break;
case CONSUME_TOKEN:
if (!*p)
{
if (quote)
{
snprintf
(error, sizeof error,
"Unterminated quoted string; expected %c", quote);
status = FALSE;
}
else if (start != p)
{
AddToken(start, p, line, FALSE);
}
state = FINISHED;
}
else
{
if (quote)
{
/* When we find a closing quote check if the following
character is a seperator or end-of-line. If not
this wasn't really a quoted string.
*/
if (*p == quote)
{
if (IsClosure(p + 1, sep_chars[stage]))
{
state = CONSUME_WS;
}
else
{
/* Wasn't a real closue, so add the quote as
a character onto the string.
*/
quote = 0;
open_quote = 0;
start--;
}
}
}
else if (strchr(sep_chars[stage], *p) || *p == ';')
{
state = CONSUME_WS;
}
if (state == CONSUME_WS)
{
AddToken(start, p - 1, line, open_quote);
if (quote)
{
open_quote = 0;
quote = 0;
p++;
}
if ((line->no_tokens == 2 && line->first_column) ||
(line->no_tokens == 1 && !line->first_column))
{
stage = 1;
}
}
else
{
p++;
}
}
break;
default:
break;
}
}
return status;
}
void ParseFree(Line *line)
{
int f;
if (line->token)
{
for(f = 0; f < line->no_tokens; f++)
{
free(line->token[f]);
}
free(line->token);
free(line->quoted);
}
if (line->comment)
{
free(line->comment);
}
line->token = NULL;
line->quoted = NULL;
line->comment = NULL;
}
const char *ParseError(void)
{
return error;
}
const ValueTable *ParseTable(const char *str, const ValueTable *table)
{
while(table && table->str)
{
if (CompareString(str, table->str))
{
return table;
}
table++;
}
return NULL;
}
int ParseTrueFalse(const char *str, int def)
{
const ValueTable *val;
val = ParseTable(str, bool_table);
return val == NULL ? def : val->value;
}
int ParseInTable(const char *str, const char *names[])
{
int f;
for(f = 0; names[f]; f++)
{
if (CompareString(str, names[f]))
{
return TRUE;
}
}
return FALSE;
}
/*
vim: ai sw=4 ts=8 expandtab
*/