diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f116f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/cmake-build-debug/ +/.idea/ diff --git a/acs_lib/general.acs b/acs_lib/general.acs index 6ec4ef3..14d1c29 100644 --- a/acs_lib/general.acs +++ b/acs_lib/general.acs @@ -6,9 +6,12 @@ extern *: (float64, float64) -> float64; //logical operators extern <: (float64, float64) -> bool; extern <=: (float64, float64) -> bool; +extern =: (float64, float64) -> bool; extern and: (bool, bool) -> bool; extern not: bool -> bool; extern or: (bool, bool) -> bool; //general purpose +extern []: (float64, float64) -> float64; //TODO: make this a generic, make this work with ... +extern __set: (any, string, any) -> any; //TODO: this is ugly extern print: any -> null; \ No newline at end of file diff --git a/examples/05_fib_linear.acs b/examples/05_fib_linear.acs new file mode 100644 index 0000000..e805a4c --- /dev/null +++ b/examples/05_fib_linear.acs @@ -0,0 +1,8 @@ +let helper := { + (current_n, n, a, _) | current_n = n -> a, + (current_n, n, a, b) -> self( (current_n + 1, n, b, a + b) ) +}; + +let fib := n -> helper( (0, n, 0, 1) ); + +print(fib(30)); \ No newline at end of file diff --git a/examples/06_objects.acs b/examples/06_objects.acs new file mode 100644 index 0000000..6066154 --- /dev/null +++ b/examples/06_objects.acs @@ -0,0 +1,30 @@ +/*let square := x -> x * x; + +let test := square(5); +print(test);*/ + + + +let new_vec := { + x: 5, + y: 5, + + test: { x, y } -> x * y +}; +print(new_vec); +//print(new_vec.test()); + + + +/* +let new_vec := (x, y) -> { + x: x, + y: y, + + *: ( { x, y }, scaleFactor) -> new_vec( (x * scaleFactor, y * scaleFactor) ) +}; + +let vec := new_vec( (5, 5) ); + +let vecTest := square(vec); +print(vecTest);*/ \ No newline at end of file diff --git a/examples/objects.acs b/examples/objects.acs deleted file mode 100644 index 0a31952..0000000 --- a/examples/objects.acs +++ /dev/null @@ -1,17 +0,0 @@ -vec := < x: 0, y: 0 >; - -vec.add := func (dx, dy) => { - this.x += dx; - this.y += dy; -} - -vec.scale := func scale => { - this.x *= scale; - this.y *= scale; -} - -Print(vec); -vec.add(1, 2); -Print(vec); -vec.scale(2); -Print(vec); \ No newline at end of file diff --git a/include/acsb/Opcode.hpp b/include/acsb/Opcode.hpp index 2679fa3..ec2b848 100644 --- a/include/acsb/Opcode.hpp +++ b/include/acsb/Opcode.hpp @@ -24,7 +24,10 @@ enum class Opcode : uint8 CallExtern, JumpOnFalse, LoadConstant, + NewDictionary, NewTuple, - PushParameter, + Pop, + PopAssign, + Push, Return, }; \ No newline at end of file diff --git a/parser/lexer.l b/parser/lexer.l index a3e2f1a..b6aa59e 100644 --- a/parser/lexer.l +++ b/parser/lexer.l @@ -28,6 +28,7 @@ extern AST::ParserState* g_parserState; %% [ \t\r\n] ; //ignore whitespaces "//".* {} //skip comments +[/][*][^*]*[*]+([^*/][^*]*[*]+)*[/] {} //skip comments "," { return TOKEN_COMMA; } ":" { return TOKEN_COLON; } @@ -43,10 +44,11 @@ extern AST::ParserState* g_parserState; "+" { return TOKEN_PLUS; } "-" { return TOKEN_MINUS; } +"=" { return TOKEN_EQUALS; } "extern" { return TOKEN_KEYWORD_EXTERN; } "let" { return TOKEN_KEYWORD_LET; } [0-9]+ { yylval.str = g_parserState->CreateString(yytext); return TOKEN_NATURAL_LITERAL; } -[a-zA-Z0-9\-\*<=]+ { yylval.str = g_parserState->CreateString(yytext); return TOKEN_IDENTIFIER; } +[a-zA-Z0-9\-\*<=_\[\]]+ { yylval.str = g_parserState->CreateString(yytext); return TOKEN_IDENTIFIER; } %% \ No newline at end of file diff --git a/parser/parser.y b/parser/parser.y index f8da69f..6111db7 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -38,6 +38,7 @@ void yyerror(AST::ParserState* parserState, const char* s); %token TOKEN_PLUS %token TOKEN_MINUS +%token TOKEN_EQUALS %token TOKEN_KEYWORD_EXTERN %token TOKEN_KEYWORD_LET @@ -48,9 +49,6 @@ void yyerror(AST::ParserState* parserState, const char* s); %start module -%left TOKEN_PAREN_OPEN - - %union { @@ -58,6 +56,7 @@ void yyerror(AST::ParserState* parserState, const char* s); AST::Expression* expr; AST::FunctionExpression* funcExpr; + AST::ObjectExpression* objExpr; AST::ExpressionList* exprList; AST::LeftValue* lvalue; @@ -72,125 +71,136 @@ void yyerror(AST::ParserState* parserState, const char* s); %type function_name %type expression +%type expression_without_function_value %type value_expression -%type function_call -%type function_expression -%type functionRules -%type pattern -%type tuple_expression -%type tuple_expressions + +%type function_rule_leaf +%type function_rules + +%type object_entry +%type object_entries + +%type tuple_entries + %type statement %type typespec %type function_type -%type tuple_type +%type tuple_type %type tuple_type_entries %type left_value %parse-param { AST::ParserState* parserState } -%% +%right infixprec +%left TOKEN_IDENTIFIER +%left TOKEN_MAP +%% module: - statement { parserState->ModuleStatements().AddStatement($1); } - | statement module { parserState->ModuleStatements().AddStatement($1); } + statement { parserState->ModuleStatements().AddStatement($1); } + | statement module { parserState->ModuleStatements().AddStatement($1); } ; statement: TOKEN_KEYWORD_EXTERN function_name TOKEN_COLON typespec TOKEN_SEMICOLON { $$ = new AST::ExternalDeclarationStatement(*$2, $4); } - | function_call TOKEN_SEMICOLON { $$ = new AST::ExpressionStatement($1); } | TOKEN_KEYWORD_LET left_value TOKEN_ASSIGN expression TOKEN_SEMICOLON { $$ = new AST::VariableDefinitionStatement($2, $4); } + | expression TOKEN_SEMICOLON { $$ = new AST::ExpressionStatement($1); } ; - - - -typespec: - TOKEN_IDENTIFIER { $$ = new AST::IdentifierTypeSpec(*$1); } - | function_type { $$ = $1; } - | tuple_type { $$ = $1; } +function_name: + TOKEN_IDENTIFIER { $$ = $1; } + | TOKEN_PLUS { $$ = parserState->CreateString(u8"+"); } + | TOKEN_MINUS { $$ = parserState->CreateString(u8"-"); } + | TOKEN_EQUALS { $$ = parserState->CreateString(u8"="); } ; -function_type: - typespec TOKEN_MAP typespec { $$ = new AST::FunctionTypeSpec($1, $3); } +left_value: + TOKEN_IDENTIFIER { $$ = new AST::IdentifierLeftValue(*$1); } ; -tuple_type_entries: - typespec { $$ = new AST::TupleTypeSpec($1); } - | typespec TOKEN_MORE { $$ = new AST::TupleTypeSpec($1, true); } - | typespec TOKEN_COMMA tuple_type_entries { $$ = $3; $3->AddTypeSpec($1); } -; -tuple_type: - TOKEN_PAREN_OPEN tuple_type_entries TOKEN_PAREN_CLOSE { $$ = $2; } -; +expression: + function_rule_leaf { $$ = $1; } + | TOKEN_BRACE_OPEN function_rules TOKEN_BRACE_CLOSE { $$ = $2; } + | expression_without_function_value { $$ = $1; } +; +expression_without_function_value: + TOKEN_IDENTIFIER TOKEN_PAREN_OPEN expression TOKEN_PAREN_CLOSE { $$ = new AST::CallExpression(*$1, $3); } + | value_expression { $$ = $1; } + //infix + | expression_without_function_value TOKEN_IDENTIFIER expression_without_function_value { $$ = new AST::CallExpression(*$2, new AST::TupleExpression(new AST::ExpressionList($1, $3))); } + | expression_without_function_value TOKEN_PLUS expression_without_function_value { $$ = new AST::CallExpression(u8"+", new AST::TupleExpression(new AST::ExpressionList($1, $3))); } + | expression_without_function_value TOKEN_EQUALS expression_without_function_value { $$ = new AST::CallExpression(u8"=", new AST::TupleExpression(new AST::ExpressionList($1, $3))); } +; -function_name: - TOKEN_IDENTIFIER { $$ = $1; } - | TOKEN_PLUS { $$ = parserState->CreateString(u8"+"); } - | TOKEN_MINUS { $$ = parserState->CreateString(u8"-"); } +value_expression: + TOKEN_PAREN_OPEN tuple_entries TOKEN_PAREN_CLOSE { $$ = new AST::TupleExpression($2); } + | TOKEN_BRACE_OPEN object_entries TOKEN_BRACE_CLOSE { $$ = $2; } + | TOKEN_NATURAL_LITERAL { $$ = new AST::NaturalLiteralExpression(*$1); } + | TOKEN_IDENTIFIER { $$ = new AST::IdentifierExpression(*$1); } ; +function_rule_leaf: + value_expression TOKEN_MAP expression_without_function_value { $$ = new AST::FunctionExpression($1, nullptr, $3); } + | value_expression TOKEN_GUARD expression_without_function_value TOKEN_MAP expression_without_function_value { $$ = new AST::FunctionExpression($1, $3, $5); } +; -function_call: - TOKEN_IDENTIFIER TOKEN_PAREN_OPEN expression TOKEN_PAREN_CLOSE { $$ = new AST::CallExpression(*$1, $3); } +function_rules: + function_rule_leaf { $$ = $1; } + | function_rule_leaf TOKEN_COMMA function_rules { $$ = $3; $3->CombineRulesAndPrepend($1); } ; +tuple_entries: + expression { $$ = new AST::ExpressionList($1); } + | expression TOKEN_COMMA tuple_entries { $3->InsertAtBeginning($1); $$ = $3; } +; -expression: - value_expression { $$ = $1; } - | TOKEN_IDENTIFIER { $$ = new AST::IdentifierExpression(*$1); } - | function_call { $$ = $1; } - | expression function_name expression /*infix notation call*/ { $$ = new AST::CallExpression(*$2, new AST::TupleExpression(new AST::ExpressionList($1, $3))); } +object_entry: + function_name { $$ = new AST::ObjectExpression(*$1); } + | function_name TOKEN_COLON expression { $$ = new AST::ObjectExpression(*$1, $3); } ; -value_expression: - TOKEN_NATURAL_LITERAL { $$ = new AST::NaturalLiteralExpression(*$1); } - | function_expression { $$ = $1; } - | tuple_expression { $$ = $1; } +object_entries: + object_entry { $$ = $1; } + | object_entry TOKEN_COMMA object_entries { $$ = $3; $3->AddMember($1); } ; -function_expression: - pattern TOKEN_MAP expression { $$ = new AST::FunctionExpression($1, nullptr, $3); } - | TOKEN_BRACE_OPEN functionRules TOKEN_BRACE_CLOSE { $$ = $2; } -; -functionRules: - pattern TOKEN_MAP expression { $$ = new AST::FunctionExpression($1, nullptr, $3); } - | pattern TOKEN_MAP expression TOKEN_COMMA functionRules { $$ = $5; $5->AddRule($1, nullptr, $3); } - | pattern TOKEN_GUARD expression TOKEN_MAP expression TOKEN_COMMA functionRules { $$ = $7; $7->AddRule($1, $3, $5); } -; -tuple_expressions: - expression { $$ = new AST::ExpressionList($1); } - | expression TOKEN_COMMA tuple_expressions { $3->InsertAtBeginning($1); $$ = $3; } -; -tuple_expression: - TOKEN_PAREN_OPEN tuple_expressions TOKEN_PAREN_CLOSE { $$ = new AST::TupleExpression($2); } -; +typespec: + TOKEN_IDENTIFIER { $$ = new AST::IdentifierTypeSpec(*$1); } + | function_type { $$ = $1; } + | tuple_type { $$ = $1; } +; -left_value: - TOKEN_IDENTIFIER { $$ = new AST::IdentifierLeftValue(*$1); } +function_type: + typespec TOKEN_MAP typespec { $$ = new AST::FunctionTypeSpec($1, $3); } ; -pattern: - TOKEN_NATURAL_LITERAL { $$ = new AST::NaturalLiteralExpression(*$1); } - | TOKEN_IDENTIFIER { $$ = new AST::IdentifierExpression(*$1); } +tuple_type_entries: + typespec { $$ = new AST::TupleTypeSpec($1); } + | typespec TOKEN_MORE { $$ = new AST::TupleTypeSpec($1, true); } + | typespec TOKEN_COMMA tuple_type_entries { $$ = $3; $3->AddTypeSpec($1); } +; + +tuple_type: + TOKEN_PAREN_OPEN tuple_type_entries TOKEN_PAREN_CLOSE { $$ = $2; } ; %% \ No newline at end of file diff --git a/src/acsb/Compiler.cpp b/src/acsb/Compiler.cpp index c8bc962..f680f57 100644 --- a/src/acsb/Compiler.cpp +++ b/src/acsb/Compiler.cpp @@ -47,14 +47,6 @@ void ACSB::Compiler::CompileCall(const IR::CallOperation & op) this->executionStack.Pop(); this->executionStack.Pop(); - this->executionStack.Pop(); - this->executionStack.Push(&op.GetValue()); -} - -void Compiler::CompileCreateArray(const IR::CreateArrayOperation & op) -{ - this->AddInstruction(Opcode::CreateArray); - this->executionStack.Push(&op.GetValue()); } void Compiler::CompileStore(const IR::StoreOperation& op) @@ -120,21 +112,6 @@ void ACSB::Compiler::CompileSymbol(const IR::Symbol & symbol) this->AddInstruction(Opcode::PushProc, symbol.GetNumber()); } break; - case IR::SymbolType::Temporary: - { - uint32 index = Unsigned::Max(); - for (int32 idx = this->executionStack.GetNumberOfElements() - 1; idx >= 0; idx--) - { - if (*this->executionStack[idx] == symbol) - { - index = idx; - break; - } - } - - this->AddInstruction(Opcode::Push, index); - } - break; case IR::SymbolType::This: this->opcodes.Push(Opcode::PushThis); break; diff --git a/src/acsb/Compiler.hpp b/src/acsb/Compiler.hpp index 4f23aa8..38c1286 100644 --- a/src/acsb/Compiler.hpp +++ b/src/acsb/Compiler.hpp @@ -36,7 +36,6 @@ namespace ACSB void BeginProcedure() override; void CompileAccess(const IR::AccessOperation & op) override; void CompileCall(const IR::CallOperation & op) override; - void CompileCreateArray(const IR::CreateArrayOperation & op) override; void CompileStore(const IR::StoreOperation& op) override; void CompileWait(const IR::WaitOperation & op) override; void EndProcedure() override; @@ -55,7 +54,6 @@ namespace ACSB Map localMap; Map globalMap; uint16 nextLocalIdx; - DynamicArray executionStack; uint32 lastProcOffset; //Methods diff --git a/src/acsb/Opcode.hpp b/src/acsb/Opcode.hpp index f4975a4..cf14d63 100644 --- a/src/acsb/Opcode.hpp +++ b/src/acsb/Opcode.hpp @@ -24,7 +24,6 @@ enum Opcode : byte AssignGlobal, AssignLocal, CreateArray, - Push, PushGlobal, PushLocal, PushNull, @@ -38,7 +37,6 @@ enum Opcode : byte LessOrEqual, Minus, Multiply, - Pop, PushBool, PushSelf, */ diff --git a/src/acsb/VM.cpp b/src/acsb/VM.cpp index dba557f..7ec8307 100644 --- a/src/acsb/VM.cpp +++ b/src/acsb/VM.cpp @@ -97,15 +97,6 @@ void VM::ExecuteTask(RuntimeCall* task) executionStack.Push(RuntimeObject::Array()); } break; - case Opcode::Push: - { - uint32 stackIndex = *(uint32*)pc; - pc += sizeof(stackIndex); - - RuntimeObject obj = executionStack[stackIndex]; - executionStack.Push( obj ); - } - break; case Opcode::PushGlobal: { uint16 globalIndex = *(uint16*)pc; diff --git a/src_compiler/AST/AllNodes.hpp b/src_compiler/AST/AllNodes.hpp index edaa906..d6f99ce 100644 --- a/src_compiler/AST/AllNodes.hpp +++ b/src_compiler/AST/AllNodes.hpp @@ -22,6 +22,7 @@ #include "expressions/FunctionExpression.hpp" #include "expressions/IdentifierExpression.hpp" #include "expressions/NaturalLiteralExpression.hpp" +#include "expressions/ObjectExpression.hpp" #include "expressions/TupleExpression.hpp" #include "leftValues/IdentifierLeftValue.hpp" diff --git a/src_compiler/AST/CMakeLists.txt b/src_compiler/AST/CMakeLists.txt index 41d1f56..8170b48 100644 --- a/src_compiler/AST/CMakeLists.txt +++ b/src_compiler/AST/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCE_FILES_COMPILER ${CMAKE_CURRENT_SOURCE_DIR}/expressions/FunctionExpression.hpp ${CMAKE_CURRENT_SOURCE_DIR}/expressions/IdentifierExpression.hpp ${CMAKE_CURRENT_SOURCE_DIR}/expressions/NaturalLiteralExpression.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/expressions/ObjectExpression.hpp ${CMAKE_CURRENT_SOURCE_DIR}/expressions/TupleExpression.hpp ${CMAKE_CURRENT_SOURCE_DIR}/leftValues/LeftValue.hpp diff --git a/src_compiler/AST/expressions/FunctionExpression.hpp b/src_compiler/AST/expressions/FunctionExpression.hpp index 032911e..1057fc9 100644 --- a/src_compiler/AST/expressions/FunctionExpression.hpp +++ b/src_compiler/AST/expressions/FunctionExpression.hpp @@ -46,6 +46,14 @@ namespace AST this->rules.InsertFront({ Move(parameter), Move(guard), Move(statementBlock) }); } + inline void CombineRulesAndPrepend(UniquePointer functionExpression) + { + while (!functionExpression->Rules().IsEmpty()) + { + this->rules.InsertFront(functionExpression->rules.PopTail()); + } + } + void Visit(ExpressionVisitor &visitor) const override { visitor.OnVisitingFunctionExpression(*this); diff --git a/src_compiler/AST/expressions/ObjectExpression.hpp b/src_compiler/AST/expressions/ObjectExpression.hpp new file mode 100644 index 0000000..0e0f5cd --- /dev/null +++ b/src_compiler/AST/expressions/ObjectExpression.hpp @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +#pragma once +//Local +#include "Expression.hpp" + +namespace AST +{ + class ObjectExpression : public Expression + { + public: + //Constructor + inline ObjectExpression(const String& memberName) + { + this->members[memberName] = nullptr; + } + + inline ObjectExpression(const String& memberName, UniquePointer&& expr) + { + this->members[memberName] = Move(expr); + } + + //Properties + inline const Map>& Members() const + { + return this->members; + } + + //Methods + void Visit(ExpressionVisitor &visitor) const override + { + visitor.OnVisitingObjectExpression(*this); + } + + //Inline + inline void AddMember(UniquePointer&& objectExpression) + { + auto it = objectExpression->members.begin(); + this->members[(*it).key] = Move((*it).value); + objectExpression->members.Release(); + } + + private: + //Members + Map> members; + }; +} \ No newline at end of file diff --git a/src_compiler/AST/visitors/ExpressionVisitor.hpp b/src_compiler/AST/visitors/ExpressionVisitor.hpp index 047ebc6..0033ac0 100644 --- a/src_compiler/AST/visitors/ExpressionVisitor.hpp +++ b/src_compiler/AST/visitors/ExpressionVisitor.hpp @@ -25,6 +25,7 @@ namespace AST class FunctionExpression; class IdentifierExpression; class NaturalLiteralExpression; + class ObjectExpression; class TupleExpression; class ExpressionVisitor @@ -35,6 +36,7 @@ namespace AST virtual void OnVisitingFunctionExpression(const FunctionExpression& functionExpression) = 0; virtual void OnVisitingIdentifier(const IdentifierExpression& identifierExpression) = 0; virtual void OnVisitingNaturalLiteral(const NaturalLiteralExpression& naturalLiteralExpression) = 0; + virtual void OnVisitingObjectExpression(const ObjectExpression& objectExpression) = 0; virtual void OnVisitedTupleExpression(const TupleExpression& tupleExpression) = 0; }; } \ No newline at end of file diff --git a/src_compiler/IR/Builder.hpp b/src_compiler/IR/Builder.hpp index ae358f6..7e021fd 100644 --- a/src_compiler/IR/Builder.hpp +++ b/src_compiler/IR/Builder.hpp @@ -29,6 +29,8 @@ #include "NullValue.hpp" #include "instructions/CallInstruction.hpp" #include "instructions/BranchOnTrueInstruction.hpp" +#include "instructions/CreateNewObjectInstruction.hpp" +#include "ConstantString.hpp" namespace IR { @@ -73,6 +75,14 @@ namespace IR return this->Register(v); } + inline Value* CreateConstant(const String& value) + { + Value* v = new ConstantString(value); + v->type = this->typeCatalog.GetLeafType(LeafTypeEnum::String); + + return this->Register(v); + } + inline External* CreateExternal(const String& externalName, const ::Type* returnType, const ::Type* argumentType) { External* external = new External(externalName, returnType, argumentType); @@ -88,13 +98,25 @@ namespace IR inline Procedure* CreateMainProcedure() { - Procedure* proc = new Procedure(this->typeCatalog.GetLeafType(LeafTypeEnum::Null), this->typeCatalog.GetEmptyTupleType(), nullptr); + Procedure* proc = new Procedure(this->typeCatalog.GetLeafType(LeafTypeEnum::Null), this->typeCatalog.GetLeafType(LeafTypeEnum::Null), nullptr); proc->type = this->typeCatalog.GetFunctionType(proc->returnType, proc->argumentType); proc->name = u8"main"; return this->Register( proc ); } + inline Instruction* CreateNewObject(Map&& members) + { + CreateNewObjectInstruction* instruction = new CreateNewObjectInstruction(Move(members)); + + Map types; + for(const auto& member : instruction->Members()) + types.Insert(member.key, member.value->type); + instruction->type = this->typeCatalog.GetObjectType(Move(types)); + + return this->InsertInstruction(instruction); + } + inline Instruction *CreateNewTuple(DynamicArray&& values) { CreateNewTupleInstruction* instruction = new CreateNewTupleInstruction(Move(values)); diff --git a/src_compiler/IR/CMakeLists.txt b/src_compiler/IR/CMakeLists.txt index af15dbb..80bb711 100644 --- a/src_compiler/IR/CMakeLists.txt +++ b/src_compiler/IR/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCE_FILES_COMPILER ${CMAKE_CURRENT_SOURCE_DIR}/instructions/BranchOnTrueInstruction.hpp ${CMAKE_CURRENT_SOURCE_DIR}/instructions/CallInstruction.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/instructions/CreateNewObjectInstruction.hpp ${CMAKE_CURRENT_SOURCE_DIR}/instructions/CreateNewTupleInstruction.hpp ${CMAKE_CURRENT_SOURCE_DIR}/instructions/ExternalCallInstruction.hpp ${CMAKE_CURRENT_SOURCE_DIR}/instructions/ReturnInstruction.hpp @@ -16,6 +17,7 @@ set(SOURCE_FILES_COMPILER ${CMAKE_CURRENT_SOURCE_DIR}/BasicBlock.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Builder.hpp ${CMAKE_CURRENT_SOURCE_DIR}/ConstantFloat.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/ConstantString.hpp ${CMAKE_CURRENT_SOURCE_DIR}/External.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Instruction.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Module.hpp diff --git a/src_compiler/IR/ConstantString.hpp b/src_compiler/IR/ConstantString.hpp new file mode 100644 index 0000000..cebe648 --- /dev/null +++ b/src_compiler/IR/ConstantString.hpp @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +#pragma once +//Local +#include "Value.hpp" + +namespace IR +{ + class ConstantString : public Value + { + public: + //Constructor + inline ConstantString(const String& value) : value(value) + { + } + + //Properties + inline const String& Value() const + { + return this->value; + } + + //Methods + String ToString() const override + { + return this->value; + } + + void Visit(ValueVisitor &visitor) const override + { + visitor.OnVisitingConstantString(*this); + } + + private: + //Members + String value; + }; +} \ No newline at end of file diff --git a/src_compiler/IR/Instruction.hpp b/src_compiler/IR/Instruction.hpp index 11c276a..f91cbe9 100644 --- a/src_compiler/IR/Instruction.hpp +++ b/src_compiler/IR/Instruction.hpp @@ -31,7 +31,7 @@ namespace IR void Visit(ValueVisitor &visitor) const override { - + visitor.OnVisitingInstructionResultValue(*this); } }; } \ No newline at end of file diff --git a/src_compiler/IR/Module.hpp b/src_compiler/IR/Module.hpp index c1c26db..0a39ff6 100644 --- a/src_compiler/IR/Module.hpp +++ b/src_compiler/IR/Module.hpp @@ -43,6 +43,14 @@ namespace IR return this->procedures; } + template + inline void Procedures(const Iterable& iterable) + { + this->procedures.Release(); + for(const auto& current : iterable) + this->procedures.InsertTail(current); + } + //Inline inline void AddExternal(External* external) { @@ -62,6 +70,12 @@ namespace IR return (*it).value; } + inline Procedure* GetMainProcedure() + { + ASSERT_EQUALS(u8"main", this->procedures.Last()->name); + return this->procedures.Last(); + } + private: //Members Map externals; diff --git a/src_compiler/IR/Procedure.hpp b/src_compiler/IR/Procedure.hpp index d328365..c86a5e8 100644 --- a/src_compiler/IR/Procedure.hpp +++ b/src_compiler/IR/Procedure.hpp @@ -50,6 +50,12 @@ namespace IR return this->basicBlocks[0]; } + //Methods + void Visit(ValueVisitor &visitor) const override + { + visitor.OnVisitingProcedure(*this); + } + //Inline inline void AddBlock(BasicBlock* basicBlock) { diff --git a/src_compiler/IR/Symbol.hpp b/src_compiler/IR/Symbol.hpp index 84d8647..8d55cac 100644 --- a/src_compiler/IR/Symbol.hpp +++ b/src_compiler/IR/Symbol.hpp @@ -37,10 +37,5 @@ namespace IR { return this->name + u8": " + TypePointerToString(this->type); } - - void Visit(ValueVisitor &visitor) const override - { - - } }; } \ No newline at end of file diff --git a/src_compiler/IR/instructions/BranchOnTrueInstruction.hpp b/src_compiler/IR/instructions/BranchOnTrueInstruction.hpp index 01cc508..1394f9c 100644 --- a/src_compiler/IR/instructions/BranchOnTrueInstruction.hpp +++ b/src_compiler/IR/instructions/BranchOnTrueInstruction.hpp @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with ACScript. If not, see . */ +#pragma once //Local #include "../Instruction.hpp" #include "../BasicBlock.hpp" @@ -32,6 +33,11 @@ namespace IR } //Properties + inline const Value* Condition() const + { + return this->condition; + } + inline const BasicBlock* ElseBlock() const { return this->elseBlock; @@ -45,7 +51,7 @@ namespace IR String ToString() const override { - return Symbol::ToString() + u8" <-- brt " + this->condition->ToReferenceString() + u8", " + this->thenBlock->Name() + u8", " + this->elseBlock->Name(); + return u8"br " + this->condition->ToReferenceString() + u8", " + this->thenBlock->Name() + u8", " + this->elseBlock->Name(); } private: diff --git a/src_compiler/IR/instructions/CallInstruction.hpp b/src_compiler/IR/instructions/CallInstruction.hpp index 62d0b69..bcefbe9 100644 --- a/src_compiler/IR/instructions/CallInstruction.hpp +++ b/src_compiler/IR/instructions/CallInstruction.hpp @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with ACScript. If not, see . */ +#pragma once //Local #include "../External.hpp" #include "../Instruction.hpp" diff --git a/src_compiler/IR/instructions/CreateNewObjectInstruction.hpp b/src_compiler/IR/instructions/CreateNewObjectInstruction.hpp new file mode 100644 index 0000000..2873f40 --- /dev/null +++ b/src_compiler/IR/instructions/CreateNewObjectInstruction.hpp @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +#pragma once +//Local +#include "../Instruction.hpp" + +namespace IR +{ + class CreateNewObjectInstruction : public Instruction + { + public: + //Constructor + inline CreateNewObjectInstruction(Map&& members) : members(Move(members)) + { + } + + //Properties + inline const Map& Members() const + { + return this->members; + } + + void Visit(BasicBlockVisitor &visitor) override + { + visitor.OnVisitingNewObjectInstruction(*this); + } + + String ToString() const override + { + DynamicArray strings; + for(const auto& kv : this->members) + strings.Push(kv.key + u8": " + kv.value->ToReferenceString()); + + return Symbol::ToString() + u8" <-- new_object " + String::Join(strings, u8", "); + } + + private: + //Members + Map members; + }; +} \ No newline at end of file diff --git a/src_compiler/IR/instructions/CreateNewTupleInstruction.hpp b/src_compiler/IR/instructions/CreateNewTupleInstruction.hpp index dee2a1e..d264ab2 100644 --- a/src_compiler/IR/instructions/CreateNewTupleInstruction.hpp +++ b/src_compiler/IR/instructions/CreateNewTupleInstruction.hpp @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with ACScript. If not, see . */ +#pragma once //Local #include "../Instruction.hpp" diff --git a/src_compiler/IR/instructions/ExternalCallInstruction.hpp b/src_compiler/IR/instructions/ExternalCallInstruction.hpp index ae187eb..1fd826f 100644 --- a/src_compiler/IR/instructions/ExternalCallInstruction.hpp +++ b/src_compiler/IR/instructions/ExternalCallInstruction.hpp @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with ACScript. If not, see . */ +#pragma once //Local #include "../External.hpp" #include "../Instruction.hpp" diff --git a/src_compiler/IR/instructions/ReturnInstruction.hpp b/src_compiler/IR/instructions/ReturnInstruction.hpp index 00c6d77..22efc48 100644 --- a/src_compiler/IR/instructions/ReturnInstruction.hpp +++ b/src_compiler/IR/instructions/ReturnInstruction.hpp @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with ACScript. If not, see . */ +#pragma once //Local #include "../Instruction.hpp" diff --git a/src_compiler/IR/visitors/AllSymbols.hpp b/src_compiler/IR/visitors/AllSymbols.hpp index 749ee9c..795b6ff 100644 --- a/src_compiler/IR/visitors/AllSymbols.hpp +++ b/src_compiler/IR/visitors/AllSymbols.hpp @@ -18,10 +18,12 @@ */ #include "../instructions/BranchOnTrueInstruction.hpp" #include "../instructions/CallInstruction.hpp" +#include "../instructions/CreateNewObjectInstruction.hpp" #include "../instructions/CreateNewTupleInstruction.hpp" #include "../instructions/ExternalCallInstruction.hpp" #include "../instructions/ReturnInstruction.hpp" #include "../ConstantFloat.hpp" +#include "../ConstantString.hpp" #include "../External.hpp" #include "../Procedure.hpp" \ No newline at end of file diff --git a/src_compiler/IR/visitors/BasicBlockVisitor.hpp b/src_compiler/IR/visitors/BasicBlockVisitor.hpp index 9e9ee98..ce2777e 100644 --- a/src_compiler/IR/visitors/BasicBlockVisitor.hpp +++ b/src_compiler/IR/visitors/BasicBlockVisitor.hpp @@ -25,6 +25,7 @@ namespace IR //Forward declarations class BranchOnTrueInstruction; class CallInstruction; + class CreateNewObjectInstruction; class CreateNewTupleInstruction; class ExternalCallInstruction; class ReturnInstruction; @@ -36,7 +37,8 @@ namespace IR virtual void OnVisitingCallInstruction(CallInstruction& callInstruction) = 0; virtual void OnVisitingConditionalBranchInstruction(const BranchOnTrueInstruction& branchOnTrueInstruction) = 0; virtual void OnVisitingExternalCallInstruction(const ExternalCallInstruction& externalCallInstruction) = 0; + virtual void OnVisitingNewObjectInstruction(const CreateNewObjectInstruction& createNewObjectInstruction) = 0; virtual void OnVisitingNewTupleInstruction(CreateNewTupleInstruction& createNewTupleInstruction) = 0; - virtual void OnVisitingReturnInstruction(const ReturnInstruction& returnInstruction) = 0; + virtual void OnVisitingReturnInstruction(ReturnInstruction& returnInstruction) = 0; }; } \ No newline at end of file diff --git a/src_compiler/IR/visitors/ValueVisitor.hpp b/src_compiler/IR/visitors/ValueVisitor.hpp index 4d5910e..f5165a8 100644 --- a/src_compiler/IR/visitors/ValueVisitor.hpp +++ b/src_compiler/IR/visitors/ValueVisitor.hpp @@ -24,15 +24,21 @@ namespace IR { //Forward declarations class ConstantFloat; + class ConstantString; class External; + class Instruction; class Parameter; + class Procedure; class ValueVisitor { public: //Abstract virtual void OnVisitingConstantFloat(const ConstantFloat& constantFloat) = 0; + virtual void OnVisitingConstantString(const ConstantString& constantString) = 0; virtual void OnVisitingExternal(const External& external) = 0; + virtual void OnVisitingInstructionResultValue(const Instruction& instruction) = 0; virtual void OnVisitingParameter(const Parameter& parameter) = 0; + virtual void OnVisitingProcedure(const Procedure& procedure) = 0; }; } \ No newline at end of file diff --git a/src_compiler/acsb/Compiler.cpp b/src_compiler/acsb/Compiler.cpp index a00ab30..faa4243 100644 --- a/src_compiler/acsb/Compiler.cpp +++ b/src_compiler/acsb/Compiler.cpp @@ -20,6 +20,7 @@ #include "Compiler.hpp" //Local #include "../IR/visitors/AllSymbols.hpp" +#include "../types/ObjectType.hpp" //Namespaces using namespace ACSB; using namespace IR; @@ -27,9 +28,19 @@ using namespace IR; //Public methods void Compiler::Compile(const Module &module) { + this->module = &module; + for(const auto& proc : module.Procedures()) { + Optimization::DependencyGraph dependencyGraph(*proc); + for(const auto& kv : dependencyGraph.Dependencies()) + this->instructionReferenceCounts[kv.key] = kv.value.GetNumberOfElements(); + this->procedureOffsetMap[proc->name] = this->codeSegment.GetRemainingBytes(); + + this->valueStack.Release(); + this->valueStack.Push(proc->parameter); + for(const auto& basicBlock : proc->BasicBlocks()) { this->blockOffsets[basicBlock] = this->codeSegment.GetRemainingBytes(); @@ -41,7 +52,7 @@ void Compiler::Compile(const Module &module) void Compiler::Write(OutputStream &outputStream) { DataWriter dataWriter(true, outputStream); - TextWriter textWriter(outputStream, TextCodecType::ASCII); + TextWriter textWriter(outputStream, TextCodecType::UTF8); textWriter.WriteString(u8"ACSB"); @@ -50,8 +61,14 @@ void Compiler::Write(OutputStream &outputStream) textWriter.WriteStringZeroTerminated(externalName); dataWriter.WriteUInt16(this->constants.GetNumberOfElements()); - for(float64 constant : this->constants) - dataWriter.WriteFloat64(constant); + for(const Constant& constant : this->constants) + { + dataWriter.WriteByte(constant.isString); + if(constant.isString) + textWriter.WriteStringZeroTerminated(constant.string); + else + dataWriter.WriteFloat64(constant.f64); + } dataWriter.WriteUInt16(this->procedureOffsetMap[u8"main"]); @@ -73,6 +90,27 @@ void Compiler::Write(OutputStream &outputStream) outputStream.WriteBytes(tmpBuffer.Data(), tmpBuffer.Size()); } +//Private methods +void Compiler::PushValue(const IR::Value* value) +{ + uint32 offset = Unsigned::Max(); + for (int32 idx = this->valueStack.GetNumberOfElements() - 1; idx >= 0; idx--) + { + if (this->valueStack[idx] == value) + { + offset = this->valueStack.GetNumberOfElements() - 1 - idx; + break; + } + } + ASSERT(offset != Unsigned::Max(), u8"Value not found on stack"); + + if(--this->instructionReferenceCounts[value] > 0) + { + this->AddInstruction(Opcode::Push, offset); + this->valueStack.Push(this->valueStack[this->valueStack.GetNumberOfElements() - 1 - offset]); + } +} + //Event handlers void Compiler::OnVisitingCallInstruction(CallInstruction &callInstruction) { @@ -80,12 +118,19 @@ void Compiler::OnVisitingCallInstruction(CallInstruction &callInstruction) const Procedure* procedure = dynamic_cast(callInstruction.function); this->AddInstruction(Opcode::Call, this->procedureOffsetMap[procedure->name]); + + this->valueStack.Pop(); + this->valueStack.Push(&callInstruction); } void Compiler::OnVisitingConditionalBranchInstruction(const BranchOnTrueInstruction &branchOnTrueInstruction) { + branchOnTrueInstruction.Condition()->Visit(*this); + this->missingBlockOffsets[branchOnTrueInstruction.ElseBlock()].Push(this->codeSegment.GetRemainingBytes() + 1); this->AddInstruction(Opcode::JumpOnFalse, 0); + + this->valueStack.Pop(); } void ACSB::Compiler::OnVisitingExternalCallInstruction(const IR::ExternalCallInstruction &externalCallInstruction) @@ -94,25 +139,79 @@ void ACSB::Compiler::OnVisitingExternalCallInstruction(const IR::ExternalCallIns externalCallInstruction.argument->Visit(*this); this->AddInstruction(Opcode::CallExtern, this->externalMap[externalCallInstruction.external]); + + this->valueStack.Pop(); + this->valueStack.Push(&externalCallInstruction); +} + +void Compiler::OnVisitingNewObjectInstruction(const CreateNewObjectInstruction &createNewObjectInstruction) +{ + this->AddInstruction(Opcode::NewDictionary); + const IR::Value* dictInstance = &createNewObjectInstruction; + this->valueStack.Push(dictInstance); + + const External* setter = this->module->FindExternal(u8"__set"); + + for(const auto& kv : createNewObjectInstruction.Members()) + { + DynamicArray values; + values.Push(const_cast(dictInstance)); + + ConstantString constantString(kv.key); + values.Push(&constantString); + + values.Push(kv.value); + + IR::CreateNewTupleInstruction argInstruction(Move(values)); + argInstruction.Visit(*this); + + IR::ExternalCallInstruction externalCallInstruction(setter, &argInstruction); + externalCallInstruction.Visit(*this); + + this->AddInstruction(Opcode::Pop); + this->valueStack.Pop(); + } } void ACSB::Compiler::OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) { for(uint32 i = 0; i < createNewTupleInstruction.Values().GetNumberOfElements(); i++) createNewTupleInstruction.Values()[createNewTupleInstruction.Values().GetNumberOfElements() - 1 - i]->Visit(*this); + this->AddInstruction(Opcode::NewTuple, createNewTupleInstruction.Values().GetNumberOfElements()); + + for(uint32 i = 0; i < createNewTupleInstruction.Values().GetNumberOfElements(); i++) + this->valueStack.Pop(); + this->valueStack.Push(&createNewTupleInstruction); } -void Compiler::OnVisitingReturnInstruction(const ReturnInstruction &returnInstruction) +void Compiler::OnVisitingReturnInstruction(ReturnInstruction &returnInstruction) { returnInstruction.returnValue->Visit(*this); + //remove spilled + bool hasReturn = true;//this->currentProcedure->name != u8"main"; + while (this->valueStack.GetNumberOfElements() > (hasReturn ? 1 : 0)) + this->AddPopAssignInstruction(); + this->AddInstruction(Opcode::Return); + + if(hasReturn) + this->valueStack.Pop(); } void Compiler::OnVisitingConstantFloat(const ConstantFloat &constantFloat) { this->AddInstruction(Opcode::LoadConstant, this->AddConstant(constantFloat.Value())); + + this->valueStack.Push(&constantFloat); +} + +void Compiler::OnVisitingConstantString(const ConstantString &constantString) +{ + this->AddInstruction(Opcode::LoadConstant, this->AddConstant(constantString.Value())); + + this->valueStack.Push(&constantString); } void Compiler::OnVisitingExternal(const External &external) @@ -124,7 +223,18 @@ void Compiler::OnVisitingExternal(const External &external) this->externalMap[&external] = idx; } +void Compiler::OnVisitingInstructionResultValue(const Instruction &instruction) +{ + this->PushValue(&instruction); +} + void Compiler::OnVisitingParameter(const Parameter ¶meter) { - this->AddInstruction(Opcode::PushParameter); -} \ No newline at end of file + this->PushValue(¶meter); +} + +void Compiler::OnVisitingProcedure(const Procedure &procedure) +{ + this->AddInstruction(Opcode::LoadConstant, this->AddConstant(this->procedureOffsetMap[procedure.name])); + this->valueStack.Push(&procedure); +} diff --git a/src_compiler/acsb/Compiler.hpp b/src_compiler/acsb/Compiler.hpp index 4973c94..e867090 100644 --- a/src_compiler/acsb/Compiler.hpp +++ b/src_compiler/acsb/Compiler.hpp @@ -21,11 +21,18 @@ #include "../IR/visitors/BasicBlockVisitor.hpp" #include "../IR/External.hpp" #include "../IR/Module.hpp" +#include "../optimization/DependencyGraph.hpp" namespace ACSB { class Compiler : private IR::BasicBlockVisitor, private IR::ValueVisitor { + struct Constant + { + bool isString; + float64 f64; + String string; + }; public: //Methods void Compile(const IR::Module& module); @@ -36,15 +43,26 @@ namespace ACSB DynamicArray externals; Map externalMap; Map procedureOffsetMap; - DynamicArray constants; + DynamicArray constants; FIFOBuffer codeSegment; Map blockOffsets; Map> missingBlockOffsets; + DynamicArray valueStack; + const IR::Module* module; + Map instructionReferenceCounts; + + //Methods + void PushValue(const IR::Value* value); //Inline inline uint16 AddConstant(float64 value) { - return this->constants.Push(value); + return this->constants.Push({ .isString = false, .f64 = value }); + } + + inline uint16 AddConstant(const String& value) + { + return this->constants.Push({ .isString = true, .string = value }); } inline void AddInstruction(Opcode op) @@ -59,15 +77,26 @@ namespace ACSB dataWriter.WriteUInt16(arg0); } + inline void AddPopAssignInstruction() + { + this->AddInstruction(Opcode::PopAssign); + const IR::Value* top = this->valueStack.Pop(); + this->valueStack.Last() = top; + } + //Event handlers void OnVisitingCallInstruction(IR::CallInstruction &callInstruction) override; void OnVisitingConditionalBranchInstruction(const IR::BranchOnTrueInstruction &branchOnTrueInstruction) override; void OnVisitingExternalCallInstruction(const IR::ExternalCallInstruction &externalCallInstruction) override; + void OnVisitingNewObjectInstruction(const IR::CreateNewObjectInstruction &createNewObjectInstruction) override; void OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) override; - void OnVisitingReturnInstruction(const IR::ReturnInstruction &returnInstruction) override; + void OnVisitingReturnInstruction(IR::ReturnInstruction &returnInstruction) override; void OnVisitingConstantFloat(const IR::ConstantFloat &constantFloat) override; + void OnVisitingConstantString(const IR::ConstantString &constantString) override; void OnVisitingExternal(const IR::External &external) override; + void OnVisitingInstructionResultValue(const IR::Instruction &instruction) override; void OnVisitingParameter(const IR::Parameter ¶meter) override; + void OnVisitingProcedure(const IR::Procedure &procedure) override; }; } \ No newline at end of file diff --git a/src_compiler/acsb/Disassembler.cpp b/src_compiler/acsb/Disassembler.cpp index bede00b..30f9048 100644 --- a/src_compiler/acsb/Disassembler.cpp +++ b/src_compiler/acsb/Disassembler.cpp @@ -23,38 +23,64 @@ using namespace ACSB; //Public methods -void Disassembler::Disassemble(InputStream &inputStream) const +void Disassembler::Disassemble(FIFOBuffer &inputStream) { DataReader dataReader(true, inputStream); - TextReader textReader(inputStream, TextCodecType::ASCII); + TextReader textReader(inputStream, TextCodecType::UTF8); dataReader.Skip(4); //skip signature this->textWriter << u8".import" << endl; uint16 nImports = dataReader.ReadUInt16(); + this->externals.Resize(nImports); for(uint16 i = 0; i < nImports; i++) { this->textWriter.WriteTabs(1); - this->textWriter << u8"i" << i << u8": " << textReader.ReadZeroTerminatedString() << endl; + this->externals[i] = textReader.ReadZeroTerminatedString(); + this->textWriter << u8"i" << i << u8": " << this->externals[i] << endl; } this->textWriter << endl; this->textWriter << u8".rodata" << endl; uint16 nConstants = dataReader.ReadUInt16(); + this->constantsAsStrings.Resize(nConstants); for(uint16 i = 0; i < nConstants; i++) { this->textWriter.WriteTabs(1); - this->textWriter << u8"c" << i << u8": " << dataReader.ReadFloat64() << endl; + this->textWriter << u8"c" << i << u8": "; + + if(dataReader.ReadByte()) + this->constantsAsStrings[i] = u8"\"" + textReader.ReadZeroTerminatedString() + u8"\""; + else + this->constantsAsStrings[i] = String::Number(dataReader.ReadFloat64()); + + this->textWriter << this->constantsAsStrings[i] << endl; } this->textWriter << endl; this->textWriter << u8".code" << endl; - dataReader.ReadUInt16(); //entry point + uint16 entryPoint = dataReader.ReadUInt16(); + + FixedSizeBuffer buffer(inputStream.GetRemainingBytes()); + inputStream.PeekBytes(buffer.Data(), 0, buffer.Size()); + BufferInputStream bufferInputStream(buffer.Data(), buffer.Size()); + DataReader dataReaderBuffer(true, bufferInputStream); + + this->jumpOffsets.Insert(entryPoint, Unsigned::Max()); + this->FindJumpOffsets(dataReaderBuffer); uint16 offset = 0; + uint16 jumpLabelCounter = 0; while(!inputStream.IsAtEnd()) { + if(this->jumpOffsets.Contains(offset)) + { + this->jumpOffsets[offset] = jumpLabelCounter; + this->textWriter << u8"__lbl" << jumpLabelCounter << u8":" << endl; + jumpLabelCounter++; + } + this->textWriter.WriteTabs(1); this->textWriter << String::HexNumber(offset, 4); @@ -70,29 +96,27 @@ void Disassembler::DisassembleInstruction(DataReader &dataReader, uint16& offset { Opcode op = static_cast(dataReader.ReadByte()); offset++; + offset += this->GetArgumentsSize(op); switch (op) { case Opcode::Call: { uint16 callOffset = dataReader.ReadUInt16(); - offset += 2; - this->OutputMnemonicWithOneArgument(u8"call", callOffset); + this->OutputMnemonicWithOneArgument(u8"call", u8"__lbl" + String::Number(this->jumpOffsets[callOffset])); } break; case Opcode::CallExtern: { uint16 externalIndex = dataReader.ReadUInt16(); - offset += 2; - this->OutputMnemonicWithOneArgument(u8"callext", u8"i" + String::Number(externalIndex)); + this->OutputMnemonicWithOneArgumentAndComment(u8"callext", u8"i" + String::Number(externalIndex), this->externals[externalIndex]); } break; case Opcode::JumpOnFalse: { uint16 jumpOffset = dataReader.ReadUInt16(); - offset += 2; this->OutputMnemonicWithOneArgument(u8"jmpf", jumpOffset); } @@ -100,22 +124,36 @@ void Disassembler::DisassembleInstruction(DataReader &dataReader, uint16& offset case Opcode::LoadConstant: { uint16 constantIndex = dataReader.ReadUInt16(); - offset += 2; - this->OutputMnemonicWithOneArgument(u8"ldc", u8"c" + String::Number(constantIndex)); + this->OutputMnemonicWithOneArgumentAndComment(u8"ldc", u8"c" + String::Number(constantIndex), this->constantsAsStrings[constantIndex]); + } + break; + case Opcode::NewDictionary: + { + this->OutputMnemonic(u8"ndict"); } break; case Opcode::NewTuple: { uint16 count = dataReader.ReadUInt16(); - offset += 2; this->OutputMnemonicWithOneArgument(u8"ntp", count); } break; - case Opcode::PushParameter: + case Opcode::Pop: + { + this->OutputMnemonic(u8"pop"); + } + break; + case Opcode::PopAssign: { - this->OutputMnemonic(u8"pusharg"); + this->OutputMnemonic(u8"popa"); + } + break; + case Opcode::Push: + { + uint16 backIndex = dataReader.ReadUInt16(); + this->OutputMnemonicWithOneArgument(u8"push", backIndex); } break; case Opcode::Return: @@ -127,3 +165,38 @@ void Disassembler::DisassembleInstruction(DataReader &dataReader, uint16& offset this->textWriter << u8"??? Unknown instruction ???: " << String::HexNumber(static_cast(op)); } } + +void Disassembler::FindJumpOffsets(DataReader &dataReader) +{ + while(!dataReader.InputStream().IsAtEnd()) + { + Opcode op = static_cast(dataReader.ReadByte()); + + switch(op) + { + case Opcode::Call: + { + uint16 callOffset = dataReader.ReadUInt16(); + this->jumpOffsets.Insert(callOffset, Unsigned::Max()); + } + break; + default: + dataReader.Skip(this->GetArgumentsSize(op)); + } + } +} + +uint8 Disassembler::GetArgumentsSize(Opcode opcode) const +{ + switch(opcode) + { + case Opcode::Call: + case Opcode::CallExtern: + case Opcode::JumpOnFalse: + case Opcode::LoadConstant: + case Opcode::NewTuple: + case Opcode::Push: + return 2; + } + return 0; +} diff --git a/src_compiler/acsb/Disassembler.hpp b/src_compiler/acsb/Disassembler.hpp index dc3da2d..9cad2b7 100644 --- a/src_compiler/acsb/Disassembler.hpp +++ b/src_compiler/acsb/Disassembler.hpp @@ -30,14 +30,19 @@ namespace ACSB } //Methods - void Disassemble(InputStream &inputStream) const; + void Disassemble(FIFOBuffer &inputStream); private: //Members TextWriter &textWriter; + Map jumpOffsets; + DynamicArray externals; + DynamicArray constantsAsStrings; //Methods void DisassembleInstruction(DataReader& dataReader, uint16& offset) const; + void FindJumpOffsets(DataReader& dataReader); + uint8 GetArgumentsSize(Opcode opcode) const; //Inline inline void OutputMnemonic(const String& mnemonic) const @@ -58,5 +63,12 @@ namespace ACSB this->OutputMnemonic(mnemonic); this->textWriter << arg; } + + inline void OutputMnemonicWithOneArgumentAndComment(const String& mnemonic, const String& arg, const String& comment) const + { + this->OutputMnemonicWithOneArgument(mnemonic, arg); + this->textWriter.WriteTabs(2); + this->textWriter << u8"//" << comment; + } }; } \ No newline at end of file diff --git a/src_compiler/optimization/CMakeLists.txt b/src_compiler/optimization/CMakeLists.txt index 2ddb64c..3932660 100644 --- a/src_compiler/optimization/CMakeLists.txt +++ b/src_compiler/optimization/CMakeLists.txt @@ -5,6 +5,8 @@ set(SOURCE_FILES_COMPILER ${CMAKE_CURRENT_SOURCE_DIR}/typeInference/ProcedureTypeInferer.hpp ${CMAKE_CURRENT_SOURCE_DIR}/typeInference/ProcedureTypeReplacer.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/CallGraph.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/DependencyGraph.hpp ${CMAKE_CURRENT_SOURCE_DIR}/PassManager.hpp ${CMAKE_CURRENT_SOURCE_DIR}/ProcedureTransformer.hpp diff --git a/src_compiler/optimization/CallGraph.hpp b/src_compiler/optimization/CallGraph.hpp new file mode 100644 index 0000000..62f9c38 --- /dev/null +++ b/src_compiler/optimization/CallGraph.hpp @@ -0,0 +1,108 @@ +/* +* Copyright (c) 2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +#include +using namespace StdXX; +//Local +#include "../IR/visitors/AllSymbols.hpp" +#include "../IR/Procedure.hpp" + +namespace Optimization +{ + class CallGraph : private IR::BasicBlockVisitor + { + public: + //Constructor + inline CallGraph(const LinkedList& procedures) + { + for(IR::Procedure*const& procedure : procedures) + { + this->callLinks[procedure] = {}; + + this->currentProc = procedure; + this->AnalyzeCalls(procedure); + } + this->currentProc = nullptr; + } + + //Methods + DynamicArray QueryProceduresOrdered() + { + DynamicArray result; + BinaryTreeSet marked; + + for(auto& kv : this->callLinks) + Collect(result, marked, kv.key); + + return result; + } + + private: + //Members + IR::Procedure* currentProc; + Map> callLinks; + + //Inline + inline void AnalyzeCalls(IR::Procedure*const& procedure) + { + for(const auto& basicBlock : procedure->BasicBlocks()) + basicBlock->Visit(*this); + } + + inline void Collect(DynamicArray& result, BinaryTreeSet& marked, IR::Procedure*const& procedure) + { + if(marked.Contains(procedure)) + return; + marked.Insert(procedure); //mark now because of self-recursive functions + + BinaryTreeSet& dependencies = this->callLinks[procedure]; + for(auto& dependency : dependencies) + Collect(result, marked, dependency); + + result.Push(procedure); + } + + //Event handlers + void OnVisitingCallInstruction(IR::CallInstruction &callInstruction) override + { + IR::Procedure* called = const_cast(dynamic_cast(callInstruction.function)); + + this->callLinks[this->currentProc].Insert(called); + } + + void OnVisitingConditionalBranchInstruction(const IR::BranchOnTrueInstruction &branchOnTrueInstruction) override + { + } + + void OnVisitingExternalCallInstruction(const IR::ExternalCallInstruction &externalCallInstruction) override + { + } + + void OnVisitingNewObjectInstruction(const IR::CreateNewObjectInstruction &createNewObjectInstruction) override + { + } + + void OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) override + { + } + + void OnVisitingReturnInstruction(IR::ReturnInstruction &returnInstruction) override + { + } + }; +} \ No newline at end of file diff --git a/src_compiler/optimization/DependencyGraph.hpp b/src_compiler/optimization/DependencyGraph.hpp new file mode 100644 index 0000000..099bf9e --- /dev/null +++ b/src_compiler/optimization/DependencyGraph.hpp @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +#include +using namespace StdXX; +//Local +#include "../IR/visitors/AllSymbols.hpp" +#include "../IR/Procedure.hpp" + +namespace Optimization +{ + class DependencyGraph : private IR::BasicBlockVisitor + { + public: + //Constructor + inline DependencyGraph(const IR::Procedure& procedure) + { + for(const auto& basicBlock : procedure.BasicBlocks()) + basicBlock->Visit(*this); + } + + //Properties + inline const Map>& Dependencies() const + { + return this->dependencies; + } + + private: + //Members + Map> dependencies; + + //Event handlers + void OnVisitingCallInstruction(IR::CallInstruction &callInstruction) override + { + } + + void OnVisitingConditionalBranchInstruction(const IR::BranchOnTrueInstruction &branchOnTrueInstruction) override + { + + } + + void OnVisitingExternalCallInstruction(const IR::ExternalCallInstruction &externalCallInstruction) override + { + + } + + void OnVisitingNewObjectInstruction(const IR::CreateNewObjectInstruction &createNewObjectInstruction) override + { + + } + + void OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) override + { + for(const IR::Value*const& value : createNewTupleInstruction.Values()) + this->AddDependency(&createNewTupleInstruction, value); + } + + void OnVisitingReturnInstruction(IR::ReturnInstruction &returnInstruction) override + { + + } + + //Inline + inline void AddDependency(IR::Instruction* target, const IR::Value* referencedValue) + { + this->dependencies[target].Insert(referencedValue); + } + }; +} \ No newline at end of file diff --git a/src_compiler/optimization/PassManager.hpp b/src_compiler/optimization/PassManager.hpp index 5093b7b..9daed49 100644 --- a/src_compiler/optimization/PassManager.hpp +++ b/src_compiler/optimization/PassManager.hpp @@ -18,6 +18,7 @@ */ //Local #include "../IR/Module.hpp" +#include "CallGraph.hpp" namespace Optimization { @@ -27,13 +28,16 @@ namespace Optimization //Constructor PassManager(IR::Module& module, TypeCatalog& typeCatalog) : module(module), typeCatalog(typeCatalog) { + CallGraph callGraph(this->module.Procedures()); + DynamicArray orderedProcedures = callGraph.QueryProceduresOrdered(); + module.Procedures(orderedProcedures); } //Inline template inline void AddProcedurePass(const String& passName) { - for(IR::Procedure*const& procedure : this->module.Procedures()) + for(IR::Procedure*const& procedure : module.Procedures()) { T pass(*procedure, this->typeCatalog); pass.Transform(); diff --git a/src_compiler/optimization/typeInference/ProcedureTypeInferer.cpp b/src_compiler/optimization/typeInference/ProcedureTypeInferer.cpp index 1cc07fc..4c611b3 100644 --- a/src_compiler/optimization/typeInference/ProcedureTypeInferer.cpp +++ b/src_compiler/optimization/typeInference/ProcedureTypeInferer.cpp @@ -72,6 +72,10 @@ void ProcedureTypeInferer::OnVisitingExternalCallInstruction(const IR::ExternalC this->InferTypesMustBeEqual(externalCallInstruction.argument->type, externalCallInstruction.external->argumentType); } +void ProcedureTypeInferer::OnVisitingNewObjectInstruction(const IR::CreateNewObjectInstruction &createNewObjectInstruction) +{ +} + void ProcedureTypeInferer::OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) { DynamicArray types; @@ -80,13 +84,24 @@ void ProcedureTypeInferer::OnVisitingNewTupleInstruction(IR::CreateNewTupleInstr createNewTupleInstruction.type = this->typeCatalog.GetTupleType(Move(types)); } -void ProcedureTypeInferer::OnVisitingReturnInstruction(const IR::ReturnInstruction &returnInstruction) +void ProcedureTypeInferer::OnVisitingReturnInstruction(IR::ReturnInstruction &returnInstruction) { + returnInstruction.type = returnInstruction.returnValue->type; + if(this->procedure.returnType == nullptr) + { this->procedure.returnType = returnInstruction.type; - else + this->procedure.type = nullptr; + } + else if(returnInstruction.type != nullptr) + { this->InferTypesMustBeEqual(this->procedure.returnType, returnInstruction.type); - this->procedure.type = nullptr; + this->procedure.type = nullptr; + } + else + { + //returnInstruction.type should be nullptr only if we come from recursion + } if(this->procedure.type == nullptr) { diff --git a/src_compiler/optimization/typeInference/ProcedureTypeInferer.hpp b/src_compiler/optimization/typeInference/ProcedureTypeInferer.hpp index cd07630..6ce0f38 100644 --- a/src_compiler/optimization/typeInference/ProcedureTypeInferer.hpp +++ b/src_compiler/optimization/typeInference/ProcedureTypeInferer.hpp @@ -44,7 +44,8 @@ namespace Optimization void OnVisitingCallInstruction(IR::CallInstruction &callInstruction) override; void OnVisitingConditionalBranchInstruction(const IR::BranchOnTrueInstruction &branchOnTrueInstruction) override; void OnVisitingExternalCallInstruction(const IR::ExternalCallInstruction &externalCallInstruction) override; + void OnVisitingNewObjectInstruction(const IR::CreateNewObjectInstruction &createNewObjectInstruction) override; void OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) override; - void OnVisitingReturnInstruction(const IR::ReturnInstruction &returnInstruction) override; + void OnVisitingReturnInstruction(IR::ReturnInstruction &returnInstruction) override; }; } \ No newline at end of file diff --git a/src_compiler/optimization/typeInference/ProcedureTypeReplacer.hpp b/src_compiler/optimization/typeInference/ProcedureTypeReplacer.hpp index e7eff0c..fadf8e5 100644 --- a/src_compiler/optimization/typeInference/ProcedureTypeReplacer.hpp +++ b/src_compiler/optimization/typeInference/ProcedureTypeReplacer.hpp @@ -68,12 +68,16 @@ namespace Optimization { } + void OnVisitingNewObjectInstruction(const IR::CreateNewObjectInstruction &createNewObjectInstruction) override + { + } + void OnVisitingNewTupleInstruction(IR::CreateNewTupleInstruction &createNewTupleInstruction) override { this->ReplaceType(createNewTupleInstruction.type); } - void OnVisitingReturnInstruction(const IR::ReturnInstruction &returnInstruction) override + void OnVisitingReturnInstruction(IR::ReturnInstruction &returnInstruction) override { } diff --git a/src_compiler/translation/AST2IRTranslator.cpp b/src_compiler/translation/AST2IRTranslator.cpp index 37bba27..c52919a 100644 --- a/src_compiler/translation/AST2IRTranslator.cpp +++ b/src_compiler/translation/AST2IRTranslator.cpp @@ -34,5 +34,5 @@ void AST2IRTranslator::Translate(const AST::StatementBlock &statementBlock) mainProc->AddBlock(basicBlock); ASTFunction2IRTranslator functionTranslator(this->builder, this->typeCatalog, *mainProc); - functionTranslator.Translate(statementBlock); + functionTranslator.TranslateMain(statementBlock); } \ No newline at end of file diff --git a/src_compiler/translation/ASTFunction2IRTranslator.cpp b/src_compiler/translation/ASTFunction2IRTranslator.cpp index b2d17b4..069daca 100644 --- a/src_compiler/translation/ASTFunction2IRTranslator.cpp +++ b/src_compiler/translation/ASTFunction2IRTranslator.cpp @@ -53,6 +53,11 @@ void ASTFunction2IRTranslator::OnVisitingFunctionExpression(const AST::FunctionE this->builder.Module().AddProcedure(proc, &this->procedure); IR::BasicBlock* basicBlock = this->builder.CreateBasicBlock(u8"entry"); + if(this->procedure.name == u8"main") + { + //the function to compile is defined within the module, derive variables + basicBlock->namedValues = this->GetCurrentBlock()->namedValues; + } proc->AddBlock(basicBlock); ASTFunction2IRTranslator functionTranslator(this->builder, this->typeCatalog, *proc); @@ -91,7 +96,7 @@ void ASTFunction2IRTranslator::OnVisitedTupleExpression(const AST::TupleExpressi this->AddInstruction(instruction); } -void ASTFunction2IRTranslator::Translate(const AST::StatementBlock &statementBlock) +void ASTFunction2IRTranslator::TranslateMain(const AST::StatementBlock &statementBlock) { this->blockStack.Push(this->procedure.EntryBlock()); statementBlock.Visit(*this); @@ -118,7 +123,8 @@ void ASTFunction2IRTranslator::Translate(const LinkedListvalueStack.Pop(); if(condition) { - NOT_IMPLEMENTED_ERROR; + this->blockStack.Push(lastBlock); + const IR::External* andExternal = this->builder.Module().FindExternal(u8"and"); DynamicArray values; @@ -130,6 +136,8 @@ void ASTFunction2IRTranslator::Translate(const LinkedListbuilder.CreateExternalCall(andExternal, argInstruction); this->AddInstruction(andInstruction); + this->blockStack.Pop(); + condition = andInstruction; } else @@ -177,6 +185,19 @@ void ASTFunction2IRTranslator::OnVisitingExternalDeclaration(const AST::External builder.Module().AddExternal(external); } +void ASTFunction2IRTranslator::OnVisitingObjectExpression(const AST::ObjectExpression &objectExpression) +{ + Map values; + for(const auto& kv : objectExpression.Members()) + { + kv.value->Visit(*this); + values.Insert(kv.key, this->valueStack.Pop()); + } + + IR::Instruction* instruction = this->builder.CreateNewObject(Move(values)); + this->AddInstruction(instruction); +} + void ASTFunction2IRTranslator::OnVisitingReturnStatement(const AST::ReturnStatement &returnStatement) { returnStatement.EmbeddedExpression().Visit(*this); diff --git a/src_compiler/translation/ASTFunction2IRTranslator.hpp b/src_compiler/translation/ASTFunction2IRTranslator.hpp index 25ec2c5..90ec8f4 100644 --- a/src_compiler/translation/ASTFunction2IRTranslator.hpp +++ b/src_compiler/translation/ASTFunction2IRTranslator.hpp @@ -39,7 +39,7 @@ class ASTFunction2IRTranslator : private AST::StatementVisitor, public AST::Expr void OnVisitingNaturalLiteral(const AST::NaturalLiteralExpression &naturalLiteralExpression) override; void OnVisitedTupleExpression(const AST::TupleExpression &tupleExpression) override; - void Translate(const AST::StatementBlock &statementBlock); + void TranslateMain(const AST::StatementBlock &statementBlock); private: //Members @@ -67,6 +67,7 @@ class ASTFunction2IRTranslator : private AST::StatementVisitor, public AST::Expr //Event handlers void OnVisitingExternalDeclaration(const AST::ExternalDeclarationStatement &externalDeclaration) override; void OnVisitingExpressionStatement(const AST::ExpressionStatement &expressionStatement) override; + void OnVisitingObjectExpression(const AST::ObjectExpression &objectExpression) override; void OnVisitingReturnStatement(const AST::ReturnStatement &returnStatement) override; void OnVisitingVariableDefinitionStatement(const AST::VariableDefinitionStatement &variableDefinitionStatement) override; }; \ No newline at end of file diff --git a/src_compiler/translation/CMakeLists.txt b/src_compiler/translation/CMakeLists.txt index efd4e2e..6a90f16 100644 --- a/src_compiler/translation/CMakeLists.txt +++ b/src_compiler/translation/CMakeLists.txt @@ -5,6 +5,7 @@ set(SOURCE_FILES_COMPILER ${CMAKE_CURRENT_SOURCE_DIR}/AST2IRTranslator.hpp ${CMAKE_CURRENT_SOURCE_DIR}/ASTFunction2IRTranslator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ASTFunction2IRTranslator.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/PatternMatching2IRTranslator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/PatternMatching2IRTranslator.hpp ${CMAKE_CURRENT_SOURCE_DIR}/TypeEvaluator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TypeEvaluator.hpp diff --git a/src_compiler/translation/PatternMatching2IRTranslator.cpp b/src_compiler/translation/PatternMatching2IRTranslator.cpp new file mode 100644 index 0000000..786ad06 --- /dev/null +++ b/src_compiler/translation/PatternMatching2IRTranslator.cpp @@ -0,0 +1,108 @@ +/* +* Copyright (c) 2018-2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +//Class header +#include "PatternMatching2IRTranslator.hpp" + +//Event handlers +void PatternMatching2IRTranslator::OnVisitingNaturalLiteral(const AST::NaturalLiteralExpression &naturalLiteralExpression) +{ + IR::Value* argument = this->valueStack.Pop(); + + const IR::External* lessThanExternal = this->builder.Module().FindExternal(u8"<"); + const IR::External* orExternal = this->builder.Module().FindExternal(u8"or"); + const IR::External* notExternal = this->builder.Module().FindExternal(u8"not"); + + DynamicArray values; + values.Push(this->builder.CreateConstant(naturalLiteralExpression.Value())); + values.Push(argument); + IR::Instruction* argInstruction = this->builder.CreateNewTuple(Move(values)); + this->AddInstruction(argInstruction); + + IR::Instruction* cmpInstruction1 = this->builder.CreateExternalCall(lessThanExternal, argInstruction); + this->AddInstruction(cmpInstruction1); + + values.Push(argument); + values.Push(this->builder.CreateConstant(naturalLiteralExpression.Value())); + argInstruction = this->builder.CreateNewTuple(Move(values)); + this->AddInstruction(argInstruction); + + IR::Instruction* cmpInstruction2 = this->builder.CreateExternalCall(lessThanExternal, argInstruction); + this->AddInstruction(cmpInstruction2); + + values.Push(cmpInstruction1); + values.Push(cmpInstruction2); + argInstruction = this->builder.CreateNewTuple(Move(values)); + this->AddInstruction(argInstruction); + + IR::Instruction* andInstruction = this->builder.CreateExternalCall(orExternal, argInstruction); + this->AddInstruction(andInstruction); + + IR::Instruction* notInstruction = this->builder.CreateExternalCall(notExternal, andInstruction); + this->AddInstruction(notInstruction); + + this->valueStack.Push(notInstruction); +} + +void PatternMatching2IRTranslator::OnVisitingObjectExpression(const AST::ObjectExpression &objectExpression) +{ + const IR::External* subscript = this->builder.Module().FindExternal(u8"[]"); + + IR::Value* dictValue = this->valueStack.Pop(); + + for(const auto& kv : objectExpression.Members()) + { + ASSERT_EQUALS(true, kv.value.IsNull()); //TODO: implement value conditions for objects + + DynamicArray values; + values.Push(dictValue); + values.Push(this->builder.CreateConstant(kv.key)); + IR::Instruction* argInstruction = this->builder.CreateNewTuple(Move(values)); + this->AddInstruction(argInstruction); + + IR::Instruction* selectInstruction = this->builder.CreateExternalCall(subscript, argInstruction); + this->AddInstruction(selectInstruction); + + this->valueStack.Push(selectInstruction); + this->basicBlock->namedValues[kv.key] = selectInstruction; + } +} + +void PatternMatching2IRTranslator::OnVisitedTupleExpression(const AST::TupleExpression &tupleExpression) +{ + const IR::External* subscript = this->builder.Module().FindExternal(u8"[]"); + + IR::Value* tupleValue = this->valueStack.Pop(); + + for(uint32 i = 0; i < tupleExpression.Expressions().GetNumberOfElements(); i++) + { + const auto& expression = tupleExpression.Expressions()[i]; + + DynamicArray values; + values.Push(tupleValue); + values.Push(this->builder.CreateConstant(i)); + IR::Instruction* argInstruction = this->builder.CreateNewTuple(Move(values)); + this->AddInstruction(argInstruction); + + IR::Instruction* selectInstruction = this->builder.CreateExternalCall(subscript, argInstruction); + this->AddInstruction(selectInstruction); + + this->valueStack.Push(selectInstruction); + expression->Visit(*this); + } +} \ No newline at end of file diff --git a/src_compiler/translation/PatternMatching2IRTranslator.hpp b/src_compiler/translation/PatternMatching2IRTranslator.hpp index be81e22..31f6505 100644 --- a/src_compiler/translation/PatternMatching2IRTranslator.hpp +++ b/src_compiler/translation/PatternMatching2IRTranslator.hpp @@ -28,13 +28,16 @@ class PatternMatching2IRTranslator : private AST::ExpressionVisitor inline PatternMatching2IRTranslator(IR::Builder& builder, IR::Procedure& procedure, IR::BasicBlock* basicBlock) : builder(builder), procedure(procedure), basicBlock(basicBlock) { + this->valueStack.Push(procedure.parameter); } //Inline inline IR::Value* Translate(const AST::Expression& expression) { expression.Visit(*this); - return this->result; + if(this->valueStack.IsEmpty()) + return nullptr; + return this->valueStack.Pop(); } private: @@ -42,7 +45,7 @@ class PatternMatching2IRTranslator : private AST::ExpressionVisitor IR::Builder& builder; IR::Procedure& procedure; IR::BasicBlock* basicBlock; - IR::Value* result; + DynamicArray valueStack; //Event handlers void OnVisitedCall(const AST::CallExpression &callExpression) override @@ -58,52 +61,12 @@ class PatternMatching2IRTranslator : private AST::ExpressionVisitor void OnVisitingIdentifier(const AST::IdentifierExpression &identifierExpression) override { String varName = identifierExpression.Identifier(); - this->basicBlock->namedValues[varName] = this->procedure.parameter; - - this->result = nullptr; + this->basicBlock->namedValues[varName] = this->valueStack.Pop(); } - void OnVisitingNaturalLiteral(const AST::NaturalLiteralExpression &naturalLiteralExpression) override - { - const IR::External* lessThanExternal = this->builder.Module().FindExternal(u8"<"); - const IR::External* orExternal = this->builder.Module().FindExternal(u8"or"); - const IR::External* notExternal = this->builder.Module().FindExternal(u8"not"); - - DynamicArray values; - values.Push(this->builder.CreateConstant(naturalLiteralExpression.Value())); - values.Push(this->procedure.parameter); - IR::Instruction* argInstruction = this->builder.CreateNewTuple(Move(values)); - this->AddInstruction(argInstruction); - - IR::Instruction* cmpInstruction1 = this->builder.CreateExternalCall(lessThanExternal, argInstruction); - this->AddInstruction(cmpInstruction1); - - values.Push(this->procedure.parameter); - values.Push(this->builder.CreateConstant(naturalLiteralExpression.Value())); - argInstruction = this->builder.CreateNewTuple(Move(values)); - this->AddInstruction(argInstruction); - - IR::Instruction* cmpInstruction2 = this->builder.CreateExternalCall(lessThanExternal, argInstruction); - this->AddInstruction(cmpInstruction2); - - values.Push(cmpInstruction1); - values.Push(cmpInstruction2); - argInstruction = this->builder.CreateNewTuple(Move(values)); - this->AddInstruction(argInstruction); - - IR::Instruction* andInstruction = this->builder.CreateExternalCall(orExternal, argInstruction); - this->AddInstruction(andInstruction); - - IR::Instruction* notInstruction = this->builder.CreateExternalCall(notExternal, andInstruction); - this->AddInstruction(notInstruction); - - this->result = notInstruction; - } - - void OnVisitedTupleExpression(const AST::TupleExpression &tupleExpression) override - { - NOT_IMPLEMENTED_ERROR; - } + void OnVisitingNaturalLiteral(const AST::NaturalLiteralExpression &naturalLiteralExpression) override; + void OnVisitingObjectExpression(const AST::ObjectExpression &objectExpression) override; + void OnVisitedTupleExpression(const AST::TupleExpression &tupleExpression) override; //Inline inline void AddInstruction(IR::Instruction* instruction) diff --git a/src_compiler/translation/TypeEvaluator.cpp b/src_compiler/translation/TypeEvaluator.cpp index 547a7de..2eca34d 100644 --- a/src_compiler/translation/TypeEvaluator.cpp +++ b/src_compiler/translation/TypeEvaluator.cpp @@ -53,6 +53,8 @@ void Translation::TypeEvaluator::OnVisitedIdentifierTypeSpec(const AST::Identifi this->typeStack.Push(this->typeCatalog.GetLeafType(LeafTypeEnum::Float64)); else if(identifierTypeSpec.Identififer() == u8"null") this->typeStack.Push(this->typeCatalog.GetLeafType(LeafTypeEnum::Null)); + else if(identifierTypeSpec.Identififer() == u8"string") + this->typeStack.Push(this->typeCatalog.GetLeafType(LeafTypeEnum::String)); else NOT_IMPLEMENTED_ERROR; } diff --git a/src_compiler/types/CMakeLists.txt b/src_compiler/types/CMakeLists.txt index 28cd946..69ae219 100644 --- a/src_compiler/types/CMakeLists.txt +++ b/src_compiler/types/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCE_FILES_COMPILER ${CMAKE_CURRENT_SOURCE_DIR}/FunctionType.hpp ${CMAKE_CURRENT_SOURCE_DIR}/GenericType.hpp ${CMAKE_CURRENT_SOURCE_DIR}/LeafType.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/ObjectType.hpp ${CMAKE_CURRENT_SOURCE_DIR}/TupleType.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Type.hpp ${CMAKE_CURRENT_SOURCE_DIR}/TypeCatalog.hpp diff --git a/src_compiler/types/LeafType.hpp b/src_compiler/types/LeafType.hpp index 7542f29..b265b65 100644 --- a/src_compiler/types/LeafType.hpp +++ b/src_compiler/types/LeafType.hpp @@ -26,6 +26,7 @@ enum class LeafTypeEnum Bool, Float64, Null, + String, }; class LeafType : public ::Type @@ -36,6 +37,12 @@ class LeafType : public ::Type { } + //Properties + inline LeafTypeEnum EnumType() const + { + return this->type; + } + //Public methods bool IsTriviallyAssignableTo(const Type &other) const override { @@ -44,12 +51,13 @@ class LeafType : public ::Type { if(this->type == otherLeafType->type) return true; + if(otherLeafType->type == LeafTypeEnum::Any) + return true; switch(this->type) { case LeafTypeEnum::Any: - NOT_IMPLEMENTED_ERROR; - break; + return false; case LeafTypeEnum::Bool: NOT_IMPLEMENTED_ERROR; break; @@ -57,20 +65,36 @@ class LeafType : public ::Type { switch(otherLeafType->type) { - case LeafTypeEnum::Any: - return true; case LeafTypeEnum::Bool: NOT_IMPLEMENTED_ERROR; break; case LeafTypeEnum::Null: NOT_IMPLEMENTED_ERROR; break; + case LeafTypeEnum::String: + NOT_IMPLEMENTED_ERROR; + break; } } - break; + break; case LeafTypeEnum::Null: NOT_IMPLEMENTED_ERROR; break; + case LeafTypeEnum::String: + { + switch(otherLeafType->type) + { + case LeafTypeEnum::Bool: + NOT_IMPLEMENTED_ERROR; + break; + case LeafTypeEnum::Float64: + return false; + case LeafTypeEnum::Null: + NOT_IMPLEMENTED_ERROR; + break; + } + } + break; } } @@ -90,6 +114,8 @@ class LeafType : public ::Type return u8"float64"; case LeafTypeEnum::Null: return u8"null"; + case LeafTypeEnum::String: + return u8"string"; } RAISE(ErrorHandling::IllegalCodePathError); } diff --git a/src_compiler/types/ObjectType.hpp b/src_compiler/types/ObjectType.hpp new file mode 100644 index 0000000..cdb0fe0 --- /dev/null +++ b/src_compiler/types/ObjectType.hpp @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +#pragma once +//Local +#include "LeafType.hpp" + +class ObjectType : public ::Type +{ +public: + //Constructor + inline ObjectType(Map&& types) : types(Move(types)) + { + for(const auto& kv : this->types) + { + this->typesOrdered.Push(kv.key); + } + } + + //Properties + inline const DynamicArray& TypesOrdered() const + { + return this->typesOrdered; + } + + //Methods + bool IsTriviallyAssignableTo(const Type &other) const override + { + const LeafType* otherLeafType = dynamic_cast(&other); + if(otherLeafType && (otherLeafType->EnumType() == LeafTypeEnum::Any)) + return true; + + NOT_IMPLEMENTED_ERROR; + return false; + } + + String ToString() const override + { + DynamicArray strings; + for(const auto& kv : this->types) + strings.Push(kv.key + u8": " + TypePointerToString(kv.value)); + + return u8"{ " + String::Join(strings, u8", ") + u8" }"; + } + + void Visit(TypeVisitor &visitor) const override + { + NOT_IMPLEMENTED_ERROR; + } + +private: + //Members + Map types; + DynamicArray typesOrdered; +}; \ No newline at end of file diff --git a/src_compiler/types/TypeCatalog.hpp b/src_compiler/types/TypeCatalog.hpp index 6782e6f..b742bf3 100644 --- a/src_compiler/types/TypeCatalog.hpp +++ b/src_compiler/types/TypeCatalog.hpp @@ -22,6 +22,7 @@ #include "TupleType.hpp" #include "FunctionType.hpp" #include "GenericType.hpp" +#include "ObjectType.hpp" class TypeCatalog { @@ -53,6 +54,11 @@ class TypeCatalog return this->types[key].operator->(); } + inline const ::Type* GetObjectType(Map&& types) + { + return dynamic_cast(this->FindOrInsert(new ObjectType(Move(types)))); + } + inline const ::TupleType* GetTupleType(DynamicArray&& types, bool lastArgIsVariadic = false) { return dynamic_cast(this->FindOrInsert(new TupleType(Move(types), lastArgIsVariadic))); diff --git a/src_compiler/types/TypeVisitor.hpp b/src_compiler/types/TypeVisitor.hpp index 1702e1e..1b48e37 100644 --- a/src_compiler/types/TypeVisitor.hpp +++ b/src_compiler/types/TypeVisitor.hpp @@ -20,6 +20,7 @@ //Forward declarations class GenericType; class LeafType; +class ObjectType; class TupleType; class TypeVisitor diff --git a/src_vm/ExternalsManager.cpp b/src_vm/ExternalsManager.cpp index ae4c7b5..96b7755 100644 --- a/src_vm/ExternalsManager.cpp +++ b/src_vm/ExternalsManager.cpp @@ -23,32 +23,41 @@ void ExternalsManager::RegisterAllExternals() { //arithmetical - extern RuntimeValue Add(const RuntimeValue&); + extern RuntimeValue Add(RuntimeValue&, const Module&); this->RegisterExternal(u8"+", Add); - extern RuntimeValue Multiply(const RuntimeValue&); + extern RuntimeValue Multiply(RuntimeValue&, const Module&); this->RegisterExternal(u8"*", Multiply); - extern RuntimeValue Subtract(const RuntimeValue&); + extern RuntimeValue Subtract(RuntimeValue&, const Module&); this->RegisterExternal(u8"-", Subtract); //logical - extern RuntimeValue And(const RuntimeValue&); + extern RuntimeValue And(RuntimeValue&, const Module&); this->RegisterExternal(u8"and", And); - extern RuntimeValue LessThan(const RuntimeValue&); + extern RuntimeValue Equals(RuntimeValue&, const Module&); + this->RegisterExternal(u8"=", Equals); + + extern RuntimeValue LessThan(RuntimeValue&, const Module&); this->RegisterExternal(u8"<", LessThan); - extern RuntimeValue LessThanOrEqual(const RuntimeValue&); + extern RuntimeValue LessThanOrEqual(RuntimeValue&, const Module&); this->RegisterExternal(u8"<=", LessThanOrEqual); - extern RuntimeValue Not(const RuntimeValue&); + extern RuntimeValue Not(RuntimeValue&, const Module&); this->RegisterExternal(u8"not", Not); - extern RuntimeValue Or(const RuntimeValue&); + extern RuntimeValue Or(RuntimeValue&, const Module&); this->RegisterExternal(u8"or", Or); //other - extern RuntimeValue Print(const RuntimeValue&); + extern RuntimeValue Select(RuntimeValue&, const Module&); + this->RegisterExternal(u8"[]", Select); + + extern RuntimeValue ObjectSet(RuntimeValue&, const Module&); + this->RegisterExternal(u8"__set", ObjectSet); + + extern RuntimeValue Print(RuntimeValue&, const Module&); this->RegisterExternal(u8"print", Print); } diff --git a/src_vm/ExternalsManager.hpp b/src_vm/ExternalsManager.hpp index e530830..90de0e1 100644 --- a/src_vm/ExternalsManager.hpp +++ b/src_vm/ExternalsManager.hpp @@ -19,7 +19,9 @@ #pragma once #include "RuntimeValue.hpp" -typedef RuntimeValue(*External)(const RuntimeValue&); +//Forward declarations +class Module; +typedef RuntimeValue(*External)(RuntimeValue&, const Module&); class ExternalsManager { @@ -33,6 +35,7 @@ class ExternalsManager //Inline inline External GetExternal(const String& name) { + ASSERT(this->externals.Contains(name), u8"Missing external: " + name); return this->externals.Get(name); } diff --git a/src_vm/GarbageCollector.hpp b/src_vm/GarbageCollector.hpp index 0f129cd..0c24967 100644 --- a/src_vm/GarbageCollector.hpp +++ b/src_vm/GarbageCollector.hpp @@ -26,6 +26,10 @@ class GarbageCollector BinaryTreeSet*> toDelete = this->allocatedArrays; for (DynamicArray*const& toDeleteElement : toDelete) this->Release(toDeleteElement); + + BinaryTreeSet*> toDelete2 = this->allocatedDictionaries; + for(Map*const& toDeleteElement : toDelete2) + this->Release(toDeleteElement); } //Inline @@ -53,9 +57,17 @@ class GarbageCollector return ptr; } + inline Map* NewDictionary() + { + Map* ptr = new Map; + this->allocatedDictionaries.Insert(ptr); + return ptr; + } + private: //Members BinaryTreeSet*> allocatedArrays; + BinaryTreeSet*> allocatedDictionaries; //Inline inline void Release(DynamicArray* ptr) @@ -63,4 +75,10 @@ class GarbageCollector this->allocatedArrays.Remove(ptr); delete ptr; } + + inline void Release(Map* ptr) + { + this->allocatedDictionaries.Remove(ptr); + delete ptr; + } }; \ No newline at end of file diff --git a/src_vm/Module.cpp b/src_vm/Module.cpp index 1d4bee0..159ab8b 100644 --- a/src_vm/Module.cpp +++ b/src_vm/Module.cpp @@ -23,7 +23,7 @@ Module::Module(SeekableInputStream &inputStream, ExternalsManager& externalsManager) { DataReader dataReader(true, inputStream); - TextReader textReader(inputStream, TextCodecType::ASCII); + TextReader textReader(inputStream, TextCodecType::UTF8); dataReader.Skip(4); //skip signature @@ -36,9 +36,16 @@ Module::Module(SeekableInputStream &inputStream, ExternalsManager& externalsMana uint16 nConstants = dataReader.ReadUInt16(); this->constants.Resize(nConstants); + this->constantStrings.EnsureCapacity(nConstants); for(uint16 i = 0; i < nConstants; i++) { - this->constants[i] = dataReader.ReadFloat64(); + if(dataReader.ReadByte()) + { + uint32 index = this->constantStrings.Push(textReader.ReadZeroTerminatedString()); + this->constants[i] = &this->constantStrings[index]; + } + else + this->constants[i] = dataReader.ReadFloat64(); } this->entryPoint = dataReader.ReadUInt16(); diff --git a/src_vm/Module.hpp b/src_vm/Module.hpp index acf449d..e71c69e 100644 --- a/src_vm/Module.hpp +++ b/src_vm/Module.hpp @@ -34,12 +34,13 @@ class Module return this->GetCodeAtOffset(this->entryPoint); } + //Inline inline const void* GetCodeAtOffset(uint16 offset) const { return reinterpret_cast( &((const uint8 *) this->code)[offset] ); } - inline float64 GetConstant(uint16 constantIndex) const + inline const RuntimeValue& GetConstant(uint16 constantIndex) const { return this->constants[constantIndex]; } @@ -53,6 +54,7 @@ class Module //Members uint16 entryPoint; void* code; - DynamicArray constants; + DynamicArray constants; + DynamicArray constantStrings; DynamicArray moduleExternals; }; \ No newline at end of file diff --git a/src_vm/RuntimeValue.hpp b/src_vm/RuntimeValue.hpp index 7e15c5e..2219776 100644 --- a/src_vm/RuntimeValue.hpp +++ b/src_vm/RuntimeValue.hpp @@ -23,8 +23,10 @@ using namespace StdXX; enum class RuntimeValueType { Bool, + Dictionary, Float64, Null, + String, Tuple }; @@ -44,6 +46,10 @@ class RuntimeValue { } + inline RuntimeValue(String* string) : type(RuntimeValueType::String), string(string) + { + } + //Properties inline RuntimeValueType Type() const { @@ -60,12 +66,40 @@ class RuntimeValue return this->f64; } + inline DynamicArray& ValuesArray() + { + return *this->array; + } + inline const DynamicArray& ValuesArray() const { return *this->array; } + inline Map& ValuesDictionary() + { + return *this->dictionary; + } + + inline const Map& ValuesDictionary() const + { + return *this->dictionary; + } + + inline const String& ValueString() const + { + return *this->string; + } + //Functions + inline static RuntimeValue CreateDictionary(Map* values) + { + RuntimeValue v; + v.type = RuntimeValueType::Dictionary; + v.dictionary = values; + return v; + } + inline static RuntimeValue CreateTuple(DynamicArray* values) { RuntimeValue v; @@ -82,5 +116,7 @@ class RuntimeValue bool b; float64 f64; DynamicArray* array; + Map* dictionary; + String* string; }; }; \ No newline at end of file diff --git a/src_vm/VM.cpp b/src_vm/VM.cpp index 5f320ab..25d6b70 100644 --- a/src_vm/VM.cpp +++ b/src_vm/VM.cpp @@ -27,7 +27,8 @@ void VM::Run() const uint8* pc = static_cast(this->module.EntryPoint()); DynamicArray executionStack; DynamicArray callStack; - DynamicArray argumentStack; + + executionStack.Push(RuntimeValue()); //arg for main function while(true) { @@ -40,15 +41,14 @@ void VM::Run() callStack.Push(pc); pc = static_cast(this->module.GetCodeAtOffset(offset)); - - argumentStack.Push(executionStack.Pop()); } break; case Opcode::CallExtern: { uint16 externIndex = this->ExtractUInt16FromProgramCounter(pc); - const auto& arg = executionStack.Pop(); - auto result = this->module.GetExternal(externIndex)(arg); + RuntimeValue arg = executionStack.Pop(); + const auto& external = this->module.GetExternal(externIndex); + auto result = external(arg, this->module); executionStack.Push(result); } break; @@ -66,11 +66,19 @@ void VM::Run() executionStack.Push(this->module.GetConstant(constantIndex)); } break; + case Opcode::NewDictionary: + { + this->garbageCollector.CleanUp(executionStack); + Map* values = this->garbageCollector.NewDictionary(); + + executionStack.Push(RuntimeValue::CreateDictionary(values)); + } + break; case Opcode::NewTuple: { uint16 nEntries = this->ExtractUInt16FromProgramCounter(pc); - this->garbageCollector.CleanUp(argumentStack); + this->garbageCollector.CleanUp(executionStack); DynamicArray* values = this->garbageCollector.NewArray(); for(uint16 i = 0; i < nEntries; i++) @@ -79,9 +87,22 @@ void VM::Run() executionStack.Push(RuntimeValue::CreateTuple(values)); } break; - case Opcode::PushParameter: + case Opcode::Pop: { - executionStack.Push(argumentStack.Last()); + executionStack.Pop(); + } + break; + case Opcode::PopAssign: + { + RuntimeValue value = executionStack.Pop(); + executionStack.Last() = value; + } + break; + case Opcode::Push: + { + uint16 offset = this->ExtractUInt16FromProgramCounter(pc); + RuntimeValue value = executionStack[executionStack.GetNumberOfElements() - 1 - offset]; + executionStack.Push(value); } break; case Opcode::Return: @@ -89,10 +110,7 @@ void VM::Run() if(callStack.IsEmpty()) return; else - { pc = callStack.Pop(); - argumentStack.Pop(); - } } break; default: diff --git a/src_vm/externals/Arithmetical.cpp b/src_vm/externals/Arithmetical.cpp index d0210aa..88ea7c4 100644 --- a/src_vm/externals/Arithmetical.cpp +++ b/src_vm/externals/Arithmetical.cpp @@ -18,8 +18,9 @@ */ //Local #include "../RuntimeValue.hpp" +#include "../Module.hpp" -RuntimeValue Add(const RuntimeValue& arg) +RuntimeValue Add(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { @@ -37,7 +38,7 @@ RuntimeValue Add(const RuntimeValue& arg) return RuntimeValue(); } -RuntimeValue Multiply(const RuntimeValue& arg) +RuntimeValue Multiply(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { @@ -55,7 +56,7 @@ RuntimeValue Multiply(const RuntimeValue& arg) return RuntimeValue(); } -RuntimeValue Subtract(const RuntimeValue& arg) +RuntimeValue Subtract(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { diff --git a/src_vm/externals/CMakeLists.txt b/src_vm/externals/CMakeLists.txt index 0d7c265..417dd55 100644 --- a/src_vm/externals/CMakeLists.txt +++ b/src_vm/externals/CMakeLists.txt @@ -1,8 +1,9 @@ set(SOURCE_FILES_VM ${SOURCE_FILES_VM} - ${CMAKE_CURRENT_SOURCE_DIR}/Logical.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Arithmetical.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Logical.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Misc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Print.cpp PARENT_SCOPE) \ No newline at end of file diff --git a/src_vm/externals/Logical.cpp b/src_vm/externals/Logical.cpp index 1d1ee18..b877525 100644 --- a/src_vm/externals/Logical.cpp +++ b/src_vm/externals/Logical.cpp @@ -18,8 +18,9 @@ */ //Local #include "../RuntimeValue.hpp" +#include "../Module.hpp" -RuntimeValue And(const RuntimeValue& arg) +RuntimeValue And(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { @@ -38,7 +39,26 @@ RuntimeValue And(const RuntimeValue& arg) return RuntimeValue(); } -RuntimeValue LessThan(const RuntimeValue& arg) +RuntimeValue Equals(RuntimeValue& arg, const Module&) +{ + if(arg.Type() == RuntimeValueType::Tuple) + { + if( + (arg.ValuesArray().GetNumberOfElements() == 2) + && (arg.ValuesArray()[0].Type() == RuntimeValueType::Float64) + && (arg.ValuesArray()[1].Type() == RuntimeValueType::Float64) + ) + { + float64 lhs = arg.ValuesArray()[0].ValueF64(); + float64 rhs = arg.ValuesArray()[1].ValueF64(); + return { lhs == rhs }; + } + } + NOT_IMPLEMENTED_ERROR; + return RuntimeValue(); +} + +RuntimeValue LessThan(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { @@ -57,7 +77,7 @@ RuntimeValue LessThan(const RuntimeValue& arg) return RuntimeValue(); } -RuntimeValue LessThanOrEqual(const RuntimeValue& arg) +RuntimeValue LessThanOrEqual(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { @@ -76,7 +96,7 @@ RuntimeValue LessThanOrEqual(const RuntimeValue& arg) return RuntimeValue(); } -RuntimeValue Not(const RuntimeValue& arg) +RuntimeValue Not(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Bool) { @@ -86,7 +106,7 @@ RuntimeValue Not(const RuntimeValue& arg) return RuntimeValue(); } -RuntimeValue Or(const RuntimeValue& arg) +RuntimeValue Or(RuntimeValue& arg, const Module&) { if(arg.Type() == RuntimeValueType::Tuple) { diff --git a/src_vm/externals/Misc.cpp b/src_vm/externals/Misc.cpp new file mode 100644 index 0000000..9774a21 --- /dev/null +++ b/src_vm/externals/Misc.cpp @@ -0,0 +1,61 @@ +/* +* Copyright (c) 2018-2020 Amir Czwink (amir130@hotmail.de) +* +* This file is part of ACScript. +* +* ACScript is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* ACScript is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with ACScript. If not, see . +*/ +//Local +#include "../RuntimeValue.hpp" +#include "../Module.hpp" + +RuntimeValue ObjectSet(RuntimeValue& arg, const Module&) +{ + if(arg.Type() == RuntimeValueType::Tuple) + { + if( + (arg.ValuesArray().GetNumberOfElements() == 3) + && (arg.ValuesArray()[0].Type() == RuntimeValueType::Dictionary) + && (arg.ValuesArray()[1].Type() == RuntimeValueType::String) + ) + { + auto& dict = arg.ValuesArray()[0].ValuesDictionary(); + auto& key = arg.ValuesArray()[1].ValueString(); + dict[key] = arg.ValuesArray()[2]; + return arg.ValuesArray()[0]; + } + } + NOT_IMPLEMENTED_ERROR; + return RuntimeValue(); +} + +RuntimeValue Select(RuntimeValue& arg, const Module&) +{ + if(arg.Type() == RuntimeValueType::Tuple) + { + if( + (arg.ValuesArray().GetNumberOfElements() == 2) + && (arg.ValuesArray()[0].Type() == RuntimeValueType::Tuple) + && (arg.ValuesArray()[1].Type() == RuntimeValueType::Float64) + && (arg.ValuesArray()[0].ValuesArray().GetNumberOfElements() > arg.ValuesArray()[1].ValueF64()) + ) + { + const DynamicArray& array = arg.ValuesArray()[0].ValuesArray(); + float64 index = arg.ValuesArray()[1].ValueF64(); + return { array[index] }; + } + } + NOT_IMPLEMENTED_ERROR; + return RuntimeValue(); +} \ No newline at end of file diff --git a/src_vm/externals/Print.cpp b/src_vm/externals/Print.cpp index dd231d3..2a98d78 100644 --- a/src_vm/externals/Print.cpp +++ b/src_vm/externals/Print.cpp @@ -18,6 +18,7 @@ */ //Local #include "../RuntimeValue.hpp" +#include "../Module.hpp" static String ToString(const RuntimeValue& value) { @@ -29,18 +30,25 @@ static String ToString(const RuntimeValue& value) return String::Number(value.ValueF64()); case RuntimeValueType::Null: return u8"null"; + case RuntimeValueType::Dictionary: + { + DynamicArray strings; + for(const auto& kv : value.ValuesDictionary()) + strings.Push(kv.key + u8": " + ToString(kv.value)); + return u8"{ " + String::Join(strings, u8", ") + u8" }"; + } case RuntimeValueType::Tuple: { DynamicArray strings; for(const RuntimeValue& subValue : value.ValuesArray()) - strings.Push(ToString((subValue))); + strings.Push(ToString(subValue)); return u8"(" + String::Join(strings, u8", ") + u8")"; } } return String(); } -RuntimeValue Print(const RuntimeValue& arg) +RuntimeValue Print(RuntimeValue& arg, const Module& module) { stdOut << ToString(arg) << endl; return RuntimeValue();