diff --git a/modules/juce_blocks_basics/littlefoot/juce_LittleFootCompiler.h b/modules/juce_blocks_basics/littlefoot/juce_LittleFootCompiler.h index fbd8cd5570..4fad19b98d 100644 --- a/modules/juce_blocks_basics/littlefoot/juce_LittleFootCompiler.h +++ b/modules/juce_blocks_basics/littlefoot/juce_LittleFootCompiler.h @@ -98,6 +98,7 @@ private: struct Statement; struct Expression; + struct Variable; struct BlockStatement; struct Function; struct AllocatedObject { virtual ~AllocatedObject() noexcept {} }; @@ -375,6 +376,7 @@ private: { blockBeingParsed = allocate (location, nullptr, nullptr, false); parseCode(); + heapSizeRequired += arrayHeapSize; } void parseCode() @@ -449,6 +451,7 @@ private: Array includedSourceCode; const Array& nativeFunctions; uint32 heapSizeRequired; + uint32 arrayHeapSize = 0; template Type* allocate (Args... args) { auto o = new Type (args...); allAllocatedObjects.add (o); return o; } @@ -542,21 +545,21 @@ private: location.throwError ("File not found: " + include); } + //TODO: should there be a max array size? 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::openBracket)) + { + int arraySize = 0; + parseGlobalArray (arraySize, type, name, nullptr); + arrayHeapSize += uint32 (arraySize * 4); + } + else + { + parseGlobalVariable (isConst, type, name); + } if (matchIf (Token::comma)) { @@ -569,6 +572,40 @@ private: } } + void parseGlobalArray (int& arraySize, Type type, const String& name, Variable* parent) + { + const auto value = parseIntegerLiteral(); + match (Token::closeBracket); + + blockBeingParsed->addVariable ({ {}, type, true, false, {}, value, parent }, location); + + auto& newArray = blockBeingParsed->arrays.getReference (blockBeingParsed->arrays.size() - 1); + + if (parent != nullptr) + parent->nextArray = &newArray; + + if (matchIf (Token::openBracket)) + { + parseGlobalArray (arraySize, type, name, &newArray); + arraySize *= value; + } + else + { + newArray.name = name; + arraySize = value; + } + } + + void parseGlobalVariable (bool isConst, Type type, String name) + { + var constantInitialiser; + + if (isConst) + constantInitialiser = parseConstantExpressionInitialiser (type); + + blockBeingParsed->addVariable ({ name, type, true, isConst, constantInitialiser, 0 }, location); + } + var parseConstantExpressionInitialiser (Type expectedType) { var result; @@ -701,7 +738,7 @@ private: if (matchIf (Token::assign)) { auto loc = location; - return allocate (loc, blockBeingParsed, getIdentifierFromExpression (lhs), parseExpression(), false); + return allocate (loc, blockBeingParsed, lhs, parseExpression(), false); } return lhs; @@ -831,26 +868,31 @@ private: return {}; } - if (matchIf (Token::openBracket)) - { - auto s = allocate (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); + if (matchIf (Token::openBracket)) return parseArraySubscript (input); + if (matchIf (Token::plusplus)) return parsePostIncDec (input, Token::plus); + if (matchIf (Token::minusminus)) return parsePostIncDec (input, Token::minus); return input; } + ExpPtr parseArraySubscript (ExpPtr input) + { + auto s = allocate (location, blockBeingParsed); + s->object = input; + s->index = parseExpression(); + match (Token::closeBracket); + + if (matchIf (Token::openBracket)) + return parseArraySubscript (s); + + return s; + } + ExpPtr parseInPlaceOpExpression (ExpPtr lhs, TokenType opType) { auto loc = location; auto rhs = parseExpression(); - return allocate (loc, blockBeingParsed, getIdentifierFromExpression (lhs), + return allocate (loc, blockBeingParsed, lhs, allocate (location, blockBeingParsed, lhs, rhs, opType), false); } @@ -858,14 +900,14 @@ private: { auto lhs = parseFactor(); auto one = allocate (location, blockBeingParsed, (int) 1); - return allocate (location, blockBeingParsed, getIdentifierFromExpression (lhs), + return allocate (location, blockBeingParsed, lhs, allocate (location, blockBeingParsed, lhs, one, opType), false); } ExpPtr parsePostIncDec (ExpPtr lhs, TokenType opType) { auto one = allocate (location, blockBeingParsed, (int) 1); - return allocate (location, blockBeingParsed, getIdentifierFromExpression (lhs), + return allocate (location, blockBeingParsed, lhs, allocate (location, blockBeingParsed, lhs, one, opType), true); } @@ -896,17 +938,17 @@ private: for (StatementPtr result = nullptr;;) { - auto name = parseIdentifier(); + auto identifier = allocate (location, blockBeingParsed, parseIdentifier()); auto loc = location; if (isConst) { auto constantValue = parseConstantExpressionInitialiser (type); - blockBeingParsed->addVariable ({ name, type, false, true, constantValue }, loc); + blockBeingParsed->addVariable ({ identifier->getIdentifier(), type, false, true, constantValue }, loc); } else { - blockBeingParsed->addVariable ({ name, type, false, false, {} }, loc); + blockBeingParsed->addVariable ({ identifier->getIdentifier(), type, false, false, {} }, loc); auto assignedValue = matchIf (Token::assign) ? parseExpression() : nullptr; @@ -919,7 +961,7 @@ private: if (assignedValue == nullptr) assignedValue = allocate (loc, blockBeingParsed, (int) 0); - auto assignment = allocate (loc, blockBeingParsed, name, assignedValue, false); + auto assignment = allocate (loc, blockBeingParsed, identifier, assignedValue, false); if (result == nullptr) { @@ -996,15 +1038,6 @@ private: return name; } - String getIdentifierFromExpression (ExpPtr e) - { - if (auto i = dynamic_cast (e)) - return i->name; - - location.throwError ("This operator requires an assignable variable"); - return {}; - } - ExpPtr parseFunctionCall (const String& name) { auto call = allocate (location, blockBeingParsed); @@ -1034,7 +1067,7 @@ private: //============================================================================== struct CodeGenerator { - CodeGenerator (Array& output, const SyntaxTreeBuilder& stb) + CodeGenerator (Array& output, SyntaxTreeBuilder& stb) : outputCode (output), syntaxTree (stb) {} void generateCode (BlockPtr outerBlock, uint32 heapSizeBytesRequired) @@ -1070,7 +1103,7 @@ private: //============================================================================== Array& outputCode; - const SyntaxTreeBuilder& syntaxTree; + SyntaxTreeBuilder& syntaxTree; struct Marker { int index = 0; }; struct MarkerAndAddress { Marker marker; int address; }; @@ -1221,6 +1254,44 @@ private: emitCast (sourceType, requiredType, location); } + + void emitArrayElementIndex (const Expression* target, BlockPtr parentBlock, + int stackDepth, const CodeLocation& errorLocation) + { + if (auto currentSubscript = dynamic_cast (target)) + { + const auto identifier = currentSubscript->getIdentifier(); + auto array = parentBlock->getArray (identifier, errorLocation); + + ExpPtr elementIndent = nullptr; + auto currentArray = &array; + + while (currentSubscript != nullptr && currentArray != nullptr) + { + auto lhs = syntaxTree.allocate (errorLocation, parentBlock, parentBlock->getArrayElementSizeInBytes (*currentArray)); + ExpPtr subscriptIndent = syntaxTree.allocate (errorLocation, parentBlock, lhs, currentSubscript->index, Token::times); + + if (elementIndent == nullptr) + elementIndent = subscriptIndent; + else + elementIndent = syntaxTree.allocate (errorLocation, parentBlock, elementIndent, subscriptIndent, Token::plus); + + currentSubscript = dynamic_cast (currentSubscript->object); + currentArray = currentArray->previousArray; + } + + auto arrayStart = (int) (syntaxTree.heapSizeRequired - syntaxTree.arrayHeapSize) + parentBlock->getArrayStart (identifier, errorLocation); + auto lhs = syntaxTree.allocate (errorLocation, parentBlock, arrayStart); + elementIndent = syntaxTree.allocate (errorLocation, parentBlock, lhs, elementIndent, Token::plus); + + elementIndent = elementIndent->simplify (syntaxTree); + elementIndent->emit (*this, Type::int_, stackDepth); + } + else + { + errorLocation.throwError ("Cannot cast Expression to ArraySubscript"); + } + } }; //============================================================================== @@ -1248,6 +1319,7 @@ private: Expression (const CodeLocation& l, BlockPtr parent) noexcept : Statement (l, parent) {} virtual Type getType (CodeGenerator&) const = 0; virtual ExpPtr simplify (SyntaxTreeBuilder&) override { return this; } + virtual String getIdentifier() const { location.throwError ("This operator requires an assignable variable"); return {}; } }; struct Variable @@ -1256,6 +1328,9 @@ private: Type type; bool isGlobal, isConst; var constantValue; + int numElements = 0; + Variable* previousArray = nullptr; + Variable* nextArray = nullptr; }; //============================================================================== @@ -1387,7 +1462,8 @@ private: // returns -ve values for globals int getVariableDepth (const String& name, const CodeLocation& locationForError) const { - int index = indexOf (variables, name); + auto index = indexOf (variables, name); + if (index >= 0) return getNumVariablesInParentBlocks() + index; @@ -1400,11 +1476,49 @@ private: return i + 1 + function->getNumLocals(); index = indexOf (getGlobalVariables(), name); + if (index >= 0) return -(index + 1); locationForError.throwError ("Unknown variable '" + name + "'"); - return 0; + } + + Variable getArray (const String& name, const CodeLocation& locationForError) const + { + for (const auto& array : getGlobalArrays()) + if (array.name == name) + return array; + + locationForError.throwError ("Unknown array '" + name + "'"); + } + + int getArraySizeInBytes (const Variable& array) const + { + return array.numElements * getArrayElementSizeInBytes (array); + } + + int getArrayElementSizeInBytes (const Variable& array) const + { + if (array.nextArray != nullptr) + return getArraySizeInBytes (*array.nextArray); + + return numBytesInType; + } + + int getArrayStart (const String& name, const CodeLocation& locationForError) const + { + int start = 0; + + for (const auto& array : getGlobalArrays()) + { + if (array.name == name) + return start; + + if (array.name.isNotEmpty()) + start += getArraySizeInBytes (array); + } + + locationForError.throwError ("Unknown array '" + name + "'"); } int getNumVariablesInParentBlocks() const noexcept @@ -1415,6 +1529,7 @@ private: const Array& getGlobalVariables() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalVariables() : variables; } const Array& getGlobalConstants() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalConstants() : constants; } + const Array& getGlobalArrays() const noexcept { return parentBlock != nullptr ? parentBlock->getGlobalArrays() : arrays; } const Variable& getVariable (const String& name, const CodeLocation& locationForError) const { @@ -1442,15 +1557,19 @@ private: if (v.name == name) return v; + for (auto& v : getGlobalArrays()) + 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) + if (v.name.isNotEmpty() && (indexOf (variables, v.name) >= 0 || indexOf (constants, v.name) >= 0 || indexOf (arrays, v.name) >= 0)) locationForError.throwError ("Variable '" + v.name + "' already exists"); - (v.isConst ? constants : variables).add (v); + (v.numElements == 0 ? (v.isConst ? constants : variables) : arrays).add (v); } static int indexOf (const Array& vars, const String& name) noexcept @@ -1464,7 +1583,7 @@ private: Function* function; Array statements; - Array variables, constants; + Array variables, constants, arrays; bool isMainBlockOfFunction; }; @@ -1752,6 +1871,11 @@ private: return this; } + String getIdentifier() const override + { + return name; + } + String name; }; @@ -1994,7 +2118,7 @@ private: struct Assignment : public Expression { - Assignment (const CodeLocation& l, BlockPtr parent, const String& dest, ExpPtr source, bool isPost) noexcept + Assignment (const CodeLocation& l, BlockPtr parent, ExpPtr 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 @@ -2003,42 +2127,50 @@ private: if (isPostAssignment && requiredType != Type::void_) { - cg.emitVariableRead (variableType, requiredType, stackDepth, - parentBlock->getVariableDepth (target, location), location); + target->emit (cg, requiredType, stackDepth); ++stackDepth; requiredType = Type::void_; } newValue->emit (cg, variableType, stackDepth); - auto index = parentBlock->getVariableDepth (target, location); - if (requiredType != Type::void_) + if (auto a = dynamic_cast (target)) { - cg.emit (OpCode::dup); - ++stackDepth; - } - - if (index < 0) - { - cg.emit (OpCode::dropToGlobal, (int16) ((-index) - 1)); + cg.emitArrayElementIndex (a, parentBlock, stackDepth, location); + cg.emit (OpCode::setHeapInt); } else { - index += stackDepth; + auto index = parentBlock->getVariableDepth (target->getIdentifier(), location); - if (index >= 128) - cg.emit (OpCode::dropToStack16, (int16) index); + if (requiredType != Type::void_) + { + cg.emit (OpCode::dup); + ++stackDepth; + } + + if (index < 0) + { + cg.emit (OpCode::dropToGlobal, (int16) ((-index) - 1)); + } else - cg.emit (OpCode::dropToStack, (int8) index); - } + { + index += stackDepth; - if (requiredType != Type::void_) - cg.emitCast (variableType, requiredType, location); + 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; + return parentBlock->getVariable (target->getIdentifier(), location).type; } void visitSubStatements (Statement::Visitor& visit) const override @@ -2052,8 +2184,7 @@ private: return this; } - String target; - ExpPtr newValue; + ExpPtr target, newValue; bool isPostAssignment; }; @@ -2214,9 +2345,10 @@ private: { ArraySubscript (const CodeLocation& l, BlockPtr parent) noexcept : Expression (l, parent) {} - void emit (CodeGenerator&, Type /*requiredType*/, int /*stackDepth*/) const override + void emit (CodeGenerator& cg, Type /*requiredType*/, int stackDepth) const override { - location.throwError ("Arrays are not implemented yet!"); + cg.emitArrayElementIndex (this, parentBlock, stackDepth, location); + cg.emit (OpCode::getHeapInt); } void visitSubStatements (Statement::Visitor& visit) const override @@ -2236,6 +2368,18 @@ private: return object->getType (cg); } + String getIdentifier() const override + { + if (auto i = dynamic_cast (object)) + return i->name; + + if (auto s = dynamic_cast (object)) + return s->getIdentifier(); + + location.throwError ("This operator requires an assignable variable"); + return {}; + } + ExpPtr object, index; }; diff --git a/modules/juce_blocks_basics/littlefoot/juce_LittleFootRunner.h b/modules/juce_blocks_basics/littlefoot/juce_LittleFootRunner.h index 4ab529e1e0..788474f35a 100644 --- a/modules/juce_blocks_basics/littlefoot/juce_LittleFootRunner.h +++ b/modules/juce_blocks_basics/littlefoot/juce_LittleFootRunner.h @@ -130,6 +130,8 @@ enum class Type : uint8 float_ = 'f' }; +const int numBytesInType = 4; + //============================================================================== /** Defines a native function that the program can call.