bool ConstMathParse(MathEl curEl)
        {
            bool canEnd = false;

            Stack <MathEl> stack = new Stack <MathEl>();

            stack.Push(curEl);

            uint DoOp(uint left, char op, uint right)
            {
                switch (op)
                {
                case '+':
                    left += right;
                    break;

                case '-':
                    left -= right;
                    break;
                }
                return(left);
            }

            bool DoVal(uint val)
            {
                if (curEl.op != 0 && curEl.val != null)
                {
                    curEl.val = DoOp((uint)curEl.val, curEl.op, val);
                }
                else if (curEl.val == null)
                {
                    curEl.val = val;
                }
                else
                {
                    return(AddError("Error: This shouldn't happen."));
                }

                if (stack.Count == 1)
                {
                    canEnd = true;
                }

                return(true);
            }

            while (i < iMax)
            {
                uint newVal;

                switch (tokens[i].token)
                {
                case "+":
                case "-":
                    curEl.op = tokens[i].token[0];
                    canEnd   = false;
                    break;

                case ";":
                    if (canEnd)
                    {
                        return(true);
                    }
                    else
                    {
                        return(AddError("Unexpected ';'."));
                    }

                case "(":
                    if ((curEl.op == 0 && curEl.val == null) ||
                        curEl.val != null)
                    {
                        curEl = new MathEl();
                        stack.Push(curEl);
                        canEnd = false;
                    }
                    else
                    {
                        return(AddError("Unexpected '('."));
                    }
                    break;

                case ")":
                    if (stack.Count > 1)
                    {
                        stack.Pop();
                        MathEl prevEl = stack.Peek();
                        if (prevEl.op != 0)
                        {
                            prevEl.val = DoOp((uint)prevEl.val, prevEl.op, (uint)curEl.val);
                        }
                        else
                        {
                            prevEl.val = curEl.val;
                        }

                        curEl = prevEl;

                        if (stack.Count == 1)
                        {
                            canEnd = true;
                        }
                    }
                    else if (canEnd)
                    {
                        return(true);
                    }
                    else
                    {
                        return(AddError("Unexpected ')'."));
                    }
                    break;

                default:
                    if (canEnd)
                    {
                        return(true);
                    }
                    else if (ParseLiteral(tokens[i].token, out newVal))
                    {
                        if (DoVal(newVal) == false)
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        Var foundVar;
                        if (FindVar(MakeNamespace(tokens[i].token), out foundVar, consts) == false)
                        {
                            return(AddError("Can't parse this literal, or undefined constant: " + tokens[i].token));
                        }

                        if (DoVal((uint)foundVar.value) == false)
                        {
                            return(false);
                        }

                        canEnd = true;
                    }
                    break;
                }

                i++;
            }

            return(false);
        }
        public bool FoxasmCompile(string filePath)
        {
            string fileExtension = null;

            List <Token> tokens = new List <Token>();

            LexerParse(filePath, tokens);

            Function curFunction = new Function();

            entryPoint = curFunction;

            int iMax = tokens.Count;
            int i    = 0;

            if (curUnit == null)
            {
                curUnit = new UnitInfo();
            }



            bool ParseSide(out MovSide side)
            {
                side = new MovSide();

                if (tokens[i].token == "[")
                {
                    i++;
                    side.isMemoryAccess = true;
                }
                else if (StringLiteralTryParse(tokens[i].token, out side.stringValue))
                {
                    side.byteCode = ByteCode.StringLiteralFeedMe;
                    side.type     = MovSide.SideType.StringLiteral;
                    return(true);
                }

                uint immediate;

                if (RegisterTryParse(tokens[i].token, out side.byteCode, out side.width))
                {
                    side.type = MovSide.SideType.Register;
                }
                else if (ParseLiteral(tokens[i].token, out immediate))
                {
                    side.byteCode = (ByteCode)immediate;
                    side.type     = MovSide.SideType.ImmediateValue;
                }
                else
                {
                    side.stringValue = tokens[i].token;
                    side.type        = MovSide.SideType.VariableReference;
                }

                if (side.isMemoryAccess)
                {
                    i++;

                    while (i < iMax)
                    {
                        switch (tokens[i].token)
                        {
                        case "+":
                        {                                         // @TODO shouldn't allow offsets for immediate values?
                            uint val;
                            i++;
                            if (ParseLiteral(tokens[i].token, out val))
                            {
                                side.offset = (int)val;
                            }
                            else
                            {
                                return(false);
                            }
                            break;
                        }

                        case "]":
                            return(true);

                        default:
                            return(false);
                        }

                        i++;
                    }
                }

                return(true);
            }

            while (i < iMax)
            {
                Token  tok   = tokens[i];
                string token = tok.token;


                bool AddError(string message)
                {
                    outputMessages.Add(new OutputMessage
                    {
                        type     = OutputMessage.MessageType.Error,
                        message  = message,
                        token    = tok,
                        filename = filePath,
                    });
                    return(false);
                }

                /*void AddWarning(string message)
                 * {
                 *      outputMessages.Add(new OutputMessage
                 *      {
                 *              type = OutputMessage.MessageType.Warning,
                 *              message = message,
                 *              token = tok,
                 *              filename = filePath,
                 *      });
                 * }*/


                ByteCode inByte;
                if (token[0] == '.' && tokens[i + 1].token == ":")                 // .FooLabel:
                {
                    if (token == ".data")
                    {
                        i += 2;
                        break;
                    }

                    curFunction.labels.Add(new SymbolReference
                    {
                        pos    = curFunction.byteCode.Count,
                        symbol = token,
                    });
                    i += 1;
                }
                else if (token == "#bits")
                {
                    uint bitNum;
                    if (ParseLiteral(tokens[i + 1].token, out bitNum))
                    {
                        if (bitNum == 32)
                        {
                            curFunction.bits = Bits.Bits32;
                        }
                        else if (bitNum == 16)
                        {
                            curFunction.bits = Bits.Bits16;
                        }
                        else
                        {
                            return(AddError("Unsupported #bits value."));
                        }
                        i += 1;
                    }
                    else
                    {
                        AddError("Can't parse this literal.");                         // @TODO
                        return(false);
                    }
                }
                else if (token == "#Put4")
                {
                    i++;

                    curFunction.byteCode.Add(ByteCode.Put4BytesHere);

                    uint ii;

                    if (tokens[i].token == "#address")                     // @TODO cleanup
                    {
                        curFunction.byteCode.Add((ByteCode)curUnit.relativeAddress);
                    }
                    else if (ParseLiteral(tokens[i].token, out ii))
                    {
                        curFunction.byteCode.Add((ByteCode)ii);
                    }
                    else if (tokens[i].token[0] == '.')
                    {
                        curFunction.urLabelsUnresolved.Add(new UnresolvedReference()
                        {
                            symbol     = tokens[i].token,
                            pos        = curFunction.byteCode.Count,
                            token      = tok,
                            filename   = filePath,
                            isAbsolute = true,
                        });

                        curFunction.byteCode.Add(ByteCode.LabelFeedMe);
                    }
                    else
                    {
                        // @TODO cleanup
                        curFunction.urVarsUnresolved.Add(new UnresolvedReference
                        {
                            symbol   = tokens[i].token,
                            pos      = curFunction.byteCode.Count,
                            token    = tok,
                            filename = filePath,
                        });

                        curFunction.byteCode.Add(ByteCode.VarFeedMe);
                    }
                }
                else if (token == "#Align")
                {
                    i++;

                    curFunction.byteCode.Add(ByteCode.Align);

                    uint ii;

                    if (ParseLiteral(tokens[i].token, out ii))
                    {
                        curFunction.byteCode.Add((ByteCode)ii);
                    }
                    else
                    {
                        return(AddError("#Align needs a numeric literal."));
                    }
                }
                else if (token == "#WriteData")
                {
                    curFunction.byteCode.Add(ByteCode.WriteDataHere);
                }
                else if (token == "#format")
                {
                    UnitInfo.Format format;

                    if (Enum.TryParse(tokens[i + 1].token, out format))
                    {
                        //AddWarning("#format isn't implemented – is always Flat. Ignoring for now."); // @TODO
                        i += 1;
                    }
                    else
                    {
                        return(AddError("Invalid #format."));
                    }
                }
                else if (token == "#address")
                {
                    if (ParseLiteral(tokens[i + 1].token, out curUnit.relativeAddress))
                    {
                        i += 1;
                    }
                    else
                    {
                        AddError("Can't parse this literal.");
                        return(false);
                    }
                }
                else if (token == "#extension")
                {
                    if (StringLiteralTryParse(tokens[i + 1].token, out fileExtension) == false)
                    {
                        return(AddError("Invalid string literal."));
                    }

                    i += 1;
                }
                else if (Enum.TryParse(token, out inByte))
                {
                    int width = 0;

                    switch (inByte)
                    {
                    case ByteCode.Ret:
                        curFunction.byteCode.Add(ByteCode.RetNear);
                        break;

                    case ByteCode.Hlt:
                    case ByteCode.Cli:
                    case ByteCode.LodsB:
                        curFunction.byteCode.Add(inByte);
                        break;

                    case ByteCode.Int:
                    {
                        uint ii;
                        if (ParseLiteral(tokens[i + 1].token, out ii))
                        {
                            curFunction.byteCode.Add(ByteCode.IntImmB);
                            curFunction.byteCode.Add((ByteCode)ii);
                            i += 1;
                        }
                        else
                        {
                            AddError("Can't parse this literal.");                                             // @TODO
                            return(false);
                        }
                        break;
                    }

                    case ByteCode.MovB:
                        width = 1;
                        goto MovCommon;

                    case ByteCode.MovL:
                        width = 4;
                        goto MovCommon;

                    case ByteCode.Mov:
                        width = 0;
                        goto MovCommon;

MovCommon:
                        {
                            MovSide left, right;

                            i++;
                            if (ParseSide(out left) == false)
                            {
                                return(AddError("Can't parse left side."));                                        // @TODO better errors?
                            }
                            if (left.isMemoryAccess == false)
                            {
                                if (left.type == MovSide.SideType.StringLiteral)
                                {
                                    return(AddError("Can't Mov to string literal."));
                                }
                                else if (left.type == MovSide.SideType.ImmediateValue)
                                {
                                    return(AddError("Can't Mov to immediate value."));
                                }
                            }
                            // @TODO variable references on left side


                            i++;
                            if (tokens[i].token != "=" && tokens[i].token != ",")
                            {
                                return(AddError("Mov foo = bar."));                                        // @TODO better errors?
                            }
                            i++;
                            if (ParseSide(out right) == false)
                            {
                                return(AddError("Can't parse right side."));                                        // @TODO better errors?
                            }
                            if (left.isMemoryAccess && right.isMemoryAccess)
                            {
                                return(AddError("Can't access memory on both sides."));
                            }

                            if (right.type == MovSide.SideType.StringLiteral)
                            {
                                curFunction.literalReferences.Add(new SymbolReference
                                {
                                    pos    = curFunction.byteCode.Count + 2,                                          // @TODO Might not be true?
                                    symbol = right.stringValue,
                                });
                            }
                            else if (right.stringValue != null)
                            {
                                curFunction.urVarsUnresolved.Add(new UnresolvedReference
                                {
                                    symbol   = right.stringValue,
                                    pos      = curFunction.byteCode.Count + 2,                                        // @TODO Might not be true?
                                    token    = tok,
                                    filename = filePath,
                                });
                            }


                            if (left.isMemoryAccess)                                     // @TODO cleanup
                            {
                                if (left.type != MovSide.SideType.Register)
                                {
                                    if (right.type == MovSide.SideType.Register)
                                    {
                                        if (width == 0)
                                        {
                                            width = right.width;
                                        }

                                        if (width == 4)
                                        {
                                            curFunction.byteCode.Add(ByteCode.MovRmRL);
                                        }
                                        else if (width == 2)
                                        {
                                            curFunction.byteCode.Add(ByteCode.MovRmRW);
                                        }
                                        else if (width == 1)
                                        {
                                            curFunction.byteCode.Add(ByteCode.MovRmRB);
                                        }
                                        else
                                        {
                                            return(AddError("No register size detected. This shouldn't happen! (#1)"));
                                        }

                                        curFunction.byteCode.Add(ByteCode.RMemImm);

                                        // 89 mov order: to, from
                                        curFunction.byteCode.Add(right.byteCode);
                                        curFunction.byteCode.Add(left.byteCode);
                                    }
                                    else
                                    {
                                        return(AddError("Moving immediate value into immediate memory not supported."));
                                    }
                                }
                                else if (right.type == MovSide.SideType.Register)
                                {
                                    if (width == 0)
                                    {
                                        width = right.width;
                                    }

                                    if (width == 4)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmRL);
                                    }
                                    else if (width == 2)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmRW);
                                    }
                                    else if (width == 1)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmRB);
                                    }
                                    else
                                    {
                                        return(AddError("No register size detected. This shouldn't happen! (#1)"));
                                    }

                                    if (left.byteCode == ByteCode.Esp)                                             // @TODO
                                    {
                                        return(AddError("%Esp memory access isn't supported."));
                                    }
                                    else if (left.offset != 0 || left.byteCode == ByteCode.Ebp)
                                    {
                                        curFunction.byteCode.Add(ByteCode.RRMemOffset1);
                                    }
                                    else
                                    {
                                        curFunction.byteCode.Add(ByteCode.RRMem);
                                    }

                                    // 89 mov order: to, from
                                    curFunction.byteCode.Add(right.byteCode);
                                    curFunction.byteCode.Add(left.byteCode);

                                    if (left.offset != 0 || left.byteCode == ByteCode.Ebp)
                                    {
                                        curFunction.byteCode.Add((ByteCode)left.offset);
                                    }
                                }
                                else
                                {
                                    if (width == 4)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmImmL);
                                    }
                                    else if (width == 2)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmImmW);
                                    }
                                    else if (width == 1)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmImmB);
                                    }
                                    else
                                    {
                                        return(AddError("Indeterminant operand size on right side."));                                                // @TODO better error message
                                    }
                                    curFunction.byteCode.Add(ByteCode.RRMem);

                                    curFunction.byteCode.Add(left.byteCode);
                                    curFunction.byteCode.Add(right.byteCode);
                                }
                            }
                            else if (right.isMemoryAccess)
                            {
                                if (right.type != MovSide.SideType.Register)
                                {
                                    if (left.type == MovSide.SideType.Register)
                                    {
                                        if (width == 0)
                                        {
                                            width = left.width;
                                        }

                                        if (width == 4)
                                        {
                                            curFunction.byteCode.Add(ByteCode.MovRRmL);
                                        }
                                        else if (width == 2)
                                        {
                                            curFunction.byteCode.Add(ByteCode.MovRRmW);
                                        }
                                        else if (width == 1)
                                        {
                                            curFunction.byteCode.Add(ByteCode.MovRRmB);
                                        }
                                        else
                                        {
                                            return(AddError("No register size detected. This shouldn't happen! (#1)"));
                                        }

                                        curFunction.byteCode.Add(ByteCode.RMemImm);

                                        curFunction.byteCode.Add(left.byteCode);
                                        curFunction.byteCode.Add(right.byteCode);
                                    }
                                    else
                                    {
                                        return(AddError("Moving immediate value into immediate memory not supported."));
                                    }
                                }
                                else if (left.type == MovSide.SideType.Register)
                                {
                                    if (width == 0)
                                    {
                                        width = left.width;
                                    }

                                    if (width == 4)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRRmL);
                                    }
                                    else if (width == 2)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRRmW);
                                    }
                                    else if (width == 1)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRRmB);
                                    }
                                    else
                                    {
                                        return(AddError("No register size detected. This shouldn't happen! (#2)")); // @TODO cleanup numbers
                                    }
                                    if (right.byteCode == ByteCode.Esp)                                             // @TODO
                                    {
                                        return(AddError("%Esp memory access isn't supported."));
                                    }
                                    else if (right.offset != 0 || right.byteCode == ByteCode.Ebp)
                                    {
                                        curFunction.byteCode.Add(ByteCode.RRMemOffset1);
                                    }
                                    else
                                    {
                                        curFunction.byteCode.Add(ByteCode.RRMem);
                                    }

                                    curFunction.byteCode.Add(left.byteCode);
                                    curFunction.byteCode.Add(right.byteCode);

                                    if (right.offset != 0 || right.byteCode == ByteCode.Ebp)
                                    {
                                        curFunction.byteCode.Add((ByteCode)right.offset);
                                    }
                                }
                                else
                                {
                                    return(AddError("This probably shouldn't happen?"));
                                }
                            }
                            else
                            {
                                if (width == 0)
                                {
                                    width = left.width;
                                }

                                if (right.type == MovSide.SideType.Register)
                                {
                                    if (width != right.width)
                                    {
                                        return(AddError("Operand size doesn't match."));
                                    }

                                    if (width == 4)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmRL);
                                    }
                                    else if (width == 2)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmRW);
                                    }
                                    else if (width == 1)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRmRB);
                                    }
                                    else
                                    {
                                        return(AddError("No register size detected. This shouldn't happen! (#3)"));                                                // @TODO cleanup numbers
                                    }
                                    curFunction.byteCode.Add(ByteCode.RToR);

                                    // 89 mov order: to, from
                                    curFunction.byteCode.Add(right.byteCode);
                                    curFunction.byteCode.Add(left.byteCode);
                                }
                                else
                                {
                                    if (width == 4)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRImmL);
                                    }
                                    else if (width == 2)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRImmW);
                                    }
                                    else if (width == 1)
                                    {
                                        curFunction.byteCode.Add(ByteCode.MovRImmB);
                                    }
                                    else
                                    {
                                        return(AddError("Indeterminant operand size on left side. (#2)"));                                                // @TODO better error message
                                    }
                                    curFunction.byteCode.Add(left.byteCode);
                                    curFunction.byteCode.Add(right.byteCode);
                                }
                            }


                            break;
                        }

                    case ByteCode.PushL:
                    case ByteCode.Push:
                    {
                        string   t = tokens[i + 1].token;
                        string   literal;
                        ByteCode register;

                        if (StringLiteralTryParse(t, out literal))
                        {
                            if (curFunction.bits == Bits.Bits16)
                            {
                                curFunction.byteCode.Add(ByteCode.PushImmW);
                            }
                            else
                            {
                                curFunction.byteCode.Add(ByteCode.PushImmL);
                            }
                            curFunction.byteCode.Add(ByteCode.StringLiteralFeedMe);
                            curFunction.literalReferences.Add(new SymbolReference
                                {
                                    pos    = curFunction.byteCode.Count - 1,
                                    symbol = literal,
                                });
                            i += 1;
                        }
                        else if (RegisterTryParse(t, out register, out width))
                        {
                            curFunction.byteCode.Add(ByteCode.PushRL);
                            curFunction.byteCode.Add(register);
                            i += 1;
                        }
                        else
                        {
                            return(AddError("Not a string literal or register: " + t));
                        }

                        break;
                    }

                    case ByteCode.Call:
                        if (curFunction.bits == Bits.Bits16)
                        {
                            curFunction.byteCode.Add(ByteCode.CallRelW);
                        }
                        else
                        {
                            curFunction.byteCode.Add(ByteCode.CallRelL);
                        }
                        curFunction.byteCode.Add(ByteCode.LabelFeedMe);

                        curFunction.urLabelsUnresolved.Add(new UnresolvedReference
                        {
                            symbol   = tokens[i + 1].token,
                            pos      = curFunction.byteCode.Count - 1,
                            token    = tokens[i + 1],
                            filename = filePath,
                        });

                        i += 1;
                        break;

                    case ByteCode.PopW:
                    case ByteCode.Pop:
                    {
                        ByteCode left;
                        width = 4;

                        if (inByte == ByteCode.PopW)
                        {
                            width = 2;
                        }

                        string t = tokens[i + 1].token;
                        if (RegisterTryParse(t, out left, out width) == false)                         // @TODO check width matches
                        {
                            AddError("Unknown register.");                                             // @TODO
                            return(false);
                        }

                        if (width == 2)
                        {
                            curFunction.byteCode.Add(ByteCode.PopRW);
                        }
                        else
                        {
                            curFunction.byteCode.Add(ByteCode.PopRL);
                        }
                        curFunction.byteCode.Add(left);

                        i += 1;

                        break;
                    }

                    case ByteCode.CmpB:
                    {
                        bool     isLeftMem = false;
                        ByteCode left;

                        string t;
                        if (tokens[i + 1].token == "[" && tokens[i + 3].token == "]")
                        {
                            t         = tokens[i + 2].token;
                            isLeftMem = true;
                            i        += 3;
                        }
                        else
                        {
                            t  = tokens[i + 1].token;
                            i += 1;
                        }

                        if (RegisterTryParse(t, out left, out width) == false)
                        {
                            AddError("Unknown register.");                                             // @TODO
                            return(false);
                        }

                        if (tokens[i + 1].token != ",")
                        {
                            AddError("CmpB foo, bar.");                                             // @TODO
                            return(false);
                        }

                        uint ii;
                        if (ParseLiteral(tokens[i + 2].token, out ii))
                        {
                            if (isLeftMem)
                            {
                                curFunction.byteCode.Add(ByteCode.CmpRMemImmB);
                            }
                            else
                            {
                                curFunction.byteCode.Add(ByteCode.CmpRImmB);                                                 // @TODO check register size
                            }
                            curFunction.byteCode.Add(left);
                            curFunction.byteCode.Add((ByteCode)ii);
                        }
                        else
                        {
                            AddError("Can't parse this literal.");                                             // @TODO
                            return(false);
                        }

                        i += 2;

                        break;
                    }

                    case ByteCode.Jmp:
                        curFunction.byteCode.Add(ByteCode.JmpRelB);
                        goto JmpCommon;

                    case ByteCode.Je:
                        curFunction.byteCode.Add(ByteCode.JeRelB);
                        goto JmpCommon;

                    case ByteCode.Jne:
                        curFunction.byteCode.Add(ByteCode.JneRelB);
JmpCommon:
                        {
                            curFunction.byteCode.Add(ByteCode.LabelFeedMe);

                            token = tokens[i + 1].token;

                            curFunction.urLabelsUnresolved.Add(new UnresolvedReference()
                            {
                                symbol   = token,
                                pos      = curFunction.byteCode.Count - 1,
                                token    = tok,
                                filename = filePath,
                            });

                            i += 1;
                            break;
                        }

                    case ByteCode.Inc:
                    {
                        ByteCode left;
                        string   t = tokens[i + 1].token;

                        if (RegisterTryParse(t, out left, out width) == false)
                        {
                            AddError("Unknown register.");                                             // @TODO
                            return(false);
                        }

                        curFunction.byteCode.Add(ByteCode.IncR);
                        curFunction.byteCode.Add(left);

                        i += 1;
                        break;
                    }

                    default:
                        AddError("Instruction not implemented.");
                        return(false);
                    }
                }
                else
                {
                    AddError("Unknown instruction.");
                    return(false);
                }

                i++;
            }

            #region .data segment
            while (i < iMax)
            {
                Token  tok   = tokens[i];
                string token = tok.token;


                bool AddError(string message)                 // @TODO @cleanup
                {
                    outputMessages.Add(new OutputMessage
                    {
                        type     = OutputMessage.MessageType.Error,
                        message  = message,
                        token    = tok,
                        filename = filePath,
                    });
                    return(false);
                }

                if (tokens[i + 1].token == "=")
                {
                    i += 2;

                    bool canEnd = false;

                    Stack <MathEl> stack = new Stack <MathEl>();
                    MathEl         curEl = new MathEl();
                    stack.Push(curEl);

                    uint DoOp(uint left, char op, uint right)
                    {
                        switch (op)
                        {
                        case '+':
                            left += right;
                            break;

                        case '-':
                            left -= right;
                            break;
                        }
                        return(left);
                    }

                    bool DoVar(uint val)
                    {
                        if (curEl.op != 0 && curEl.val != null)
                        {
                            curEl.val = DoOp((uint)curEl.val, curEl.op, val);
                        }
                        else if (curEl.val == null)
                        {
                            curEl.val = val;
                        }
                        else
                        {
                            return(AddError("Error: This shouldn't happen."));
                        }

                        if (stack.Count == 1)
                        {
                            canEnd = true;
                        }

                        return(true);
                    }

                    while (i < iMax)
                    {
                        uint newVal;

                        if (ParseLiteral(tokens[i].token, out newVal))
                        {
                            if (DoVar(newVal) == false)
                            {
                                return(false);
                            }
                        }
                        else
                        {
                            switch (tokens[i].token)
                            {
                            case "+":
                            case "-":
                                curEl.op = tokens[i].token[0];
                                canEnd   = false;
                                break;

                            case "(":
                                if ((curEl.op == 0 && curEl.val == null) ||
                                    curEl.val != null)
                                {
                                    curEl = new MathEl();
                                    stack.Push(curEl);
                                    canEnd = false;
                                }
                                else
                                {
                                    return(AddError("Unexpected '('."));
                                }
                                break;

                            case ")":
                                if (stack.Count > 1)
                                {
                                    stack.Pop();
                                    MathEl prevEl = stack.Peek();
                                    if (prevEl.op != 0)
                                    {
                                        prevEl.val = DoOp((uint)prevEl.val, prevEl.op, (uint)curEl.val);
                                    }
                                    else
                                    {
                                        prevEl.val = curEl.val;
                                    }

                                    curEl = prevEl;

                                    if (stack.Count == 1)
                                    {
                                        canEnd = true;
                                    }
                                }
                                else
                                {
                                    return(AddError("Unexpected ')'."));
                                }
                                break;

                            default:
                                if (canEnd)
                                {
                                    goto DoEnd;
                                }
                                else
                                {
                                    Var foundVar;
                                    FindVar(tokens[i].token, out foundVar, vars);

                                    if (foundVar == null)
                                    {
                                        return(AddError("Can't parse this literal, or undefined constant: " + tokens[i].token));
                                    }

                                    if (DoVar((uint)foundVar.value) == false)
                                    {
                                        return(false);
                                    }

                                    canEnd = true;
                                }
                                break;
                            }
                        }

                        i++;

                        continue;
DoEnd:
                        break;
                    }

                    vars.Add(new Var
                    {
                        symbol = token,
                        value  = curEl.val
                    });
                }
                else
                {
                    AddError("Foo = bar");                     // @TODO
                    return(false);
                }
            }
            #endregion

            foreach (var r in curFunction.urLabelsUnresolved) // @TODO @cleanup
            {
                bool AddError(string message)                 // @TODO @cleanup
                {
                    outputMessages.Add(new OutputMessage
                    {
                        type     = OutputMessage.MessageType.Error,
                        message  = message,
                        token    = r.token,
                        filename = filePath,
                    });
                    return(false);
                }

                SymbolReference foundSym = null;
                foreach (var sym in curFunction.labels)
                {
                    if (sym.symbol == r.symbol)
                    {
                        foundSym = sym;
                        break;
                    }
                }

                if (foundSym == null)
                {
                    return(AddError("Label not found."));
                }

                r.reference = foundSym;
                if (r.isAbsolute)
                {
                    curFunction.byteCode[r.pos] = (ByteCode)(foundSym.pos);
                }
                else
                {
                    curFunction.byteCode[r.pos] = (ByteCode)(foundSym.pos - (r.pos + 1));
                }
            }

            foreach (var r in curFunction.urVarsUnresolved)   // @TODO @cleanup
            {
                bool AddError(string message)                 // @TODO @cleanup
                {
                    outputMessages.Add(new OutputMessage
                    {
                        type     = OutputMessage.MessageType.Error,
                        message  = message,
                        token    = r.token,
                        filename = filePath,
                    });
                    return(false);
                }

                Var foundVar;
                FindVar(r.symbol, out foundVar, vars);

                if (foundVar == null)
                {
                    return(AddError("Undefined: " + r.symbol));
                }

                curFunction.byteCode[r.pos] = (ByteCode)(foundVar.value);
            }

            if (output == null)
            {
                if (fileExtension == null)
                {
                    fileExtension = ".bin";
                }

                output = Path.ChangeExtension(filePath, fileExtension);
            }

            if (BytecodeCompileToBinary(output) == false)
            {
                return(false);                // @TODO error
            }
            return(true);
        }