Created
December 7, 2013 08:44
-
-
Save anonymous/7838706 to your computer and use it in GitHub Desktop.
Tiny assembler v0.04a, latest heavy code cleanup. Fully command line operable, will compile, and run TINY instruction files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <iomanip> | |
#include <fstream> | |
#include <sstream> | |
#include <string> | |
#include <map> | |
#include <unordered_map> | |
#include <vector> | |
#include <ctime> | |
#include <climits> | |
#include <cstdlib> | |
using namespace std; | |
// Tiny Assembler | |
// v0.04alpha | |
// Heavy code cleanup | |
namespace tiny { | |
// mostly incorporated this throughout | |
struct tiny_error { | |
tiny_error(string m) : msg(m) {} | |
string msg; | |
}; // struct tiny_error | |
// helper functions for parser | |
bool IsLabel(const string &_str) { | |
// returns true if label format met | |
// anything_before_colon: used with ex. "jmp anything_before_colon" | |
if(_str.find_first_of(':') != std::string::npos) // found label delim | |
return true; | |
else | |
return false; | |
} | |
bool IsIndex(const string &_str) { | |
if(_str.find_first_of("[]") != std::string::npos) { // Found brackets | |
if(_str.substr(1,_str.size()-2).find_first_of("[]") == std::string::npos) { // No internal brackets | |
if(_str.at(0) == '[') { // Found opening bracket | |
if(_str.at(_str.size()-1) == ']') // Found closing bracket | |
return true; // Proper Index(mostly) | |
else | |
throw tiny_error("Missing closing bracket."); | |
} | |
else if(_str.at(_str.size()-1) == ']') // Found closing bracket | |
throw tiny_error("Missing opening bracket."); | |
else // catches bracket reversal ]x[ | |
throw tiny_error("Misplaced bracket(s)."); | |
} | |
else // brackets mid string | |
throw tiny_error("Misplaced bracket(s)."); | |
} | |
else // no brackets | |
return false; // not indexed(mostly) | |
} // tiny::IsIndex(string) | |
void StripIndex(string &_str) { | |
_str = _str.substr(1,_str.size()-2); | |
} | |
// Mingw gcc 4.7.7 to_string workaround | |
// some more for stoull, stof, stod would be nice also | |
template<typename T> std::string to_string(const T& _str) { | |
std::stringstream oss; | |
oss << _str; | |
return oss.str(); | |
} | |
// really filling in for stoull | |
// lazy hacks .. but meh | |
template<typename T, typename N> uint64_t stoi(const T& _str, const N& _sz) { | |
std::stringstream oss(_str); | |
uint64_t _tmp = 0; | |
if(_sz == 8) // octal | |
oss >> oct >> _tmp; | |
else if(_sz == 10) // decimal | |
oss >> dec >> _tmp; | |
else if(_sz == 16) // hexadecimal | |
oss >> hex >> _tmp; | |
else | |
oss >> _tmp; // Let it sort itself | |
return _tmp; | |
} | |
enum MNEM : uint64_t { | |
AND = 0x00, OR = 0x02, XOR = 0x04, NOT = 0x06, | |
MOV = 0x07, RANDOM = 0x09, ADD = 0x0A, SUB = 0x0C, | |
JMP = 0x0E, JZ = 0x10, JEQ = 0x14, JLS = 0x18, | |
JGT = 0x1C, APRINT = 0x20, DPRINT = 0x22, HALT = 0xFF, | |
INVALID = 0x100, | |
}; | |
// bitshiftable for usage | |
enum SIG : uint64_t { | |
N = 0, M = 0x1, L = 0x2, | |
MM = 0x11, ML = 0x12, | |
LM = 0x21, LL = 0x22, | |
MMM = 0x111, LMM = 0x211, | |
MML = 0x112, LML = 0x212, | |
}; | |
map<string, MNEM> MnemonicMap { | |
{"AND", AND}, {"OR", OR}, {"XOR", XOR}, {"NOT", NOT}, {"MOV", MOV}, | |
{"RANDOM", RANDOM}, {"ADD", ADD}, {"SUB", SUB}, {"JMP", JMP}, {"JZ", JZ}, | |
{"JEQ", JEQ}, {"JLS", JLS}, {"JGT", JGT}, {"HALT", HALT}, | |
{"APRINT", APRINT}, {"DPRINT", DPRINT}, | |
}; | |
// Opcode lookup table | |
pair<MNEM, pair<SIG, uint64_t>> InstructionData [] = { | |
{AND, {MM, 0x00}}, {AND, {ML, 0x01}}, | |
{OR, {MM, 0x02}}, {OR, {ML, 0x03}}, | |
{XOR, {MM, 0x04}}, {XOR, {ML, 0x05}}, | |
{NOT, {M, 0x06}}, {MOV, {MM, 0x07}}, | |
{MOV, {ML, 0x08}}, {RANDOM,{M, 0x09}}, | |
{ADD, {MM, 0x0a}}, {ADD, {ML, 0x0b}}, | |
{SUB, {MM, 0x0c}}, {SUB, {ML, 0x0d}}, | |
{JMP, {M, 0x0e}}, {JMP, {L, 0x0f}}, | |
{JZ, {MM, 0x10}}, {JZ, {ML, 0x11}}, | |
{JZ, {LM, 0x12}}, {JZ, {LL, 0x13}}, | |
{JEQ, {MMM, 0x14}}, {JEQ, {LMM, 0x15}}, | |
{JEQ, {MML, 0x16}}, {JEQ, {LML, 0x17}}, | |
{JLS, {MMM, 0x18}}, {JLS, {LMM, 0x19}}, | |
{JLS, {MML, 0x1a}}, {JLS, {LML, 0x1b}}, | |
{JGT, {MMM, 0x1c}}, {JGT, {LMM, 0x1d}}, | |
{JGT, {MML, 0x1e}}, {JGT, {LML, 0x1f}}, | |
{APRINT, {M, 0x20}}, {APRINT, {L, 0x21}}, | |
{DPRINT, {M, 0x22}}, {DPRINT, {L, 0x23}}, | |
{HALT, {N, 0xff}}, | |
}; | |
unordered_multimap<MNEM, pair<SIG, uint64_t>, hash<uint64_t>> | |
InstructionMap(InstructionData, InstructionData + (sizeof(InstructionData) / sizeof(InstructionData[0]))); | |
// used by the parser during initial pass | |
class Mnemonic_Instruction { | |
public: | |
Mnemonic_Instruction() : mnem(INVALID), sig(N) {} | |
Mnemonic_Instruction(string s) : mnem(INVALID), sig(N) { // one parser uses | |
stringstream ss(s); | |
string mnem_str; | |
ss >> mnem_str; // get mnemonic instrucion | |
for(auto &c : mnem_str) | |
c = toupper(c); // mnemonic touppercase | |
auto mnem_iter = MnemonicMap.find(mnem_str); | |
if(mnem_iter != MnemonicMap.end()) { // valid mnemonic | |
this->mnem = MnemonicMap[mnem_str]; // set mnemonic | |
string op_str; | |
uint64_t op_cnt = 0; | |
while(ss >> op_str) { | |
op_cnt++; | |
this->sig <<= 4; | |
if(IsIndex(op_str)) { // | |
StripIndex(op_str); // remove brackets | |
this->sig |= 1; | |
} | |
else | |
this->sig |= 2; | |
if(op_cnt == 1 && this->mnem >= 0x0e && this->mnem <=0x1f) { | |
// do nothing, lets jump instruction label targets get through | |
} | |
else if(op_str.find_first_not_of("1234567890") != std::string::npos) { // found a non-digit | |
throw tiny_error("Parser - Expected base 10 data: "+op_str); | |
} | |
this->ops.push_back(op_str); // load op vector | |
} | |
// validation of MNEM + SIG combo | |
auto match = InstructionMap.equal_range(this->mnem); | |
bool found = false; | |
for(auto it = match.first; it != match.second; it++) { | |
if(it->second.first == (SIG)this->sig) { // Found match | |
found = true; | |
} | |
} | |
if(!found) // No match for signature | |
throw tiny_error("Parser - Invalid instruction signature: "+tiny::to_string(this->sig)); | |
} | |
else // invalid mnemonic | |
throw tiny_error("Parser - Invalid Mnemonic instruction: "+mnem_str); | |
} | |
// Nice easy lazy way .. | |
operator MNEM() { return mnem; } | |
operator SIG() { return (SIG)sig; } | |
operator string() const { | |
string _tmp = ""; | |
for(auto &op : ops) | |
_tmp += " " + op; | |
return _tmp; | |
} | |
uint64_t size() { return ops.size()+1; } | |
private: | |
MNEM mnem; | |
uint64_t sig; | |
vector<string> ops; | |
}; | |
// used by the parser for final pass output, | |
// and the machine when loading | |
class Translated_Instruction { | |
public: | |
Translated_Instruction() {} | |
// Convienance members | |
uint64_t Opcode() { return ops.at(0); } | |
uint64_t Op(uint64_t i) { | |
if(i < this->size()) { | |
return ops.at(i); | |
} | |
else | |
throw tiny_error("Parser::TI - out of range."); | |
} | |
uint64_t Op1() { | |
if(ops.size() >= 2) | |
return ops.at(1); | |
} | |
uint64_t Op2() { | |
if(ops.size() >= 3) | |
return ops.at(2); | |
} | |
uint64_t Op3() { | |
if(ops.size() > 3) | |
return ops.at(3); | |
} | |
void Push(uint64_t op) { | |
ops.push_back(op); | |
} | |
uint64_t size() { return ops.size(); } | |
operator string() const { | |
string _tmp = ""; | |
for(uint64_t i = 1; i < ops.size(); i++) { | |
_tmp += tiny::to_string(ops.at(i)) + " "; | |
} | |
return _tmp; | |
} | |
private: | |
vector<uint64_t> ops; | |
}; | |
class Parser { | |
public: | |
Parser() : _ready(false), _offset(0) {} | |
bool Push(string &s) { | |
bool valid = false; | |
if(!s.empty()) { // Something to push .. | |
s.erase(0,s.find_first_not_of(" \t")); // clear leading whitespace | |
auto comment = s.find(';'); // comment delim in line? | |
if(comment != std::string::npos) { // found a comment delim | |
s.erase(comment,s.size()); // erase from comment on | |
s = s.substr(0,s.find_last_not_of(" \t")+1); // erase trailing whitespace | |
cout << " ; trimmed"; | |
} | |
if(IsLabel(s)) { // We have a label | |
string label_str = s.substr(0,s.find_first_of(':')); | |
if(_labelmap.find(label_str) == _labelmap.end()) { | |
_labelmap.insert(make_pair(label_str, _offset)); | |
cout << " Added label: " << label_str; | |
valid = true; | |
} | |
else { // duplicate label | |
throw tiny_error("Parser::Push() - Duplicate Label: "+label_str); | |
} | |
} | |
else { // Not a label, parse for instruction | |
// start parsing instructions | |
if(!s.empty()) { | |
Mnemonic_Instruction mi(s); | |
if((MNEM)mi != INVALID) { | |
valid = true; | |
_mnemmap.insert(make_pair(_offset,mi)); | |
_offset += mi.size(); | |
} | |
else | |
throw tiny_error("Parser::Push() - Invalid mnemonic instruction."); | |
} | |
else | |
return valid; | |
} | |
} | |
else // empty line | |
return valid; | |
return valid; | |
} | |
void Resolve() { | |
for(auto &raw : _mnemmap) { | |
Translated_Instruction ti; | |
bool found = false; | |
auto range = InstructionMap.equal_range((MNEM)raw.second); | |
for(auto it = range.first; it != range.second; it++) { | |
if(it->second.first == (SIG)raw.second) { | |
found = true; // found the opcode | |
ti.Push(it->second.second); // push it in | |
} | |
} | |
if(found) { // Continue translating mnemonic instruction | |
if(ti.Opcode() >= 0x0e && ti.Opcode() <= 0x1f) { // jump instruction range | |
stringstream ss((string)raw.second); | |
string label; | |
ss >> label; | |
auto lit = _labelmap.find(label); | |
if(lit != _labelmap.end()) // matched the label | |
ti.Push(lit->second); | |
else | |
throw tiny_error("Parser::Resolve() - Invalid label target: "+label); | |
// fill any remaining ops | |
while(ss >> label) { | |
ti.Push(tiny::stoi(label,10)); | |
} | |
} | |
else { // regular non-jump opcodes | |
stringstream ss((string)raw.second); | |
string tmp; | |
while(ss >> tmp) { | |
ti.Push(tiny::stoi(tmp,10)); | |
} | |
} | |
} | |
else // should never see this | |
throw tiny_error("Parser::Resolve() - Invalid mnemonic instruction."); | |
_transmap.insert(make_pair(raw.first,ti)); | |
} | |
return; | |
} // Parser::Resolve() | |
void Debug() { | |
// Debugging member function | |
cout << "\nParser debugging beginning.\n\n" | |
<< "Constructed label map: [name,target offset]" << endl; | |
for(auto &lab : _labelmap) { | |
cout << lab.first << ", " << lab.second << endl; | |
} | |
cout << "\nConstructed Mnemonic Instruction map: [Offset, Mnemonic, sig, ops]" << endl; | |
for(auto &m : _mnemmap) { | |
cout << setw(4) << m.first << ", Mnemonic: " << setw(3) << (MNEM)m.second << ", SIG: " | |
<< setw(3) << (SIG)m.second; // << (m.second.size() > 1 ? ", Ops:" : ""); | |
if(m.second.size() > 1) { // ops to show | |
cout << ", Ops:" << (string)m.second; | |
} | |
cout << endl; | |
} | |
cout << "\nConstructed Translated Instruction map: [offset, opcode, ops]" << endl; | |
for(auto &t : _transmap) { | |
cout << setw(4) << t.first << ", Opcode: " << setw(3) << t.second.Opcode(); | |
if(t.second.size() > 1) { | |
cout << ", Ops: " << (string)t.second; | |
} | |
cout << endl; | |
} | |
} | |
bool Load(const string &file) { | |
bool valid = false; | |
if(this->_ready) // File previously loaded | |
this->Unload(); // Unload | |
ifstream in(file,ios::in); | |
if(!in.is_open()) // file not opened | |
throw tiny_error("Parser::Load() - Could not load: "+file); | |
else { | |
string line = ""; | |
uint64_t errors = 0; | |
uint64_t linenum = 0; | |
while(getline(in,line)) { | |
linenum++; | |
try { // catch tiny_errors in this feed loop to | |
// validate the instructions read in | |
// and provide valuable feedback on errors | |
// to the user. | |
cout << setw(4) << linenum; | |
if(!line.empty()) { // something to push.. | |
if(this->Push(line)) | |
cout << " Valid"; | |
else if(line.empty()) | |
cout << " Empty"; | |
else | |
cout << " Invalid"; | |
} | |
else | |
cout << " Empty"; | |
cout << endl; | |
} catch(tiny::tiny_error err) { | |
errors++; | |
cout << " Error: " << err.msg << endl; | |
} | |
} | |
if(errors == 0) { // no errors | |
valid = true; | |
this->_ready = true; // file loaded, flip ready flag | |
} | |
in.close(); | |
} | |
return valid; | |
} | |
bool Write(const string &file) { | |
bool valid = false; | |
if(!this->_ready) // No file loaded to write | |
throw tiny_error("Parser::Write() - Must load a file first."); | |
ofstream out(file,ios::trunc); // open outputfile, overwrite existing | |
if(!out.is_open()) { // file not open/doesn't exist | |
throw tiny_error("Parser::Write() - Could not write: "+file); | |
} | |
else { // file open, begin writing it | |
for(auto &t : _transmap) { | |
out << showbase <<hex << t.second.Opcode(); | |
for(uint64_t i = 1; i < t.second.size(); i++) { | |
out << " " << showbase << hex << t.second.Op(i); | |
} | |
out << endl; | |
} | |
valid = true; | |
} | |
out.close(); | |
return valid; | |
} | |
private: | |
void Unload() { | |
if(this->_ready) { | |
this->_ready = false; // no longer ready | |
this->_offset = 0; // reset offset marker | |
this->_mnemmap.clear(); // clear the data | |
this->_transmap.clear(); | |
this->_labelmap.clear(); | |
} | |
} | |
bool _ready; | |
uint64_t _offset; | |
map<uint64_t, Mnemonic_Instruction> _mnemmap; | |
map<uint64_t, Translated_Instruction> _transmap; | |
map<string, uint64_t> _labelmap; | |
}; | |
class Machine { | |
public: | |
Machine() : _ready(false), _offset(0) {} | |
void Push(const string &s) { | |
stringstream ss(s); | |
uint64_t op = 0; | |
Translated_Instruction ti; | |
while(ss >> hex >> op) | |
ti.Push(op); | |
_transmap.insert(make_pair(_offset,ti)); | |
_offset += ti.size(); | |
} | |
bool Load(const string &file) { | |
bool valid = false; | |
if(this->_ready) // file previously loaded | |
this->Unload(); // unload | |
ifstream in(file,ios::in); | |
if(!in.is_open()) | |
throw tiny_error("Machine::Load() - Could not load: "+file); | |
else { // open file, good to load | |
string line = ""; | |
while(getline(in,line)) { | |
if(!line.empty()) | |
this->Push(line); | |
} | |
valid = true; | |
} | |
this->_ready = valid; | |
return valid; | |
} | |
void Run() { // runs the loaded file | |
if(!this->_ready) | |
throw tiny_error("Machine::Run() - Must load a *.tny file first."); | |
else { | |
cout << "\nProgram Running." << endl; | |
auto current = _transmap.begin(); | |
auto next = _transmap.end(); | |
uint64_t mem[256] = {0}; | |
uint64_t stop = 0; | |
bool running = true; | |
srand(time(NULL)); // prep for random | |
while(running && (current != _transmap.end())) { | |
if(stop >= ULLONG_MAX-1) | |
running = false; // thats a lot of instructions | |
switch(current->second.Opcode()) { // switch on opcode | |
case 0x00: | |
mem[current->second.Op(1)] &= mem[current->second.Op(2)]; | |
break; | |
case 0x01: | |
mem[current->second.Op(1)] &= current->second.Op(2); | |
break; | |
case 0x02: | |
mem[current->second.Op(1)] |= mem[current->second.Op(2)]; | |
break; | |
case 0x03: | |
mem[current->second.Op(1)] |= current->second.Op(2); | |
break; | |
case 0x04: | |
mem[current->second.Op(1)] ^= mem[current->second.Op(2)]; | |
break; | |
case 0x05: | |
mem[current->second.Op(1)] ^= current->second.Op(2); | |
break; | |
case 0x06: //Not [a] bitwise not ~ | |
mem[current->second.Op(1)] = ~mem[current->second.Op(1)]; | |
break; | |
case 0x07: | |
mem[current->second.Op(1)] = mem[current->second.Op(2)]; | |
break; | |
case 0x08: | |
mem[current->second.Op(1)] = current->second.Op(2); | |
break; | |
case 0x09: | |
mem[current->second.Op(1)] = rand() % 25+1; | |
break; | |
case 0x0a: | |
mem[current->second.Op(1)] += mem[current->second.Op(2)]; | |
break; | |
case 0x0b: | |
mem[current->second.Op(1)] += current->second.Op(2); | |
break; | |
case 0x0c: | |
mem[current->second.Op(1)] -= mem[current->second.Op(2)]; | |
break; | |
case 0x0d: | |
mem[current->second.Op(1)] -= current->second.Op(2); | |
break; | |
case 0x0e: // Jmp [x] | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x0f: // jmp x | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x10: // jz [x] [a] | |
if(mem[current->second.Op(2)] == 0) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x11: // jz x [a] | |
if(current->second.Op(2) == 0) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x12: // jz [x] a | |
if(mem[current->second.Op(2)] == 0) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x13: // jz x a | |
if(current->second.Op(2) == 0) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x14: // jeq [x] [a] [b] | |
if(mem[current->second.Op(2)] == mem[current->second.Op(3)]) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x15: | |
if(mem[current->second.Op(2)] == mem[current->second.Op(3)]) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x16: | |
if(mem[current->second.Op(2)] == current->second.Op(3)) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x17: | |
if(mem[current->second.Op(2)] == current->second.Op(3)) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x18: // jls [x] [a] [b] | |
if(mem[current->second.Op(2)] < mem[current->second.Op(3)]) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x19: | |
if(mem[current->second.Op(2)] < mem[current->second.Op(3)]) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x1a: | |
if(mem[current->second.Op(2)] < current->second.Op(3)) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x1b: // jls x [a] b | |
if(mem[current->second.Op(2)] < current->second.Op(3)) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x1c: // jgt [x] [a] [b] | |
if(mem[current->second.Op(2)] > mem[current->second.Op(3)]) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x1d: | |
if(mem[current->second.Op(2)] > mem[current->second.Op(3)]) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x1e: | |
if(mem[current->second.Op(2)] > current->second.Op(3)) | |
next = _transmap.find(mem[current->second.Op(1)]); | |
break; | |
case 0x1f: // jgt x [a] b | |
if(mem[current->second.Op(2)] > current->second.Op(3)) | |
next = _transmap.find(current->second.Op(1)); | |
break; | |
case 0x20: // aprint [a] | |
cout << (char)mem[current->second.Op(1)]; | |
break; | |
case 0x21: // aprint a | |
cout << (char)current->second.Op(1); | |
break; | |
case 0x22: // dprint [a] | |
cout << dec << mem[current->second.Op(1)]; | |
break; | |
case 0x23: // dprint a | |
cout << dec << current->second.Op(1); | |
break; | |
case 0xff: // halt | |
running = false; | |
break; | |
default: | |
cout << "Illegal instruction. Exiting." << endl; | |
running = false; | |
break; | |
} // massive switch | |
stop++; // increment executed instruction count. | |
if(next != _transmap.end()) { // jmp instruction called | |
current = next; // set current ptr to jmp target | |
next = _transmap.end(); // reset next sentinal | |
} | |
else { | |
current++; // increment instruction ptr | |
if(current == _transmap.end()) // at the end for some reason | |
running = false; // stop running | |
} | |
} // while() | |
cout << "Executed: " << stop << " Instructions." << endl; | |
} | |
return; | |
} | |
void Debug() { | |
cout << "\nMachine debugging starting." << endl; | |
cout << "\nLoaded Runfile. [offset,opcode,ops...]" << endl; | |
for(auto &t : _transmap) { | |
cout << setw(4) << showbase << hex << t.first; | |
for(uint64_t i = 0; i < t.second.size(); i++) { | |
cout << " " << setw(4) << showbase << hex << t.second.Op(i); | |
} | |
cout << endl; | |
} | |
} | |
private: | |
void Unload() { | |
if(this->_ready) { | |
this->_ready = false; | |
this->_offset = 0; | |
this->_transmap.clear(); | |
} | |
} | |
bool _ready; | |
uint64_t _offset; | |
map<uint64_t, Translated_Instruction> _transmap; | |
}; | |
} // namespace tiny | |
struct Options { | |
bool help = false; // -h | |
bool debug = false; // -d | |
bool compile = false; // -c | |
bool output = false; // -o | |
bool run = false; // -r | |
string sourcefile = ""; // -c <sourcefile> | |
string outputfile = ""; // -o <outputfile> | |
string runfile = ""; // -r <runfile> | |
operator string() { // debugging string operator | |
string _tmp = "Help: "; _tmp += (help ? "Yes" : "No"); | |
_tmp += "\nDebug: "; _tmp += (debug ? "Yes" : "No"); | |
_tmp += "\nCompile: "; _tmp += (compile ? "Yes" : "No"); | |
_tmp += "\nOutput: "; _tmp += (output ? "Yes" : "No"); | |
_tmp += "\nRuncode: "; _tmp += (run ? "Yes" : "No"); | |
_tmp += "\nSourcefile: "; _tmp += (sourcefile.empty() ? "None" : sourcefile); | |
_tmp += "\nOutputfile: "; _tmp += (outputfile.empty() ? "None" : outputfile); | |
_tmp += "\nRunfile: "; _tmp += (runfile.empty() ? "None" : runfile); | |
_tmp += "\n"; | |
return _tmp; | |
} | |
}; | |
void Usage() { // prints program usage/ -h | |
cout << "Usage: tinyasm -d ; Runs internal tests\n" | |
<< " : tinyasm -h ; Prints this helpfile\n" | |
<< " : tinyasm -c <source.txt> ; Sets tiny sourcefile to compile\n" | |
<< " : tinyasm -o <output.tny> ; Sets outputfile, defaults to source.tny\n" | |
<< " : tinyasm -r <file.tny> ; runs file.tny, defaults to output.tny\n\n" | |
<< "EX: tinyasm -c <src> -o <out> -r ; Compiles src->out then runs <out> if valid\n" | |
<< "EX: tinyasm -c <src> -r ; Compiles then runs src.tny if valid\n" | |
<< "EX: tinyasm -c <src> -o -r <foo> ; Compiles src to src.tny then runs foo" | |
<< endl; | |
} | |
bool ParseCommandLine(Options &opts, vector<string> &args) { | |
// Still a little buggy, needs a better parsing pattern as this just seems | |
// clunky to me. Further it will blindly take the next arg when dealing with -c, -o, -r | |
// Need to fine tune | |
// EX: prog -c -d -r, will try to compile '-d' and then try to run '-d.tny', with no debugging | |
// Mostly a minor problem, as they will be caught later, but should be done here | |
if(args.empty()) // no args | |
return false; | |
while(!args.empty()) { // command line arguments to parse! | |
if(args.at(0) == "-h") { // help called for | |
opts.help = true; | |
args.erase(args.begin()); | |
return false; // break out of parse loop, help doesn't care about other switches | |
} | |
else if(args.at(0) == "-d") { // debug called | |
opts.debug = true; | |
args.erase(args.begin()); | |
} | |
else if(args.at(0) == "-c") { // compile called | |
opts.compile = true; | |
if(args.size() > 1) { // requires sourcefile argument | |
opts.sourcefile = args.at(1); | |
args.erase(args.begin(), args.begin()+1); | |
} | |
else { | |
args.erase(args.begin()); | |
cout << "-c requires a filename argument. EX: -c source.txt" << endl; | |
return false; // switch requirement not met, | |
} | |
} | |
else if(args.at(0) == "-o") { // outputfile called | |
opts.output = true; | |
if(args.size() > 1) { | |
opts.outputfile = args.at(1); | |
args.erase(args.begin(), args.begin()+1); | |
} | |
else | |
args.erase(args.begin()); | |
} | |
else if(args.at(0) == "-r") { // runfile called | |
opts.run = true; | |
if(args.size() > 1) { | |
opts.runfile = args.at(1); | |
args.erase(args.begin(), args.begin()+1); | |
} | |
else | |
args.erase(args.begin()); | |
} | |
else // Invalid command line switch | |
args.erase(args.begin()); | |
} // no more args | |
// work out the filenames as needed | |
bool compile = opts.compile; | |
bool output = opts.output; | |
bool run = opts.run; | |
if(!compile && output) { // -o called w/o -c, error | |
cout << "-o must be called with -c <source>" << endl; | |
return false; | |
} | |
if(compile && !output) { // -c called w/o -o, default | |
opts.output = true; // activate output, outputfile caught next | |
output = true; | |
} | |
if(output && opts.outputfile.empty()) // -o called w/o argument, default | |
opts.outputfile = opts.sourcefile + ".tny"; // to sourcefile.tny | |
if(run && opts.runfile.empty()) {// -r called w/o argument, | |
if(compile) // -c called as well, default | |
opts.runfile = opts.outputfile; // to outputfile name | |
else { // -c NOT called, error | |
cout << "-r requires a runfile if not called with -c" << endl; | |
return false; | |
} | |
} | |
if(opts.debug) // debug invoked, this included | |
cout << "Options As Follows\n" << (string)opts << endl; | |
return true; | |
} | |
int main(int argc, char** argv) { | |
cout << "Tiny Assembler v0.04a" << endl; | |
// Begin startup | |
Options opts; // command line option structure | |
vector<string> args; // vector to fill command line arguments with | |
for(int i = 1; i < argc; i++) // start at argv[1] to skip program pathname | |
args.push_back(argv[i]); // load the arg vector | |
bool startup = ParseCommandLine(opts,args); | |
if(!startup) { // Not ready to startup | |
Usage(); // Print help | |
return -1; // exit | |
} | |
using namespace tiny; | |
// Begin | |
uint64_t errors = 0; | |
if(opts.compile) { // -c called. Do our work | |
Parser p; // Fire up the parser | |
try { | |
if(p.Load(opts.sourcefile)) // -c <sourcefile> | |
cout << opts.sourcefile << " Loaded." << endl; | |
p.Resolve(); | |
if(p.Write(opts.outputfile)) // -o inferred with -c | |
cout << opts.outputfile << " Written." << endl; | |
} catch(tiny::tiny_error err) { | |
errors++; | |
cout << "Error: " << err.msg << endl; | |
} | |
if(opts.debug) | |
p.Debug(); | |
} | |
if(opts.run) { // -r | |
Machine m; // Start the machine | |
try { | |
if(m.Load(opts.runfile)) | |
cout << opts.runfile << " Loaded." << endl; | |
m.Run(); | |
} catch(tiny::tiny_error err) { | |
errors++; | |
cout << "Error: " << err.msg << endl; | |
} | |
if(opts.debug) | |
m.Debug(); | |
} | |
cout << setw(2) << errors << " Errors." << endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment