1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

LittleFoot: Added the ability to #include other LittleFoot scripts

This commit is contained in:
Dimitri Sudell 2018-09-14 15:36:58 +01:00 committed by Tom Poole
parent c59cd2d9f1
commit 33a724ec5d
3 changed files with 103 additions and 13 deletions

View file

@ -215,6 +215,9 @@ public:
/** Returns the LittleFoot program to execute on the BLOCKS device. */
virtual juce::String getLittleFootProgram() = 0;
/** Returns an array of search paths to use when resolving includes. **/
virtual juce::Array<juce::File> getSearchPaths() { return {}; }
Block& block;
};

View file

@ -61,11 +61,11 @@ struct Compiler
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)
Result compile (const String& sourceCode, uint32 defaultHeapSize, const Array<File>& searchPaths = {})
{
try
{
SyntaxTreeBuilder stb (sourceCode, nativeFunctions, defaultHeapSize);
SyntaxTreeBuilder stb (sourceCode, nativeFunctions, defaultHeapSize, searchPaths);
stb.compile();
stb.simplify();
@ -141,8 +141,8 @@ private:
//==============================================================================
struct CodeLocation
{
CodeLocation (const String& code) noexcept : program (code), location (program.getCharPointer()) {}
CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {}
CodeLocation (const String& code, const File& srcFile) noexcept : program (code), location (program.getCharPointer()), sourceFile (srcFile) {}
CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location), sourceFile (other.sourceFile) {}
[[noreturn]] void throwError (const String& message) const
{
@ -154,17 +154,19 @@ private:
if (*i == '\n') { col = 1; ++line; }
}
throw "Line " + String (line) + ", column " + String (col) + " : " + message;
auto filePath = sourceFile == File() ? String() : (sourceFile.getFullPathName() + ": ");
throw filePath + "Line " + String (line) + ", column " + String (col) + " : " + message;
}
String program;
String::CharPointerType location;
File sourceFile;
};
//==============================================================================
struct TokenIterator
{
TokenIterator (const String& code) : location (code), p (code.getCharPointer()) { skip(); }
TokenIterator (const String& code) : location (code, {}), p (code.getCharPointer()) { skip(); }
TokenType skip()
{
@ -189,15 +191,16 @@ private:
bool matchesAny (TokenType t1, Args... others) const noexcept { return currentType == t1 || matchesAny (others...); }
bool matchesAny (TokenType t1) const noexcept { return currentType == t1; }
void throwErrorExpecting (const String& expected) { location.throwError ("Found " + getTokenDescription (currentType) + " when expecting " + expected); }
CodeLocation location;
TokenType currentType;
var currentValue;
void throwErrorExpecting (const String& expected) { location.throwError ("Found " + getTokenDescription (currentType) + " when expecting " + expected); }
private:
protected:
String::CharPointerType p;
private:
static bool isIdentifierStart (juce_wchar c) noexcept { return CharacterFunctions::isLetter (c) || c == '_'; }
static bool isIdentifierBody (juce_wchar c) noexcept { return CharacterFunctions::isLetterOrDigit (c) || c == '_'; }
@ -365,12 +368,23 @@ private:
//==============================================================================
struct SyntaxTreeBuilder : private TokenIterator
{
SyntaxTreeBuilder (const String& code, const Array<NativeFunction>& nativeFns, uint32 defaultHeapSize)
: TokenIterator (code), nativeFunctions (nativeFns), heapSizeRequired (defaultHeapSize) {}
SyntaxTreeBuilder (const String& code, const Array<NativeFunction>& nativeFns, uint32 defaultHeapSize, const Array<File>& searchPathsToUse)
: TokenIterator (code), searchPaths (searchPathsToUse), nativeFunctions (nativeFns), heapSizeRequired (defaultHeapSize) {}
void compile()
{
blockBeingParsed = allocate<BlockStatement> (location, nullptr, nullptr, false);
parseCode();
}
void parseCode()
{
const auto programHash = location.program.hashCode64();
if (includedSourceCode.contains (programHash))
return;
includedSourceCode.add (programHash);
while (currentType != Token::eof)
{
@ -431,6 +445,8 @@ private:
//==============================================================================
BlockPtr blockBeingParsed = nullptr;
Array<Function*> functions;
Array<File> searchPaths;
Array<int64> includedSourceCode;
const Array<NativeFunction>& nativeFunctions;
uint32 heapSizeRequired;
@ -449,10 +465,81 @@ private:
{
match (Token::colon);
heapSizeRequired = (((uint32) parseIntegerLiteral()) + 3) & ~3u;
}
else if (name == "include")
{
parseIncludeDirective();
}
else
{
location.throwError ("Unknown compiler directive");
}
}
void parseIncludeDirective()
{
match (Token::literal);
if (! currentValue.isString())
{
location.throwError ("Expected file path");
return;
}
location.throwError ("Unknown compiler directive");
juce::File fileToInclude = resolveIncludePath (currentValue.toString());
if (fileToInclude == File())
return;
searchPaths.add (fileToInclude);
auto codeToInclude = fileToInclude.loadFileAsString();
auto locationToRestore = location;
auto currentTypeToRestore = currentType;
auto currentValueToRestore = currentValue;
auto pToRestore = p;
location = CodeLocation (codeToInclude, fileToInclude);
p = codeToInclude.getCharPointer();
skip();
parseCode();
location = locationToRestore;
currentType = currentTypeToRestore;
currentValue = currentValueToRestore;
p = pToRestore;
}
File resolveIncludePath (String include)
{
if (include.substring (include.length() - 11) != ".littlefoot")
{
location.throwError ("File extension must be .littlefoot");
return {};
}
if (File::isAbsolutePath (include) && File (include).existsAsFile())
return { include };
auto fileName = include.fromLastOccurrenceOf ("/", false, false);
for (auto path : searchPaths)
{
if (path == File())
continue;
if (! path.isDirectory())
path = path.getParentDirectory();
if (path.getChildFile (include).existsAsFile())
return path.getChildFile (include);
if (path.getChildFile (fileName).existsAsFile())
return path.getChildFile (fileName);
}
location.throwError ("File not found: " + include);
}
void parseGlobalVariableDeclaraion (bool isConst, Type type, String name)

View file

@ -1695,7 +1695,7 @@ struct PhysicalTopologySource::Internal
littlefoot::Compiler compiler;
compiler.addNativeFunctions (PhysicalTopologySource::getStandardLittleFootFunctions());
auto err = compiler.compile (program->getLittleFootProgram(), 512);
auto err = compiler.compile (program->getLittleFootProgram(), 512, program->getSearchPaths());
if (err.failed())
return err;