2209 lines
84 KiB
C
2209 lines
84 KiB
C
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2017 - ROLI Ltd.
|
||
|
|
||
|
JUCE is an open source library subject to commercial or open-source
|
||
|
licensing.
|
||
|
|
||
|
The code included in this file is provided under the terms of the ISC license
|
||
|
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||
|
To use, copy, modify, and/or distribute this software for any purpose with or
|
||
|
without fee is hereby granted provided that the above copyright notice and
|
||
|
this permission notice appear in all copies.
|
||
|
|
||
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||
|
DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
#if JUCE_MSVC
|
||
|
#pragma warning (push)
|
||
|
#pragma warning (disable: 4702)
|
||
|
#endif
|
||
|
|
||
|
namespace littlefoot
|
||
|
{
|
||
|
|
||
|
/**
|
||
|
This class compiles littlefoot source code into a littlefoot::Program object
|
||
|
which can be executed by a littlefoot::Runner.
|
||
|
|
||
|
@tags{Blocks}
|
||
|
*/
|
||
|
struct Compiler
|
||
|
{
|
||
|
Compiler() = default;
|
||
|
|
||
|
/** Gives the compiler a zero-terminated list of native function prototypes to
|
||
|
use when parsing function calls.
|
||
|
*/
|
||
|
void addNativeFunctions (const char* const* functionPrototypes)
|
||
|
{
|
||
|
for (; *functionPrototypes != nullptr; ++functionPrototypes)
|
||
|
nativeFunctions.add (NativeFunction (*functionPrototypes, nullptr));
|
||
|
}
|
||
|
|
||
|
/** Tells the compiler to use the list of native function prototypes from
|
||
|
this littlefoot::Runner object.
|
||
|
*/
|
||
|
template <typename RunnerType>
|
||
|
void addNativeFunctions (const RunnerType& runner)
|
||
|
{
|
||
|
for (int i = 0; i < runner.getNumNativeFunctions(); ++i)
|
||
|
nativeFunctions.add (runner.getNativeFunction (i));
|
||
|
}
|
||
|
|
||
|
/** Compiles a littlefoot program.
|
||
|
If there's an error, this returns it, otherwise the compiled bytecode is
|
||
|
placed in the compiledObjectCode member.
|
||
|
*/
|
||
|
Result compile (const String& sourceCode, uint32 defaultHeapSize)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
SyntaxTreeBuilder stb (sourceCode, nativeFunctions, defaultHeapSize);
|
||
|
stb.compile();
|
||
|
stb.simplify();
|
||
|
|
||
|
compiledObjectCode.clear();
|
||
|
|
||
|
CodeGenerator codeGen (compiledObjectCode, stb);
|
||
|
codeGen.generateCode (stb.blockBeingParsed, stb.heapSizeRequired);
|
||
|
return Result::ok();
|
||
|
}
|
||
|
catch (String error)
|
||
|
{
|
||
|
return Result::fail (error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** After a successful compilation, this returns the finished Program. */
|
||
|
Program getCompiledProgram() const noexcept
|
||
|
{
|
||
|
return Program (compiledObjectCode.begin(), (uint32) compiledObjectCode.size());
|
||
|
}
|
||
|
|
||
|
/** After a successful call to compile(), this contains the bytecode generated.
|
||
|
A littlefoot::Program object can be created directly from this array.
|
||
|
*/
|
||
|
Array<uint8> compiledObjectCode;
|
||
|
|
||
|
private:
|
||
|
|
||
|
#ifndef DOXYGEN
|
||
|
|
||
|
struct Statement;
|
||
|
struct Expression;
|
||
|
struct BlockStatement;
|
||
|
struct Function;
|
||
|
struct AllocatedObject { virtual ~AllocatedObject() noexcept {} };
|
||
|
using StatementPtr = Statement*;
|
||
|
using ExpPtr = Expression*;
|
||
|
using BlockPtr = BlockStatement*;
|
||
|
using TokenType = const char*;
|
||
|
|
||
|
#define LITTLEFOOT_KEYWORDS(X) \
|
||
|
X(if_, "if") X(else_, "else") X(do_, "do") \
|
||
|
X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") \
|
||
|
X(void_, "void") X(int_, "int") X(float_, "float") X(bool_, "bool") \
|
||
|
X(return_, "return") X(true_, "true") X(false_, "false") X(const_, "const")
|
||
|
|
||
|
#define LITTLEFOOT_OPERATORS(X) \
|
||
|
X(semicolon, ";") X(dot, ".") X(comma, ",") X(hash, "#") \
|
||
|
X(openParen, "(") X(closeParen, ")") X(openBrace, "{") X(closeBrace, "}") \
|
||
|
X(openBracket, "[") X(closeBracket, "]") X(colon, ":") X(question, "?") \
|
||
|
X(equals, "==") X(assign, "=") X(notEquals, "!=") X(logicalNot, "!") \
|
||
|
X(plusEquals, "+=") X(plusplus, "++") X(plus, "+") \
|
||
|
X(minusEquals, "-=") X(minusminus, "--") X(minus, "-") \
|
||
|
X(timesEquals, "*=") X(times, "*") X(divideEquals, "/=") X(divide, "/") \
|
||
|
X(moduloEquals, "%=") X(modulo, "%") X(xorEquals, "^=") X(bitwiseXor, "^") X(bitwiseNot, "~") \
|
||
|
X(andEquals, "&=") X(logicalAnd, "&&") X(bitwiseAnd, "&") \
|
||
|
X(orEquals, "|=") X(logicalOr, "||") X(bitwiseOr, "|") \
|
||
|
X(leftShiftEquals, "<<=") X(lessThanOrEqual, "<=") X(leftShift, "<<") X(lessThan, "<") \
|
||
|
X(rightShiftUnsigned, ">>>") X(rightShiftEquals, ">>=") X(rightShift, ">>") X(greaterThanOrEqual, ">=") X(greaterThan, ">")
|
||
|
|
||
|
struct Token
|
||
|
{
|
||
|
#define DECLARE_LITTLEFOOT_TOKEN(name, str) static constexpr const char* name = str;
|
||
|
LITTLEFOOT_KEYWORDS (DECLARE_LITTLEFOOT_TOKEN)
|
||
|
LITTLEFOOT_OPERATORS (DECLARE_LITTLEFOOT_TOKEN)
|
||
|
DECLARE_LITTLEFOOT_TOKEN (eof, "$eof")
|
||
|
DECLARE_LITTLEFOOT_TOKEN (literal, "$literal")
|
||
|
DECLARE_LITTLEFOOT_TOKEN (identifier, "$identifier")
|
||
|
};
|
||
|
|
||
|
Array<NativeFunction> nativeFunctions;
|
||
|
|
||
|
//==============================================================================
|
||
|
struct CodeLocation
|
||
|
{
|
||
|
CodeLocation (const String& code) noexcept : program (code), location (program.getCharPointer()) {}
|
||
|
CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {}
|
||
|
|
||
|
[[noreturn]] void throwError (const String& message) const
|
||
|
{
|
||
|
int col = 1, line = 1;
|
||
|
|
||
|
for (auto i = program.getCharPointer(); i < location && ! i.isEmpty(); ++i)
|
||
|
{
|
||
|
++col;
|
||
|
if (*i == '\n') { col = 1; ++line; }
|
||
|
}
|
||
|
|
||
|
throw "Line " + String (line) + ", column " + String (col) + " : " + message;
|
||
|
}
|
||
|
|
||
|
String program;
|
||
|
String::CharPointerType location;
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
struct TokenIterator
|
||
|
{
|
||
|
TokenIterator (const String& code) : location (code), p (code.getCharPointer()) { skip(); }
|
||
|
|
||
|
TokenType skip()
|
||
|
{
|
||
|
skipWhitespaceAndComments();
|
||
|
location.location = p;
|
||
|
auto last = currentType;
|
||
|
currentType = matchNextToken();
|
||
|
return last;
|
||
|
}
|
||
|
|
||
|
void match (TokenType expected)
|
||
|
{
|
||
|
if (currentType != expected)
|
||
|
throwErrorExpecting (getTokenDescription (expected));
|
||
|
|
||
|
skip();
|
||
|
}
|
||
|
|
||
|
bool matchIf (TokenType expected) { if (currentType == expected) { skip(); return true; } return false; }
|
||
|
|
||
|
template <typename... Args>
|
||
|
bool matchesAny (TokenType t1, Args... others) const noexcept { return currentType == t1 || matchesAny (others...); }
|
||
|
bool matchesAny (TokenType t1) const noexcept { return currentType == t1; }
|
||
|
|
||
|
CodeLocation location;
|
||
|
TokenType currentType;
|
||
|
var currentValue;
|
||
|
|
||
|
void throwErrorExpecting (const String& expected) { location.throwError ("Found " + getTokenDescription (currentType) + " when expecting " + expected); }
|
||
|
|
||
|
private:
|
||
|
String::CharPointerType p;
|
||
|
|
||
|
static bool isIdentifierStart (juce_wchar c) noexcept { return CharacterFunctions::isLetter (c) || c == '_'; }
|
||
|
static bool isIdentifierBody (juce_wchar c) noexcept { return CharacterFunctions::isLetterOrDigit (c) || c == '_'; }
|
||
|
|
||
|
TokenType matchNextToken()
|
||
|
{
|
||
|
if (isIdentifierStart (*p))
|
||
|
{
|
||
|
auto end = p;
|
||
|
while (isIdentifierBody (*++end)) {}
|
||
|
|
||
|
const size_t len = (size_t) (end - p);
|
||
|
#define LITTLEFOOT_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (Token::name, len)) return Token::name;
|
||
|
LITTLEFOOT_KEYWORDS (LITTLEFOOT_COMPARE_KEYWORD)
|
||
|
|
||
|
currentValue = String (p, end); p = end;
|
||
|
return Token::identifier;
|
||
|
}
|
||
|
|
||
|
if (p.isDigit())
|
||
|
{
|
||
|
if (parseHexLiteral() || parseFloatLiteral() || parseOctalLiteral() || parseDecimalLiteral())
|
||
|
return Token::literal;
|
||
|
|
||
|
location.throwError ("Syntax error in numeric constant");
|
||
|
}
|
||
|
|
||
|
if (parseStringLiteral (*p) || (*p == '.' && parseFloatLiteral()))
|
||
|
return Token::literal;
|
||
|
|
||
|
#define LITTLEFOOT_COMPARE_OPERATOR(name, str) if (matchToken (Token::name, sizeof (str) - 1)) return Token::name;
|
||
|
LITTLEFOOT_OPERATORS (LITTLEFOOT_COMPARE_OPERATOR)
|
||
|
|
||
|
if (! p.isEmpty())
|
||
|
location.throwError ("Unexpected character '" + String::charToString (*p) + "' in source");
|
||
|
|
||
|
return Token::eof;
|
||
|
}
|
||
|
|
||
|
bool matchToken (TokenType name, const size_t len) noexcept
|
||
|
{
|
||
|
if (p.compareUpTo (CharPointer_ASCII (name), (int) len) != 0) return false;
|
||
|
p += (int) len; return true;
|
||
|
}
|
||
|
|
||
|
void skipWhitespaceAndComments()
|
||
|
{
|
||
|
for (;;)
|
||
|
{
|
||
|
p = p.findEndOfWhitespace();
|
||
|
|
||
|
if (*p == '/')
|
||
|
{
|
||
|
auto c2 = p[1];
|
||
|
|
||
|
if (c2 == '/') { p = CharacterFunctions::find (p, (juce_wchar) '\n'); continue; }
|
||
|
|
||
|
if (c2 == '*')
|
||
|
{
|
||
|
location.location = p;
|
||
|
p = CharacterFunctions::find (p + 2, CharPointer_ASCII ("*/"));
|
||
|
if (p.isEmpty()) location.throwError ("Unterminated '/*' comment");
|
||
|
p += 2; continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool parseStringLiteral (juce_wchar quoteType)
|
||
|
{
|
||
|
if (quoteType != '"' && quoteType != '\'')
|
||
|
return false;
|
||
|
|
||
|
auto r = JSON::parseQuotedString (p, currentValue);
|
||
|
if (r.failed()) location.throwError (r.getErrorMessage());
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool parseHexLiteral()
|
||
|
{
|
||
|
if (*p != '0' || (p[1] != 'x' && p[1] != 'X')) return false;
|
||
|
|
||
|
auto t = ++p;
|
||
|
auto v = CharacterFunctions::getHexDigitValue (*++t);
|
||
|
if (v < 0) return false;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
auto digit = CharacterFunctions::getHexDigitValue (*++t);
|
||
|
if (digit < 0) break;
|
||
|
v = v * 16 + digit;
|
||
|
}
|
||
|
|
||
|
currentValue = v; p = t;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool parseFloatLiteral()
|
||
|
{
|
||
|
int numDigits = 0;
|
||
|
auto t = p;
|
||
|
while (t.isDigit()) { ++t; ++numDigits; }
|
||
|
|
||
|
const bool hasPoint = (*t == '.');
|
||
|
|
||
|
if (hasPoint)
|
||
|
while ((++t).isDigit()) ++numDigits;
|
||
|
|
||
|
if (numDigits == 0)
|
||
|
return false;
|
||
|
|
||
|
auto c = *t;
|
||
|
const bool hasExponent = (c == 'e' || c == 'E');
|
||
|
|
||
|
if (hasExponent)
|
||
|
{
|
||
|
c = *++t;
|
||
|
if (c == '+' || c == '-') ++t;
|
||
|
if (! t.isDigit()) return false;
|
||
|
while ((++t).isDigit()) {}
|
||
|
}
|
||
|
|
||
|
if (! (hasExponent || hasPoint)) return false;
|
||
|
|
||
|
currentValue = CharacterFunctions::getDoubleValue (p); p = t;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool parseOctalLiteral()
|
||
|
{
|
||
|
auto t = p;
|
||
|
int64 v = *t - '0';
|
||
|
if (v != 0) return false; // first digit of octal must be 0
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
auto digit = (int) (*++t - '0');
|
||
|
if (isPositiveAndBelow (digit, 8)) v = v * 8 + digit;
|
||
|
else if (isPositiveAndBelow (digit, 10)) location.throwError ("Decimal digit in octal constant");
|
||
|
else break;
|
||
|
}
|
||
|
|
||
|
currentValue = v; p = t;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool parseDecimalLiteral()
|
||
|
{
|
||
|
int64 v = 0;
|
||
|
|
||
|
for (;; ++p)
|
||
|
{
|
||
|
auto digit = (int) (*p - '0');
|
||
|
if (isPositiveAndBelow (digit, 10)) v = v * 10 + digit;
|
||
|
else break;
|
||
|
}
|
||
|
|
||
|
currentValue = v;
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
//==============================================================================
|
||
|
struct SyntaxTreeBuilder : private TokenIterator
|
||
|
{
|
||
|
SyntaxTreeBuilder (const String& code, const Array<NativeFunction>& nativeFns, uint32 defaultHeapSize)
|
||
|
: TokenIterator (code), nativeFunctions (nativeFns), heapSizeRequired (defaultHeapSize) {}
|
||
|
|
||
|
void compile()
|
||
|
{
|
||
|
blockBeingParsed = allocate<BlockStatement> (location, nullptr, nullptr, false);
|
||
|
|
||
|
while (currentType != Token::eof)
|
||
|
{
|
||
|
if (matchIf (Token::hash))
|
||
|
{
|
||
|
parseCompilerDirective();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const bool isConstVariable = matchIf (Token::const_);
|
||
|
|
||
|
if (! matchesAnyTypeOrVoid())
|
||
|
throwErrorExpecting ("a global variable or function");
|
||
|
|
||
|
auto type = tokenToType (skip());
|
||
|
auto name = parseIdentifier();
|
||
|
|
||
|
if (matchIf (Token::openParen))
|
||
|
{
|
||
|
if (isConstVariable)
|
||
|
location.throwError ("Return type of a function cannot be const");
|
||
|
|
||
|
parseFunctionDeclaration (type, name);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (type == Type::void_)
|
||
|
location.throwError ("A variable type cannot be 'void'");
|
||
|
|
||
|
parseGlobalVariableDeclaraion (isConstVariable, type, name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void simplify()
|
||
|
{
|
||
|
for (auto f : functions)
|
||
|
f->block->simplify (*this);
|
||
|
}
|
||
|
|
||
|
const Function* findFunction (FunctionID functionID) const noexcept
|
||
|
{
|
||
|
for (auto f : functions)
|
||
|
if (f->functionID == functionID)
|
||
|
return f;
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
const NativeFunction* findNativeFunction (FunctionID functionID) const noexcept
|
||
|
{
|
||
|
for (auto& f : nativeFunctions)
|
||
|
if (f.functionID == functionID)
|
||
|
return &f;
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
BlockPtr blockBeingParsed = nullptr;
|
||
|
Array<Function*> functions;
|
||
|
const Array<NativeFunction>& nativeFunctions;
|
||
|
uint32 heapSizeRequired;
|
||
|
|
||
|
template <typename Type, typename... Args>
|
||
|
Type* allocate (Args... args) { auto o = new Type (args...); allAllocatedObjects.add (o); return o; }
|
||
|
|
||
|
private:
|
||
|
OwnedArray<AllocatedObject> allAllocatedObjects;
|
||
|
|
||
|
//==============================================================================
|
||
|
void parseCompilerDirective()
|
||
|
{
|
||
|
auto name = parseIdentifier();
|
||
|
|
||
|
if (name == "heapsize")
|
||
|
{
|
||
|
match (Token::colon);
|
||
|
heapSizeRequired = (((uint32) parseIntegerLiteral()) + 3) & ~3u;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
location.throwError ("Unknown compiler directive");
|
||
|
}
|
||
|
|
||
|
void parseGlobalVariableDeclaraion (bool isConst, Type type, String name)
|
||
|
{
|
||
|
for (;;)
|
||
|
{
|
||
|
int arraySize = matchIf (Token::openBracket) ? parseIntegerLiteral() : 0;
|
||
|
|
||
|
if (arraySize > 0)
|
||
|
location.throwError ("Arrays not yet implemented!");
|
||
|
|
||
|
var constantInitialiser;
|
||
|
|
||
|
if (isConst)
|
||
|
constantInitialiser = parseConstantExpressionInitialiser (type);
|
||
|
|
||
|
blockBeingParsed->addVariable ({ name, type, true, isConst, constantInitialiser }, location);
|
||
|
|
||
|
if (matchIf (Token::comma))
|
||
|
{
|
||
|
name = parseIdentifier();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
match (Token::semicolon);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var parseConstantExpressionInitialiser (Type expectedType)
|
||
|
{
|
||
|
var result;
|
||
|
match (Token::assign);
|
||
|
auto e = parseExpression();
|
||
|
|
||
|
if (auto literal = dynamic_cast<LiteralValue*> (e->simplify (*this)))
|
||
|
{
|
||
|
result = literal->value;
|
||
|
|
||
|
if (getTypeOfVar (result) != expectedType)
|
||
|
location.throwError ("Expected a constant expression of type " + getTypeName (expectedType));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
location.throwError ("Expected a constant expression");
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void parseFunctionDeclaration (Type returnType, const String& name)
|
||
|
{
|
||
|
auto f = allocate<Function>();
|
||
|
|
||
|
while (matchesAnyType())
|
||
|
{
|
||
|
auto type = tokenToType (skip());
|
||
|
f->arguments.add ({ parseIdentifier(), type, false, false, 0 });
|
||
|
|
||
|
if (f->arguments.size() > 127)
|
||
|
location.throwError ("Too many function arguments");
|
||
|
|
||
|
if (currentType == Token::closeParen)
|
||
|
break;
|
||
|
|
||
|
match (Token::comma);
|
||
|
}
|
||
|
|
||
|
match (Token::closeParen);
|
||
|
f->functionID = createFunctionID (name, returnType, f->getArgumentTypes());
|
||
|
|
||
|
if (findFunction (f->functionID) != nullptr || findNativeFunction (f->functionID) != nullptr)
|
||
|
location.throwError ("Duplicate function declaration");
|
||
|
|
||
|
functions.add (f);
|
||
|
|
||
|
f->block = parseBlock (true);
|
||
|
f->returnType = returnType;
|
||
|
|
||
|
if (! f->block->alwaysReturns())
|
||
|
{
|
||
|
if (returnType != Type::void_)
|
||
|
location.throwError ("This function must return a value");
|
||
|
|
||
|
f->block->statements.add (allocate<ReturnStatement> (location, f->block, nullptr));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int parseIntegerLiteral()
|
||
|
{
|
||
|
auto e = parseExpression();
|
||
|
|
||
|
if (auto literal = dynamic_cast<LiteralValue*> (e->simplify (*this)))
|
||
|
{
|
||
|
if (literal->value.isInt() || literal->value.isInt64())
|
||
|
{
|
||
|
auto value = static_cast<int> (literal->value);
|
||
|
|
||
|
if (value > 0)
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
location.throwError ("Expected an integer constant");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
BlockPtr parseBlock (bool isMainBlockOfFunction)
|
||
|
{
|
||
|
match (Token::openBrace);
|
||
|
auto b = allocate<BlockStatement> (location, blockBeingParsed, functions.getLast(), isMainBlockOfFunction);
|
||
|
auto lastBlock = blockBeingParsed;
|
||
|
blockBeingParsed = b;
|
||
|
|
||
|
while (! matchIf (Token::closeBrace))
|
||
|
b->statements.add (parseStatement());
|
||
|
|
||
|
blockBeingParsed = lastBlock;
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
StatementPtr parseStatement()
|
||
|
{
|
||
|
if (currentType == Token::openBrace) return parseBlock (false);
|
||
|
if (matchIf (Token::if_)) return parseIf();
|
||
|
if (matchIf (Token::while_)) return parseDoOrWhileLoop (false);
|
||
|
if (matchIf (Token::do_)) return parseDoOrWhileLoop (true);
|
||
|
if (matchIf (Token::for_)) return parseForLoop();
|
||
|
if (matchIf (Token::return_)) return parseReturn();
|
||
|
if (matchIf (Token::break_)) return matchEndOfStatement (allocate<BreakStatement> (location, blockBeingParsed));
|
||
|
if (matchIf (Token::continue_)) return matchEndOfStatement (allocate<ContinueStatement> (location, blockBeingParsed));
|
||
|
if (matchIf (Token::semicolon)) return matchEndOfStatement (allocate<Statement> (location, blockBeingParsed));
|
||
|
if (matchIf (Token::plusplus)) return matchEndOfStatement (parsePreIncDec (Token::plus));
|
||
|
if (matchIf (Token::minusminus)) return matchEndOfStatement (parsePreIncDec (Token::minus));
|
||
|
if (matchesAny (Token::openParen)) return matchEndOfStatement (parseFactor());
|
||
|
if (matchIf (Token::const_)) return parseVariableDeclaration (true);
|
||
|
if (matchesAnyType()) return parseVariableDeclaration (false);
|
||
|
|
||
|
if (matchesAny (Token::identifier, Token::literal, Token::minus))
|
||
|
return matchEndOfStatement (parseExpression());
|
||
|
|
||
|
throwErrorExpecting ("a statement");
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
ExpPtr parseExpression()
|
||
|
{
|
||
|
auto lhs = parseLogicOperator();
|
||
|
|
||
|
if (matchIf (Token::question)) return parseTernaryOperator (lhs);
|
||
|
if (matchIf (Token::plusEquals)) return parseInPlaceOpExpression (lhs, Token::plus);
|
||
|
if (matchIf (Token::minusEquals)) return parseInPlaceOpExpression (lhs, Token::minus);
|
||
|
if (matchIf (Token::timesEquals)) return parseInPlaceOpExpression (lhs, Token::times);
|
||
|
if (matchIf (Token::divideEquals)) return parseInPlaceOpExpression (lhs, Token::divide);
|
||
|
if (matchIf (Token::moduloEquals)) return parseInPlaceOpExpression (lhs, Token::modulo);
|
||
|
if (matchIf (Token::leftShiftEquals)) return parseInPlaceOpExpression (lhs, Token::leftShift);
|
||
|
if (matchIf (Token::rightShiftEquals)) return parseInPlaceOpExpression (lhs, Token::rightShift);
|
||
|
|
||
|
if (matchIf (Token::assign))
|
||
|
{
|
||
|
auto loc = location;
|
||
|
return allocate<Assignment> (loc, blockBeingParsed, getIdentifierFromExpression (lhs), parseExpression(), false);
|
||
|
}
|
||
|
|
||
|
return lhs;
|
||
|
}
|
||
|
|
||
|
ExpPtr parseTernaryOperator (ExpPtr condition)
|
||
|
{
|
||
|
auto e = allocate<TernaryOp> (location, blockBeingParsed);
|
||
|
e->condition = condition;
|
||
|
e->trueBranch = parseExpression();
|
||
|
match (Token::colon);
|
||
|
e->falseBranch = parseExpression();
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
ExpPtr parseLogicOperator()
|
||
|
{
|
||
|
for (auto a = parseComparator();;)
|
||
|
{
|
||
|
if (! matchesAny (Token::logicalAnd, Token::logicalOr, Token::bitwiseOr,
|
||
|
Token::bitwiseAnd, Token::bitwiseXor))
|
||
|
return a;
|
||
|
|
||
|
auto loc = location;
|
||
|
auto type = skip();
|
||
|
a = allocate<BinaryOperator> (loc, blockBeingParsed, a, parseComparator(), type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExpPtr parseComparator()
|
||
|
{
|
||
|
for (auto a = parseShiftOperator();;)
|
||
|
{
|
||
|
if (! matchesAny (Token::equals, Token::notEquals, Token::lessThan,
|
||
|
Token::lessThanOrEqual, Token::greaterThan, Token::greaterThanOrEqual))
|
||
|
return a;
|
||
|
|
||
|
auto loc = location;
|
||
|
auto type = skip();
|
||
|
a = allocate<BinaryOperator> (loc, blockBeingParsed, a, parseShiftOperator(), type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExpPtr parseShiftOperator()
|
||
|
{
|
||
|
for (auto a = parseAdditionSubtraction();;)
|
||
|
{
|
||
|
if (! matchesAny (Token::leftShift, Token::rightShift, Token::rightShiftUnsigned))
|
||
|
return a;
|
||
|
|
||
|
auto loc = location;
|
||
|
auto type = skip();
|
||
|
a = allocate<BinaryOperator> (loc, blockBeingParsed, a, parseExpression(), type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExpPtr parseAdditionSubtraction()
|
||
|
{
|
||
|
for (auto a = parseMultiplyDivide();;)
|
||
|
{
|
||
|
if (! matchesAny (Token::plus, Token::minus))
|
||
|
return a;
|
||
|
|
||
|
auto loc = location;
|
||
|
auto type = skip();
|
||
|
a = allocate<BinaryOperator> (loc, blockBeingParsed, a, parseMultiplyDivide(), type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExpPtr parseMultiplyDivide()
|
||
|
{
|
||
|
for (auto a = parseUnary();;)
|
||
|
{
|
||
|
if (! matchesAny (Token::times, Token::divide, Token::modulo))
|
||
|
return a;
|
||
|
|
||
|
auto loc = location;
|
||
|
auto type = skip();
|
||
|
a = allocate<BinaryOperator> (loc, blockBeingParsed, a, parseUnary(), type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExpPtr parseUnary()
|
||
|
{
|
||
|
if (matchIf (Token::plusplus)) return parsePreIncDec (Token::plus);
|
||
|
if (matchIf (Token::minusminus)) return parsePreIncDec (Token::minus);
|
||
|
|
||
|
if (matchesAny (Token::minus, Token::logicalNot, Token::bitwiseNot))
|
||
|
{
|
||
|
auto loc = location;
|
||
|
auto type = skip();
|
||
|
return allocate<UnaryOp> (loc, blockBeingParsed, parseUnary(), type);
|
||
|
}
|
||
|
|
||
|
return parseFactor();
|
||
|
}
|
||
|
|
||
|
ExpPtr parseFactor()
|
||
|
{
|
||
|
if (currentType == Token::identifier) return parseSuffixes (allocate<Identifier> (location, blockBeingParsed, parseIdentifier()));
|
||
|
if (matchIf (Token::openParen)) return parseSuffixes (matchCloseParen (parseExpression()));
|
||
|
if (matchIf (Token::true_)) return parseSuffixes (allocate<LiteralValue> (location, blockBeingParsed, true));
|
||
|
if (matchIf (Token::false_)) return parseSuffixes (allocate<LiteralValue> (location, blockBeingParsed, false));
|
||
|
|
||
|
if (currentType == Token::literal)
|
||
|
{
|
||
|
auto lit = allocate<LiteralValue> (location, blockBeingParsed, currentValue);
|
||
|
skip();
|
||
|
return parseSuffixes (lit);
|
||
|
}
|
||
|
|
||
|
if (matchesAny (Token::int_, Token::float_, Token::bool_))
|
||
|
return parseSuffixes (parseFunctionCall (skip()));
|
||
|
|
||
|
throwErrorExpecting ("an expression");
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
ExpPtr parseSuffixes (ExpPtr input)
|
||
|
{
|
||
|
if (currentType == Token::openParen)
|
||
|
{
|
||
|
if (auto functionName = dynamic_cast<Identifier*> (input))
|
||
|
return parseSuffixes (parseFunctionCall (functionName->name));
|
||
|
|
||
|
location.throwError ("Malformed function call");
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
if (matchIf (Token::openBracket))
|
||
|
{
|
||
|
auto s = allocate<ArraySubscript> (location, blockBeingParsed);
|
||
|
s->object = input;
|
||
|
s->index = parseExpression();
|
||
|
match (Token::closeBracket);
|
||
|
return parseSuffixes (s);
|
||
|
}
|
||
|
|
||
|
if (matchIf (Token::plusplus)) return parsePostIncDec (input, Token::plus);
|
||
|
if (matchIf (Token::minusminus)) return parsePostIncDec (input, Token::minus);
|
||
|
|
||
|
return input;
|
||
|
}
|
||
|
|
||
|
ExpPtr parseInPlaceOpExpression (ExpPtr lhs, TokenType opType)
|
||
|
{
|
||
|
auto loc = location;
|
||
|
auto rhs = parseExpression();
|
||
|
return allocate<Assignment> (loc, blockBeingParsed, getIdentifierFromExpression (lhs),
|
||
|
allocate<BinaryOperator> (location, blockBeingParsed, lhs, rhs, opType), false);
|
||
|
}
|
||
|
|
||
|
ExpPtr parsePreIncDec (TokenType opType)
|
||
|
{
|
||
|
auto lhs = parseFactor();
|
||
|
auto one = allocate<LiteralValue> (location, blockBeingParsed, (int) 1);
|
||
|
return allocate<Assignment> (location, blockBeingParsed, getIdentifierFromExpression (lhs),
|
||
|
allocate<BinaryOperator> (location, blockBeingParsed, lhs, one, opType), false);
|
||
|
}
|
||
|
|
||
|
ExpPtr parsePostIncDec (ExpPtr lhs, TokenType opType)
|
||
|
{
|
||
|
auto one = allocate<LiteralValue> (location, blockBeingParsed, (int) 1);
|
||
|
return allocate<Assignment> (location, blockBeingParsed, getIdentifierFromExpression (lhs),
|
||
|
allocate<BinaryOperator> (location, blockBeingParsed, lhs, one, opType), true);
|
||
|
}
|
||
|
|
||
|
StatementPtr parseIf()
|
||
|
{
|
||
|
auto s = allocate<IfStatement> (location, blockBeingParsed);
|
||
|
match (Token::openParen);
|
||
|
s->condition = matchCloseParen (parseExpression());
|
||
|
s->trueBranch = parseStatement();
|
||
|
s->falseBranch = matchIf (Token::else_) ? parseStatement() : nullptr;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
StatementPtr parseReturn()
|
||
|
{
|
||
|
auto value = matchIf (Token::semicolon) ? nullptr : parseExpression();
|
||
|
auto returnStatement = allocate<ReturnStatement> (location, blockBeingParsed, value);
|
||
|
matchIf (Token::semicolon);
|
||
|
return returnStatement;
|
||
|
}
|
||
|
|
||
|
StatementPtr parseVariableDeclaration (bool isConst)
|
||
|
{
|
||
|
if (isConst && ! matchesAnyType())
|
||
|
throwErrorExpecting ("a type");
|
||
|
|
||
|
auto type = tokenToType (skip());
|
||
|
|
||
|
for (StatementPtr result = nullptr;;)
|
||
|
{
|
||
|
auto name = parseIdentifier();
|
||
|
auto loc = location;
|
||
|
|
||
|
if (isConst)
|
||
|
{
|
||
|
auto constantValue = parseConstantExpressionInitialiser (type);
|
||
|
blockBeingParsed->addVariable ({ name, type, false, true, constantValue }, loc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
blockBeingParsed->addVariable ({ name, type, false, false, {} }, loc);
|
||
|
|
||
|
auto assignedValue = matchIf (Token::assign) ? parseExpression() : nullptr;
|
||
|
|
||
|
if (auto literal = dynamic_cast<LiteralValue*> (assignedValue))
|
||
|
if (static_cast<double> (literal->value) == 0)
|
||
|
assignedValue = nullptr;
|
||
|
|
||
|
if (assignedValue != nullptr || ! blockBeingParsed->isMainBlockOfFunction) // no need to assign 0 for variables in the outer scope
|
||
|
{
|
||
|
if (assignedValue == nullptr)
|
||
|
assignedValue = allocate<LiteralValue> (loc, blockBeingParsed, (int) 0);
|
||
|
|
||
|
auto assignment = allocate<Assignment> (loc, blockBeingParsed, name, assignedValue, false);
|
||
|
|
||
|
if (result == nullptr)
|
||
|
{
|
||
|
result = assignment;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
auto block = dynamic_cast<BlockPtr> (result);
|
||
|
|
||
|
if (block == nullptr)
|
||
|
{
|
||
|
block = allocate<BlockStatement> (loc, blockBeingParsed, functions.getLast(), false);
|
||
|
block->statements.add (result);
|
||
|
result = block;
|
||
|
}
|
||
|
|
||
|
block->statements.add (assignment);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (matchIf (Token::semicolon))
|
||
|
return result != nullptr ? result : allocate<Statement> (location, blockBeingParsed);
|
||
|
|
||
|
match (Token::comma);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
StatementPtr parseForLoop()
|
||
|
{
|
||
|
auto oldBlock = blockBeingParsed;
|
||
|
auto block = allocate<BlockStatement> (location, oldBlock, functions.getLast(), false);
|
||
|
blockBeingParsed = block;
|
||
|
auto loopStatement = allocate<LoopStatement> (location, blockBeingParsed, false);
|
||
|
block->statements.add (loopStatement);
|
||
|
match (Token::openParen);
|
||
|
|
||
|
loopStatement->initialiser = parseStatement();
|
||
|
loopStatement->condition = matchIf (Token::semicolon) ? allocate<LiteralValue> (location, blockBeingParsed, true)
|
||
|
: matchEndOfStatement (parseExpression());
|
||
|
loopStatement->iterator = matchIf (Token::closeParen) ? allocate<Statement> (location, blockBeingParsed)
|
||
|
: matchCloseParen (parseExpression());
|
||
|
loopStatement->body = parseStatement();
|
||
|
|
||
|
blockBeingParsed = oldBlock;
|
||
|
return block;
|
||
|
}
|
||
|
|
||
|
StatementPtr parseDoOrWhileLoop (bool isDoLoop)
|
||
|
{
|
||
|
auto loopStatement = allocate<LoopStatement> (location, blockBeingParsed, isDoLoop);
|
||
|
loopStatement->initialiser = allocate<Statement> (location, blockBeingParsed);
|
||
|
loopStatement->iterator = allocate<Statement> (location, blockBeingParsed);
|
||
|
|
||
|
if (isDoLoop)
|
||
|
{
|
||
|
loopStatement->body = parseBlock (false);
|
||
|
match (Token::while_);
|
||
|
}
|
||
|
|
||
|
match (Token::openParen);
|
||
|
loopStatement->condition = matchCloseParen (parseExpression());
|
||
|
|
||
|
if (! isDoLoop)
|
||
|
loopStatement->body = parseStatement();
|
||
|
|
||
|
return loopStatement;
|
||
|
}
|
||
|
|
||
|
String parseIdentifier()
|
||
|
{
|
||
|
auto name = currentValue.toString();
|
||
|
match (Token::identifier);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
String getIdentifierFromExpression (ExpPtr e)
|
||
|
{
|
||
|
if (auto i = dynamic_cast<Identifier*> (e))
|
||
|
return i->name;
|
||
|
|
||
|
location.throwError ("This operator requires an assignable variable");
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
ExpPtr parseFunctionCall (const String& name)
|
||
|
{
|
||
|
auto call = allocate<FunctionCall> (location, blockBeingParsed);
|
||
|
call->functionName = name;
|
||
|
match (Token::openParen);
|
||
|
|
||
|
while (currentType != Token::closeParen)
|
||
|
{
|
||
|
call->arguments.add (parseExpression());
|
||
|
|
||
|
if (currentType == Token::closeParen)
|
||
|
break;
|
||
|
|
||
|
match (Token::comma);
|
||
|
}
|
||
|
|
||
|
return matchCloseParen (call);
|
||
|
}
|
||
|
|
||
|
bool matchesAnyType() const noexcept { return matchesAny (Token::int_, Token::float_, Token::bool_); }
|
||
|
bool matchesAnyTypeOrVoid() const noexcept { return matchesAnyType() || currentType == Token::void_; }
|
||
|
ExpPtr matchCloseParen (ExpPtr e) { match (Token::closeParen); return e; }
|
||
|
template<typename ExpType> ExpType matchEndOfStatement (ExpType e) { match (Token::semicolon); return e; }
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
//==============================================================================
|
||
|
struct CodeGenerator
|
||
|
{
|
||
|
CodeGenerator (Array<uint8>& output, const SyntaxTreeBuilder& stb)
|
||
|
: outputCode (output), syntaxTree (stb) {}
|
||
|
|
||
|
void generateCode (BlockPtr outerBlock, uint32 heapSizeBytesRequired)
|
||
|
{
|
||
|
for (auto f : syntaxTree.functions)
|
||
|
{
|
||
|
f->address = createMarker();
|
||
|
f->unwindAddress = createMarker();
|
||
|
}
|
||
|
|
||
|
emit ((int16) 0); // checksum
|
||
|
emit ((int16) 0); // size
|
||
|
emit ((int16) syntaxTree.functions.size());
|
||
|
emit ((int16) outerBlock->variables.size());
|
||
|
emit ((int16) heapSizeBytesRequired);
|
||
|
|
||
|
for (auto f : syntaxTree.functions)
|
||
|
emit (f->functionID, f->address);
|
||
|
|
||
|
auto codeStart = outputCode.size();
|
||
|
|
||
|
for (auto f : syntaxTree.functions)
|
||
|
f->emit (*this);
|
||
|
|
||
|
removeJumpsToNextInstruction (codeStart);
|
||
|
resolveMarkers();
|
||
|
|
||
|
Program::writeInt16 (outputCode.begin() + 2, (int16) outputCode.size());
|
||
|
const Program program (outputCode.begin(), (uint32) outputCode.size());
|
||
|
Program::writeInt16 (outputCode.begin(), (int16) program.calculateChecksum());
|
||
|
jassert (program.checksumMatches());
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
Array<uint8>& outputCode;
|
||
|
const SyntaxTreeBuilder& syntaxTree;
|
||
|
|
||
|
struct Marker { int index = 0; };
|
||
|
struct MarkerAndAddress { Marker marker; int address; };
|
||
|
|
||
|
int nextMarker = 0;
|
||
|
Array<MarkerAndAddress> markersToResolve, resolvedMarkers;
|
||
|
|
||
|
Marker createMarker() noexcept { Marker m; m.index = ++nextMarker; return m; }
|
||
|
void attachMarker (Marker m) { resolvedMarkers.add ({ m, outputCode.size() }); }
|
||
|
|
||
|
int getResolvedMarkerAddress (Marker marker) const
|
||
|
{
|
||
|
for (auto m : resolvedMarkers)
|
||
|
if (m.marker.index == marker.index)
|
||
|
return m.address;
|
||
|
|
||
|
jassertfalse;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Marker getMarkerAtAddress (int address) const noexcept
|
||
|
{
|
||
|
for (auto m : markersToResolve)
|
||
|
if (m.address == address)
|
||
|
return m.marker;
|
||
|
|
||
|
jassertfalse;
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
void resolveMarkers()
|
||
|
{
|
||
|
for (auto m : markersToResolve)
|
||
|
Program::writeInt16 (outputCode.begin() + m.address, (int16) getResolvedMarkerAddress (m.marker));
|
||
|
}
|
||
|
|
||
|
void removeCode (int address, int size)
|
||
|
{
|
||
|
outputCode.removeRange (address, size);
|
||
|
|
||
|
for (int i = markersToResolve.size(); --i >= 0;)
|
||
|
{
|
||
|
auto& m = markersToResolve.getReference (i);
|
||
|
|
||
|
if (m.address >= address + size)
|
||
|
m.address -= size;
|
||
|
else if (m.address >= address)
|
||
|
markersToResolve.remove (i);
|
||
|
}
|
||
|
|
||
|
for (auto& m : resolvedMarkers)
|
||
|
if (m.address >= address + size)
|
||
|
m.address -= size;
|
||
|
}
|
||
|
|
||
|
void removeJumpsToNextInstruction (int address)
|
||
|
{
|
||
|
while (address < outputCode.size())
|
||
|
{
|
||
|
auto op = (OpCode) outputCode.getUnchecked (address);
|
||
|
auto opSize = 1 + Program::getNumExtraBytesForOpcode (op);
|
||
|
|
||
|
if (op == OpCode::jump)
|
||
|
{
|
||
|
auto marker = getMarkerAtAddress (address + 1);
|
||
|
|
||
|
if (marker.index != 0)
|
||
|
{
|
||
|
if (getResolvedMarkerAddress (marker) == address + opSize)
|
||
|
{
|
||
|
removeCode (address, opSize);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
address += opSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Marker breakTarget, continueTarget;
|
||
|
|
||
|
//==============================================================================
|
||
|
void emit (OpCode op) { emit ((int8) op); }
|
||
|
void emit (Marker m) { markersToResolve.add ({ m, outputCode.size() }); emit ((int16) 0); }
|
||
|
void emit (int8 value) { outputCode.add ((uint8) value); }
|
||
|
void emit (int16 value) { uint8 d[2]; Program::writeInt16 (d, value); outputCode.insertArray (-1, d, (int) sizeof (d)); }
|
||
|
void emit (int32 value) { uint8 d[4]; Program::writeInt32 (d, value); outputCode.insertArray (-1, d, (int) sizeof (d)); }
|
||
|
|
||
|
template <typename Arg1, typename... Args>
|
||
|
void emit (Arg1 arg1, Args... args)
|
||
|
{
|
||
|
emit (arg1);
|
||
|
emit (args...);
|
||
|
}
|
||
|
|
||
|
void emitPush (const var& value)
|
||
|
{
|
||
|
if (value.isDouble())
|
||
|
{
|
||
|
const float v = value;
|
||
|
|
||
|
if (v == 0) emit (OpCode::push0);
|
||
|
else emit (OpCode::push32, Program::floatToInt (v));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const int v = value;
|
||
|
|
||
|
if (v == 0) emit (OpCode::push0);
|
||
|
else if (v == 1) emit (OpCode::push1);
|
||
|
else if (v > 0 && v < 128) emit (OpCode::push8, (int8) v);
|
||
|
else if (v > 0 && v < 32768) emit (OpCode::push16, (int16) v);
|
||
|
else emit (OpCode::push32, (int32) v);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void emitCast (Type source, Type dest, const CodeLocation& location)
|
||
|
{
|
||
|
if (dest == source) return;
|
||
|
if (dest == Type::void_) return emit (OpCode::drop);
|
||
|
if (source == Type::bool_ && dest == Type::int_) return;
|
||
|
if (source == Type::int_ && dest == Type::bool_) return emit (OpCode::testNZ_int32);
|
||
|
if ((source == Type::int_ || source == Type::bool_) && dest == Type::float_) return emit (OpCode::int32ToFloat);
|
||
|
|
||
|
location.throwError ("Cannot cast from " + getTypeName (source) + " to " + getTypeName (dest));
|
||
|
}
|
||
|
|
||
|
void emitVariableRead (Type sourceType, Type requiredType, int stackDepth, int index, const CodeLocation& location)
|
||
|
{
|
||
|
if (index < 0)
|
||
|
{
|
||
|
emit (OpCode::dupFromGlobal, (int16) ((-index) - 1));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
index += stackDepth;
|
||
|
|
||
|
if (index == 0)
|
||
|
emit (OpCode::dup);
|
||
|
else if (index < 8)
|
||
|
emit ((OpCode) ((int) OpCode::dupOffset_01 + index - 1));
|
||
|
else if (index >= 128)
|
||
|
emit (OpCode::dupOffset16, (int16) index);
|
||
|
else
|
||
|
emit (OpCode::dupOffset, (int8) index);
|
||
|
}
|
||
|
|
||
|
emitCast (sourceType, requiredType, location);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
//==============================================================================
|
||
|
struct Statement : public AllocatedObject
|
||
|
{
|
||
|
struct Visitor
|
||
|
{
|
||
|
virtual ~Visitor() {}
|
||
|
virtual void operator()(StatementPtr) = 0;
|
||
|
};
|
||
|
|
||
|
Statement (const CodeLocation& l, BlockPtr parent) noexcept : location (l), parentBlock (parent) {}
|
||
|
virtual void emit (CodeGenerator&, Type, int /*stackDepth*/) const {}
|
||
|
virtual bool alwaysReturns() const { return false; }
|
||
|
virtual void visitSubStatements (Visitor&) const {}
|
||
|
virtual Statement* simplify (SyntaxTreeBuilder&) { return this; }
|
||
|
|
||
|
CodeLocation location;
|
||
|
BlockPtr parentBlock;
|
||
|
};
|
||
|
|
||
|
struct Expression : public Statement
|
||
|
{
|
||
|
Expression (const CodeLocation& l, BlockPtr parent) noexcept : Statement (l, parent) {}
|
||
|
virtual Type getType (CodeGenerator&) const = 0;
|
||
|
virtual ExpPtr simplify (SyntaxTreeBuilder&) override { return this; }
|
||
|
};
|
||
|
|
||
|
struct Variable
|
||
|
{
|
||
|
String name;
|
||
|
Type type;
|
||
|
bool isGlobal, isConst;
|
||
|
var constantValue;
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
struct Function : public AllocatedObject
|
||
|
{
|
||
|
FunctionID functionID;
|
||
|
Type returnType;
|
||
|
Array<Variable> arguments;
|
||
|
BlockPtr block;
|
||
|
CodeGenerator::Marker address, unwindAddress;
|
||
|
|
||
|
void emit (CodeGenerator& cg) const
|
||
|
{
|
||
|
cg.attachMarker (address);
|
||
|
|
||
|
const int numLocals = getNumLocals();
|
||
|
|
||
|
for (int num = numLocals; num > 0;)
|
||
|
{
|
||
|
if (num == 1)
|
||
|
{
|
||
|
cg.emit (OpCode::push0);
|
||
|
--num;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int numToDo = jmin (127, num);
|
||
|
cg.emit (OpCode::pushMultiple0, (int8) numToDo);
|
||
|
num -= numToDo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
block->emit (cg, Type::void_, 0);
|
||
|
cg.attachMarker (unwindAddress);
|
||
|
|
||
|
const bool keepTop = returnType != Type::void_;
|
||
|
|
||
|
for (int num = numLocals; num > 0;)
|
||
|
{
|
||
|
if (num == 1 && ! keepTop)
|
||
|
{
|
||
|
cg.emit (OpCode::drop);
|
||
|
--num;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int numToDo = jmin (127, num);
|
||
|
cg.emit (OpCode::dropMultiple, (int8) (keepTop ? -numToDo : numToDo));
|
||
|
num -= numToDo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cg.emit (keepTop ? OpCode::retValue : OpCode::retVoid, (int8) arguments.size());
|
||
|
}
|
||
|
|
||
|
Array<Type> getArgumentTypes() const
|
||
|
{
|
||
|
Array<Type> argTypes;
|
||
|
|
||
|
for (auto& arg : arguments)
|
||
|
argTypes.add (arg.type);
|
||
|
|
||
|
return argTypes;
|
||
|
}
|
||
|
|
||
|
int getNumLocals() const noexcept
|
||
|
{
|
||
|
return countMaxNumLocalVariables (block);
|
||
|
}
|
||
|
|
||
|
static int countMaxNumLocalVariables (StatementPtr s) noexcept
|
||
|
{
|
||
|
struct Counter : Statement::Visitor
|
||
|
{
|
||
|
void operator() (StatementPtr sub) override
|
||
|
{
|
||
|
num = jmax (num, countMaxNumLocalVariables (sub));
|
||
|
}
|
||
|
|
||
|
int num = 0;
|
||
|
};
|
||
|
|
||
|
Counter counter;
|
||
|
|
||
|
if (s != nullptr)
|
||
|
s->visitSubStatements (counter);
|
||
|
|
||
|
if (auto block = dynamic_cast<BlockPtr> (s))
|
||
|
counter.num += block->variables.size();
|
||
|
|
||
|
return counter.num;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
struct BlockStatement : public Statement
|
||
|
{
|
||
|
BlockStatement (const CodeLocation& l, BlockPtr parent, Function* f, bool isMainFunctionBlock)
|
||
|
: Statement (l, parent), function (f), isMainBlockOfFunction (isMainFunctionBlock) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
jassert (requiredType == Type::void_); ignoreUnused (requiredType);
|
||
|
jassert (function != nullptr);
|
||
|
|
||
|
for (auto s : statements)
|
||
|
s->emit (cg, Type::void_, stackDepth);
|
||
|
}
|
||
|
|
||
|
bool alwaysReturns() const override
|
||
|
{
|
||
|
return ! statements.isEmpty() && statements.getLast()->alwaysReturns();
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
for (auto s : statements)
|
||
|
visit (s);
|
||
|
}
|
||
|
|
||
|
Statement* simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
for (int i = 0; i < statements.size(); ++i)
|
||
|
statements.set (i, statements.getReference(i)->simplify (stb));
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
// returns -ve values for globals
|
||
|
int getVariableDepth (const String& name, const CodeLocation& locationForError) const
|
||
|
{
|
||
|
int index = indexOf (variables, name);
|
||
|
if (index >= 0)
|
||
|
return getNumVariablesInParentBlocks() + index;
|
||
|
|
||
|
if (! isMainBlockOfFunction)
|
||
|
return parentBlock->getVariableDepth (name, locationForError);
|
||
|
|
||
|
for (int i = function->arguments.size(); --i >= 0;)
|
||
|
if (function->arguments.getReference(i).name == name)
|
||
|
return i + 1 + function->getNumLocals();
|
||
|
|
||
|
index = indexOf (getGlobalVariables(), name);
|
||
|
if (index >= 0)
|
||
|
return -(index + 1);
|
||
|
|
||
|
locationForError.throwError ("Unknown variable '" + name + "'");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int getNumVariablesInParentBlocks() const noexcept
|
||
|
{
|
||
|
return isMainBlockOfFunction ? 0 : (parentBlock->getNumVariablesInParentBlocks()
|
||
|
+ parentBlock->variables.size());
|
||
|
}
|
||
|
|
||
|
const Array<Variable>& getGlobalVariables() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalVariables() : variables; }
|
||
|
const Array<Variable>& getGlobalConstants() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalConstants() : constants; }
|
||
|
|
||
|
const Variable& getVariable (const String& name, const CodeLocation& locationForError) const
|
||
|
{
|
||
|
for (auto& v : constants)
|
||
|
if (v.name == name)
|
||
|
return v;
|
||
|
|
||
|
for (auto& v : variables)
|
||
|
if (v.name == name)
|
||
|
return v;
|
||
|
|
||
|
if (! isMainBlockOfFunction)
|
||
|
return parentBlock->getVariable (name, locationForError);
|
||
|
|
||
|
for (auto& v : function->arguments)
|
||
|
if (v.name == name)
|
||
|
return v;
|
||
|
|
||
|
for (auto& v : getGlobalConstants())
|
||
|
if (v.name == name)
|
||
|
return v;
|
||
|
|
||
|
for (auto& v : getGlobalVariables())
|
||
|
if (v.name == name)
|
||
|
return v;
|
||
|
|
||
|
locationForError.throwError ("Unknown variable '" + name + "'");
|
||
|
}
|
||
|
|
||
|
void addVariable (Variable v, const CodeLocation& locationForError)
|
||
|
{
|
||
|
if (indexOf (variables, v.name) >= 0 || indexOf (constants, v.name) >= 0)
|
||
|
locationForError.throwError ("Variable '" + v.name + "' already exists");
|
||
|
|
||
|
(v.isConst ? constants : variables).add (v);
|
||
|
}
|
||
|
|
||
|
static int indexOf (const Array<Variable>& vars, const String& name) noexcept
|
||
|
{
|
||
|
for (int i = 0; i < vars.size(); ++i)
|
||
|
if (vars.getReference(i).name == name)
|
||
|
return i;
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
Function* function;
|
||
|
Array<StatementPtr> statements;
|
||
|
Array<Variable> variables, constants;
|
||
|
bool isMainBlockOfFunction;
|
||
|
};
|
||
|
|
||
|
struct IfStatement : public Statement
|
||
|
{
|
||
|
IfStatement (const CodeLocation& l, BlockPtr parent) : Statement (l, parent) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
jassert (requiredType == Type::void_); ignoreUnused (requiredType);
|
||
|
|
||
|
condition->emit (cg, Type::bool_, stackDepth);
|
||
|
auto endOfStatement = cg.createMarker();
|
||
|
|
||
|
if (falseBranch == nullptr)
|
||
|
{
|
||
|
cg.emit (OpCode::jumpIfFalse, endOfStatement);
|
||
|
trueBranch->emit (cg, Type::void_, stackDepth);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
auto elseTarget = cg.createMarker();
|
||
|
cg.emit (OpCode::jumpIfFalse, elseTarget);
|
||
|
trueBranch->emit (cg, Type::void_, stackDepth);
|
||
|
cg.emit (OpCode::jump, endOfStatement);
|
||
|
cg.attachMarker (elseTarget);
|
||
|
falseBranch->emit (cg, Type::void_, stackDepth);
|
||
|
}
|
||
|
|
||
|
cg.attachMarker (endOfStatement);
|
||
|
}
|
||
|
|
||
|
bool alwaysReturns() const override
|
||
|
{
|
||
|
return trueBranch->alwaysReturns() && falseBranch != nullptr && falseBranch->alwaysReturns();
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (condition); visit (trueBranch); visit (falseBranch);
|
||
|
}
|
||
|
|
||
|
Statement* simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
condition = condition->simplify (stb);
|
||
|
trueBranch = trueBranch->simplify (stb);
|
||
|
falseBranch = falseBranch != nullptr ? falseBranch->simplify (stb) : nullptr;
|
||
|
|
||
|
if (auto literal = dynamic_cast<LiteralValue*> (condition))
|
||
|
return literal->value ? trueBranch : (falseBranch != nullptr ? falseBranch : stb.allocate<Statement> (location, parentBlock));
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
ExpPtr condition;
|
||
|
StatementPtr trueBranch, falseBranch;
|
||
|
};
|
||
|
|
||
|
struct TernaryOp : public Expression
|
||
|
{
|
||
|
TernaryOp (const CodeLocation& l, BlockPtr parent) : Expression (l, parent) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
condition->emit (cg, Type::bool_, stackDepth);
|
||
|
auto endOfStatement = cg.createMarker();
|
||
|
auto elseTarget = cg.createMarker();
|
||
|
cg.emit (OpCode::jumpIfFalse, elseTarget);
|
||
|
trueBranch->emit (cg, requiredType, stackDepth);
|
||
|
cg.emit (OpCode::jump, endOfStatement);
|
||
|
cg.attachMarker (elseTarget);
|
||
|
falseBranch->emit (cg, requiredType, stackDepth);
|
||
|
cg.attachMarker (endOfStatement);
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator& cg) const override
|
||
|
{
|
||
|
auto type = trueBranch->getType (cg);
|
||
|
|
||
|
if (type == Type::void_) location.throwError ("The ternary operator cannot take void arguments");
|
||
|
if (type != falseBranch->getType (cg)) location.throwError ("Expected both branches of this ternary operator to have the same type");
|
||
|
|
||
|
return type;
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (condition); visit (trueBranch); visit (falseBranch);
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
condition = condition->simplify (stb);
|
||
|
trueBranch = trueBranch->simplify (stb);
|
||
|
falseBranch = falseBranch->simplify (stb);
|
||
|
|
||
|
if (auto literal = dynamic_cast<LiteralValue*> (condition))
|
||
|
return literal->value ? trueBranch : falseBranch;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
ExpPtr condition, trueBranch, falseBranch;
|
||
|
};
|
||
|
|
||
|
struct LoopStatement : public Statement
|
||
|
{
|
||
|
LoopStatement (const CodeLocation& l, BlockPtr parent, bool isDo) noexcept : Statement (l, parent), isDoLoop (isDo) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type, int stackDepth) const override
|
||
|
{
|
||
|
initialiser->emit (cg, Type::void_, stackDepth);
|
||
|
|
||
|
auto loopStart = cg.createMarker();
|
||
|
cg.attachMarker (loopStart);
|
||
|
|
||
|
auto oldBreakTarget = cg.breakTarget;
|
||
|
auto oldContinueTarget = cg.continueTarget;
|
||
|
cg.breakTarget = cg.createMarker();
|
||
|
cg.continueTarget = cg.createMarker();
|
||
|
|
||
|
if (isDoLoop)
|
||
|
{
|
||
|
body->emit (cg, Type::void_, stackDepth);
|
||
|
cg.attachMarker (cg.continueTarget);
|
||
|
condition->emit (cg, Type::bool_, stackDepth);
|
||
|
cg.emit (OpCode::jumpIfTrue, loopStart);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
condition->emit (cg, Type::bool_, stackDepth);
|
||
|
cg.emit (OpCode::jumpIfFalse, cg.breakTarget);
|
||
|
body->emit (cg, Type::void_, stackDepth);
|
||
|
cg.attachMarker (cg.continueTarget);
|
||
|
iterator->emit (cg, Type::void_, stackDepth);
|
||
|
cg.emit (OpCode::jump, loopStart);
|
||
|
}
|
||
|
|
||
|
cg.attachMarker (cg.breakTarget);
|
||
|
cg.breakTarget = oldBreakTarget;
|
||
|
cg.continueTarget = oldContinueTarget;
|
||
|
}
|
||
|
|
||
|
StatementPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
initialiser = initialiser->simplify (stb);
|
||
|
iterator = iterator->simplify (stb);
|
||
|
body = body->simplify (stb);
|
||
|
condition = condition->simplify (stb);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (condition); visit (initialiser); visit (iterator); visit (body);
|
||
|
}
|
||
|
|
||
|
StatementPtr initialiser, iterator, body;
|
||
|
ExpPtr condition;
|
||
|
bool isDoLoop;
|
||
|
};
|
||
|
|
||
|
struct ReturnStatement : public Statement
|
||
|
{
|
||
|
ReturnStatement (const CodeLocation& l, BlockPtr parent, ExpPtr v) noexcept : Statement (l, parent), returnValue (v) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type, int stackDepth) const override
|
||
|
{
|
||
|
if (auto fn = parentBlock->function)
|
||
|
{
|
||
|
if (returnValue != nullptr)
|
||
|
returnValue->emit (cg, fn->returnType, stackDepth);
|
||
|
else if (fn->returnType != Type::void_)
|
||
|
location.throwError ("Cannot return a value from a void function");
|
||
|
|
||
|
cg.emit (OpCode::jump, fn->unwindAddress);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
location.throwError ("The return statement can only be used inside a function");
|
||
|
}
|
||
|
|
||
|
bool alwaysReturns() const override { return true; }
|
||
|
|
||
|
StatementPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
if (returnValue != nullptr)
|
||
|
returnValue = returnValue->simplify (stb);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (returnValue);
|
||
|
}
|
||
|
|
||
|
ExpPtr returnValue;
|
||
|
};
|
||
|
|
||
|
struct BreakStatement : public Statement
|
||
|
{
|
||
|
BreakStatement (const CodeLocation& l, BlockPtr parent) : Statement (l, parent) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type, int) const override
|
||
|
{
|
||
|
if (cg.breakTarget.index == 0)
|
||
|
location.throwError ("The break statement can only be used inside a loop");
|
||
|
|
||
|
cg.emit (OpCode::jump, cg.breakTarget);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct ContinueStatement : public Statement
|
||
|
{
|
||
|
ContinueStatement (const CodeLocation& l, BlockPtr parent) : Statement (l, parent) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type, int) const override
|
||
|
{
|
||
|
if (cg.continueTarget.index == 0)
|
||
|
location.throwError ("The continue statement can only be used inside a loop");
|
||
|
|
||
|
cg.emit (OpCode::jump, cg.continueTarget);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct LiteralValue : public Expression
|
||
|
{
|
||
|
LiteralValue (const CodeLocation& l, BlockPtr parent, const var& v) noexcept : Expression (l, parent), value (v) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int) const override
|
||
|
{
|
||
|
if (requiredType != Type::void_)
|
||
|
{
|
||
|
auto type = getType (cg);
|
||
|
|
||
|
if (type != requiredType && value != var ((int) 0))
|
||
|
{
|
||
|
if (type == Type::int_ && requiredType == Type::bool_) return cg.emitPush (static_cast<bool> (value));
|
||
|
if (type == Type::int_ && requiredType == Type::float_) return cg.emitPush (static_cast<float> (value));
|
||
|
|
||
|
if (! (type == Type::bool_ && requiredType == Type::int_))
|
||
|
location.throwError ("Cannot cast from " + getTypeName (type) + " to " + getTypeName (requiredType));
|
||
|
}
|
||
|
|
||
|
cg.emitPush (value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator&) const override
|
||
|
{
|
||
|
auto t = getTypeOfVar (value);
|
||
|
|
||
|
if (t == Type::void_)
|
||
|
location.throwError ("Unsupported literal type");
|
||
|
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
var value;
|
||
|
};
|
||
|
|
||
|
struct Identifier : public Expression
|
||
|
{
|
||
|
Identifier (const CodeLocation& l, BlockPtr parent, const String& n) noexcept : Expression (l, parent), name (n) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
cg.emitVariableRead (getType (cg), requiredType, stackDepth,
|
||
|
parentBlock->getVariableDepth (name, location), location);
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator&) const override
|
||
|
{
|
||
|
return parentBlock->getVariable (name, location).type;
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
auto& v = parentBlock->getVariable (name, location);
|
||
|
|
||
|
if (v.isConst)
|
||
|
return stb.allocate<LiteralValue> (location, parentBlock, v.constantValue);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
String name;
|
||
|
};
|
||
|
|
||
|
struct UnaryOp : public Expression
|
||
|
{
|
||
|
UnaryOp (const CodeLocation& l, BlockPtr parent, ExpPtr a, TokenType op) noexcept
|
||
|
: Expression (l, parent), source (a), operation (op) {}
|
||
|
|
||
|
ExpPtr source;
|
||
|
TokenType operation;
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
auto sourceType = source->getType (cg);
|
||
|
|
||
|
if (operation == Token::minus)
|
||
|
{
|
||
|
cg.emitPush ((int) 0);
|
||
|
source->emit (cg, sourceType, stackDepth + 1);
|
||
|
cg.emit (sourceType == Type::float_ ? OpCode::sub_float : OpCode::sub_int32);
|
||
|
cg.emitCast (sourceType, requiredType, location);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (sourceType == Type::float_)
|
||
|
location.throwError ("Cannot perform this operation on a float");
|
||
|
|
||
|
if (operation == Token::logicalNot)
|
||
|
{
|
||
|
source->emit (cg, sourceType, stackDepth);
|
||
|
cg.emit (OpCode::logicalNot);
|
||
|
cg.emitCast (Type::bool_, requiredType, location);
|
||
|
}
|
||
|
else if (operation == Token::bitwiseNot)
|
||
|
{
|
||
|
source->emit (cg, Type::int_, stackDepth);
|
||
|
cg.emit (OpCode::bitwiseNot);
|
||
|
cg.emitCast (Type::int_, requiredType, location);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator& cg) const override
|
||
|
{
|
||
|
if (operation == Token::minus) return source->getType (cg);
|
||
|
if (operation == Token::logicalNot) return Type::bool_;
|
||
|
return Type::int_;
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (source);
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
source = source->simplify (stb);
|
||
|
|
||
|
if (auto literal = dynamic_cast<LiteralValue*> (source))
|
||
|
{
|
||
|
auto type = getTypeOfVar (literal->value);
|
||
|
|
||
|
if (type == Type::int_ && operation == Token::minus) { literal->value = -static_cast<int> (literal->value); return literal; }
|
||
|
if (type == Type::int_ && operation == Token::bitwiseNot) { literal->value = ~static_cast<int> (literal->value); return literal; }
|
||
|
if (type == Type::int_ && operation == Token::logicalNot) { literal->value = ! static_cast<int> (literal->value); return literal; }
|
||
|
if (type == Type::bool_ && operation == Token::logicalNot) { literal->value = ! static_cast<bool> (literal->value); return literal; }
|
||
|
if (type == Type::float_ && operation == Token::minus) { literal->value = -static_cast<double> (literal->value); return literal; }
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct BinaryOperator : public Expression
|
||
|
{
|
||
|
BinaryOperator (const CodeLocation& l, BlockPtr parent, ExpPtr a, ExpPtr b, TokenType op) noexcept
|
||
|
: Expression (l, parent), lhs (a), rhs (b), operation (op) {}
|
||
|
|
||
|
ExpPtr lhs, rhs;
|
||
|
TokenType operation;
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
auto typeA = lhs->getType (cg);
|
||
|
auto typeB = rhs->getType (cg);
|
||
|
|
||
|
if (typeA == Type::float_ || typeB == Type::float_)
|
||
|
{
|
||
|
lhs->emit (cg, Type::float_, stackDepth);
|
||
|
rhs->emit (cg, Type::float_, stackDepth + 1);
|
||
|
|
||
|
if (operation == Token::plus) return emitOpAndCast (cg, requiredType, OpCode::add_float);
|
||
|
if (operation == Token::minus) return emitOpAndCast (cg, requiredType, OpCode::sub_float);
|
||
|
if (operation == Token::times) return emitOpAndCast (cg, requiredType, OpCode::mul_float);
|
||
|
if (operation == Token::divide) return emitOpAndCast (cg, requiredType, OpCode::div_float);
|
||
|
|
||
|
cg.emit (OpCode::sub_float);
|
||
|
|
||
|
if (operation == Token::equals) return emitOpAndCast (cg, requiredType, OpCode::testZE_float);
|
||
|
if (operation == Token::notEquals) return emitOpAndCast (cg, requiredType, OpCode::testNZ_float);
|
||
|
if (operation == Token::lessThan) return emitOpAndCast (cg, requiredType, OpCode::testLT_float);
|
||
|
if (operation == Token::lessThanOrEqual) return emitOpAndCast (cg, requiredType, OpCode::testLE_float);
|
||
|
if (operation == Token::greaterThan) return emitOpAndCast (cg, requiredType, OpCode::testGT_float);
|
||
|
if (operation == Token::greaterThanOrEqual) return emitOpAndCast (cg, requiredType, OpCode::testGE_float);
|
||
|
|
||
|
location.throwError ("The operator " + getTokenDescription (operation) + " cannot take floating point arguments");
|
||
|
}
|
||
|
|
||
|
auto type = (typeA == Type::bool_ || typeB == Type::bool_) ? Type::bool_
|
||
|
: Type::int_;
|
||
|
lhs->emit (cg, type, stackDepth);
|
||
|
rhs->emit (cg, type, stackDepth + 1);
|
||
|
|
||
|
if (operation == Token::plus) return emitOpAndCast (cg, requiredType, OpCode::add_int32);
|
||
|
if (operation == Token::minus) return emitOpAndCast (cg, requiredType, OpCode::sub_int32);
|
||
|
if (operation == Token::times) return emitOpAndCast (cg, requiredType, OpCode::mul_int32);
|
||
|
if (operation == Token::divide) return emitOpAndCast (cg, requiredType, OpCode::div_int32);
|
||
|
if (operation == Token::modulo) return emitOpAndCast (cg, requiredType, OpCode::mod_int32);
|
||
|
if (operation == Token::logicalOr) return emitOpAndCast (cg, requiredType, OpCode::logicalOr);
|
||
|
if (operation == Token::logicalAnd) return emitOpAndCast (cg, requiredType, OpCode::logicalAnd);
|
||
|
if (operation == Token::bitwiseOr) return emitOpAndCast (cg, requiredType, OpCode::bitwiseOr);
|
||
|
if (operation == Token::bitwiseAnd) return emitOpAndCast (cg, requiredType, OpCode::bitwiseAnd);
|
||
|
if (operation == Token::bitwiseXor) return emitOpAndCast (cg, requiredType, OpCode::bitwiseXor);
|
||
|
if (operation == Token::leftShift) return emitOpAndCast (cg, requiredType, OpCode::bitShiftLeft);
|
||
|
if (operation == Token::rightShift) return emitOpAndCast (cg, requiredType, OpCode::bitShiftRight);
|
||
|
|
||
|
cg.emit (OpCode::sub_int32);
|
||
|
|
||
|
if (operation == Token::equals) return emitOpAndCast (cg, requiredType, OpCode::testZE_int32);
|
||
|
if (operation == Token::notEquals) return emitOpAndCast (cg, requiredType, OpCode::testNZ_int32);
|
||
|
if (operation == Token::lessThan) return emitOpAndCast (cg, requiredType, OpCode::testLT_int32);
|
||
|
if (operation == Token::lessThanOrEqual) return emitOpAndCast (cg, requiredType, OpCode::testLE_int32);
|
||
|
if (operation == Token::greaterThan) return emitOpAndCast (cg, requiredType, OpCode::testGT_int32);
|
||
|
if (operation == Token::greaterThanOrEqual) return emitOpAndCast (cg, requiredType, OpCode::testGE_int32);
|
||
|
|
||
|
location.throwError ("Unsupported operator");
|
||
|
jassertfalse;
|
||
|
}
|
||
|
|
||
|
void emitOpAndCast (CodeGenerator& cg, Type requiredType, OpCode op) const
|
||
|
{
|
||
|
cg.emit (op);
|
||
|
cg.emitCast (getType (cg), requiredType, location);
|
||
|
}
|
||
|
|
||
|
Type getResultType (Type typeA, Type typeB) const noexcept
|
||
|
{
|
||
|
if (operation == Token::logicalOr || operation == Token::logicalAnd
|
||
|
|| operation == Token::equals || operation == Token::notEquals
|
||
|
|| operation == Token::lessThan || operation == Token::lessThanOrEqual
|
||
|
|| operation == Token::greaterThan || operation == Token::greaterThanOrEqual)
|
||
|
return Type::bool_;
|
||
|
|
||
|
if (operation == Token::plus || operation == Token::minus
|
||
|
|| operation == Token::times || operation == Token::divide)
|
||
|
{
|
||
|
if (typeA == Type::float_ || typeB == Type::float_)
|
||
|
return Type::float_;
|
||
|
}
|
||
|
|
||
|
return Type::int_;
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator& cg) const override
|
||
|
{
|
||
|
return getResultType (lhs->getType (cg), rhs->getType (cg));
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (lhs); visit (rhs);
|
||
|
}
|
||
|
|
||
|
ExpPtr simplifyFloat (double a, double b, LiteralValue* literal)
|
||
|
{
|
||
|
if (operation == Token::plus) { literal->value = a + b; return literal; }
|
||
|
if (operation == Token::minus) { literal->value = a - b; return literal; }
|
||
|
if (operation == Token::times) { literal->value = a * b; return literal; }
|
||
|
if (operation == Token::divide) { literal->value = a / b; return literal; }
|
||
|
if (operation == Token::equals) { literal->value = a == b; return literal; }
|
||
|
if (operation == Token::notEquals) { literal->value = a != b; return literal; }
|
||
|
if (operation == Token::lessThan) { literal->value = a < b; return literal; }
|
||
|
if (operation == Token::lessThanOrEqual) { literal->value = a <= b; return literal; }
|
||
|
if (operation == Token::greaterThan) { literal->value = a > b; return literal; }
|
||
|
if (operation == Token::greaterThanOrEqual) { literal->value = a >= b; return literal; }
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
ExpPtr simplifyBool (bool a, bool b, LiteralValue* literal)
|
||
|
{
|
||
|
if (operation == Token::logicalOr) { literal->value = a || b; return literal; }
|
||
|
if (operation == Token::logicalAnd) { literal->value = a && b; return literal; }
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
ExpPtr simplifyInt (int a, int b, LiteralValue* literal)
|
||
|
{
|
||
|
if (operation == Token::plus) { literal->value = a + b; return literal; }
|
||
|
if (operation == Token::minus) { literal->value = a - b; return literal; }
|
||
|
if (operation == Token::times) { literal->value = a * b; return literal; }
|
||
|
if (operation == Token::divide) { literal->value = a / b; return literal; }
|
||
|
if (operation == Token::equals) { literal->value = a == b; return literal; }
|
||
|
if (operation == Token::notEquals) { literal->value = a != b; return literal; }
|
||
|
if (operation == Token::lessThan) { literal->value = a < b; return literal; }
|
||
|
if (operation == Token::lessThanOrEqual) { literal->value = a <= b; return literal; }
|
||
|
if (operation == Token::greaterThan) { literal->value = a > b; return literal; }
|
||
|
if (operation == Token::greaterThanOrEqual) { literal->value = a >= b; return literal; }
|
||
|
if (operation == Token::modulo) { literal->value = a % b; return literal; }
|
||
|
if (operation == Token::logicalOr) { literal->value = a || b; return literal; }
|
||
|
if (operation == Token::logicalAnd) { literal->value = a && b; return literal; }
|
||
|
if (operation == Token::bitwiseOr) { literal->value = a | b; return literal; }
|
||
|
if (operation == Token::bitwiseAnd) { literal->value = a & b; return literal; }
|
||
|
if (operation == Token::bitwiseXor) { literal->value = a ^ b; return literal; }
|
||
|
if (operation == Token::leftShift) { literal->value = a << b; return literal; }
|
||
|
if (operation == Token::rightShift) { literal->value = a >> b; return literal; }
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
lhs = lhs->simplify (stb);
|
||
|
rhs = rhs->simplify (stb);
|
||
|
|
||
|
if (auto literal1 = dynamic_cast<LiteralValue*> (lhs))
|
||
|
{
|
||
|
if (auto literal2 = dynamic_cast<LiteralValue*> (rhs))
|
||
|
{
|
||
|
auto resultType = getResultType (getTypeOfVar (literal1->value),
|
||
|
getTypeOfVar (literal2->value));
|
||
|
|
||
|
if (resultType == Type::bool_) return simplifyBool (literal1->value, literal2->value, literal1);
|
||
|
if (resultType == Type::int_) return simplifyInt (literal1->value, literal2->value, literal1);
|
||
|
if (resultType == Type::float_) return simplifyFloat (literal1->value, literal2->value, literal1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct Assignment : public Expression
|
||
|
{
|
||
|
Assignment (const CodeLocation& l, BlockPtr parent, const String& dest, ExpPtr source, bool isPost) noexcept
|
||
|
: Expression (l, parent), target (dest), newValue (source), isPostAssignment (isPost) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
auto variableType = getType (cg);
|
||
|
|
||
|
if (isPostAssignment && requiredType != Type::void_)
|
||
|
{
|
||
|
cg.emitVariableRead (variableType, requiredType, stackDepth,
|
||
|
parentBlock->getVariableDepth (target, location), location);
|
||
|
++stackDepth;
|
||
|
requiredType = Type::void_;
|
||
|
}
|
||
|
|
||
|
newValue->emit (cg, variableType, stackDepth);
|
||
|
auto index = parentBlock->getVariableDepth (target, location);
|
||
|
|
||
|
if (requiredType != Type::void_)
|
||
|
{
|
||
|
cg.emit (OpCode::dup);
|
||
|
++stackDepth;
|
||
|
}
|
||
|
|
||
|
if (index < 0)
|
||
|
{
|
||
|
cg.emit (OpCode::dropToGlobal, (int16) ((-index) - 1));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
index += stackDepth;
|
||
|
|
||
|
if (index >= 128)
|
||
|
cg.emit (OpCode::dropToStack16, (int16) index);
|
||
|
else
|
||
|
cg.emit (OpCode::dropToStack, (int8) index);
|
||
|
}
|
||
|
|
||
|
if (requiredType != Type::void_)
|
||
|
cg.emitCast (variableType, requiredType, location);
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator&) const override
|
||
|
{
|
||
|
return parentBlock->getVariable (target, location).type;
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (newValue);
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
newValue = newValue->simplify (stb);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
String target;
|
||
|
ExpPtr newValue;
|
||
|
bool isPostAssignment;
|
||
|
};
|
||
|
|
||
|
struct FunctionCall : public Expression
|
||
|
{
|
||
|
FunctionCall (const CodeLocation& l, BlockPtr parent) noexcept : Expression (l, parent) {}
|
||
|
|
||
|
void emit (CodeGenerator& cg, Type requiredType, int stackDepth) const override
|
||
|
{
|
||
|
if (functionName == Token::int_) return emitCast (cg, Type::int_, stackDepth);
|
||
|
if (functionName == Token::float_) return emitCast (cg, Type::float_, stackDepth);
|
||
|
if (functionName == Token::bool_) return emitCast (cg, Type::bool_, stackDepth);
|
||
|
|
||
|
auto functionID = getFunctionID (cg);
|
||
|
|
||
|
if (auto fn = cg.syntaxTree.findFunction (functionID))
|
||
|
{
|
||
|
emitArgs (cg, fn->getArgumentTypes(), stackDepth);
|
||
|
cg.emit (OpCode::call, fn->address);
|
||
|
cg.emitCast (fn->returnType, requiredType, location);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (auto nativeFn = cg.syntaxTree.findNativeFunction (functionID))
|
||
|
{
|
||
|
emitArgs (cg, getArgTypesFromFunctionName (nativeFn->nameAndArguments), stackDepth);
|
||
|
cg.emit (OpCode::callNative, nativeFn->functionID);
|
||
|
cg.emitCast (nativeFn->returnType, requiredType, location);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (auto b = findBuiltInFunction (functionID))
|
||
|
{
|
||
|
emitArgs (cg, getArgTypesFromFunctionName (b->name), stackDepth);
|
||
|
cg.emit (b->op);
|
||
|
cg.emitCast (b->returnType, requiredType, location);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
throwCannotFindFunctionError (cg, requiredType);
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator& cg) const override
|
||
|
{
|
||
|
if (arguments.size() == 1)
|
||
|
{
|
||
|
if (functionName == Token::float_) return Type::float_;
|
||
|
if (functionName == Token::int_) return Type::int_;
|
||
|
if (functionName == Token::bool_) return Type::bool_;
|
||
|
}
|
||
|
|
||
|
auto functionID = getFunctionID (cg);
|
||
|
|
||
|
if (auto fn = cg.syntaxTree.findFunction (functionID))
|
||
|
return fn->returnType;
|
||
|
|
||
|
if (auto nativeFn = cg.syntaxTree.findNativeFunction (functionID))
|
||
|
return nativeFn->returnType;
|
||
|
|
||
|
if (auto b = findBuiltInFunction (functionID))
|
||
|
return b->returnType;
|
||
|
|
||
|
throwCannotFindFunctionError (cg, Type::void_);
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
struct BuiltInFunction { OpCode op; Type returnType; const char* name; };
|
||
|
|
||
|
const BuiltInFunction* findBuiltInFunction (FunctionID functionID) const noexcept
|
||
|
{
|
||
|
static constexpr const BuiltInFunction builtIns[] =
|
||
|
{
|
||
|
{ OpCode::getHeapByte, Type::int_, "getHeapByte/ii" },
|
||
|
{ OpCode::getHeapInt, Type::int_, "getHeapInt/ii" },
|
||
|
{ OpCode::getHeapBits, Type::int_, "getHeapBits/iii" },
|
||
|
{ OpCode::setHeapByte, Type::void_, "setHeapByte/vii" },
|
||
|
{ OpCode::setHeapInt, Type::void_, "setHeapInt/vii" }
|
||
|
};
|
||
|
|
||
|
for (auto& b : builtIns)
|
||
|
if (functionID == NativeFunction::createID (b.name))
|
||
|
return &b;
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
void emitArgs (CodeGenerator& cg, const Array<Type>& argTypes, int stackDepth) const
|
||
|
{
|
||
|
for (int i = arguments.size(); --i >= 0;)
|
||
|
{
|
||
|
auto argType = argTypes[i];
|
||
|
auto argValue = arguments.getUnchecked(i);
|
||
|
|
||
|
if (argValue->getType (cg) != argType)
|
||
|
location.throwError ("Argument " + String (i + 1) + " requires an expression of type " + getTypeName (argType));
|
||
|
|
||
|
argValue->emit (cg, argType, stackDepth++);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void emitCast (CodeGenerator& cg, Type destType, int stackDepth) const
|
||
|
{
|
||
|
if (arguments.size() != 1)
|
||
|
location.throwError (getTypeName (destType) + " cast operation requires a single argument");
|
||
|
|
||
|
auto* arg = arguments.getReference (0);
|
||
|
const auto sourceType = arg->getType (cg);
|
||
|
arg->emit (cg, sourceType, stackDepth);
|
||
|
|
||
|
const bool sourceIsFloat = (sourceType == Type::float_);
|
||
|
const bool destIsFloat = (destType == Type::float_);
|
||
|
|
||
|
if (sourceIsFloat != destIsFloat)
|
||
|
cg.emit (destIsFloat ? OpCode::int32ToFloat : OpCode::floatToInt32);
|
||
|
}
|
||
|
|
||
|
FunctionID getFunctionID (CodeGenerator& cg) const
|
||
|
{
|
||
|
Array<Type> argTypes;
|
||
|
|
||
|
for (auto arg : arguments)
|
||
|
argTypes.add (arg->getType (cg));
|
||
|
|
||
|
return createFunctionID (functionName, Type::void_, argTypes); // NB: the ID ignores the return type so void is OK
|
||
|
}
|
||
|
|
||
|
void throwCannotFindFunctionError (CodeGenerator& cg, Type returnType) const
|
||
|
{
|
||
|
StringArray args;
|
||
|
for (auto arg : arguments)
|
||
|
args.add (getTypeName (arg->getType (cg)));
|
||
|
|
||
|
auto desc = getTypeName (returnType) + " " + functionName
|
||
|
+ "(" + args.joinIntoString (", ") + ")";
|
||
|
|
||
|
location.throwError ("Cannot find matching function: " + desc.quoted());
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
for (auto& arg : arguments)
|
||
|
visit (arg);
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
for (auto& arg : arguments)
|
||
|
arg = arg->simplify (stb);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
String functionName;
|
||
|
Array<ExpPtr> arguments;
|
||
|
};
|
||
|
|
||
|
struct ArraySubscript : public Expression
|
||
|
{
|
||
|
ArraySubscript (const CodeLocation& l, BlockPtr parent) noexcept : Expression (l, parent) {}
|
||
|
|
||
|
void emit (CodeGenerator&, Type /*requiredType*/, int /*stackDepth*/) const override
|
||
|
{
|
||
|
location.throwError ("Arrays are not implemented yet!");
|
||
|
}
|
||
|
|
||
|
void visitSubStatements (Statement::Visitor& visit) const override
|
||
|
{
|
||
|
visit (object); visit (index);
|
||
|
}
|
||
|
|
||
|
ExpPtr simplify (SyntaxTreeBuilder& stb) override
|
||
|
{
|
||
|
object = object->simplify (stb);
|
||
|
index = index->simplify (stb);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
Type getType (CodeGenerator& cg) const override
|
||
|
{
|
||
|
return object->getType (cg);
|
||
|
}
|
||
|
|
||
|
ExpPtr object, index;
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
static Array<Type> getArgTypesFromFunctionName (const char* nameAndTypes)
|
||
|
{
|
||
|
Array<Type> types;
|
||
|
auto args = String (nameAndTypes).fromFirstOccurrenceOf ("/", false, false).substring (1);
|
||
|
|
||
|
for (int i = 0; i < args.length(); ++i)
|
||
|
types.add (static_cast<Type> (args[i]));
|
||
|
|
||
|
return types;
|
||
|
}
|
||
|
|
||
|
static FunctionID createFunctionID (String name, Type returnType, const Array<Type>& types)
|
||
|
{
|
||
|
name << "/" << (char) returnType;
|
||
|
|
||
|
for (auto t : types)
|
||
|
name << (char) t;
|
||
|
|
||
|
return NativeFunction::createID (name.toRawUTF8());
|
||
|
}
|
||
|
|
||
|
static String getTokenDescription (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); }
|
||
|
|
||
|
static String getTypeName (Type t) noexcept
|
||
|
{
|
||
|
if (t == Type::int_) return "int";
|
||
|
if (t == Type::bool_) return "bool";
|
||
|
if (t == Type::float_) return "float";
|
||
|
return "void";
|
||
|
}
|
||
|
|
||
|
static Type tokenToType (TokenType t) noexcept
|
||
|
{
|
||
|
if (t == Token::int_) return Type::int_;
|
||
|
if (t == Token::bool_) return Type::bool_;
|
||
|
if (t == Token::float_) return Type::float_;
|
||
|
return Type::void_;
|
||
|
}
|
||
|
|
||
|
static Type getTypeOfVar (const var& v) noexcept
|
||
|
{
|
||
|
if (v.isInt() || v.isInt64()) return Type::int_;
|
||
|
if (v.isDouble()) return Type::float_;
|
||
|
if (v.isBool()) return Type::bool_;
|
||
|
return Type::void_;
|
||
|
}
|
||
|
|
||
|
#endif // ! DOXYGEN
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
#if JUCE_MSVC
|
||
|
#pragma warning (pop)
|
||
|
#endif
|