diff --git a/Masfix.cpp b/Masfix.cpp index 0e92120..12641db 100644 --- a/Masfix.cpp +++ b/Masfix.cpp @@ -36,6 +36,7 @@ enum TokenTypes { Tnumeric, Talpha, Tstring, + Tchar, Tspecial, Tcolon, @@ -202,7 +203,7 @@ struct Loc { }; struct Token { TokenTypes type=TokenCount; - string data; + string data; // contains only data - no quotes, quotes added when mentioning in error list tlist; Loc loc; @@ -268,18 +269,19 @@ struct Token { assert(type == Tlist); return data.at(0) + 2 - (data.at(0) == '('); } - string toStr() { + // quoted: quote strings & chars? + string toStr(bool quoted=false) { assert(this != nullptr); string out = data; if (type == Tlist) { - if (isSeparated()) out.push_back(','); out.push_back(tlistCloseChar()); } else if (type == TIexpansion) { out = "%" + out; + } else if (type == Tstring && quoted) { + return '"' + data + '"'; + } else if (type == Tchar && quoted) { + return '\'' + data + '\''; } - // } else if (type == Tstring && quotedStr) { - // return '"' + data + '"'; - // } return out; } bool isSeparated() { @@ -415,16 +417,16 @@ struct Instr { string opcodeStr; Loc opcodeLoc; - vector immFields; + list immediates; bool needsReparsing = false; // references smth which might change Instr() {} - Instr(string opcodeStr, Loc opcodeLoc) { + Instr(Loc opcodeLoc) { this->opcodeStr = opcodeStr; this->opcodeLoc = opcodeLoc; } - bool hasImm() { return immFields.size() != 0; } + bool hasImm() { return !immediates.empty(); } bool hasCond() { return suffixes.cond != Cno; } bool hasMod() { return suffixes.modifier != OPno; } bool hasReg() { return suffixes.reg != Rno; } @@ -432,9 +434,9 @@ struct Instr { string toStr() { string out = opcodeStr; - for (string s : immFields) { + for (Token& token : immediates) { out.push_back(' '); - out.append(s); + out.append(token.toStr(true)); } return out; } @@ -509,7 +511,6 @@ struct Flags { Flags flags; // checks -------------------------------------------------------------------- -#define errorQuoted(s) " '" + (s) + "'" #define unreachable() assert(("Unreachable", false)); #define continueOnFalse(cond) if (!(cond)) { errorLess = false; continue; } @@ -527,6 +528,11 @@ bool SupressErrors = false; #define returnOnErrSupress() if (SupressErrors) return false; #define returnOnWarningSupress() if (SupressErrors || !FLAG_enableWarnings) return false; +string errorQuoted(string s) { + if (s.at(0) == '\'' || s.at(0) == '"') return ' ' + s; + return " '" + s + "'"; +} + vector errors; void raiseErrors() { // TODO sort errors based on line and file for (string s : errors) { @@ -553,21 +559,21 @@ void _raiseNote(string message) { bool raiseError(string message, Token token, string note="", bool strict=false) { returnOnErrSupress(); - string err = token.loc.toStr() + " ERROR: " + message + errorQuoted(token.toStr()) "\n"; + string err = token.loc.toStr() + " ERROR: " + message + errorQuoted(token.toStr(true)) + "\n"; addError(err, strict); if (note.size()) _raiseNote(note); return false; } bool raiseError(string message, Instr instr, string note="", bool strict=false) { returnOnErrSupress(); - string err = instr.opcodeLoc.toStr() + " ERROR: " + message + errorQuoted(instr.toStr()) "\n"; + string err = instr.opcodeLoc.toStr() + " ERROR: " + message + errorQuoted(instr.toStr()) + "\n"; addError(err, strict); if (note.size()) _raiseNote(note); return false; } bool raiseWarning(string message, Instr instr) { returnOnWarningSupress(); - string err = instr.opcodeLoc.toStr() + " WARNING: " + message + errorQuoted(instr.toStr()) "\n"; + string err = instr.opcodeLoc.toStr() + " WARNING: " + message + errorQuoted(instr.toStr()) + "\n"; addError(err, false); return false; } @@ -609,7 +615,7 @@ struct Scope { /// handles the ending of a single token list /// forces parsing if apropriate void closeList() { - static_assert(TokenCount == 12, "Exhaustive closeList definition"); + static_assert(TokenCount == 13, "Exhaustive closeList definition"); Token& closedList = tlists.top().get(); tlists.pop(); itrs.pop(); if (!isPreprocessing) return; @@ -664,7 +670,7 @@ struct Scope { } /// advances iteration, opens new nested list if provided list::iterator& next(Token& tlist) { - static_assert(TokenCount == 12, "Exhaustive Scope::next definition"); + static_assert(TokenCount == 13, "Exhaustive Scope::next definition"); ++itrs.top(); if (tlist.type == Tlist || tlist.type == TIexpansion || tlist.type == TInamespace || tlist.type == TIctime) { openList(tlist); @@ -775,9 +781,9 @@ struct Scope { return tlists.size() && insideTlistOfType(Tlist); } bool tokenizeCloseList(char closeChar, Loc& loc) { - checkReturnOnFail(tokenizeHasTlist(), "Unexpected token list termination" errorQuoted(string(1, closeChar)), loc); + checkReturnOnFail(tokenizeHasTlist(), "Unexpected token list termination" + errorQuoted(string(1, closeChar)), loc); Token &tlist = tlists.top().get(); - checkReturnOnFail(closeChar == tlist.tlistCloseChar(), "Unmatched token list delimiters" errorQuoted(string(1, closeChar)), loc, + checkReturnOnFail(closeChar == tlist.tlistCloseChar(), "Unmatched token list delimiters" + errorQuoted(string(1, closeChar)), loc, "Current tlist start: " + tlist.loc.toStr()); closeList(); return true; @@ -892,17 +898,48 @@ string getCharRun(string s) { } while (i < s.size() && !!isdigit(s.at(i)) == num && !!isalpha(s.at(i)) == alpha && !!isspace(s.at(i)) == space); return out; } +const map escapeSequences = { + {'n','\n'}, + {'t','\t'}, + {'r','\r'}, + {'\\','\\'}, + {'"','"'}, + {'\'','\''}, + {'0','\0'}, +}; +char escapeCharToken(Token& t) { + assert(t.data.size() == 1 || (t.data.size() == 2 && escapeSequences.count(t.data.at(1)))); + return t.data.size() == 2 ? escapeSequences.at(t.data.at(1)) : t.data.at(0); +} +bool chopStrlit(char first, string& line, string& run, int& col, Loc loc) { + run = ""; + for (size_t i = 0; i < line.size(); ++i) { + if (line[i] == '\\') { + checkReturnOnFail(++i < line.size() && escapeSequences.count(line.at(i)), "Invalid escape sequence" + errorQuoted(line.substr(i-1, 2)), loc); + run.push_back('\\'); + run.push_back(line[i]); + col += 2; + } else if (line[i] == first) { + line = line.substr(++i); + col ++; + return true; + } else { + run.push_back(line[i]); + col ++; + } + } + return check(false, "Expected string or character termination", loc); +} #define addToken(type) scope.insertToken(Token(type, run, loc, continued, firstOnLine)); \ scope.next(scope.currToken()); /// reads file, performs lexical analysis, builds token stream /// prepares Scope for preprocessing void tokenize(ifstream& ifs, string relPath, Scope& scope) { - static_assert(TokenCount == 12, "Exhaustive tokenize definition"); + static_assert(TokenCount == 13, "Exhaustive tokenize definition"); string line; bool continued, firstOnLine, keepContinued, errorLess; for (int lineNum = 1; getline(ifs, line); ++lineNum) { continued = false; firstOnLine = true; - line = line.substr(0, line.find(';')); int col = 1; while (line.size()) { char first = line.at(0); @@ -915,6 +952,9 @@ void tokenize(ifstream& ifs, string relPath, Scope& scope) { if (isspace(first)) { continued = false; continue; + } else if (first == ';') { + line = ""; + continue; } if (isdigit(first)) { addToken(Tnumeric); @@ -932,16 +972,16 @@ void tokenize(ifstream& ifs, string relPath, Scope& scope) { continued = false; addToken(Tseparator); keepContinued = false; - } else if (first == '"') { - size_t quotePos = line.find('"'); - if (!check(quotePos != string::npos, "Expected string termination", loc)) { - line = ""; continue; + } else if (first == '"' || first == '\'') { + if (!chopStrlit(first, line, run, col, loc)) { + line = ""; + continue; + } + if (first == '\'') { + checkContinueOnFail(run.size() == 1 || (run.size() == 2 && run[0] == '\\'), "Invalid character value", loc); } - run = line.substr(0, quotePos); - col +=++ quotePos; continued = false; keepContinued = false; - addToken(Tstring); - line = line.substr(quotePos); + addToken(first == '"' ? Tstring : Tchar); } else { addToken(Tspecial); } @@ -968,18 +1008,18 @@ string noteWhereDefined(Loc& loc, Loc& definedLoc) { return "Defined at " + definedLoc.toStr(); } bool checkIdentRedefinitions(Scope& scope, string name, Loc& loc, bool label, Namespace* currNamespace=nullptr) { - checkReturnOnFail(verifyNotInstrOpcode(name), "Name shadows an instruction" errorQuoted(name), loc); - checkReturnOnFail(!DefiningDirectivesSet.count(name) && !BuiltinDirectivesSet.count(name), "Name shadows a builtin directive" errorQuoted(name), loc) + checkReturnOnFail(verifyNotInstrOpcode(name), "Name shadows an instruction" + errorQuoted(name), loc); + checkReturnOnFail(!DefiningDirectivesSet.count(name) && !BuiltinDirectivesSet.count(name), "Name shadows a builtin directive" + errorQuoted(name), loc) if (label) { - checkReturnOnFail(parseCtx.strToLabel.count(name) == 0, "Label redefinition" errorQuoted(name), loc, noteWhereDefined(loc, parseCtx.strToLabel[name].loc)); + checkReturnOnFail(parseCtx.strToLabel.count(name) == 0, "Label redefinition" + errorQuoted(name), loc, noteWhereDefined(loc, parseCtx.strToLabel[name].loc)); } else { assert(currNamespace); - checkReturnOnFail(currNamespace->defines.count(name) == 0, "Define redefinition" errorQuoted(name), loc, noteWhereDefined(loc, currNamespace->defines[name].loc)); - checkReturnOnFail(currNamespace->macros.count(name) == 0, "Macro redefinition" errorQuoted(name), loc, noteWhereDefined(loc, currNamespace->macros[name].loc)); - checkReturnOnFail(currNamespace->innerNamespaces.count(name) == 0, "Namespace redefinition" errorQuoted(name), loc, noteWhereDefined(loc, IdToNamespace[currNamespace->innerNamespaces[name]].loc)); + checkReturnOnFail(currNamespace->defines.count(name) == 0, "Define redefinition" + errorQuoted(name), loc, noteWhereDefined(loc, currNamespace->defines[name].loc)); + checkReturnOnFail(currNamespace->macros.count(name) == 0, "Macro redefinition" + errorQuoted(name), loc, noteWhereDefined(loc, currNamespace->macros[name].loc)); + checkReturnOnFail(currNamespace->innerNamespaces.count(name) == 0, "Namespace redefinition" + errorQuoted(name), loc, noteWhereDefined(loc, IdToNamespace[currNamespace->innerNamespaces[name]].loc)); int namespaceId = scope.currNamespaceId(); if (lookupNamespaceAbove(name, namespaceId, loc, true)) { - raiseWarning("Definition shadowing existing namespace" errorQuoted(name), loc); + raiseWarning("Definition shadowing existing namespace" + errorQuoted(name), loc); }; } return true; @@ -994,31 +1034,32 @@ bool eatToken(Scope& scope, Loc& loc, Token& outToken, TokenTypes type, string e if (sameLine) checkReturnOnFail(!scope->firstOnLine, errMissing, loc); outToken = scope.eatenToken(); loc = outToken.loc; return check(outToken.type == type, "Expected " + - string(type == Tnumeric ? "numeric" : type == Talpha ? "alpha" : type == Tstring ? "string" : type == Tspecial ? "special" : type == Tlist ? "list" : "another") + string(type == Tnumeric ? "numeric" : type == Talpha ? "alpha" : type == Tstring ? "string" : type == Tchar ? "char" : type == Tspecial ? "special" : type == Tlist ? "list" : "another") + " token type, got", outToken); } -void eatTokenRun(Scope& scope, string& name, Loc& loc, bool canStartLine=true, int eatAnything=0) { - static_assert(TokenCount == 12, "Exhaustive eatTokenRun definition"); +void eatTokenRun(Scope& scope, string& name, Loc& loc, bool canStartLine=true, int eatAnything=0, bool allowQuotes=false) { + static_assert(TokenCount == 13, "Exhaustive eatTokenRun definition"); if (scope.hasNext()) loc = scope->loc; bool first = true; name = ""; set allowedTypes = {Tnumeric, Talpha, Tspecial}; if (eatAnything >= 1) allowedTypes.insert(Tcolon); if (eatAnything >= 2) allowedTypes.insert(Tstring); + if (eatAnything >= 2) allowedTypes.insert(Tchar); if (eatAnything >= 3) allowedTypes.insert(Tlist); while (scope.hasNext() && (!scope->firstOnLine || (canStartLine && first)) && (first || scope->continued) && allowedTypes.count(scope->type)) { - name += scope.eatenToken().toStr(); + name += scope.eatenToken().toStr(allowQuotes); first = false; } } -bool eatIdentifier(Scope& scope, string& name, Loc& loc, string identPurpose, bool canStartLine=false, int eatAnything=0) { +bool eatIdentifier(Scope& scope, string& name, Loc& loc, string identPurpose, bool canStartLine=false, int eatAnything=0, bool allowQuotes=false) { Loc prevLoc = loc; - eatTokenRun(scope, name, loc, canStartLine, eatAnything); + eatTokenRun(scope, name, loc, canStartLine, eatAnything, allowQuotes); return check(name != "", "Missing " + identPurpose + " name", prevLoc); } #define directiveEatIdentifier(identPurpose, definition, eatAnything) \ returnOnFalse(eatIdentifier(scope, name, loc, identPurpose, false, eatAnything)); \ - returnOnFalse(isValidIdentifier(name, "Invalid " + string(identPurpose) + " name" errorQuoted(name), loc)); \ + returnOnFalse(isValidIdentifier(name, "Invalid " + string(identPurpose) + " name" + errorQuoted(name), loc)); \ if (definition) returnOnFalse(checkIdentRedefinitions(scope, name, loc, false, &scope.topNamespace())); #define directiveEatToken(type, missingErr, sameLine) returnOnFalse(eatToken(scope, loc, token, type, missingErr, sameLine)); @@ -1047,7 +1088,7 @@ string tokenizeNewModule(fs::path abspath, Scope& scope, bool mainModule=false) // preprocess ------------------------------------------------------------------------- bool lookupNamespaceAbove(string directiveName, int& namespaceId, Loc& loc, bool supressErrors=false); bool preprocess(Scope& scope); -bool eatComplexIdentifier(Scope& scope, Loc loc, string& ident, string purpose, bool canStartLine); +bool eatComplexIdentifier(Scope& scope, Loc loc, string& ident, string purpose, bool canStartLine, bool allowQuotes=false); bool arglistFromTlist(Scope& scope, Loc& loc, Macro& mac) { string name; bool first = true; @@ -1058,11 +1099,21 @@ bool arglistFromTlist(Scope& scope, Loc& loc, Macro& mac) { } first = false; directiveEatIdentifier("macro arg", true, 1); - checkReturnOnFail(mac.nameToArgIdx.count(name) == 0, "Macro argument redefinition" errorQuoted(name), loc); + checkReturnOnFail(mac.nameToArgIdx.count(name) == 0, "Macro argument redefinition" + errorQuoted(name), loc); mac.addArg(name, loc); // TODO macArg.loc not checked nor used } return true; } +bool eatDefineValue(Scope& scope, Token& outToken, Loc& loc, bool sameLine) { + TokenTypes type = scope.hasNext() && scope->type == Tchar ? Tchar : Tnumeric; + returnOnFalse(eatToken(scope, loc, outToken, type, "Define value expected", sameLine)); + if (type == Tchar) { + outToken = Token::fromCtx(Tnumeric, to_string((int)escapeCharToken(outToken)), outToken); + assert(to_string(stoi(outToken.data)) == outToken.data); // check good int + assert(stoi(outToken.data) <= WORD_MAX_VAL); + } + return true; +} bool processDefineDef(Scope& scope, string name, Loc loc, Loc percentLoc) { Token numeric; if (scope.hasNext() && scope->type == Tlist && !scope->firstOnLine) { @@ -1070,12 +1121,12 @@ bool processDefineDef(Scope& scope, string name, Loc loc, Loc percentLoc) { checkReturnOnFail(!token.continued, "Unexpected continued field", token); processArglistWrapper( bool retval = preprocess(scope); ); processArglistWrapper( - retval = eatToken(scope, loc, numeric, Tnumeric, "Define value expected", false); + retval = eatDefineValue(scope, numeric, loc, false); retval = retval && check(!scope.hasNext(), "Unexpected token after value", scope.currToken()); ); scope.eatenToken(); // tlist } else { - returnOnFalse(eatToken(scope, loc, numeric, Tnumeric, "Define value expected", true)); + returnOnFalse(eatDefineValue(scope, numeric, loc, true)); } scope.topNamespace().defines[name] = Define(name, percentLoc, numeric.data); return true; @@ -1161,7 +1212,7 @@ bool expandMacroUse(Scope& scope, int namespaceId, string macroName, Token& perc return true; } bool getDirectivePrefixes(string& firstName, list& prefixes, list& locs, Loc& loc, Scope& scope, string identPurpose="directive") { - static_assert(TokenCount == 12, "Exhaustive getDirectivePrefixes definition"); + static_assert(TokenCount == 13, "Exhaustive getDirectivePrefixes definition"); string name; bool first = true; while (true) { directiveEatIdentifier(identPurpose, false, 0); @@ -1240,7 +1291,7 @@ bool lookupNamespaceAbove(string directiveName, int& namespaceId, Loc& loc, bool if (!currNamespace->isUpperAccesible) break; namespaceId = currNamespace->upperNamespaceId; } - check(supressErrors, "Namespace not found" errorQuoted(directiveName), loc); + check(supressErrors, "Namespace not found" + errorQuoted(directiveName), loc); return false; } bool processUseDirective(string directiveName, Token& percentToken, Loc lastLoc, int namespaceId, bool namespaceSeen, Scope& scope) { @@ -1254,8 +1305,8 @@ bool processUseDirective(string directiveName, Token& percentToken, Loc lastLoc, // NOTE shouldn't allow expansion body leakages bcs '%' token is never on same line return expandMacroUse(scope, namespaceId, directiveName, percentToken); } - checkReturnOnFail(!namespaceSeen && !namespaceDefined(directiveName, namespaceId, true), "Namespace used as directive" errorQuoted(directiveName), lastLoc); - return check(false, "Undeclared identifier" errorQuoted(directiveName), lastLoc); + checkReturnOnFail(!namespaceSeen && !namespaceDefined(directiveName, namespaceId, true), "Namespace used as directive" + errorQuoted(directiveName), lastLoc); + return check(false, "Undeclared identifier" + errorQuoted(directiveName), lastLoc); } bool lookupName(Token& percentToken, string directiveName, list& prefixes, list& locs, Scope& scope) { int namespaceId = scope.currNamespaceId(); bool namespaceSeen = false; @@ -1266,7 +1317,7 @@ bool lookupName(Token& percentToken, string directiveName, list& prefixe lookupFinalAbove(directiveName, namespaceId, namespaceSeen); } while (prefixes.size()) { - checkReturnOnFail(namespaceDefined(directiveName, namespaceId), "Namespace not found" errorQuoted(directiveName), locs.front()); + checkReturnOnFail(namespaceDefined(directiveName, namespaceId), "Namespace not found" + errorQuoted(directiveName), locs.front()); directiveName = prefixes.front(); prefixes.pop_front(); locs.pop_front(); } return processUseDirective(directiveName, percentToken, locs.front(), namespaceId, namespaceSeen, scope); @@ -1280,9 +1331,9 @@ bool processUsing(Loc loc, Scope& scope) { returnOnFalse(lookupNamespaceAbove(firstName, namespaceId, locs.front())); while (prefixes.size()) { firstName = prefixes.front(); prefixes.pop_front(); locs.pop_front(); - checkReturnOnFail(namespaceDefined(firstName, namespaceId), "Namespace not found" errorQuoted(firstName), locs.front()); + checkReturnOnFail(namespaceDefined(firstName, namespaceId), "Namespace not found" + errorQuoted(firstName), locs.front()); } - check(scope.currNamespace().usedNamespaces.insert(namespaceId).second, "Namespace already used" errorQuoted(firstName), locs.back()); + check(scope.currNamespace().usedNamespaces.insert(namespaceId).second, "Namespace already used" + errorQuoted(firstName), locs.back()); return true; } fs::path processIncludePath(string str, Scope& scope) { @@ -1322,12 +1373,12 @@ bool checkDirectiveContext(Scope& scope, string dirType, string directiveName, l if (directiveName != "define" && directiveName != "macro") { checkReturnOnFail(!scope.insideMacro(), dirType + " not allowed inside macro", percentToken.loc); } - checkReturnOnFail(percentToken.firstOnLine, "Unexpected directive here" errorQuoted(directiveName), percentToken.loc); + checkReturnOnFail(percentToken.firstOnLine, "Unexpected directive here" + errorQuoted(directiveName), percentToken.loc); } return true; } bool processDirective(Token percentToken, Scope& scope) { - static_assert(TokenCount == 12, "Exhaustive processDirective definition"); + static_assert(TokenCount == 13, "Exhaustive processDirective definition"); string directiveName; list prefixes; list locs; Loc loc = percentToken.loc; returnOnFalse(complexDirectiveName(scope, directiveName, prefixes, locs, loc)); if (DefiningDirectivesSet.count(directiveName)) { @@ -1369,7 +1420,7 @@ bool preprocess(Scope& scope) { return errorLess; } // token stream parsing ----------------------------------- -bool eatComplexIdentifier(Scope& scope, Loc loc, string& ident, string purpose, bool canStartLine) { +bool eatComplexIdentifier(Scope& scope, Loc loc, string& ident, string purpose, bool canStartLine, bool allowQuotes) { string fragment; Loc fragLoc = loc; checkReturnOnFail(scope.hasNext() && (!scope->firstOnLine || canStartLine), "Missing " + purpose + " name", loc); do { @@ -1384,15 +1435,28 @@ bool eatComplexIdentifier(Scope& scope, Loc loc, string& ident, string purpose, scope.eatenToken(); ident.append(fragment); } else { - returnOnFalse(eatIdentifier(scope, fragment, fragLoc, purpose + " field", canStartLine, 2)); + returnOnFalse(eatIdentifier(scope, fragment, fragLoc, purpose + " field", canStartLine, 2, allowQuotes)); canStartLine = false; ident.append(fragment); } } while (scope.hasNext() && scope->continued); if (purpose == "instr" || purpose == "immediate" || purpose == "directive") return true; - returnOnFalse(isValidIdentifier(ident, "Invalid " + purpose + " name" errorQuoted(ident), loc)); + returnOnFalse(isValidIdentifier(ident, "Invalid " + purpose + " name" + errorQuoted(ident), loc)); return checkIdentRedefinitions(scope, ident, loc, purpose == "label", &scope.topNamespace()); } +bool parseInstrTS(Scope& scope, Loc loc, Instr& instr) { + string name = ""; + returnOnFalse(eatComplexIdentifier(scope, loc, name, "instr", true, true)); + checkReturnOnFail(name.at(name.length()-1) != ':', "Label definitions BEGIN with ':'", loc); + instr.opcodeStr = name; + while (scope.hasNext() && !scope->firstOnLine) { + Token immToken = Token::fromCtx(Talpha, "", scope.currToken()); + if (scope->type == Tnumeric || scope->type == Tchar || scope->type == Tstring) immToken.type = scope->type; + returnOnFalse(eatComplexIdentifier(scope, loc, immToken.data, "immediate", false)); + instr.immediates.push_back(immToken); + } + return true; +} bool dumpImpl(optional& dumpFile, int indent, string s) { // TODO disable in ctime @@ -1408,7 +1472,6 @@ void parseTokenStream(Scope& scope) { Loc loc; bool labelOnLine; bool errorLess; // ignored -outer_loop_continue: while (scope.advanceIteration()) { if (scope.getCurrModule() != parseCtx.lastModule) { parseCtx.lastModule = scope.getCurrModule(); @@ -1418,21 +1481,14 @@ void parseTokenStream(Scope& scope) { labelOnLine = labelOnLine && !top.firstOnLine; if (top.type == Tcolon) { scope.eatenToken(); - eatLineOnFalse(eatComplexIdentifier(scope, loc, name, "label", false)); + eatLineOnFalse(eatComplexIdentifier(scope, loc, name, "label", false, true)); checkContinueOnFail(!labelOnLine, "Max one label per line", loc); labelOnLine = true; parseCtx.strToLabel.insert(pair(name, Label(name, parseCtx.instrs.size(), loc))); dump(':' + name); } else if (top.type == Talpha) { - // NOTE ignores whole instr on any err in parsing - eatLineOnFalse(eatComplexIdentifier(scope, loc, name, "instr", true)); - eatLineOnFalse(check(name.at(name.length()-1) != ':', "Label definitions BEGIN with ':'", loc)); // note for bad label definition - Instr instr(name, loc); - while (scope.hasNext() && !scope->firstOnLine) { - string immFrag; - eatLineOnFalse(eatComplexIdentifier(scope, loc, immFrag, "immediate", false), goto outer_loop_continue); - instr.immFields.push_back(immFrag); - } + Instr instr(loc); + eatLineOnFalse(parseInstrTS(scope, loc, instr)); dump(instr.toStr()); parseCtx.instrs.push_back(instr); } else if (top.type == TIexpansion || top.type == TInamespace) { @@ -1513,17 +1569,33 @@ bool verifyNotInstrOpcode(string name) { SupressErrors = false; return ans; } +bool parseNumericalImmediate(Token& imm, Instr& instr) { + if (imm.type == Tnumeric) { + checkReturnOnFail(isdigit(imm.data.at(0)), "Invalid instruction immediate", instr); + instr.immediate = stoi(imm.data); + checkReturnOnFail(to_string(instr.immediate) == imm.data, "Invalid instruction immediate", instr); + } else if (imm.type == Tchar) { + instr.immediate = escapeCharToken(imm); + } else { + unreachable(); + } + return true; +} bool parseInstrImmediate(Instr& instr) { - checkReturnOnFail(instr.immFields.size() == 1, "Only single immediate allowed", instr); - string immVal = instr.immFields[0]; - if (parseCtx.strToLabel.count(immVal) > 0) { - instr.immediate = parseCtx.strToLabel[immVal].addr; - if (immVal == "end") instr.needsReparsing = true; - return true; + checkReturnOnFail(instr.immediates.size() == 1, "Only single immediate allowed", instr); + Token& imm = instr.immediates.front(); + if (imm.type == Talpha) { + if (!parseCtx.strToLabel.count(imm.data)) { + checkReturnOnFail(_validIdentChar(imm.data.at(0)), "Invalid instruction immediate", instr); + return check(false, "Undefined label", instr); + } + instr.immediate = parseCtx.strToLabel[imm.data].addr; + if (imm.data == "end") instr.needsReparsing = true; + } else if (imm.type == Tnumeric || imm.type == Tchar) { + returnOnFalse(parseNumericalImmediate(imm, instr)); + } else { + checkReturnOnFail(false, "Invalid instruction immediate", instr); } - checkReturnOnFail(isdigit(immVal.at(0)), "Invalid instruction immediate", instr); - instr.immediate = stoi(immVal); - checkReturnOnFail(to_string(instr.immediate) == immVal, "Invalid instruction immediate", instr); return check(0 <= instr.immediate && instr.immediate <= WORD_MAX_VAL, "Value of the immediate is out of bounds", instr); } bool parseInstrFields(Instr& instr) { @@ -2154,8 +2226,8 @@ fs::path _masfixFolder = fs::exists(__FILE__) ? fs::path checkPathArg(string arg, bool file) { fs::path p = fs::weakly_canonical(arg); if (!fs::exists(p)) p = _masfixFolder/p; - checkUsage(fs::exists(p), "Invalid argument or file not found" errorQuoted(arg)); - if (file) checkUsage(fs::is_regular_file(p), "File expected" errorQuoted(arg)); + checkUsage(fs::exists(p), "Invalid argument or file not found" + errorQuoted(arg)); + if (file) checkUsage(fs::is_regular_file(p), "File expected" + errorQuoted(arg)); else if (fs::is_regular_file(p)) p = p.parent_path(); return p; } @@ -2193,7 +2265,7 @@ Flags processLineArgs(int argc, char *argv[]) { } else if (arg == "-D" || arg == "-d" || arg == "--dump") { flags.dump = true; } else { - checkUsage(arg.at(0) != '-', "Unknown argument" errorQuoted(arg)); + checkUsage(arg.at(0) != '-', "Unknown argument" + errorQuoted(arg)); flags.inputPath = checkPathArg(arg, true); } } @@ -2202,12 +2274,12 @@ Flags processLineArgs(int argc, char *argv[]) { } ifstream openInputFile(fs::path path) { ifstream ifs(path); - checkCond(ifs.good(), "The input file" errorQuoted(path.string()) " couldn't be opened"); + checkCond(ifs.good(), "The input file" + errorQuoted(path.string()) + " couldn't be opened"); return ifs; } ofstream openOutputFile(fs::path path) { ofstream ofs(path); - checkCond(ofs.good(), "The output file" errorQuoted(path.string()) " couldn't be opened"); + checkCond(ofs.good(), "The output file" + errorQuoted(path.string()) + " couldn't be opened"); return ofs; } int runCmdEchoed(vector args, Flags& flags, bool exitOnErr=true) { diff --git a/examples/AdventOfCode2025/d3_batteries.mx b/examples/AdventOfCode2025/d3_batteries.mx index 5152219..664e4f4 100644 --- a/examples/AdventOfCode2025/d3_batteries.mx +++ b/examples/AdventOfCode2025/d3_batteries.mx @@ -119,11 +119,11 @@ %banksSum(strar) } %print_endl( - outc 10 - outc 83 - outc 117 - outc 109 - outc 32 + outc '\n' + outc 'S' + outc 'u' + outc 'm' + outc ' ' %banksSum(outum) ) } diff --git a/examples/fibonacci_asm_only.mx b/examples/fibonacci_asm_only.mx index 9c777c7..327e152 100644 --- a/examples/fibonacci_asm_only.mx +++ b/examples/fibonacci_asm_only.mx @@ -1,14 +1,14 @@ ; input prompt -outc 72 -outc 111 -outc 119 -outc 32 -outc 109 -outc 97 -outc 110 -outc 121 -outc 63 -outc 32 +outc 'H' +outc 'o' +outc 'w' +outc ' ' +outc 'm' +outc 'a' +outc 'n' +outc 'y' +outc '?' +outc ' ' inu ; input inl @@ -30,7 +30,7 @@ strr ; print a mov 0 outum -outc 10 +outc '\n' ; (a, b) = (b, a + b) ldm diff --git a/std/debug.mx b/std/debug.mx index 9e14623..06fbe21 100644 --- a/std/debug.mx +++ b/std/debug.mx @@ -31,7 +31,7 @@ %macro emitMemdumpAddr() { outc 10 outuh - outc 58 + outc ':' } ; from, count -> void (stdout) @@ -88,7 +88,7 @@ %macro dumpStackOverrun(overrun) { %_dumpStack2Top() ; show overrun - outc 124 ; separator + outc '|' ; separator %load(%SEG_DEBUG_GLOBALS) lda 1 %stack:pushr() ; from original SP @@ -101,9 +101,9 @@ %macro seeFuncFrame() { ; TODO factor to function %toggleDebugMode() - outc 10 - outc 70 - outc 58 + outc '\n' + outc 'F' + outc ':' %load(!op(a, %SEG_DEBUG_GLOBALS, %G_ARGS_PTR)) ; memdump normal args %stack:pushr() @@ -114,7 +114,7 @@ %stack:pushr() %if_else { ldm , %call(memdump) - outc 124 + outc '|' , %stack:dropN(2) } diff --git a/std/ds/array.mx b/std/ds/array.mx index 960de32..b14f8b6 100644 --- a/std/ds/array.mx +++ b/std/ds/array.mx @@ -244,11 +244,11 @@ %void_method{ array_print, 1, 1, ; locals: first | for_stop, for_idx %seg:local(0, str 1) - outc 91 + outc '[' %foreach { %if_else { %seg:local(0, lmeq 0), - outc 44 - outc 32 + outc ',' + outc ' ' , str 0 } @@ -258,8 +258,8 @@ outum ) } - outc 93 - outc 10 + outc ']' + outc '\n' } diff --git a/std/exceptions.mx b/std/exceptions.mx index d8c9b31..e5421bf 100644 --- a/std/exceptions.mx +++ b/std/exceptions.mx @@ -3,16 +3,16 @@ %define ERR_ASSERT 0 %macro error(code) { - outc 10 - outc 69 - outc 82 - outc 82 - outc 79 - outc 82 - outc 58 - outc 32 + outc '\n' + outc 'E' + outc 'R' + outc 'R' + outc 'O' + outc 'R' + outc ':' + outc ' ' outu %code - outc 10 + outc '\n' jmp end } diff --git a/std/io.mx b/std/io.mx index f3ae90d..047b22f 100644 --- a/std/io.mx +++ b/std/io.mx @@ -3,19 +3,19 @@ %macro print_spaced(instr) { %instr - outc 32 + outc ' ' } %macro print_endl(instr) { %instr - outc 10 + outc '\n' } %macro print_separator() { - outc 44 - outc 32 + outc ',' + outc ' ' } %macro print_key_val(keyChar, val) { outc %keyChar - outc 61 + outc '=' outu(%val) %print_separator() } @@ -26,7 +26,7 @@ %if { ld> 15 ; negative? , - outc 45 + outc '-' ld 0 ldsm strr @@ -56,18 +56,19 @@ ; VM / runtime delim %macro print_delim(vm) { - outc 45 - outc 32 + outc '-' + outc ' ' %if_else(ld %vm, - outc 86 - outc 77, - outc 82 - outc 85 - outc 78 + outc 'V' + outc 'M' + , + outc 'R' + outc 'U' + outc 'N' ) - outc 32 - outc 45 - outc 10 + outc ' ' + outc '-' + outc '\n' } %macro vm_runtime_delim() { ld !print_delim(1) diff --git a/tests/chars.mx b/tests/chars.mx new file mode 100644 index 0000000..443a32a --- /dev/null +++ b/tests/chars.mx @@ -0,0 +1,65 @@ + +';' +'%' +"%" +'!' +"!" +'$' +"$" +',' +"," +':' +":" +'(' +"(" + +" +' +'' +"" +' ' +'"' +'\ +'\' +'\\ +'\\' +'\0' +"\r\n\0" + +'\'' +'\"' +'"' +"\"" +"'" +"\'" +""" +''' +lll ' ' ' ' + +';';comment +;';';comment +';;comment +\; +"\; + +; complex fields +%(def('i')ne) ["LMA\0"] +%(def('i')ne) [("LMAO")] ('^') +%define escaped '\r' +out('u') '\n' + +%macro testValues(defineName) { + ; OMG I am so HIGH ~~ + outu '8' + outc '\'' + outu 8 + outc '"' + outc '\t' + outc 8 ; probably some bullshit + outc '\n' + outc '8' + outc %(%defineName) + outu %("escaped") + out('c') '\n' +} +ld !testValues("LMAO") diff --git a/tests/chars.txt b/tests/chars.txt new file mode 100644 index 0000000..cfc1059 --- /dev/null +++ b/tests/chars.txt @@ -0,0 +1,51 @@ +:returncode 1 + +:stdout 13 +56'8"  +8^13 + + +:stderr 2149 +tests\chars.mx:16:1 ERROR: Expected string or character termination +tests\chars.mx:17:1 ERROR: Expected string or character termination +tests\chars.mx:18:1 ERROR: Invalid character value +tests\chars.mx:22:1 ERROR: Invalid escape sequence '\' +tests\chars.mx:23:1 ERROR: Expected string or character termination +tests\chars.mx:24:1 ERROR: Expected string or character termination +tests\chars.mx:35:3 ERROR: Expected string or character termination +tests\chars.mx:36:1 ERROR: Invalid character value +tests\chars.mx:36:3 ERROR: Expected string or character termination +tests\chars.mx:41:1 ERROR: Expected string or character termination +tests\chars.mx:43:1 ERROR: Invalid escape sequence '\;' +tests\chars.mx:46:15 ERROR: Invalid define name 'LMA\0' +tests\chars.mx:2:1 ERROR: Unexpected token ';' +tests\chars.mx:3:1 ERROR: Unexpected token '%' +tests\chars.mx:4:1 ERROR: Unexpected token "%" +tests\chars.mx:5:1 ERROR: Unexpected token '!' +tests\chars.mx:6:1 ERROR: Unexpected token "!" +tests\chars.mx:7:1 ERROR: Unexpected token '$' +tests\chars.mx:8:1 ERROR: Unexpected token "$" +tests\chars.mx:9:1 ERROR: Unexpected token ',' +tests\chars.mx:10:1 ERROR: Unexpected token "," +tests\chars.mx:11:1 ERROR: Unexpected token ':' +tests\chars.mx:12:1 ERROR: Unexpected token ":" +tests\chars.mx:13:1 ERROR: Unexpected token '(' +tests\chars.mx:14:1 ERROR: Unexpected token "(" +tests\chars.mx:19:1 ERROR: Unexpected token "" +tests\chars.mx:20:1 ERROR: Unexpected token ' ' +tests\chars.mx:21:1 ERROR: Unexpected token '"' +tests\chars.mx:25:1 ERROR: Unexpected token '\\' +tests\chars.mx:26:1 ERROR: Unexpected token '\0' +tests\chars.mx:27:1 ERROR: Unexpected token "\r\n\0" +tests\chars.mx:29:1 ERROR: Unexpected token '\'' +tests\chars.mx:30:1 ERROR: Unexpected token '\"' +tests\chars.mx:31:1 ERROR: Unexpected token '"' +tests\chars.mx:32:1 ERROR: Unexpected token "\"" +tests\chars.mx:33:1 ERROR: Unexpected token "'" +tests\chars.mx:34:1 ERROR: Unexpected token "\'" +tests\chars.mx:35:1 ERROR: Unexpected token "" +tests\chars.mx:39:1 ERROR: Unexpected token ';' +tests\chars.mx:42:1 ERROR: Unexpected token '\' +tests\chars.mx:37:1 ERROR: Unknown condition 'lll ' ' ' '' + + diff --git a/tests/complex-names.txt b/tests/complex-names.txt index 458bb98..c5310f4 100644 --- a/tests/complex-names.txt +++ b/tests/complex-names.txt @@ -1,6 +1,6 @@ :returncode 1 -:stderr 2723 +:stderr 2728 tests\complex-names.mx:41:4 ERROR: Directive must not continue tests\complex-names.mx:42:5 ERROR: Directive must not continue tests\complex-names.mx:5:2 ERROR: Expected label field @@ -17,7 +17,7 @@ tests\complex-names.mx:22:5 ERROR: Expected label field tests\complex-names.mx:25:1 ERROR: Invalid label name 'n8$5' tests\complex-names.mx:29:1 ERROR: Invalid label name '7zaza' tests\complex-names.mx:30:2 ERROR: Simple label field expected -tests\complex-names.mx:31:1 ERROR: Invalid label name 'a:b$(,)$' +tests\complex-names.mx:31:1 ERROR: Invalid label name 'a:b$()$' tests\complex-names.mx:34:5 ERROR: Expected immediate field tests\complex-names.mx:35:4 ERROR: Expected instr field tests\complex-names.mx:36:1 ERROR: Missing immediate field name @@ -28,7 +28,7 @@ tests\complex-names.mx:43:3 ERROR: Simple instr field expected tests\complex-names.mx:47:3 ERROR: Expected instr field tests\complex-names.mx:48:1 ERROR: Unexpected token '()' tests\complex-names.mx:50:2 ERROR: Simple instr field expected -tests\complex-names.mx:59:1 ERROR: Unexpected token 'ld' +tests\complex-names.mx:59:1 ERROR: Unexpected token "ld" tests\complex-names.mx:60:1 ERROR: Unexpected token '()' tests\complex-names.mx:67:20 ERROR: Expected immediate field tests\complex-names.mx:68:20 ERROR: Simple immediate field expected @@ -43,7 +43,7 @@ tests\complex-names.mx:56:1 ERROR: Excessive immediate 'bmeqm 15' tests\complex-names.mx:61:1 ERROR: Unknown instruction 'dll' tests\complex-names.mx:62:1 ERROR: Unknown condition 'l lvm' tests\complex-names.mx:65:1 ERROR: Unknown instruction 'debug 1 234' -tests\complex-names.mx:66:1 ERROR: Unknown instruction 'debug 01 23 4 5' -tests\complex-names.mx:69:1 ERROR: Unknown condition 'stst a b' +tests\complex-names.mx:66:1 ERROR: Unknown instruction 'debug 01 23 "4" 5' +tests\complex-names.mx:69:1 ERROR: Unknown condition 'stst "a" "b"' diff --git a/tests/ctime-use.txt b/tests/ctime-use.txt index 4d6a381..e5f7c6e 100644 --- a/tests/ctime-use.txt +++ b/tests/ctime-use.txt @@ -1,6 +1,6 @@ :returncode 1 -:stderr 2390 +:stderr 2214 tests\ctime-use.mx:13:1 ERROR: Missing directive name tests\ctime-use.mx:14:2 ERROR: Invalid directive name '!' tests\ctime-use.mx:15:1 ERROR: Unexpected ctime forcing '!' @@ -10,37 +10,35 @@ tests\ctime-use.mx:18:6 ERROR: Unexpected token after macro use '!' tests\ctime-use.mx:19:7 ERROR: Unexpected macro use tests\ctime-use.mx:30:2 ERROR: Directive must not continue tests\ctime-use.mx:32:4 ERROR: Directive must not continue -tests\ctime-use.mx:4:2 ERROR: Invalid instruction immediate 'ld c' +tests\ctime-use.mx:4:2 ERROR: Undefined label 'ld c' tests\ctime-use.mx:40:8 ERROR: Directive must not continue tests\ctime-use.mx:46:7 ERROR: Directive must not continue -tests\ctime-use.mx:4:2 ERROR: Invalid instruction immediate 'ld err' -tests\ctime-use.mx:4:2 ERROR: Invalid instruction immediate 'ld grr' +tests\ctime-use.mx:4:2 ERROR: Undefined label 'ld err' +tests\ctime-use.mx:4:2 ERROR: Undefined label 'ld grr' tests\ctime-use.mx:60:27 ERROR: Undeclared identifier 'c' tests\ctime-use.mx:60:40 ERROR: Expected instr field tests\ctime-use.mx:60:45 ERROR: Undeclared identifier 'oo' tests\ctime-use.mx:19:1 ERROR: Unexpected token '2' tests\ctime-use.mx:30:1 ERROR: Missing label name tests\ctime-use.mx:31:1 ERROR: Invalid label name '2' -tests\ctime-use.mx:34:1 ERROR: Invalid label name '!m(z)' +tests\ctime-use.mx:34:1 ERROR: Invalid label name "!m(z)" tests\ctime-use.mx:35:1 ERROR: Invalid label name '0' tests\ctime-use.mx:36:1 ERROR: Invalid label name '2' tests\ctime-use.mx:37:1 ERROR: Invalid label name '!m(z)' -tests\ctime-use.mx:38:1 ERROR: Label redefinition 'l' - - NOTE: Defined at tests\ctime-use.mx:32:1 -tests\ctime-use.mx:39:1 ERROR: Label redefinition 'l' - - NOTE: Defined at tests\ctime-use.mx:32:1 +tests\ctime-use.mx:38:1 ERROR: Invalid label name "l" +tests\ctime-use.mx:39:1 ERROR: Invalid label name "l" tests\ctime-use.mx:40:1 ERROR: Label redefinition 'l' - NOTE: Defined at tests\ctime-use.mx:32:1 tests\ctime-use.mx:44:1 ERROR: Unexpected token '2' tests\ctime-use.mx:20:1 ERROR: Unknown instruction 'two 5 5' tests\ctime-use.mx:23:1 ERROR: Unknown instruction 'ctime 3 ld 4' tests\ctime-use.mx:25:1 ERROR: Unknown instruction 'oui0' -tests\ctime-use.mx:27:1 ERROR: Invalid instruction immediate 'jmp labNO' -tests\ctime-use.mx:45:1 ERROR: Only single immediate allowed 'ld s 2' -tests\ctime-use.mx:46:1 ERROR: Invalid instruction immediate 'ld t' +tests\ctime-use.mx:27:1 ERROR: Undefined label 'jmp labNO' +tests\ctime-use.mx:45:1 ERROR: Only single immediate allowed 'ld "s" 2' +tests\ctime-use.mx:46:1 ERROR: Undefined label 'ld t' tests\ctime-use.mx:47:1 ERROR: Unknown instruction 'debug i 3' tests\ctime-use.mx:52:13 ERROR: Excessive immediate 'strr 0' tests\ctime-use.mx:60:18 ERROR: Unknown instruction 'outx 1' -tests\ctime-use.mx:61:1 ERROR: Invalid instruction immediate 'ld lvl1boss' +tests\ctime-use.mx:61:1 ERROR: Undefined label 'ld lvl1boss' diff --git a/tests/directive-use.txt b/tests/directive-use.txt index 7f3e773..b0b5100 100644 --- a/tests/directive-use.txt +++ b/tests/directive-use.txt @@ -1,6 +1,6 @@ :returncode 1 -:stderr 8495 +:stderr 8439 tests\directive-use.mx:14:2 ERROR: Undeclared identifier 'undefd' tests\directive-use.mx:19:5 ERROR: Undeclared identifier 'defined_' tests\directive-use.mx:20:8 ERROR: Undeclared identifier 'defined_' @@ -88,16 +88,16 @@ tests\directive-use.mx:160:14 ERROR: Simple immediate field expected tests\directive-use.mx:183:8 ERROR: Unexpected token '420' tests\directive-use.mx:160:14 ERROR: Simple immediate field expected tests\directive-use.mx:183:8 ERROR: Unexpected token '420' -tests\directive-use.mx:13:1 ERROR: Invalid instruction immediate 'mov defined3' +tests\directive-use.mx:13:1 ERROR: Undefined label 'mov defined3' tests\directive-use.mx:17:1 ERROR: Only single immediate allowed 'ld 8 3' tests\directive-use.mx:18:1 ERROR: Only single immediate allowed 'ld 1 9' tests\directive-use.mx:19:1 ERROR: Target value expected 'ld' tests\directive-use.mx:21:1 ERROR: Target value expected 'ld' tests\directive-use.mx:36:1 ERROR: Target value expected 'mov' -tests\directive-use.mx:7:21 ERROR: Invalid instruction immediate 'ld insides' +tests\directive-use.mx:7:21 ERROR: Undefined label 'ld insides' tests\directive-use.mx:38:1 ERROR: Target value expected 'str' -tests\directive-use.mx:42:21 ERROR: Invalid instruction immediate 'outu _' -tests\directive-use.mx:42:21 ERROR: Invalid instruction immediate 'outu labelInside1' +tests\directive-use.mx:42:21 ERROR: Undefined label 'outu _' +tests\directive-use.mx:42:21 ERROR: Undefined label 'outu labelInside1' tests\directive-use.mx:93:2 ERROR: Only single immediate allowed 'outu _ 5' tests\directive-use.mx:94:2 ERROR: Only single immediate allowed 'outu _ 18' tests\directive-use.mx:88:2 ERROR: Only single immediate allowed 'outu _ 9' diff --git a/tests/imm-parse.txt b/tests/imm-parse.txt index e1c6d2c..1301927 100644 --- a/tests/imm-parse.txt +++ b/tests/imm-parse.txt @@ -1,6 +1,6 @@ :returncode 1 -:stderr 1556 +:stderr 1514 tests\imm-parse.mx:2:1 ERROR: Unknown suffix 'ld7' tests\imm-parse.mx:4:1 ERROR: Target value expected 'stra' tests\imm-parse.mx:6:1 ERROR: Only single immediate allowed 'str 5 mov' @@ -10,7 +10,7 @@ tests\imm-parse.mx:9:1 ERROR: Only single immediate allowed 'ld 8 str 5' tests\imm-parse.mx:11:1 ERROR: Invalid instruction immediate 'mov 8.5' tests\imm-parse.mx:12:1 ERROR: Invalid instruction immediate 'mov 8.' tests\imm-parse.mx:13:1 ERROR: Unknown suffix 'mov80' -tests\imm-parse.mx:14:1 ERROR: Invalid instruction immediate 'mov ld' +tests\imm-parse.mx:14:1 ERROR: Undefined label 'mov ld' tests\imm-parse.mx:16:1 ERROR: Invalid instruction immediate 'mov 05' tests\imm-parse.mx:17:1 ERROR: Invalid instruction immediate 'mov -5' tests\imm-parse.mx:19:1 ERROR: Invalid instruction immediate 'mov |100' @@ -19,8 +19,8 @@ tests\imm-parse.mx:25:1 ERROR: Value of the immediate is out of bounds 'mov 1000 tests\imm-parse.mx:26:1 ERROR: Value of the immediate is out of bounds 'lda 150000' tests\imm-parse.mx:28:1 ERROR: Value of the immediate is out of bounds 'jmp 65536' tests\imm-parse.mx:31:1 ERROR: Only single immediate allowed 'ld label 2' -tests\imm-parse.mx:32:1 ERROR: Invalid instruction immediate 'lds label2' -tests\imm-parse.mx:33:1 ERROR: Invalid instruction immediate 'str _lab-el5_' +tests\imm-parse.mx:32:1 ERROR: Undefined label 'lds label2' +tests\imm-parse.mx:33:1 ERROR: Undefined label 'str _lab-el5_' tests\imm-parse.mx:34:1 ERROR: Only single immediate allowed 'str _lab -el 5 _' tests\imm-parse.mx:35:1 ERROR: Only single immediate allowed 'str _lab mov _el5_' diff --git a/tests/includes/includes.mx b/tests/includes/includes.mx index e9b3e01..781bc26 100644 --- a/tests/includes/includes.mx +++ b/tests/includes/includes.mx @@ -5,6 +5,8 @@ %include 78 %include main %include '' +%include 'c' +%include '\n' %include "" %include "\r\n\\" %include "\$/\-" @@ -47,7 +49,7 @@ %include "procedures" %include "fibonacci" %include "examples/fibonacci_asm_only.mx" -%include "tests\includes\includee" +%include "tests\\includes\\includee" %include "tests/include_expansion" %hidden diff --git a/tests/includes/includes.txt b/tests/includes/includes.txt index 1e950b1..fdcf01b 100644 --- a/tests/includes/includes.txt +++ b/tests/includes/includes.txt @@ -1,35 +1,39 @@ :returncode 1 -:stderr 2238 +:stderr 2506 +tests\includes\includes.mx:7:10 ERROR: Invalid character value +tests\includes\includes.mx:12:10 ERROR: Invalid escape sequence '\$' tests\includes\includes.mx:3:2 ERROR: Undeclared identifier 'inclde' tests\includes\includes.mx:4:2 ERROR: Missing included path string tests\includes\includes.mx:5:10 ERROR: Expected string token type, got '78' tests\includes\includes.mx:6:10 ERROR: Expected string token type, got 'main' -tests\includes\includes.mx:7:10 ERROR: Expected string token type, got ''' -tests\includes\includes.mx:8:10 ERROR: Input file "" can't be included -tests\includes\includes.mx:9:10 ERROR: Input file "\r\n\\" can't be included -tests\includes\includes.mx:10:10 ERROR: Input file "\$/\-" can't be included -tests\includes\includes.mx:11:10 ERROR: Input file "includ" can't be included +tests\includes\includes.mx:7:2 ERROR: Missing included path string +tests\includes\includes.mx:8:10 ERROR: Expected string token type, got 'c' +tests\includes\includes.mx:9:10 ERROR: Expected string token type, got '\n' +tests\includes\includes.mx:10:10 ERROR: Input file "" can't be included +tests\includes\includes.mx:11:10 ERROR: Input file "\r\n\\" can't be included +tests\includes\includes.mx:12:2 ERROR: Missing included path string +tests\includes\includes.mx:13:10 ERROR: Input file "includ" can't be included tests\includes\includee.mx:11:15 ERROR: Undeclared identifier 'befIncl' tests\includes\includee.mx:12:2 ERROR: Undeclared identifier 'afterIncl' tests\includes\includee.mx:16:2 ERROR: Undeclared identifier 'afterIncl' tests\includes\includee.mx:18:10 ERROR: Input file "a ted uz: sere me tuze inkluze..." can't be included tests\includes\includee.mx:15:1 ERROR: Unknown instruction 'reachable? 5' -tests\includes\includes.mx:19:13 ERROR: Undeclared identifier 'defd' -tests\includes\includes.mx:24:3 ERROR: Namespace not found 'includee' +tests\includes\includes.mx:21:13 ERROR: Undeclared identifier 'defd' tests\includes\includes.mx:26:3 ERROR: Namespace not found 'includee' -tests\includes\includes.mx:27:3 ERROR: Undeclared identifier 'defd' -tests\includes\includes.mx:28:7 ERROR: Undeclared identifier 'defd' -tests\includes\includes.mx:38:10 ERROR: Input file "include" can't be included -tests\includes\includes.mx:39:2 ERROR: Invalid directive name '%' -tests\includes\includes.mx:42:10 ERROR: Input file "includes.txt" can't be included -tests\includes\includes.mx:48:10 ERROR: Input file "fibonacci" can't be included -tests\includes\includes.mx:51:10 ERROR: Input file "tests/include_expansion" can't be included -tests\includes\includes.mx:53:2 ERROR: Undeclared identifier 'hidden' -tests\includes\includes.mx:31:2 ERROR: Unexpected token '9' -tests\includes\includes.mx:18:3 ERROR: Unknown instruction 'debug 9' -tests\includes\includes.mx:32:2 ERROR: Unknown instruction 'print 65' -tests\includes\includes.mx:54:1 ERROR: Unknown instruction 'now-gonna-includeeznuts' +tests\includes\includes.mx:28:3 ERROR: Namespace not found 'includee' +tests\includes\includes.mx:29:3 ERROR: Undeclared identifier 'defd' +tests\includes\includes.mx:30:7 ERROR: Undeclared identifier 'defd' +tests\includes\includes.mx:40:10 ERROR: Input file "include" can't be included +tests\includes\includes.mx:41:2 ERROR: Invalid directive name '%' +tests\includes\includes.mx:44:10 ERROR: Input file "includes.txt" can't be included +tests\includes\includes.mx:50:10 ERROR: Input file "fibonacci" can't be included +tests\includes\includes.mx:53:10 ERROR: Input file "tests/include_expansion" can't be included +tests\includes\includes.mx:55:2 ERROR: Undeclared identifier 'hidden' +tests\includes\includes.mx:33:2 ERROR: Unexpected token '9' +tests\includes\includes.mx:20:3 ERROR: Unknown instruction 'debug 9' +tests\includes\includes.mx:34:2 ERROR: Unknown instruction 'print 65' +tests\includes\includes.mx:56:1 ERROR: Unknown instruction 'now-gonna-includeeznuts' tests\includes\includee.mx:7:2 ERROR: Unknown instruction 'print 65' diff --git a/tests/instr-parse.txt b/tests/instr-parse.txt index e0dd6f5..afe8f77 100644 --- a/tests/instr-parse.txt +++ b/tests/instr-parse.txt @@ -1,6 +1,6 @@ :returncode 1 -:stderr 2868 +:stderr 2854 tests\instr-parse.mx:2:1 ERROR: Unknown instruction 'fuhr' tests\instr-parse.mx:3:1 ERROR: Missing condition 'lu' tests\instr-parse.mx:4:2 ERROR: Missing condition 'li 85' @@ -21,7 +21,7 @@ tests\instr-parse.mx:22:1 ERROR: Excessive immediate 'strr 8' tests\instr-parse.mx:24:1 ERROR: Missing register for second operation 'strsa' tests\instr-parse.mx:26:1 ERROR: Missing register for second operation 'straa' tests\instr-parse.mx:27:1 ERROR: Excessive immediate 'strar 5' -tests\instr-parse.mx:28:1 ERROR: Invalid instruction immediate 'strar a' +tests\instr-parse.mx:28:1 ERROR: Undefined label 'strar a' tests\instr-parse.mx:29:1 ERROR: Excessive immediate 'strar 10' tests\instr-parse.mx:31:1 ERROR: Target value expected 'mov' tests\instr-parse.mx:33:1 ERROR: Target value expected 'ld' diff --git a/tests/jmp-test.mx b/tests/jmp-test.mx index 4251c7e..db43e0e 100644 --- a/tests/jmp-test.mx +++ b/tests/jmp-test.mx @@ -3,14 +3,14 @@ :top jmpar - outc 65 ; ABCDEFGH\n - outc 66 - outc 67 - outc 68 - outc 69 - outc 70 - outc 71 - outc 72 + outc 'A' ; ABCDEFGH\n + outc 'B' + outc 'C' + outc 'D' + outc 'E' + outc 'F' + outc 'G' + outc 'H' outc 10 diff --git a/tests/namespace-accessors.txt b/tests/namespace-accessors.txt index e7fa754..aef47a7 100644 --- a/tests/namespace-accessors.txt +++ b/tests/namespace-accessors.txt @@ -69,13 +69,13 @@ tests\namespace-accessors.mx:91:16 ERROR: Namespace already used 'time' tests\namespace-accessors.mx:91:21 ERROR: Unexpected token after directive '%' tests\namespace-accessors.mx:92:15 ERROR: Namespace already used 'time' tests\namespace-accessors.mx:92:20 ERROR: Unexpected token after directive '15' -tests\namespace-accessors.mx:95:8 ERROR: Namespace parameter shouldn't be in string '' -tests\namespace-accessors.mx:96:9 ERROR: Namespace parameter shouldn't be in string '' -tests\namespace-accessors.mx:97:9 ERROR: Namespace parameter shouldn't be in string 'namespace' -tests\namespace-accessors.mx:98:9 ERROR: Namespace parameter shouldn't be in string 'A' +tests\namespace-accessors.mx:95:8 ERROR: Namespace parameter shouldn't be in string "" +tests\namespace-accessors.mx:96:9 ERROR: Namespace parameter shouldn't be in string "" +tests\namespace-accessors.mx:97:9 ERROR: Namespace parameter shouldn't be in string "namespace" +tests\namespace-accessors.mx:98:9 ERROR: Namespace parameter shouldn't be in string "A" tests\namespace-accessors.mx:99:9 ERROR: Namespace not found 'a' tests\namespace-accessors.mx:100:12 ERROR: Missing namespace name tests\namespace-accessors.mx:101:9 ERROR: Missing directive name -tests\namespace-accessors.mx:94:2 ERROR: Unexpected token '%using' +tests\namespace-accessors.mx:94:2 ERROR: Unexpected token "%using" diff --git a/tests/op-parse.txt b/tests/op-parse.txt index c5b3a99..46abf37 100644 --- a/tests/op-parse.txt +++ b/tests/op-parse.txt @@ -1,13 +1,13 @@ :returncode 1 -:stderr 1367 +:stderr 1339 tests\op-parse.mx:2:1 ERROR: Target value expected 'str|' tests\op-parse.mx:4:1 ERROR: Target value expected 'str^' tests\op-parse.mx:6:1 ERROR: Excessive immediate 'str|r 5' tests\op-parse.mx:7:1 ERROR: Missing register for second operation 'str^&' tests\op-parse.mx:8:1 ERROR: Target value expected 'strt' tests\op-parse.mx:10:1 ERROR: Missing immediate 'strrt' -tests\op-parse.mx:11:1 ERROR: Invalid instruction immediate 'str t' +tests\op-parse.mx:11:1 ERROR: Undefined label 'str t' tests\op-parse.mx:12:1 ERROR: Unknown suffix 'str$ 5' tests\op-parse.mx:14:1 ERROR: Missing condition 'b|' tests\op-parse.mx:15:1 ERROR: This instruction cannot have a modifier 'beq|' @@ -20,6 +20,6 @@ tests\op-parse.mx:23:1 ERROR: This instruction cannot have a modifier 'lgt^&' tests\op-parse.mx:25:1 ERROR: This instruction cannot have a modifier 'bgeth' tests\op-parse.mx:26:1 ERROR: This instruction cannot have a modifier 'slttr' tests\op-parse.mx:27:1 ERROR: Missing immediate 'sgert' -tests\op-parse.mx:28:1 ERROR: Invalid instruction immediate 'sge t' +tests\op-parse.mx:28:1 ERROR: Undefined label 'sge t' diff --git a/tests/std-array.mx b/tests/std-array.mx index bdddd3d..70a4085 100644 --- a/tests/std-array.mx +++ b/tests/std-array.mx @@ -15,7 +15,7 @@ %func { int_lt_with_emit, 2, 0, %seg:push(%arg, 0) outur - outc 60 ; '<' + outc '<' %seg:push(%arg, 1) %print_spaced(outur) %call(uint_lt) @@ -175,7 +175,7 @@ ) ; test empty[0] - outc 69 + outc 'E' %seg:push_addr(%local, %array:sizeof) %stack:push(0) %call(index) diff --git a/tests/std-control.mx b/tests/std-control.mx index 28c29fc..d629138 100644 --- a/tests/std-control.mx +++ b/tests/std-control.mx @@ -6,21 +6,21 @@ ; false while cond %while {ld 0, - outc 65 + outc 'E' } %do_while {ld 0, - outc 66 + outc 'B' } -outc 10 +outc '\n' ; generalized for ld 107 %for_full(7, 10, %stack:top() outum - outc 32 + outc ' ' ) -outc 10 +outc '\n' %store(%MEMORY_LAYOUT:SEG_TEMP, 7) %do_while { @@ -35,7 +35,7 @@ outc 10 :no_continue outum } -outc 10 +outc '\n' ; for loop ld 10 @@ -44,5 +44,5 @@ ld 10 outum ld< 3 ; should be irrelevant } -outc 10 +outc '\n' %dumpStackOverrun(7) diff --git a/tests/std-div_pwr.mx b/tests/std-div_pwr.mx index 9ddf615..048ef96 100644 --- a/tests/std-div_pwr.mx +++ b/tests/std-div_pwr.mx @@ -18,13 +18,13 @@ outu %numerator outc %char_u outu %denominator - outc 61 + outc '=' %callOp(%op, %numerator, %denominator) outur } %macro testDivRem(numerator, denominator) { %testOp(divide, 47, %numerator, %denominator) - outc 37 + outc '%' mov %MEMORY_LAYOUT:SEG_TEMP ldt %denominator str %numerator @@ -76,14 +76,14 @@ outc 10 ; which power to break -> status %func {test2Powers, 1, 2, ; locals: maxPwr, currPwr - %print_spaced(outc 105) ; collumn headers + %print_spaced(outc 'i') ; collumn headers %print_spaced( outu 2 - outc 94 - outc 105 + outc '^' + outc 'i' ) - outc 47 - outc 10 + outc '/' + outc '\n' ld 14 %callPwrStrLocal(2, 0) ; maxPwr = 2 ** 14 @@ -119,18 +119,18 @@ outc 10 %stack:drop() lnem ; maxPwr >> idx != divide(maxPwr, currPwr) , - outc 32 - outc 69 ; emit error - outc 64 + outc ' ' + outc 'E' ; emit error + outc '@' %stack:top() %print_endl(outum) ; idx %return_imm(test2Powers, 69) ; return when values mismatch } - outc 10 + outc '\n' } %print_spaced( - outc 79 ; OK - outc 75 + outc 'O' + outc 'K' ) %stack:reserve(5) ; test correct stack alignment on function exit ld 0 ; retval @@ -152,7 +152,7 @@ outc 10 ; ld %label ; lds test_start ; outur - outc 45 + outc '-' mov %retaddrLocation strs test_start str 1234 ; -- @@ -178,8 +178,8 @@ outc 10 outc 10 %print_endl( - outc 76 - outc 115 + outc 'L' + outc 's' ) %showLabelOffset(module_end, 17) %showLabelOffset(test_return, 23) @@ -193,8 +193,8 @@ outc 10 %stack:push(%value) %print_spaced( outum ) %print_spaced( - outc 45 - outc 62 + outc '-' + outc '>' ) %call(round_up_to_2_power) %print_endl( diff --git a/tests/strings.mx b/tests/strings.mx index c68d653..6c065dc 100644 --- a/tests/strings.mx +++ b/tests/strings.mx @@ -6,12 +6,12 @@ nuefounland " eldorado Where's " my quote?! -He:replied: " don't know! \-/ +He:replied: " don't know! "got the loc bro?" %no-where-is-it(?) "( } ) '!" ( - %define ")()}{][ ! % / / \" + %define ")()}{][ ! % / / \" " ). "" :Michal "Emmet" Martinek: @@ -25,5 +25,21 @@ a "\n" b s"trin"g debug "" debug aa "sd"$a" " "ld" " " "\t" -NASA's (" deep" )( "space")"banger"Juice +NASA`s (" deep" )( "space")"banger"Juice help ("mouse")( " is") ("inside" " him") "*$@#+" + +outu "str" "lit" +:"label" + +%define u 7 +%("u") +%("define") ("value") ("8") + +outc 'ii'!boob +"e" () +"e"! +"e" ! +"!"! +go "" ! +go "lou" ! + ""! diff --git a/tests/strings.txt b/tests/strings.txt index 606a47c..ba52e30 100644 --- a/tests/strings.txt +++ b/tests/strings.txt @@ -1,30 +1,50 @@ :returncode 1 -:stderr 1475 -tests\strings.mx:1:1 ERROR: Expected string termination -tests\strings.mx:5:1 ERROR: Expected string termination -tests\strings.mx:6:13 ERROR: Expected string termination -tests\strings.mx:8:9 ERROR: Expected string termination -tests\strings.mx:9:13 ERROR: Expected string termination +:stderr 2655 +tests\strings.mx:1:1 ERROR: Expected string or character termination +tests\strings.mx:5:1 ERROR: Expected string or character termination +tests\strings.mx:6:13 ERROR: Expected string or character termination +tests\strings.mx:8:6 ERROR: Expected string or character termination +tests\strings.mx:9:13 ERROR: Expected string or character termination +tests\strings.mx:38:6 ERROR: Invalid character value tests\strings.mx:10:21 ERROR: Invalid directive name 'no-where-is-it' tests\strings.mx:14:3 ERROR: Missing define name -tests\strings.mx:2:1 ERROR: Unexpected token '' -tests\strings.mx:3:1 ERROR: Unexpected token ' ' -tests\strings.mx:4:1 ERROR: Unexpected token 'Hello, world!' +tests\strings.mx:36:24 ERROR: Expected numeric token type, got "8" +tests\strings.mx:38:11 ERROR: Undeclared identifier 'boob' +tests\strings.mx:40:4 ERROR: Missing directive name +tests\strings.mx:41:5 ERROR: Missing directive name +tests\strings.mx:42:4 ERROR: Missing directive name +tests\strings.mx:43:7 ERROR: Missing directive name +tests\strings.mx:44:10 ERROR: Missing directive name +tests\strings.mx:45:5 ERROR: Missing directive name +tests\strings.mx:2:1 ERROR: Unexpected token "" +tests\strings.mx:3:1 ERROR: Unexpected token " " +tests\strings.mx:4:1 ERROR: Unexpected token "Hello, world!" tests\strings.mx:9:1 ERROR: Label definitions BEGIN with ':' -tests\strings.mx:10:1 ERROR: Unexpected token 'got the loc bro?' -tests\strings.mx:11:1 ERROR: Unexpected token '( } ) '!' +tests\strings.mx:10:1 ERROR: Unexpected token "got the loc bro?" +tests\strings.mx:11:1 ERROR: Unexpected token "( } ) '!" tests\strings.mx:13:1 ERROR: Unexpected token '()' -tests\strings.mx:17:9 ERROR: Unexpected token 'Emmet' +tests\strings.mx:17:9 ERROR: Unexpected token "Emmet" tests\strings.mx:19:1 ERROR: Unexpected token '()' tests\strings.mx:22:1 ERROR: Invalid label name 'whos there' tests\strings.mx:26:1 ERROR: Missing immediate field name tests\strings.mx:29:24 ERROR: Simple immediate field expected +tests\strings.mx:32:1 ERROR: Invalid label name "label" +tests\strings.mx:35:1 ERROR: Unexpected token '7' +tests\strings.mx:39:1 ERROR: Unexpected token "e" +tests\strings.mx:40:1 ERROR: Unexpected token "e" +tests\strings.mx:41:1 ERROR: Unexpected token "e" +tests\strings.mx:42:1 ERROR: Unexpected token "!" +tests\strings.mx:43:1 ERROR: Missing immediate field name +tests\strings.mx:45:3 ERROR: Unexpected token "" tests\strings.mx:6:1 ERROR: Unknown instruction 'nuefounland' -tests\strings.mx:8:1 ERROR: Unknown instruction 'Where's' -tests\strings.mx:24:1 ERROR: Unknown instruction 'a \n' -tests\strings.mx:25:1 ERROR: Missing condition 'b s trin g' -tests\strings.mx:27:1 ERROR: Unknown instruction 'debug aa sd $a ld \t' -tests\strings.mx:28:1 ERROR: Unknown instruction 'NASA's deepspace banger Juice' +tests\strings.mx:8:1 ERROR: Unknown instruction 'Where' +tests\strings.mx:24:1 ERROR: Unknown instruction 'a "\n"' +tests\strings.mx:25:1 ERROR: Missing condition 'b s "trin" g' +tests\strings.mx:27:1 ERROR: Unknown instruction 'debug aa "sd" $a " " "ld" " " "\t"' +tests\strings.mx:28:1 ERROR: Unknown instruction 'NASA`s deepspace "banger" Juice' +tests\strings.mx:31:1 ERROR: Only single immediate allowed 'outu "str" "lit"' +tests\strings.mx:38:1 ERROR: Target value expected 'outc' +tests\strings.mx:44:1 ERROR: Unknown instruction 'go "lou"'