├── Library ├── tests │ ├── Usings.cs │ ├── AST │ │ ├── EnumsTests.cs │ │ ├── NodeTests.cs │ │ ├── ScopeTests.cs │ │ ├── ExpressionTests.cs │ │ ├── StatementTests.cs │ │ ├── DeclarationTests.cs │ │ ├── MethodInterfaceTests.cs │ │ ├── Statements │ │ │ ├── IfStatementTests.cs │ │ │ ├── BreakStatementTests.cs │ │ │ ├── CaseStatementTests.cs │ │ │ ├── EmitStatementTests.cs │ │ │ ├── ForStatementTests.cs │ │ │ ├── ThrowStatementTests.cs │ │ │ ├── WhileStatementTests.cs │ │ │ ├── AssingStatementTests.cs │ │ │ ├── DoWhileStatementTests.cs │ │ │ ├── ReturnStatementTests.cs │ │ │ ├── SwitchStatementTests.cs │ │ │ ├── AsmBlockStatementTests.cs │ │ │ ├── ContinueStatementTests.cs │ │ │ └── MethodCallStatementTests.cs │ │ ├── Expressions │ │ │ ├── VarExpressionTests.cs │ │ │ ├── ArrayExpressionTests.cs │ │ │ ├── CastExpressionTests.cs │ │ │ ├── ConstExpressionTests.cs │ │ │ ├── MacroExpressionTests.cs │ │ │ ├── BinaryExpressionTests.cs │ │ │ ├── LiteralExpressionTests.cs │ │ │ ├── NegationExpressionTests.cs │ │ │ ├── MethodCallExpressionTests.cs │ │ │ ├── ArrayElementExpressionTests.cs │ │ │ └── StructFieldExpressionTests.cs │ │ └── Declaration │ │ │ ├── SetDeclarationTests.cs │ │ │ ├── MapDeclarationTests.cs │ │ │ ├── ListDeclarationTests.cs │ │ │ ├── MethodDeclarationTests.cs │ │ │ ├── DecimalDeclarationTests.cs │ │ │ ├── EventDeclarationTests.cs │ │ │ ├── StructDeclarationTests.cs │ │ │ ├── EnumDeclarationTests.cs │ │ │ ├── VarDeclarationTests.cs │ │ │ └── ConstDeclarationTest.cs │ ├── CodeGen │ │ ├── NFTTests.cs │ │ ├── ModuleTests.cs │ │ ├── ScriptTests.cs │ │ ├── BuiltinsTests.cs │ │ ├── ContractTests.cs │ │ ├── RegisterTests.cs │ │ └── CodeGeneratorTests.cs │ ├── Contracts │ │ ├── StructTests.cs │ │ ├── ForTests.cs │ │ ├── ConstantsTests.cs │ │ ├── BooleanTests.cs │ │ ├── SwitchTest.cs │ │ ├── ReturnTests.cs │ │ ├── PropertiesTests.cs │ │ ├── AddressTests.cs │ │ ├── EnumsTests.cs │ │ ├── VariableTests.cs │ │ ├── IfTests.cs │ │ ├── ArrayTests.cs │ │ ├── MethodTests.cs │ │ └── DecimalTests.cs │ ├── Lexers │ │ ├── SolidityLexerTests.cs │ │ └── TombLangLexerTests.cs │ ├── Compilers │ │ ├── SolidityCompilerTests.cs │ │ └── TombLangCompilerTests.cs │ └── TOMBLib.Tests.csproj └── src │ ├── AST │ ├── Declarations │ │ ├── SetDeclaration.cs │ │ ├── ListDeclaration.cs │ │ ├── DecimalDeclaration.cs │ │ ├── MapDeclaration.cs │ │ ├── VarDeclaration.cs │ │ ├── ConstDeclaration.cs │ │ ├── StructDeclaration.cs │ │ └── EnumDeclaration.cs │ ├── Declaration.cs │ ├── Statements │ │ ├── AsmBlockStatement.cs │ │ ├── ThrowStatement.cs │ │ ├── MethodCallStatement.cs │ │ ├── BreakStatement.cs │ │ ├── ContinueStatement.cs │ │ ├── CaseStatement.cs │ │ ├── DoWhileStatement.cs │ │ ├── WhileStatement.cs │ │ ├── EmitStatement.cs │ │ ├── AssignStatement.cs │ │ ├── IfStatement.cs │ │ ├── ForStatement.cs │ │ ├── SwitchStatement.cs │ │ └── ReturnStatement.cs │ ├── Node.cs │ ├── Expressions │ │ ├── VarExpression.cs │ │ ├── ConstExpression.cs │ │ ├── NegationExpression.cs │ │ ├── ArrayExpression.cs │ │ ├── StructFieldExpression.cs │ │ ├── ArrayElementExpression.cs │ │ ├── CastExpression.cs │ │ ├── MacroExpression.cs │ │ └── BinaryExpression.cs │ ├── Statement.cs │ └── Expression.cs │ ├── Exceptions.cs │ ├── CodeGen │ ├── Register.cs │ ├── CodeGenerator.cs │ ├── NFT.cs │ └── Script.cs │ ├── TOMBLib.csproj │ └── Extensions.cs ├── logo.png ├── Tests ├── AST │ ├── EnumsTests.cs │ ├── NodeTests.cs │ ├── ScopeTests.cs │ ├── ExpressionTests.cs │ ├── StatementTests.cs │ ├── DeclarationTests.cs │ ├── MethodInterfaceTests.cs │ ├── Statements │ │ ├── ForStatementTests.cs │ │ ├── IfStatementTests.cs │ │ ├── BreakStatementTests.cs │ │ ├── CaseStatementTests.cs │ │ ├── EmitStatementTests.cs │ │ ├── ThrowStatementTests.cs │ │ ├── WhileStatementTests.cs │ │ ├── AsmBlockStatementTests.cs │ │ ├── AssingStatementTests.cs │ │ ├── ContinueStatementTests.cs │ │ ├── DoWhileStatementTests.cs │ │ ├── ReturnStatementTests.cs │ │ ├── SwitchStatementTests.cs │ │ └── MethodCallStatementTests.cs │ ├── Expressions │ │ ├── VarExpressionTests.cs │ │ ├── ArrayExpressionTests.cs │ │ ├── BinaryExpressionTests.cs │ │ ├── CastExpressionTests.cs │ │ ├── ConstExpressionTests.cs │ │ ├── MacroExpressionTests.cs │ │ ├── LiteralExpressionTests.cs │ │ ├── NegationExpressionTests.cs │ │ ├── MethodCallExpressionTests.cs │ │ ├── StructFieldExpressionTests.cs │ │ └── ArrayElementExpressionTests.cs │ └── Declaration │ │ ├── MapDeclarationTests.cs │ │ ├── SetDeclarationTests.cs │ │ ├── ListDeclarationTests.cs │ │ ├── MethodDeclarationTests.cs │ │ ├── DecimalDeclarationTests.cs │ │ ├── EventDeclarationTests.cs │ │ ├── StructDeclarationTests.cs │ │ ├── EnumDeclarationTests.cs │ │ ├── VarDeclarationTests.cs │ │ └── ConstDeclarationTest.cs ├── CodeGen │ ├── NFTTests.cs │ ├── ModuleTests.cs │ ├── ScriptTests.cs │ ├── BuiltinsTests.cs │ ├── ContractTests.cs │ ├── RegisterTests.cs │ └── CodeGeneratorTests.cs ├── Contracts │ ├── StructTests.cs │ ├── ForTests.cs │ ├── BooleanTests.cs │ ├── ConstantsTests.cs │ ├── SwitchTest.cs │ ├── ReturnTests.cs │ ├── PropertiesTests.cs │ ├── AddressTests.cs │ ├── EnumsTests.cs │ ├── VariableTests.cs │ ├── IfTests.cs │ ├── ArrayTests.cs │ ├── MethodTests.cs │ └── DecimalTests.cs ├── Lexers │ ├── SolidityLexerTests.cs │ └── TombLangLexerTests.cs ├── Compilers │ ├── SolidityCompilerTests.cs │ └── TombLangCompilerTests.cs └── Tests.csproj ├── .gitignore ├── Compiler ├── TombCompiler.csproj ├── Properties │ └── PublishProfiles │ │ └── FolderProfile.pubxml └── builtins.tomb ├── Samples ├── example1.sol ├── example6.sol ├── example2.sol ├── example7.sol ├── example4.sol ├── example5.sol ├── example3.sol ├── basic_token.sol └── example8.sol ├── .github └── workflows │ ├── comment-on-pr.yml │ ├── dotnet-publish.yml │ └── dotnet-core.yml ├── LICENSE └── TombCompiler.sln /Library/tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phantasma-io-archive/TOMB/HEAD/logo.png -------------------------------------------------------------------------------- /Tests/AST/EnumsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class EnumsTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/NodeTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class NodeTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/ScopeTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class ScopeTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/NFTTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class NFTTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/ExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class ExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/StatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class StatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/ModuleTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class ModuleTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/ScriptTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class ScriptTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/EnumsTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class EnumsTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/NodeTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class NodeTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/ScopeTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class ScopeTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/DeclarationTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class DeclarationTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/BuiltinsTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class BuiltinsTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/ContractTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class ContractTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/RegisterTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class RegisterTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/Contracts/StructTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Contracts; 2 | 3 | public class StructTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/NFTTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | public class NFTTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/MethodInterfaceTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST; 2 | 3 | public class MethodInterfaceTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/ExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class ExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/StatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class StatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/ModuleTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | public class ModuleTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/ScriptTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | public class ScriptTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/CodeGen/CodeGeneratorTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.CodeGen; 2 | 3 | public class CodeGeneratorTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/Lexers/SolidityLexerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Lexers; 2 | 3 | public class SolidityLexerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/Lexers/TombLangLexerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Lexers; 2 | 3 | public class TombLangLexerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/DeclarationTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class DeclarationTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/BuiltinsTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | public class BuiltinsTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/ContractTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | public class ContractTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/RegisterTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | public class RegisterTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/StructTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.Contracts; 2 | 3 | public class StructTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/MethodInterfaceTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST; 2 | 3 | public class MethodInterfaceTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/ForStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class ForStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/IfStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class IfStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/Compilers/SolidityCompilerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Compilers; 2 | 3 | public class SolidityCompilerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/Compilers/TombLangCompilerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.Compilers; 2 | 3 | public class TombLangCompilerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/Lexers/SolidityLexerTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.Lexers; 2 | 3 | public class SolidityLexerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/Lexers/TombLangLexerTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.Lexers; 2 | 3 | public class TombLangLexerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/VarExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class VarExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/BreakStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class BreakStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/CaseStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class CaseStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/EmitStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class EmitStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/ThrowStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class ThrowStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/WhileStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class WhileStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/ArrayExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class ArrayExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/BinaryExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class BinaryExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/CastExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class CastExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/ConstExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class ConstExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/MacroExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class MacroExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/AsmBlockStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class AsmBlockStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/AssingStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class AssingStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/ContinueStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class ContinueStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/DoWhileStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class DoWhileStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/ReturnStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class ReturnStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/SwitchStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class SwitchStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/IfStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class IfStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/Compilers/SolidityCompilerTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.Compilers; 2 | 3 | public class SolidityCompilerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/Compilers/TombLangCompilerTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.Compilers; 2 | 3 | public class TombLangCompilerTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/LiteralExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class LiteralExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/NegationExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class NegationExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Statements/MethodCallStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Statements; 2 | 3 | public class MethodCallStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/VarExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class VarExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/BreakStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class BreakStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/CaseStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class CaseStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/EmitStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class EmitStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/ForStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class ForStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/ThrowStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class ThrowStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/WhileStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class WhileStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/MethodCallExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class MethodCallExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/StructFieldExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class StructFieldExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/ArrayExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class ArrayExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/CastExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class CastExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/ConstExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class ConstExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/MacroExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class MacroExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/AssingStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class AssingStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/DoWhileStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class DoWhileStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/ReturnStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class ReturnStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/SwitchStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class SwitchStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Tests/AST/Expressions/ArrayElementExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace Tests.AST.Expressions; 2 | 3 | public class ArrayElementExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/BinaryExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class BinaryExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/LiteralExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class LiteralExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/NegationExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class NegationExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/AsmBlockStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class AsmBlockStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/ContinueStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class ContinueStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Statements/MethodCallStatementTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Statements; 2 | 3 | public class MethodCallStatementTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/MethodCallExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class MethodCallExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/ArrayElementExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class ArrayElementExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Library/tests/AST/Expressions/StructFieldExpressionTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.AST.Expressions; 2 | 3 | public class StructFieldExpressionTests 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vs/ 3 | obj/ 4 | bin/ 5 | Publish/ 6 | *.user 7 | 8 | Compiler/Properties/launchSettings.json 9 | .idea/ 10 | coverage/ 11 | 12 | Releases/ 13 | -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/SetDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.Lexers; 2 | 3 | namespace TOMBLib.Tests.AST.Declaration; 4 | 5 | public class SetDeclarationTests 6 | { 7 | [SetUp] 8 | public void Setup() 9 | { 10 | TombLangLexer lexer = new TombLangLexer(); 11 | } 12 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/MapDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace Tests.AST.Declaration; 5 | 6 | public class MapDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/SetDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace Tests.AST.Declaration; 5 | 6 | public class SetDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/ListDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace Tests.AST.Declaration; 5 | 6 | public class ListDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/MethodDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace Tests.AST.Declaration; 5 | 6 | public class MethodDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/MapDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace TOMBLib.Tests.AST.Declaration; 5 | 6 | public class MapDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/ListDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace TOMBLib.Tests.AST.Declaration; 5 | 6 | public class ListDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/MethodDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.Lexers; 3 | 4 | namespace TOMBLib.Tests.AST.Declaration; 5 | 6 | public class MethodDeclarationTests 7 | { 8 | [SetUp] 9 | public void Setup() 10 | { 11 | TombLangLexer lexer = new TombLangLexer(); 12 | } 13 | } -------------------------------------------------------------------------------- /Compiler/TombCompiler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | Phantasma.Tomb 7 | disable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/SetDeclaration.cs: -------------------------------------------------------------------------------- 1 | namespace Phantasma.Tomb.AST.Declarations 2 | { 3 | public class SetDeclaration : VarDeclaration 4 | { 5 | public VarType ValueKind; 6 | 7 | public SetDeclaration(Scope parentScope, string name, VarType valKind) : base(parentScope, name, VarType.Find(VarKind.Storage_Set), VarStorage.Global) 8 | { 9 | this.ValueKind = valKind; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/ListDeclaration.cs: -------------------------------------------------------------------------------- 1 | namespace Phantasma.Tomb.AST.Declarations 2 | { 3 | public class ListDeclaration : VarDeclaration 4 | { 5 | public VarType ValueKind; 6 | 7 | public ListDeclaration(Scope parentScope, string name, VarType valKind) : base(parentScope, name, VarType.Find(VarKind.Storage_List), VarStorage.Global) 8 | { 9 | this.ValueKind = valKind; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/DecimalDeclaration.cs: -------------------------------------------------------------------------------- 1 | namespace Phantasma.Tomb.AST.Declarations 2 | { 3 | public class DecimalDeclaration : VarDeclaration 4 | { 5 | public readonly int Decimals; 6 | 7 | public DecimalDeclaration(Scope parentScope, string name, int decimals, VarStorage storage) : base(parentScope, name, VarType.Find(VarKind.Decimal, decimals), storage) 8 | { 9 | this.Decimals = decimals; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Samples/example1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.10; 2 | 3 | import "Phantasma/Runtime.tomb"; 4 | import "Phantasma/Random.tomb"; 5 | 6 | //the very first example 7 | contract example1 { 8 | uint counter; 9 | 10 | function getCounter(uint n) public view returns (uint) { 11 | Runtime.log("Returning the current value of counter"); 12 | return counter; 13 | } 14 | 15 | function getRandom() public view returns (uint) { 16 | Runtime.log("Generating a random number"); 17 | return Random.generate(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/MapDeclaration.cs: -------------------------------------------------------------------------------- 1 | namespace Phantasma.Tomb.AST.Declarations 2 | { 3 | public class MapDeclaration: VarDeclaration 4 | { 5 | public VarType KeyKind; 6 | public VarType ValueKind; 7 | 8 | public MapDeclaration(Scope parentScope, string name, VarType keyKind, VarType valKind) : base(parentScope, name, VarType.Find(VarKind.Storage_Map), VarStorage.Global) 9 | { 10 | this.KeyKind = keyKind; 11 | this.ValueKind = valKind; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Compiler/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Debug 8 | Any CPU 9 | C:\Code\TOMB\Publish\ 10 | FileSystem 11 | net7.0 12 | false 13 | 14 | -------------------------------------------------------------------------------- /Samples/example6.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.5.00; 2 | 3 | //the very sixth example 4 | contract Example6 { 5 | 6 | mapping (address => mapping (bytes32 => uint)) public stamps; 7 | 8 | function store(bytes32 hash) public { 9 | stamps[msg.sender][hash] = block.timestamp; 10 | } 11 | 12 | function hashIt(string memory data) public pure returns (bytes32) { 13 | return keccak256(abi.encodePacked(data)); 14 | } 15 | 16 | function verify(address recipient, string memory data) public view returns (uint) { 17 | return stamps[recipient][keccak256(abi.encodePacked(data))]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Library/src/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST; 2 | using System; 3 | 4 | namespace Phantasma.Tomb 5 | { 6 | public class CompilerException : Exception 7 | { 8 | private static string FetchCurrentLine() 9 | { 10 | return Compiler.Instance != null ? Compiler.Instance.CurrentLine.ToString() : "???"; 11 | } 12 | 13 | public CompilerException(string msg) : base($"line {FetchCurrentLine()}: {msg}") 14 | { 15 | 16 | } 17 | 18 | public CompilerException(Node node, string msg) : base($"line {node.LineNumber}: {msg}") 19 | { 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Library/src/CodeGen/Register.cs: -------------------------------------------------------------------------------- 1 | namespace Phantasma.Tomb.CodeGen 2 | { 3 | public class Register 4 | { 5 | public readonly int Index; 6 | public readonly string Alias; 7 | 8 | public readonly static Register Temporary = new Register(0, null); 9 | 10 | public Register(int index, string alias = null) 11 | { 12 | Index = index; 13 | Alias = alias; 14 | } 15 | 16 | public override string ToString() 17 | { 18 | if (Alias != null) 19 | { 20 | return "$" + Alias; 21 | 22 | } 23 | return "r"+Index; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Library/src/AST/Declaration.cs: -------------------------------------------------------------------------------- 1 | namespace Phantasma.Tomb.AST 2 | { 3 | public abstract class Declaration: Node 4 | { 5 | public readonly string Name; 6 | public Scope ParentScope { get; internal set; } 7 | 8 | protected Declaration(Scope parentScope, string name) 9 | { 10 | Name = name; 11 | ParentScope = parentScope; 12 | ValidateName(); 13 | } 14 | 15 | protected virtual void ValidateName() 16 | { 17 | if (!Lexer.Instance.IsValidIdentifier(Name)) 18 | { 19 | throw new CompilerException("Invalid identifier: " + Name); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Samples/example2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.4.10; 2 | 3 | //the very second example 4 | contract Example2 { 5 | 6 | uint counter=0; 7 | mapping (uint => string) stringList; //maps an integer to a string (creates an array) 8 | 9 | function push(string memory info) public { 10 | stringList[counter] = info; //saves the input string (info) into the list using the index "counter" 11 | counter++; //increment the counter 12 | } 13 | 14 | function get(uint nr) public view returns (string memory) { 15 | return stringList[nr]; //returns the string that is mapped to the index nr 16 | } 17 | function getCounter() public view returns (uint) { 18 | return counter; //return the number of strings 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Samples/example7.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.4.10; 2 | 3 | //the very seventh example 4 | contract Example7 { 5 | 6 | address owner; 7 | mapping (address => uint) accounts; 8 | 9 | constructor() public { 10 | owner = msg.sender; 11 | } 12 | 13 | function mint(address recipient, uint value) public { 14 | if(msg.sender == owner) { 15 | accounts[recipient] += value; 16 | } 17 | } 18 | 19 | function transfer(address to, uint value) public{ 20 | if(accounts[msg.sender] >= value) { 21 | accounts[msg.sender] -= value; 22 | accounts[to] += value; 23 | } 24 | } 25 | 26 | function balance(address addr) public view returns (uint) { 27 | return accounts[addr]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Library/src/AST/Statements/AsmBlockStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class AsmBlockStatement : Statement 7 | { 8 | public string[] lines; 9 | 10 | public AsmBlockStatement(string[] lines) : base() 11 | { 12 | this.lines = lines; 13 | } 14 | 15 | public override void Visit(Action callback) 16 | { 17 | callback(this); 18 | } 19 | 20 | public override bool IsNodeUsed(Node node) 21 | { 22 | return (node == this); 23 | } 24 | 25 | public override void GenerateCode(CodeGenerator output) 26 | { 27 | foreach (var line in lines) 28 | { 29 | output.AppendLine(this, line); 30 | } 31 | } 32 | } 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Library/src/AST/Statements/ThrowStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class ThrowStatement : Statement 7 | { 8 | public readonly Expression expr; 9 | 10 | public ThrowStatement(Expression expr) : base() 11 | { 12 | this.expr = expr; 13 | } 14 | 15 | public override void Visit(Action callback) 16 | { 17 | callback(this); 18 | } 19 | 20 | public override bool IsNodeUsed(Node node) 21 | { 22 | return (node == this); 23 | } 24 | 25 | public override void GenerateCode(CodeGenerator output) 26 | { 27 | var reg = expr.GenerateCode(output); 28 | output.AppendLine(this, $"THROW {reg}"); 29 | Compiler.Instance.DeallocRegister(ref reg); 30 | } 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Library/src/TOMBLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | Phantasma.TOMB 8 | Provides runtime compilation of TOMB smart contracts and tokens. 9 | logo.png 10 | 1.5.3 11 | True 12 | Phantasma.TOMB 13 | https://github.com/phantasma-io/TOMB 14 | 15 | 16 | 17 | 18 | True 19 | \ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Library/src/AST/Node.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Phantasma.Tomb.AST 4 | { 5 | public abstract class Node 6 | { 7 | public int LineNumber; 8 | public int Column; 9 | public string NodeID; 10 | 11 | public Node() 12 | { 13 | if (Compiler.Instance != null) 14 | { 15 | this.LineNumber = Compiler.Instance.CurrentLine; 16 | this.Column = Compiler.Instance.CurrentColumn; 17 | this.NodeID = this.GetType().Name.ToLower() + Compiler.Instance.AllocateLabel(); 18 | } 19 | else 20 | { 21 | this.LineNumber = -1; 22 | this.Column = -1; 23 | this.NodeID = this.GetType().Name.ToLower(); 24 | } 25 | } 26 | 27 | public abstract bool IsNodeUsed(Node node); 28 | 29 | public abstract void Visit(Action callback); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/VarDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Declarations 6 | { 7 | public class VarDeclaration : Declaration 8 | { 9 | public VarType Type; 10 | public VarStorage Storage; 11 | public Register Register = null; 12 | 13 | public VarDeclaration(Scope parentScope, string name, VarType type, VarStorage storage) : base(parentScope, name) 14 | { 15 | this.Type = type; 16 | this.Storage = storage; 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return $"var {Name}:{Type}"; 22 | } 23 | 24 | public override void Visit(Action callback) 25 | { 26 | callback(this); 27 | } 28 | 29 | public override bool IsNodeUsed(Node node) 30 | { 31 | return node == this; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Library/src/AST/Statements/MethodCallStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Expressions; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Statements 6 | { 7 | public class MethodCallStatement : Statement 8 | { 9 | public MethodCallExpression expression; 10 | 11 | public MethodCallStatement() : base() 12 | { 13 | 14 | } 15 | 16 | public override void Visit(Action callback) 17 | { 18 | callback(this); 19 | expression.Visit(callback); 20 | } 21 | 22 | public override bool IsNodeUsed(Node node) 23 | { 24 | return (node == this) || expression.IsNodeUsed(node); 25 | } 26 | 27 | public override void GenerateCode(CodeGenerator output) 28 | { 29 | var reg = expression.GenerateCode(output); 30 | Compiler.Instance.DeallocRegister(ref reg); 31 | } 32 | } 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /Library/tests/TOMBLib.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/ConstDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Declarations 6 | { 7 | public class ConstDeclaration : Declaration 8 | { 9 | public VarType Type; 10 | public string Value; 11 | 12 | public ConstDeclaration(Scope parentScope, string name, VarType kind, string value) : base(parentScope, name) 13 | { 14 | this.Type = kind; 15 | this.Value = value; 16 | } 17 | 18 | public void GenerateCode(CodeGenerator output) 19 | { 20 | // DO NOTHING 21 | } 22 | 23 | public override string ToString() 24 | { 25 | return $"const {Name}:{Type}"; 26 | } 27 | 28 | public override void Visit(Action callback) 29 | { 30 | callback(this); 31 | } 32 | 33 | public override bool IsNodeUsed(Node node) 34 | { 35 | return node == this; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/comment-on-pr.yml: -------------------------------------------------------------------------------- 1 | name: Comment on the pull request 2 | 3 | # read-write repo token 4 | # access to secrets 5 | on: 6 | workflow_run: 7 | workflows: [".NET Core"] 8 | types: 9 | - completed 10 | permissions: 11 | pull-requests: write 12 | actions: write 13 | issues: write 14 | contents: write 15 | 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.PR_KEY }} 18 | 19 | jobs: 20 | build: 21 | name: Comment on PR 22 | runs-on: ubuntu-latest 23 | if: > 24 | github.event.workflow_run.event == 'pull_request' && 25 | github.event.workflow_run.conclusion == 'success' 26 | permissions: write-all 27 | steps: 28 | - uses: actions/checkout@v3 29 | 30 | - uses: actions/download-artifact@v3 31 | with: 32 | name: my-artifact 33 | 34 | - name: Add Coverage PR Comment 35 | uses: marocchino/sticky-pull-request-comment@v2 36 | with: 37 | recreate: true 38 | path: code-coverage-results.md 39 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sérgio Flores 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/StructDeclaration.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Phantasma.Tomb.AST.Declarations 7 | { 8 | public struct StructField 9 | { 10 | public readonly string name; 11 | public readonly VarType type; 12 | 13 | public StructField(string name, VarType type) 14 | { 15 | this.name = name; 16 | this.type = type; 17 | } 18 | 19 | public StructField(string name, VarKind kind) : this(name, VarType.Find(kind)) 20 | { 21 | } 22 | } 23 | 24 | public class StructDeclaration: Declaration 25 | { 26 | public StructField[] fields; 27 | 28 | public StructDeclaration(string name, IEnumerable fields) : base(null, name) 29 | { 30 | this.fields = fields.ToArray(); 31 | } 32 | 33 | public override bool IsNodeUsed(Node node) 34 | { 35 | return (node == this); 36 | } 37 | 38 | public override void Visit(Action callback) 39 | { 40 | callback(this); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Samples/example4.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.4.10; 2 | 3 | //the very fourth example 4 | contract Example4 { 5 | 6 | event Message( 7 | string msg 8 | ); 9 | 10 | struct Account { 11 | string addr; 12 | uint amount; //default is 256bits 13 | } 14 | 15 | uint counter=1; 16 | mapping (uint => Account) accounts; 17 | address public owner; 18 | 19 | constructor() public { 20 | owner = msg.sender; 21 | } 22 | function create(string memory addr) public { 23 | accounts[counter++] = Account(addr, 42); 24 | owner = msg.sender; 25 | } 26 | 27 | function get(uint nr) public view returns (string memory) { 28 | return accounts[nr].addr; 29 | } 30 | function getAmount(uint nr) public view returns (uint) { 31 | return accounts[nr].amount; 32 | } 33 | 34 | function set(uint nr, string memory addr) public returns (bool) { 35 | if(owner == msg.sender) { 36 | accounts[counter++] = Account(addr, nr); 37 | emit Message("all set!"); //raises the event "Message" 38 | return true; 39 | } else { 40 | return false; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/Contracts/ForTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Utils; 6 | using Phantasma.Tomb.Compilers; 7 | 8 | namespace Tests.Contracts; 9 | 10 | public class ForTests 11 | { 12 | [Test] 13 | public void ForLoop() 14 | { 15 | var sourceCode = 16 | @" 17 | contract test { 18 | public countStuff():number { 19 | local x:number = 0; 20 | for (local i=0; i<9; i+=1) 21 | { 22 | x+=2; 23 | } 24 | return x; 25 | } 26 | }"; 27 | 28 | var parser = new TombLangCompiler(); 29 | var contract = parser.Process(sourceCode).First(); 30 | 31 | var storage = new Dictionary(new ByteArrayComparer()); 32 | 33 | var countStuff = contract.abi.FindMethod("countStuff"); 34 | Assert.IsNotNull(countStuff); 35 | 36 | var vm = new TestVM(contract, storage, countStuff); 37 | var result = vm.Execute(); 38 | Assert.IsTrue(result == ExecutionState.Halt); 39 | 40 | Assert.IsTrue(vm.Stack.Count == 1); 41 | var val = vm.Stack.Pop().AsNumber(); 42 | Assert.IsTrue(val == 18); 43 | } 44 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/BreakStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class BreakStatement : Statement 7 | { 8 | public readonly Scope scope; 9 | 10 | public BreakStatement(Scope scope) : base() 11 | { 12 | this.scope = scope; 13 | } 14 | 15 | public override bool IsNodeUsed(Node node) 16 | { 17 | return (node == this); 18 | } 19 | 20 | public override void Visit(Action callback) 21 | { 22 | callback(this); 23 | } 24 | 25 | public override void GenerateCode(CodeGenerator output) 26 | { 27 | if (Compiler.Instance.CurrentLoop == null) 28 | { 29 | if (this.scope.Method != null && this.scope.Method.@interface.Kind == MethodKind.Trigger) 30 | { 31 | throw new CompilerException("trigger break not implemented"); 32 | } 33 | 34 | throw new CompilerException("not inside a loop"); 35 | } 36 | 37 | output.AppendLine(this, $"JMP @loop_end_{ Compiler.Instance.CurrentLoop.NodeID}"); 38 | } 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Samples/example5.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.4.10; 2 | 3 | //the very fifth example 4 | contract Example5 { 5 | 6 | event Message( 7 | string msg 8 | ); 9 | 10 | mapping (address => uint) accounts; //maps an address to an integer 11 | 12 | function getContractBalance() public view returns (uint256){ 13 | return address(this).balance; 14 | } 15 | 16 | function deposit() public payable { //payable means that you can send ether to the contract (depositing in the accounts array mantained by the smart contract) 17 | accounts[msg.sender] += msg.value; 18 | emit Message("deposit!"); 19 | } 20 | function balance() public view returns (uint) { //return the balance of your account 21 | return accounts[msg.sender]; 22 | } 23 | function withdraw(uint amount) public returns (bool){ //withdraw funds from your account 24 | if(accounts[msg.sender] >= amount) { 25 | accounts[msg.sender]-= amount; 26 | msg.sender.transfer(amount); //transfer the amount from the smart contract to the sender of the msg 27 | emit Message("withdraw!"); 28 | return true; 29 | } 30 | emit Message("no withdraw!"); 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/AST/Declaration/DecimalDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.CodeGen; 5 | using Phantasma.Tomb.Lexers; 6 | 7 | namespace Tests.AST.Declaration; 8 | 9 | public class DecimalDeclarationTests 10 | { 11 | [SetUp] 12 | public void Setup() 13 | { 14 | TombLangLexer lexer = new TombLangLexer(); 15 | } 16 | 17 | [Test] 18 | public void DecimalDeclaration_Constructor_SetsProperties() 19 | { 20 | // Arrange 21 | var module = new Contract("myself", ModuleKind.Contract); 22 | var parentScope = new Scope(module); 23 | var name = "myDecimal"; 24 | var type = VarType.Find(VarKind.Decimal, 2); 25 | var storage = VarStorage.Local; 26 | var value = "3.12"; 27 | var decimals = 2; 28 | 29 | // Act 30 | var constDeclaration = new DecimalDeclaration(parentScope, name, decimals, storage); 31 | 32 | // Assert 33 | Assert.AreEqual(parentScope, constDeclaration.ParentScope); 34 | Assert.AreEqual(name, constDeclaration.Name); 35 | Assert.AreEqual(type, constDeclaration.Type); 36 | Assert.AreEqual(decimals, constDeclaration.Decimals); 37 | } 38 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/ContinueStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class ContinueStatement : Statement 7 | { 8 | public readonly Scope scope; 9 | 10 | public ContinueStatement(Scope scope) : base() 11 | { 12 | this.scope = scope; 13 | } 14 | 15 | public override bool IsNodeUsed(Node node) 16 | { 17 | return (node == this); 18 | } 19 | 20 | public override void Visit(Action callback) 21 | { 22 | callback(this); 23 | } 24 | 25 | public override void GenerateCode(CodeGenerator output) 26 | { 27 | if (Compiler.Instance.CurrentLoop == null) 28 | { 29 | if (this.scope.Method != null && this.scope.Method.@interface.Kind == MethodKind.Trigger) 30 | { 31 | throw new CompilerException("trigger continuenot implemented"); 32 | } 33 | 34 | throw new CompilerException("not inside a loop"); 35 | } 36 | 37 | output.AppendLine(this, $"JMP @loop_start_{ Compiler.Instance.CurrentLoop.NodeID}"); 38 | } 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/DecimalDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.CodeGen; 5 | using Phantasma.Tomb.Lexers; 6 | 7 | namespace TOMBLib.Tests.AST.Declaration; 8 | 9 | public class DecimalDeclarationTests 10 | { 11 | [SetUp] 12 | public void Setup() 13 | { 14 | TombLangLexer lexer = new TombLangLexer(); 15 | } 16 | 17 | [Test] 18 | public void DecimalDeclaration_Constructor_SetsProperties() 19 | { 20 | // Arrange 21 | var module = new Contract("myself", ModuleKind.Contract); 22 | var parentScope = new Scope(module); 23 | var name = "myDecimal"; 24 | var type = VarType.Find(VarKind.Decimal, 2); 25 | var storage = VarStorage.Local; 26 | var value = "3.12"; 27 | var decimals = 2; 28 | 29 | // Act 30 | var constDeclaration = new DecimalDeclaration(parentScope, name, decimals, storage); 31 | 32 | // Assert 33 | Assert.AreEqual(parentScope, constDeclaration.ParentScope); 34 | Assert.AreEqual(name, constDeclaration.Name); 35 | Assert.AreEqual(type, constDeclaration.Type); 36 | Assert.AreEqual(decimals, constDeclaration.Decimals); 37 | } 38 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/ForTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Domain.Execution.Enums; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace TOMBLib.Tests.Contracts; 10 | 11 | public class ForTests 12 | { 13 | [Test] 14 | public void ForLoop() 15 | { 16 | var sourceCode = 17 | @" 18 | contract test { 19 | public countStuff():number { 20 | local x:number = 0; 21 | for (local i=0; i<9; i+=1) 22 | { 23 | x+=2; 24 | } 25 | return x; 26 | } 27 | }"; 28 | 29 | var parser = new TombLangCompiler(); 30 | var contract = parser.Process(sourceCode).First(); 31 | 32 | var storage = new Dictionary(new ByteArrayComparer()); 33 | 34 | var countStuff = contract.abi.FindMethod("countStuff"); 35 | Assert.IsNotNull(countStuff); 36 | 37 | var vm = new TestVM(contract, storage, countStuff); 38 | var result = vm.Execute(); 39 | Assert.IsTrue(result == ExecutionState.Halt); 40 | 41 | Assert.IsTrue(vm.Stack.Count == 1); 42 | var val = vm.Stack.Pop().AsNumber(); 43 | Assert.IsTrue(val == 18); 44 | } 45 | } -------------------------------------------------------------------------------- /Samples/example3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.4.10; 2 | 3 | //the very third example 4 | contract Example3 { 5 | struct Account { 6 | string addr; 7 | uint amount; //default is 256bits 8 | } //creates a structure 9 | 10 | uint counter=1; 11 | mapping (uint => Account) accounts; 12 | address public owner; 13 | 14 | constructor() public { 15 | owner = msg.sender; 16 | } 17 | 18 | function create(string memory addr) public { //creates the first object in the array and set your address as the owner 19 | accounts[counter++] = Account(addr, 42); 20 | owner = msg.sender; 21 | } 22 | 23 | function get(uint nr) public view returns (string memory) { //returns the address of the position "nr" 24 | return accounts[nr].addr; 25 | } 26 | 27 | function getAmount(uint nr) public view returns (uint) { //returns the amount saved for the position "nr" 28 | return accounts[nr].amount; 29 | } 30 | 31 | function set(uint nr, string memory addr) public returns (bool) { //sets an amount for an address (only possible if the sender of the msg is the owner) 32 | if(owner == msg.sender) { 33 | accounts[counter++] = Account(addr, nr); 34 | return true; 35 | } else { 36 | return false; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Library/src/AST/Declarations/EnumDeclaration.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Phantasma.Tomb.AST.Declarations 6 | { 7 | public struct EnumEntry 8 | { 9 | public readonly string name; 10 | public readonly uint value; 11 | 12 | public EnumEntry(string name, uint value) 13 | { 14 | this.name = name; 15 | this.value = value; 16 | } 17 | } 18 | 19 | public class EnumDeclaration : Declaration 20 | { 21 | public Dictionary entryNames; 22 | 23 | public EnumDeclaration(string name, IEnumerable entries) : base(null, name) 24 | { 25 | entryNames = new Dictionary(); 26 | 27 | foreach (var entry in entries) 28 | { 29 | if (entryNames.ContainsKey(entry.name)) 30 | { 31 | throw new CompilerException($"Duplicated entry {entry.value} in enum {name}"); 32 | } 33 | 34 | entryNames[entry.name] = entry.value; 35 | } 36 | } 37 | 38 | public override bool IsNodeUsed(Node node) 39 | { 40 | return (node == this); 41 | } 42 | 43 | public override void Visit(Action callback) 44 | { 45 | callback(this); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Library/src/AST/Expressions/VarExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | 4 | using System; 5 | 6 | namespace Phantasma.Tomb.AST.Expressions 7 | { 8 | public class VarExpression : Expression 9 | { 10 | public VarDeclaration decl; 11 | 12 | public VarExpression(Scope parentScope, VarDeclaration declaration) : base(parentScope) 13 | { 14 | this.decl = declaration; 15 | } 16 | 17 | public override string ToString() 18 | { 19 | return decl.Name; 20 | } 21 | 22 | public override Register GenerateCode(CodeGenerator output) 23 | { 24 | if (decl.Register == null) 25 | { 26 | throw new CompilerException(this, $"var not initialized:" + decl.Name); 27 | } 28 | 29 | var reg = Compiler.Instance.AllocRegister(output, this); 30 | output.AppendLine(this, $"COPY {decl.Register} {reg}"); 31 | return reg; 32 | } 33 | 34 | public override void Visit(Action callback) 35 | { 36 | callback(this); 37 | decl.Visit(callback); 38 | } 39 | 40 | public override bool IsNodeUsed(Node node) 41 | { 42 | return (node == this) || node == decl; 43 | } 44 | 45 | public override VarType ResultType => decl.Type; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Tests/Contracts/BooleanTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace Tests.Contracts; 10 | 11 | public class BooleanTests 12 | { 13 | [Test] 14 | public void Bools() 15 | { 16 | string[] sourceCode = new string[] 17 | { 18 | "token TEST {", 19 | "global _contractPaused:bool;", 20 | "property name: string = \"Ghost\"; ", 21 | " constructor(owner:address) {", 22 | " _contractPaused= false;", 23 | "}}" 24 | }; 25 | 26 | var parser = new TombLangCompiler(); 27 | var contract = parser.Process(sourceCode).First(); 28 | 29 | var storage = new Dictionary(new ByteArrayComparer()); 30 | 31 | TestVM vm; 32 | 33 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 34 | Assert.IsNotNull(constructor); 35 | 36 | var keys = PhantasmaKeys.Generate(); 37 | 38 | vm = new TestVM(contract, storage, constructor); 39 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 40 | var result = vm.Execute(); 41 | Assert.IsTrue(result == ExecutionState.Halt); 42 | 43 | Assert.IsTrue(storage.Count == 1); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/EventDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.Lexers; 5 | 6 | namespace Tests.AST.Declaration; 7 | 8 | public class EventDeclarationTests 9 | { 10 | private Scope _scope; 11 | private VarType _returnType; 12 | private byte[] _descriptionScript; 13 | 14 | [SetUp] 15 | public void Setup() 16 | { 17 | TombLangLexer lexer = new TombLangLexer(); 18 | _scope = null; 19 | _returnType = VarType.Find(VarKind.String); 20 | _descriptionScript = EventDeclaration.GenerateScriptFromString(_returnType, "\"{address}: {data}\""); 21 | } 22 | 23 | /*[Test] 24 | public void TestEventDeclarationConstructor() 25 | { 26 | var eventDeclaration = new EventDeclaration(_scope, "TestEvent", 1, _returnType, _descriptionScript); 27 | 28 | Assert.AreEqual(_scope, eventDeclaration.scope); 29 | Assert.AreEqual(1, eventDeclaration.value); 30 | Assert.AreEqual(_returnType, eventDeclaration.returnType); 31 | Assert.AreEqual(_descriptionScript, eventDeclaration.descriptionScript); 32 | } 33 | 34 | [Test] 35 | public void TestValidate() 36 | { 37 | var eventDeclaration = new EventDeclaration(_scope, "TestEvent", 1, _returnType, _descriptionScript); 38 | Assert.DoesNotThrow(() => eventDeclaration.Validate()); 39 | }*/ 40 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/EventDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.Lexers; 5 | 6 | namespace TOMBLib.Tests.AST.Declaration; 7 | 8 | public class EventDeclarationTests 9 | { 10 | private Scope _scope; 11 | private VarType _returnType; 12 | private byte[] _descriptionScript; 13 | 14 | [SetUp] 15 | public void Setup() 16 | { 17 | TombLangLexer lexer = new TombLangLexer(); 18 | _scope = null; 19 | _returnType = VarType.Find(VarKind.String); 20 | _descriptionScript = EventDeclaration.GenerateScriptFromString(_returnType, "\"{address}: {data}\""); 21 | } 22 | 23 | /*[Test] 24 | public void TestEventDeclarationConstructor() 25 | { 26 | var eventDeclaration = new EventDeclaration(_scope, "TestEvent", 1, _returnType, _descriptionScript); 27 | 28 | Assert.AreEqual(_scope, eventDeclaration.scope); 29 | Assert.AreEqual(1, eventDeclaration.value); 30 | Assert.AreEqual(_returnType, eventDeclaration.returnType); 31 | Assert.AreEqual(_descriptionScript, eventDeclaration.descriptionScript); 32 | } 33 | 34 | [Test] 35 | public void TestValidate() 36 | { 37 | var eventDeclaration = new EventDeclaration(_scope, "TestEvent", 1, _returnType, _descriptionScript); 38 | Assert.DoesNotThrow(() => eventDeclaration.Validate()); 39 | }*/ 40 | } -------------------------------------------------------------------------------- /Tests/Contracts/ConstantsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Utils; 6 | using Phantasma.Tomb.Compilers; 7 | 8 | namespace Tests.Contracts; 9 | 10 | public class ConstantsTests 11 | { 12 | [Test] 13 | public void Constants() 14 | { 15 | var VAL_A = 30; 16 | var VAL_B = 4; 17 | 18 | string[] sourceCode = new string[] 19 | { 20 | $"const VAL_A : number = {VAL_A};", 21 | "contract test{", 22 | $"const VAL_B : number = {VAL_B};", 23 | "public getValue() : number {", 24 | "return VAL_A + VAL_B;}", 25 | "}" 26 | }; 27 | 28 | var expectedVal = VAL_A + VAL_B; 29 | 30 | var parser = new TombLangCompiler(); 31 | var contract = parser.Process(sourceCode).First(); 32 | 33 | var storage = new Dictionary(new ByteArrayComparer()); 34 | 35 | TestVM vm; 36 | 37 | var getValue = contract.abi.FindMethod("getValue"); 38 | Assert.IsNotNull(getValue); 39 | 40 | vm = new TestVM(contract, storage, getValue); 41 | var result = vm.Execute(); 42 | Assert.IsTrue(result == ExecutionState.Halt); 43 | 44 | Assert.IsTrue(vm.Stack.Count == 1); 45 | 46 | var obj = vm.Stack.Pop(); 47 | var newVal = obj.AsNumber(); 48 | 49 | Assert.IsTrue(newVal == expectedVal); 50 | } 51 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/CaseStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Expressions; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Statements 6 | { 7 | public class CaseStatement: Statement 8 | { 9 | public LiteralExpression value; 10 | public StatementBlock body; 11 | 12 | internal Register variable; 13 | internal string endLabel; 14 | 15 | public CaseStatement(LiteralExpression value, StatementBlock body) : base() 16 | { 17 | this.value = value; 18 | this.body = body; 19 | } 20 | 21 | public override void GenerateCode(CodeGenerator output) 22 | { 23 | var reg = value.GenerateCode(output); 24 | 25 | output.AppendLine(this, $"EQUAL {variable} {reg} {reg}"); 26 | 27 | output.AppendLine(this, $"JMPNOT {reg} @skip_{this.NodeID}"); 28 | body.GenerateCode(output); 29 | output.AppendLine(this, $"JMP {endLabel}"); 30 | output.AppendLine(this, $"@skip_{this.NodeID}: NOP"); 31 | 32 | Compiler.Instance.DeallocRegister(ref reg); 33 | } 34 | 35 | public override bool IsNodeUsed(Node node) 36 | { 37 | return value.IsNodeUsed(node) || body.IsNodeUsed(node); 38 | } 39 | 40 | public override void Visit(Action callback) 41 | { 42 | value.Visit(callback); 43 | body.Visit(callback); 44 | } 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /Library/tests/Contracts/ConstantsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Domain.Execution.Enums; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace TOMBLib.Tests.Contracts; 10 | 11 | public class ConstantsTests 12 | { 13 | [Test] 14 | public void Constants() 15 | { 16 | var VAL_A = 30; 17 | var VAL_B = 4; 18 | 19 | string[] sourceCode = new string[] 20 | { 21 | $"const VAL_A : number = {VAL_A};", 22 | "contract test{", 23 | $"const VAL_B : number = {VAL_B};", 24 | "public getValue() : number {", 25 | "return VAL_A + VAL_B;}", 26 | "}" 27 | }; 28 | 29 | var expectedVal = VAL_A + VAL_B; 30 | 31 | var parser = new TombLangCompiler(); 32 | var contract = parser.Process(sourceCode).First(); 33 | 34 | var storage = new Dictionary(new ByteArrayComparer()); 35 | 36 | TestVM vm; 37 | 38 | var getValue = contract.abi.FindMethod("getValue"); 39 | Assert.IsNotNull(getValue); 40 | 41 | vm = new TestVM(contract, storage, getValue); 42 | var result = vm.Execute(); 43 | Assert.IsTrue(result == ExecutionState.Halt); 44 | 45 | Assert.IsTrue(vm.Stack.Count == 1); 46 | 47 | var obj = vm.Stack.Pop(); 48 | var newVal = obj.AsNumber(); 49 | 50 | Assert.IsTrue(newVal == expectedVal); 51 | } 52 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/BooleanTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Contract; 7 | using Phantasma.Core.Domain.Execution.Enums; 8 | using Phantasma.Core.Domain.VM; 9 | using Phantasma.Core.Utils; 10 | using Phantasma.Tomb.Compilers; 11 | 12 | namespace TOMBLib.Tests.Contracts; 13 | 14 | public class BooleanTests 15 | { 16 | [Test] 17 | public void Bools() 18 | { 19 | string[] sourceCode = new string[] 20 | { 21 | "token TEST {", 22 | "global _contractPaused:bool;", 23 | "property name: string = \"Ghost\"; ", 24 | " constructor(owner:address) {", 25 | " _contractPaused= false;", 26 | "}}" 27 | }; 28 | 29 | var parser = new TombLangCompiler(); 30 | var contract = parser.Process(sourceCode).First(); 31 | 32 | var storage = new Dictionary(new ByteArrayComparer()); 33 | 34 | TestVM vm; 35 | 36 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 37 | Assert.IsNotNull(constructor); 38 | 39 | var keys = PhantasmaKeys.Generate(); 40 | 41 | vm = new TestVM(contract, storage, constructor); 42 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 43 | var result = vm.Execute(); 44 | Assert.IsTrue(result == ExecutionState.Halt); 45 | 46 | Assert.IsTrue(storage.Count == 1); 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /Library/src/AST/Statement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Phantasma.Tomb.AST 6 | { 7 | public abstract class Statement: Node 8 | { 9 | public abstract void GenerateCode(CodeGenerator output); 10 | 11 | } 12 | 13 | public class StatementBlock : Node 14 | { 15 | public readonly List Commands = new List(); 16 | 17 | public Scope ParentScope { get; } 18 | 19 | public StatementBlock(Scope scope) : base() 20 | { 21 | this.ParentScope = scope; 22 | } 23 | 24 | public void GenerateCode(CodeGenerator output) 25 | { 26 | foreach (var cmd in Commands) 27 | { 28 | cmd.GenerateCode(output); 29 | } 30 | } 31 | 32 | public override void Visit(Action callback) 33 | { 34 | callback(this); 35 | foreach (var cmd in Commands) 36 | { 37 | cmd.Visit(callback); 38 | } 39 | } 40 | 41 | public override bool IsNodeUsed(Node node) 42 | { 43 | if (node == this) 44 | { 45 | return true; 46 | } 47 | 48 | foreach (var cmd in Commands) 49 | { 50 | if (cmd.IsNodeUsed(node)) 51 | { 52 | return true; 53 | } 54 | } 55 | 56 | return false; 57 | } 58 | } 59 | 60 | public abstract class LoopStatement: Statement 61 | { 62 | } 63 | 64 | } 65 | 66 | -------------------------------------------------------------------------------- /Library/src/AST/Expressions/ConstExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | 4 | using System; 5 | 6 | namespace Phantasma.Tomb.AST.Expressions 7 | { 8 | public class ConstExpression : Expression 9 | { 10 | public ConstDeclaration decl; 11 | 12 | public ConstExpression(Scope parentScope, ConstDeclaration declaration) : base(parentScope) 13 | { 14 | this.decl = declaration; 15 | } 16 | 17 | public override string ToString() 18 | { 19 | return decl.Name; 20 | //return decl.ToString(); 21 | } 22 | 23 | public override T AsLiteral() 24 | { 25 | if (decl.Type.Kind == VarKind.String && typeof(T) == typeof(string)) 26 | { 27 | return (T)(object)decl.Value; 28 | } 29 | 30 | return base.AsLiteral(); 31 | } 32 | 33 | public override Register GenerateCode(CodeGenerator output) 34 | { 35 | var reg = Compiler.Instance.AllocRegister(output, this, decl.Name); 36 | output.AppendLine(this, $"LOAD {reg} {decl.Value}"); 37 | this.CallNecessaryConstructors(output, decl.Type, reg); 38 | return reg; 39 | } 40 | 41 | public override void Visit(Action callback) 42 | { 43 | callback(this); 44 | decl.Visit(callback); 45 | } 46 | 47 | public override bool IsNodeUsed(Node node) 48 | { 49 | return (node == this) || node == decl; 50 | } 51 | 52 | public override VarType ResultType => decl.Type; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Library/src/AST/Expressions/NegationExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Expressions 6 | { 7 | public class NegationExpression : Expression 8 | { 9 | public Expression expr; 10 | public override VarType ResultType => expr.ResultType; 11 | 12 | public NegationExpression(Scope parentScope, Expression expr) : base(parentScope) 13 | { 14 | this.expr = expr; 15 | } 16 | 17 | public override Register GenerateCode(CodeGenerator output) 18 | { 19 | var type = expr.ResultType; 20 | 21 | var reg = expr.GenerateCode(output); 22 | 23 | switch (type.Kind) 24 | { 25 | case VarKind.Bool: 26 | output.AppendLine(this, $"NOT {reg} {reg}"); 27 | break; 28 | 29 | case VarKind.Number: 30 | output.AppendLine(this, $"NEGATE {reg} {reg}"); 31 | break; 32 | 33 | default: 34 | throw new CompilerException("Cannot negate expression of type: " + type); 35 | } 36 | 37 | return reg; 38 | } 39 | 40 | 41 | public override void Visit(Action callback) 42 | { 43 | callback(this); 44 | expr.Visit(callback); 45 | } 46 | 47 | public override bool IsNodeUsed(Node node) 48 | { 49 | return (node == this) || expr.IsNodeUsed(node); 50 | } 51 | 52 | public override string ToString() 53 | { 54 | return "!" + expr.ToString(); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-publish.yml: -------------------------------------------------------------------------------- 1 | name: .NET Nuget Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ "master" ] 7 | pull_request: 8 | branches: [ "master" ] 9 | 10 | permissions: 11 | pull-requests: write 12 | actions: write 13 | issues: write 14 | contents: write 15 | 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | 19 | jobs: 20 | build: 21 | name: Build and Publish Nuget 22 | runs-on: ubuntu-latest 23 | permissions: write-all 24 | 25 | steps: 26 | - uses: actions/checkout@v3 27 | with: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Setup .NET 31 | uses: actions/setup-dotnet@v2 32 | with: 33 | dotnet-version: 6.0.x 34 | 35 | - name: Install dependencies 36 | run: | 37 | dotnet restore 38 | 39 | - name: Build 40 | run: dotnet build --configuration Release --no-restore 41 | 42 | - name: Pack Nuget 43 | run: dotnet pack --configuration Release --no-restore 44 | 45 | - name: Publish Nuget 46 | run: | 47 | dotnet nuget push ./Library/src/bin/Release/TOMBLib.*.nupkg --skip-duplicate -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json 48 | 49 | - name: Publish Nuget Github 50 | run: | 51 | dotnet nuget add ./Library/src/bin/Release/TOMBLib.*.nupkg --skip-duplicate --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/phantasma-io/index.json" 52 | dotnet nuget push ./Library/src/bin/Release/TOMBLib.*.nupkg --skip-duplicate --api-key ${{secrets.PR_KEY}} --source "github" -------------------------------------------------------------------------------- /Library/src/AST/Expression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Expressions; 2 | using Phantasma.Tomb.CodeGen; 3 | 4 | namespace Phantasma.Tomb.AST 5 | { 6 | public abstract class Expression : Node 7 | { 8 | public abstract VarType ResultType { get; } 9 | public Scope ParentScope { get; } 10 | 11 | public Expression(Scope parentScope) : base() 12 | { 13 | this.ParentScope = parentScope; 14 | } 15 | 16 | public virtual T AsLiteral() 17 | { 18 | throw new CompilerException(this, $"{this.GetType()} can't be converted to {typeof(T).Name} literal"); 19 | } 20 | 21 | public abstract Register GenerateCode(CodeGenerator output); 22 | 23 | public static Expression AutoCast(Expression expr, VarType expectedType) 24 | { 25 | if (expr.ResultType == expectedType || expectedType.Kind == VarKind.Any) 26 | { 27 | return expr; 28 | } 29 | 30 | switch (expr.ResultType.Kind) 31 | { 32 | case VarKind.Decimal: 33 | switch (expectedType.Kind) 34 | { 35 | case VarKind.Decimal: 36 | case VarKind.Number: 37 | return new CastExpression(expr.ParentScope, expectedType, expr); 38 | } 39 | break; 40 | 41 | case VarKind.Any: 42 | return new CastExpression(expr.ParentScope, expectedType, expr); 43 | } 44 | 45 | throw new CompilerException($"expected {expectedType} expression, got {expr.ResultType} instead"); 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Library/src/AST/Statements/DoWhileStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class DoWhileStatement : LoopStatement 7 | { 8 | public Expression condition; 9 | public StatementBlock body; 10 | public Scope Scope { get; } 11 | 12 | //private int label; 13 | 14 | public DoWhileStatement(Scope parentScope) : base() 15 | { 16 | this.Scope = new Scope(parentScope, this.NodeID); 17 | //this.label = Parser.Instance.AllocateLabel(); 18 | } 19 | 20 | public override void Visit(Action callback) 21 | { 22 | callback(this); 23 | 24 | condition.Visit(callback); 25 | body.Visit(callback); 26 | } 27 | 28 | public override bool IsNodeUsed(Node node) 29 | { 30 | return (node == this) || condition.IsNodeUsed(node) || body.IsNodeUsed(node); 31 | } 32 | 33 | public override void GenerateCode(CodeGenerator output) 34 | { 35 | Compiler.Instance.PushLoop(this); 36 | 37 | output.AppendLine(this, $"@loop_start_{this.NodeID}: NOP"); 38 | 39 | this.Scope.Enter(output); 40 | 41 | body.GenerateCode(output); 42 | 43 | var reg = condition.GenerateCode(output); 44 | output.AppendLine(this, $"JMPIF {reg} @loop_start_{this.NodeID}"); 45 | 46 | output.AppendLine(this, $"@loop_end_{this.NodeID}: NOP"); 47 | 48 | this.Scope.Leave(output); 49 | 50 | Compiler.Instance.DeallocRegister(ref reg); 51 | Compiler.Instance.PopLoop(this); 52 | } 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /Tests/Contracts/SwitchTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Utils; 6 | using Phantasma.Tomb.Compilers; 7 | 8 | namespace Tests.Contracts; 9 | 10 | public class SwitchTest 11 | { 12 | [Test] 13 | public void Switch() 14 | { 15 | var sourceCode = 16 | @" 17 | contract test { 18 | public check(x:number): string { 19 | switch (x) { 20 | case 0: return ""zero""; 21 | case 1: return ""one""; 22 | case 2: return ""two""; 23 | default: return ""other""; 24 | } 25 | }}"; 26 | 27 | var parser = new TombLangCompiler(); 28 | var contract = parser.Process(sourceCode).First(); 29 | 30 | var storage = new Dictionary(new ByteArrayComparer()); 31 | 32 | var check = contract.abi.FindMethod("check"); 33 | Assert.IsNotNull(check); 34 | 35 | // test different cases 36 | for (int i = -1; i <= 4; i++) 37 | { 38 | var vm = new TestVM(contract, storage, check); 39 | vm.Stack.Push(VMObject.FromObject(i)); 40 | var state = vm.Execute(); 41 | Assert.IsTrue(state == ExecutionState.Halt); 42 | var result = vm.Stack.Pop().AsString(); 43 | 44 | string expected; 45 | switch (i) 46 | { 47 | case 0: expected = "zero"; break; 48 | case 1: expected = "one"; break; 49 | case 2: expected = "two"; break; 50 | default: expected = "other"; break; 51 | } 52 | 53 | Assert.IsTrue(result == expected); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/WhileStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class WhileStatement : LoopStatement 7 | { 8 | public Expression condition; 9 | public StatementBlock body; 10 | public Scope Scope { get; } 11 | 12 | //private int label; 13 | 14 | public WhileStatement(Scope parentScope) : base() 15 | { 16 | this.Scope = new Scope(parentScope, this.NodeID); 17 | //this.label = Parser.Instance.AllocateLabel(); 18 | } 19 | 20 | public override void Visit(Action callback) 21 | { 22 | callback(this); 23 | 24 | condition.Visit(callback); 25 | body.Visit(callback); 26 | } 27 | 28 | public override bool IsNodeUsed(Node node) 29 | { 30 | return (node == this) || condition.IsNodeUsed(node) || body.IsNodeUsed(node); 31 | } 32 | 33 | public override void GenerateCode(CodeGenerator output) 34 | { 35 | Compiler.Instance.PushLoop(this); 36 | 37 | output.AppendLine(this, $"@loop_start_{this.NodeID}: NOP"); 38 | 39 | var reg = condition.GenerateCode(output); 40 | 41 | this.Scope.Enter(output); 42 | 43 | output.AppendLine(this, $"JMPNOT {reg} @loop_end_{this.NodeID}"); 44 | body.GenerateCode(output); 45 | 46 | output.AppendLine(this, $"JMP @loop_start_{this.NodeID}"); 47 | output.AppendLine(this, $"@loop_end_{this.NodeID}: NOP"); 48 | 49 | this.Scope.Leave(output); 50 | 51 | Compiler.Instance.DeallocRegister(ref reg); 52 | Compiler.Instance.PopLoop(this); 53 | } 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Library/tests/Contracts/SwitchTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Domain.Execution.Enums; 6 | using Phantasma.Core.Domain.VM; 7 | using Phantasma.Core.Utils; 8 | using Phantasma.Tomb.Compilers; 9 | 10 | namespace TOMBLib.Tests.Contracts; 11 | 12 | public class SwitchTest 13 | { 14 | [Test] 15 | public void Switch() 16 | { 17 | var sourceCode = 18 | @" 19 | contract test { 20 | public check(x:number): string { 21 | switch (x) { 22 | case 0: return ""zero""; 23 | case 1: return ""one""; 24 | case 2: return ""two""; 25 | default: return ""other""; 26 | } 27 | }}"; 28 | 29 | var parser = new TombLangCompiler(); 30 | var contract = parser.Process(sourceCode).First(); 31 | 32 | var storage = new Dictionary(new ByteArrayComparer()); 33 | 34 | var check = contract.abi.FindMethod("check"); 35 | Assert.IsNotNull(check); 36 | 37 | // test different cases 38 | for (int i = -1; i <= 4; i++) 39 | { 40 | var vm = new TestVM(contract, storage, check); 41 | vm.Stack.Push(VMObject.FromObject(i)); 42 | var state = vm.Execute(); 43 | Assert.IsTrue(state == ExecutionState.Halt); 44 | var result = vm.Stack.Pop().AsString(); 45 | 46 | string expected; 47 | switch (i) 48 | { 49 | case 0: expected = "zero"; break; 50 | case 1: expected = "one"; break; 51 | case 2: expected = "two"; break; 52 | default: expected = "other"; break; 53 | } 54 | 55 | Assert.IsTrue(result == expected); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Library/src/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using Phantasma.Tomb.AST; 3 | 4 | namespace Phantasma.Tomb 5 | { 6 | public static class Extensions 7 | { 8 | public static string UppercaseFirst(this string s) 9 | { 10 | if (string.IsNullOrEmpty(s)) 11 | { 12 | return string.Empty; 13 | } 14 | char[] a = s.ToCharArray(); 15 | a[0] = char.ToUpper(a[0]); 16 | return new string(a); 17 | } 18 | public static bool IsLogicalOperator(this OperatorKind op) 19 | { 20 | return op != OperatorKind.Unknown && op < OperatorKind.Addition; 21 | } 22 | 23 | public static void CallNecessaryConstructors(this Node node, CodeGenerator output, VarType type, Register reg) 24 | { 25 | CallNecessaryConstructors(node, output, type.Kind, reg); 26 | } 27 | 28 | public static void CallNecessaryConstructors(this Node node, CodeGenerator output, VarKind kind, Register reg) 29 | { 30 | switch (kind) 31 | { 32 | case VarKind.Hash: 33 | case VarKind.Address: 34 | case VarKind.Timestamp: 35 | { 36 | var constructorName = kind.ToString(); 37 | output.AppendLine(node, $"PUSH {reg}"); 38 | output.AppendLine(node, $"EXTCALL \"{constructorName}()\""); 39 | output.AppendLine(node, $"POP {reg}"); 40 | break; 41 | } 42 | 43 | case VarKind.Struct: 44 | { 45 | output.AppendLine(node, $"UNPACK {reg} {reg}"); 46 | break; 47 | } 48 | } 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Library/src/AST/Statements/EmitStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Statements 6 | { 7 | public class EmitStatement : Statement 8 | { 9 | public EventDeclaration eventDecl; 10 | 11 | public Expression valueExpr; 12 | public Expression addressExpr; 13 | 14 | public EmitStatement(EventDeclaration evt, Expression addrExpr, Expression valueExpr) : base() 15 | { 16 | this.addressExpr = addrExpr; 17 | this.valueExpr = valueExpr; 18 | this.eventDecl = evt; 19 | } 20 | 21 | public override void Visit(Action callback) 22 | { 23 | callback(this); 24 | eventDecl.Visit(callback); 25 | addressExpr.Visit(callback); 26 | valueExpr.Visit(callback); 27 | } 28 | 29 | public override bool IsNodeUsed(Node node) 30 | { 31 | return (node == this) || eventDecl.IsNodeUsed(node) || addressExpr.IsNodeUsed(node) || valueExpr.IsNodeUsed(node); 32 | } 33 | 34 | public override void GenerateCode(CodeGenerator output) 35 | { 36 | var reg = valueExpr.GenerateCode(output); 37 | output.AppendLine(this, $"PUSH {reg}"); 38 | Compiler.Instance.DeallocRegister(ref reg); 39 | 40 | reg = addressExpr.GenerateCode(output); 41 | output.AppendLine(this, $"PUSH {reg}"); 42 | 43 | output.AppendLine(this, $"LOAD {reg} {eventDecl.value}"); 44 | output.AppendLine(this, $"PUSH {reg}"); 45 | 46 | output.AppendLine(this, $"LOAD {reg} \"Runtime.Notify\""); 47 | output.AppendLine(this, $"EXTCALL {reg}"); 48 | 49 | Compiler.Instance.DeallocRegister(ref reg); 50 | } 51 | } 52 | 53 | } 54 | 55 | -------------------------------------------------------------------------------- /Tests/Contracts/ReturnTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Utils; 6 | using Phantasma.Tomb; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace Tests.Contracts; 10 | 11 | public class ReturnTests 12 | { 13 | [Test] 14 | public void MultiResultsSimple() 15 | { 16 | var sourceCode = 17 | @" 18 | contract test{ 19 | public getStrings(): string* { 20 | return ""hello""; 21 | return ""world""; 22 | } 23 | }"; 24 | 25 | var parser = new TombLangCompiler(); 26 | var contract = parser.Process(sourceCode).First(); 27 | 28 | var storage = new Dictionary(new ByteArrayComparer()); 29 | 30 | TestVM vm; 31 | 32 | var getStrings = contract.abi.FindMethod("getStrings"); 33 | Assert.IsNotNull(getStrings); 34 | 35 | vm = new TestVM(contract, storage, getStrings); 36 | var result = vm.Execute(); 37 | Assert.IsTrue(result == ExecutionState.Halt); 38 | 39 | Assert.IsTrue(vm.Stack.Count == 2); 40 | 41 | var obj = vm.Stack.Pop(); 42 | var x = obj.AsString(); 43 | Assert.IsTrue(x == "world"); 44 | 45 | obj = vm.Stack.Pop(); 46 | x = obj.AsString(); 47 | Assert.IsTrue(x == "hello"); 48 | } 49 | 50 | [Test] 51 | public void MultiResultsEarlyReturn() 52 | { 53 | var sourceCode = 54 | @" 55 | contract test{ 56 | public getStrings(): string* { 57 | return ""ok""; 58 | return; 59 | return ""bug""; // this line should not compile 60 | } 61 | }"; 62 | 63 | var parser = new TombLangCompiler(); 64 | 65 | Assert.Catch(() => 66 | { 67 | var contract = parser.Process(sourceCode).First(); 68 | }); 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /Library/src/AST/Expressions/ArrayExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Phantasma.Tomb.AST.Expressions 6 | { 7 | public class ArrayExpression : Expression 8 | { 9 | public List elements; 10 | 11 | public ArrayExpression(Scope parentScope) : base(parentScope) 12 | { 13 | this.elements = new List(); 14 | } 15 | 16 | public override string ToString() 17 | { 18 | return $"array[{ResultType}: {elements.Count}]"; 19 | } 20 | 21 | public override Register GenerateCode(CodeGenerator output) 22 | { 23 | var reg = Compiler.Instance.AllocRegister(output, this, this.NodeID); 24 | 25 | output.AppendLine(this, $"CLEAR {reg}"); 26 | 27 | var idxReg = Compiler.Instance.AllocRegister(output, this, "_array_init_idx"); 28 | 29 | for (int i=0; i callback) 42 | { 43 | callback(this); 44 | } 45 | 46 | public override bool IsNodeUsed(Node node) 47 | { 48 | return (node == this); 49 | } 50 | 51 | public override VarType ResultType 52 | { 53 | get 54 | { 55 | var elementType = elements.Count > 0 ? elements[0].ResultType : VarType.Find(VarKind.Unknown); 56 | return VarType.Find(VarKind.Array, elementType); 57 | } 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Library/tests/Contracts/ReturnTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Domain.Execution.Enums; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb; 8 | using Phantasma.Tomb.Compilers; 9 | 10 | namespace TOMBLib.Tests.Contracts; 11 | 12 | public class ReturnTests 13 | { 14 | [Test] 15 | public void MultiResultsSimple() 16 | { 17 | var sourceCode = 18 | @" 19 | contract test{ 20 | public getStrings(): string* { 21 | return ""hello""; 22 | return ""world""; 23 | } 24 | }"; 25 | 26 | var parser = new TombLangCompiler(); 27 | var contract = parser.Process(sourceCode).First(); 28 | 29 | var storage = new Dictionary(new ByteArrayComparer()); 30 | 31 | TestVM vm; 32 | 33 | var getStrings = contract.abi.FindMethod("getStrings"); 34 | Assert.IsNotNull(getStrings); 35 | 36 | vm = new TestVM(contract, storage, getStrings); 37 | var result = vm.Execute(); 38 | Assert.IsTrue(result == ExecutionState.Halt); 39 | 40 | Assert.IsTrue(vm.Stack.Count == 2); 41 | 42 | var obj = vm.Stack.Pop(); 43 | var x = obj.AsString(); 44 | Assert.IsTrue(x == "world"); 45 | 46 | obj = vm.Stack.Pop(); 47 | x = obj.AsString(); 48 | Assert.IsTrue(x == "hello"); 49 | } 50 | 51 | [Test] 52 | public void MultiResultsEarlyReturn() 53 | { 54 | var sourceCode = 55 | @" 56 | contract test{ 57 | public getStrings(): string* { 58 | return ""ok""; 59 | return; 60 | return ""bug""; // this line should not compile 61 | } 62 | }"; 63 | 64 | var parser = new TombLangCompiler(); 65 | 66 | Assert.Catch(() => 67 | { 68 | var contract = parser.Process(sourceCode).First(); 69 | }); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/AssignStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Statements 6 | { 7 | public class AssignStatement : Statement 8 | { 9 | public VarDeclaration variable; 10 | public Expression valueExpression; 11 | public Expression keyExpression; // can be null, if not null it should be an expression that resolves into a key (struct field name or array index) 12 | 13 | public AssignStatement() : base() 14 | { 15 | 16 | } 17 | 18 | public override void Visit(Action callback) 19 | { 20 | callback(this); 21 | variable.Visit(callback); 22 | valueExpression.Visit(callback); 23 | keyExpression?.Visit(callback); 24 | } 25 | 26 | public override bool IsNodeUsed(Node node) 27 | { 28 | return (node == this) || variable.IsNodeUsed(node) || valueExpression.IsNodeUsed(node) || (keyExpression != null && keyExpression.IsNodeUsed(node)); 29 | } 30 | 31 | public override void GenerateCode(CodeGenerator output) 32 | { 33 | if (variable.Register == null) 34 | { 35 | variable.Register = Compiler.Instance.AllocRegister(output, variable, variable.Name); 36 | } 37 | 38 | var srcReg = valueExpression.GenerateCode(output); 39 | 40 | if (keyExpression != null) 41 | { 42 | var idxReg = keyExpression.GenerateCode(output); 43 | 44 | output.AppendLine(this, $"PUT {srcReg} {variable.Register} {idxReg}"); 45 | 46 | Compiler.Instance.DeallocRegister(ref idxReg); 47 | } 48 | else 49 | { 50 | output.AppendLine(this, $"COPY {srcReg} {variable.Register}"); 51 | } 52 | 53 | Compiler.Instance.DeallocRegister(ref srcReg); 54 | } 55 | } 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Tests/Contracts/PropertiesTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace Tests.Contracts; 10 | 11 | public class PropertiesTests 12 | { 13 | [Test] 14 | public void Properties() 15 | { 16 | string[] sourceCode = new string[] 17 | { 18 | "token TEST {", 19 | "property name:string = \"Unit test\";", 20 | " global _feesSymbol:string;", 21 | $" property feesSymbol:string = _feesSymbol;", 22 | " constructor(owner:address) {", 23 | " _feesSymbol = \"KCAL\";", 24 | "}}" 25 | }; 26 | 27 | var parser = new TombLangCompiler(); 28 | var contract = parser.Process(sourceCode).First(); 29 | 30 | var storage = new Dictionary(new ByteArrayComparer()); 31 | 32 | TestVM vm; 33 | 34 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 35 | Assert.IsNotNull(constructor); 36 | 37 | var keys = PhantasmaKeys.Generate(); 38 | 39 | vm = new TestVM(contract, storage, constructor); 40 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 41 | var result = vm.Execute(); 42 | Assert.IsTrue(result == ExecutionState.Halt); 43 | 44 | Assert.IsTrue(storage.Count == 1); 45 | 46 | // call getFeesSymbol 47 | var getValue = contract.abi.FindMethod("getFeesSymbol"); 48 | Assert.IsNotNull(getValue); 49 | 50 | vm = new TestVM(contract, storage, getValue); 51 | result = vm.Execute(); 52 | Assert.IsTrue(result == ExecutionState.Halt); 53 | 54 | Assert.IsTrue(vm.Stack.Count == 1); 55 | 56 | var obj = vm.Stack.Pop(); 57 | var newVal = obj.AsString(); 58 | var expectedVal = "KCAL"; 59 | 60 | Assert.IsTrue(newVal == expectedVal); 61 | } 62 | } -------------------------------------------------------------------------------- /.github/workflows/dotnet-core.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master, dev] 8 | 9 | permissions: 10 | pull-requests: write 11 | issues: write 12 | contents: write 13 | actions: write 14 | id-token: write 15 | packages: write 16 | deployments: write 17 | discussions: write 18 | 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | jobs: 23 | build: 24 | name: Build TOMB 25 | runs-on: ubuntu-latest 26 | permissions: write-all 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | - name: Setup .NET 31 | uses: actions/setup-dotnet@v2 32 | with: 33 | dotnet-version: 6.0.x 34 | 35 | - name: Install dependencies 36 | run: | 37 | sudo apt-get install libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev libzstd-dev 38 | sudo apt-get install libc6-dev libicu-dev libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev libzstd-dev librocksdb-dev 39 | sudo apt-get install librocksdb-dev 40 | dotnet tool install --global dotnet-reportgenerator-globaltool 41 | dotnet restore 42 | 43 | - name: Build 44 | run: dotnet build --configuration Release --no-restore 45 | 46 | - name: Test 47 | run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage 48 | 49 | - name: Combine Coverage Output 50 | run: reportgenerator "-reports:coverage/**/coverage.cobertura.xml" -targetdir:"coverage" -reporttypes:Cobertura -assemblyfilters:"-TOMBLib.Tests" 51 | 52 | - name: Code Coverage Summary Report 53 | uses: irongut/CodeCoverageSummary@v1.3.0 54 | with: 55 | filename: coverage/Cobertura.xml 56 | badge: true 57 | fail_below_min: false 58 | format: markdown 59 | hide_branch_rate: false 60 | hide_complexity: false 61 | indicators: true 62 | output: both 63 | thresholds: '60 80' -------------------------------------------------------------------------------- /Tests/Contracts/AddressTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace Tests.Contracts; 10 | 11 | public class AddressTests 12 | { 13 | [Test] 14 | public void QueryMethodAddress() 15 | { 16 | string[] sourceCode = new string[] 17 | { 18 | "token TEST {", 19 | "property name:string = \"Unit test\";", 20 | " global _feesAddress:address;", 21 | $" property feesAddress:address = _feesAddress;", 22 | " constructor(owner:address) {", 23 | " _feesAddress = @P2KEYzWsbrMbPNtW1tBzzDKeYxYi4hjzpx4EfiyRyaoLkMM;", 24 | "}}" 25 | }; 26 | 27 | var parser = new TombLangCompiler(); 28 | var contract = parser.Process(sourceCode).First(); 29 | 30 | var storage = new Dictionary(new ByteArrayComparer()); 31 | 32 | TestVM vm; 33 | 34 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 35 | Assert.IsNotNull(constructor); 36 | 37 | var keys = PhantasmaKeys.Generate(); 38 | 39 | vm = new TestVM(contract, storage, constructor); 40 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 41 | var result = vm.Execute(); 42 | Assert.IsTrue(result == ExecutionState.Halt); 43 | 44 | Assert.IsTrue(storage.Count == 1); 45 | 46 | // call getFeesAddress 47 | var getValue = contract.abi.FindMethod("getFeesAddress"); 48 | Assert.IsNotNull(getValue); 49 | 50 | vm = new TestVM(contract, storage, getValue); 51 | result = vm.Execute(); 52 | Assert.IsTrue(result == ExecutionState.Halt); 53 | 54 | Assert.IsTrue(vm.Stack.Count == 1); 55 | 56 | var obj = vm.Stack.Pop(); 57 | var newVal = obj.AsString(); 58 | var expectedVal = "P2KEYzWsbrMbPNtW1tBzzDKeYxYi4hjzpx4EfiyRyaoLkMM"; 59 | 60 | Assert.IsTrue(newVal == expectedVal); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/PropertiesTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Contract; 7 | using Phantasma.Core.Domain.Execution.Enums; 8 | using Phantasma.Core.Domain.VM; 9 | using Phantasma.Core.Utils; 10 | using Phantasma.Tomb.Compilers; 11 | 12 | namespace TOMBLib.Tests.Contracts; 13 | 14 | public class PropertiesTests 15 | { 16 | [Test] 17 | public void Properties() 18 | { 19 | string[] sourceCode = new string[] 20 | { 21 | "token TEST {", 22 | "property name:string = \"Unit test\";", 23 | " global _feesSymbol:string;", 24 | $" property feesSymbol:string = _feesSymbol;", 25 | " constructor(owner:address) {", 26 | " _feesSymbol = \"KCAL\";", 27 | "}}" 28 | }; 29 | 30 | var parser = new TombLangCompiler(); 31 | var contract = parser.Process(sourceCode).First(); 32 | 33 | var storage = new Dictionary(new ByteArrayComparer()); 34 | 35 | TestVM vm; 36 | 37 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 38 | Assert.IsNotNull(constructor); 39 | 40 | var keys = PhantasmaKeys.Generate(); 41 | 42 | vm = new TestVM(contract, storage, constructor); 43 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 44 | var result = vm.Execute(); 45 | Assert.IsTrue(result == ExecutionState.Halt); 46 | 47 | Assert.IsTrue(storage.Count == 1); 48 | 49 | // call getFeesSymbol 50 | var getValue = contract.abi.FindMethod("getFeesSymbol"); 51 | Assert.IsNotNull(getValue); 52 | 53 | vm = new TestVM(contract, storage, getValue); 54 | result = vm.Execute(); 55 | Assert.IsTrue(result == ExecutionState.Halt); 56 | 57 | Assert.IsTrue(vm.Stack.Count == 1); 58 | 59 | var obj = vm.Stack.Pop(); 60 | var newVal = obj.AsString(); 61 | var expectedVal = "KCAL"; 62 | 63 | Assert.IsTrue(newVal == expectedVal); 64 | } 65 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/IfStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | using System; 3 | 4 | namespace Phantasma.Tomb.AST.Statements 5 | { 6 | public class IfStatement : Statement 7 | { 8 | public Expression condition; 9 | public StatementBlock body; 10 | public StatementBlock @else; 11 | public Scope Scope { get; } 12 | 13 | //private int label; 14 | 15 | public IfStatement(Scope parentScope) : base() 16 | { 17 | this.Scope = new Scope(parentScope, this.NodeID); 18 | //this.label = Parser.Instance.AllocateLabel(); 19 | } 20 | 21 | public override void Visit(Action callback) 22 | { 23 | callback(this); 24 | 25 | condition.Visit(callback); 26 | body.Visit(callback); 27 | @else?.Visit(callback); 28 | } 29 | 30 | public override bool IsNodeUsed(Node node) 31 | { 32 | if (@else != null && @else.IsNodeUsed(node)) 33 | { 34 | return true; 35 | } 36 | 37 | return (node == this) || condition.IsNodeUsed(node) || body.IsNodeUsed(node); 38 | } 39 | 40 | public override void GenerateCode(CodeGenerator output) 41 | { 42 | var reg = condition.GenerateCode(output); 43 | 44 | this.Scope.Enter(output); 45 | if (@else != null) 46 | { 47 | output.AppendLine(this, $"JMPNOT {reg} @else_{this.NodeID}"); 48 | body.GenerateCode(output); 49 | output.AppendLine(this, $"JMP @then_{this.NodeID}"); 50 | output.AppendLine(this, $"@else_{this.NodeID}: NOP"); 51 | @else.GenerateCode(output); 52 | } 53 | else 54 | { 55 | output.AppendLine(this, $"JMPNOT {reg} @then_{this.NodeID}"); 56 | body.GenerateCode(output); 57 | } 58 | output.AppendLine(this, $"@then_{this.NodeID}: NOP"); 59 | this.Scope.Leave(output); 60 | 61 | Compiler.Instance.DeallocRegister(ref reg); 62 | 63 | } 64 | } 65 | 66 | } 67 | 68 | -------------------------------------------------------------------------------- /Library/src/AST/Expressions/StructFieldExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | 4 | using System; 5 | 6 | namespace Phantasma.Tomb.AST.Expressions 7 | { 8 | public class StructFieldExpression : Expression 9 | { 10 | public VarDeclaration varDecl; 11 | public string fieldName; 12 | 13 | public override VarType ResultType { get; } 14 | 15 | public StructFieldExpression(Scope parentScope, VarDeclaration varDecl, string fieldName) : base(parentScope) 16 | { 17 | var structInfo = ((StructVarType)varDecl.Type); 18 | 19 | VarType fieldType = null; 20 | 21 | foreach (var field in structInfo.decl.fields) 22 | { 23 | if (field.name == fieldName) 24 | { 25 | fieldType = field.type; 26 | } 27 | } 28 | 29 | if (fieldType == null) 30 | { 31 | throw new CompilerException($"Struct {varDecl.Type} does not contain field: {fieldName}"); 32 | } 33 | 34 | this.varDecl = varDecl; 35 | this.fieldName = fieldName; 36 | this.ResultType = fieldType; 37 | } 38 | 39 | public override void Visit(Action callback) 40 | { 41 | callback(this); 42 | varDecl.Visit(callback); 43 | } 44 | 45 | public override bool IsNodeUsed(Node node) 46 | { 47 | return (node == this || node == varDecl); 48 | } 49 | 50 | public override Register GenerateCode(CodeGenerator output) 51 | { 52 | var reg = Compiler.Instance.AllocRegister(output, this/*, $"{varDecl.Name}.{fieldName}"*/); 53 | 54 | var tempReg = Compiler.Instance.AllocRegister(output, this); 55 | 56 | output.AppendLine(this, $"COPY {varDecl.Register} {reg}"); 57 | output.AppendLine(this, $"LOAD {tempReg} \"{fieldName}\""); 58 | output.AppendLine(this, $"GET {reg} {reg} {tempReg}"); 59 | 60 | Compiler.Instance.DeallocRegister(ref tempReg); 61 | 62 | return reg; 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /TombCompiler.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32811.315 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TombCompiler", "Compiler\TombCompiler.csproj", "{A669C9C4-AA5E-4BBD-9A8A-569B6B50A5D4}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TOMBLib", "Library\src\TOMBLib.csproj", "{86AA09AA-ED89-4097-97A3-E3E3E0EF5A86}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TOMBLib.Tests", "Library\tests\TOMBLib.Tests.csproj", "{EA79025B-F50F-4608-B2BC-77D776AE99EB}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {A669C9C4-AA5E-4BBD-9A8A-569B6B50A5D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {A669C9C4-AA5E-4BBD-9A8A-569B6B50A5D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {A669C9C4-AA5E-4BBD-9A8A-569B6B50A5D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {A669C9C4-AA5E-4BBD-9A8A-569B6B50A5D4}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {86AA09AA-ED89-4097-97A3-E3E3E0EF5A86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {86AA09AA-ED89-4097-97A3-E3E3E0EF5A86}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {86AA09AA-ED89-4097-97A3-E3E3E0EF5A86}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {86AA09AA-ED89-4097-97A3-E3E3E0EF5A86}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {EA79025B-F50F-4608-B2BC-77D776AE99EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {EA79025B-F50F-4608-B2BC-77D776AE99EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {EA79025B-F50F-4608-B2BC-77D776AE99EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {EA79025B-F50F-4608-B2BC-77D776AE99EB}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {BD1476E1-CAAE-4BFB-8E2F-FBA3F82EE3A8} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /Tests/Contracts/EnumsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace Tests.Contracts; 10 | 11 | public class EnumsTests 12 | { 13 | public enum MyEnum 14 | { 15 | A, 16 | B, 17 | C, 18 | } 19 | 20 | [Test] 21 | public void Enums() 22 | { 23 | string[] sourceCode = new string[] 24 | { 25 | "enum MyEnum { A, B, C}", 26 | "contract test{", 27 | $"global state: MyEnum;", 28 | "constructor(owner:address) {", 29 | "state = MyEnum.B;}", 30 | "public getValue():MyEnum {", 31 | "return state;}", 32 | "public isSet(val:MyEnum):bool {", 33 | "return state.isSet(val);}", 34 | "}" 35 | }; 36 | 37 | var parser = new TombLangCompiler(); 38 | var contract = parser.Process(sourceCode).First(); 39 | 40 | var storage = new Dictionary(new ByteArrayComparer()); 41 | 42 | TestVM vm; 43 | 44 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 45 | Assert.IsNotNull(constructor); 46 | 47 | var keys = PhantasmaKeys.Generate(); 48 | 49 | vm = new TestVM(contract, storage, constructor); 50 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 51 | var result = vm.Execute(); 52 | Assert.IsTrue(result == ExecutionState.Halt); 53 | 54 | Assert.IsTrue(storage.Count == 1); 55 | 56 | // call getVal 57 | var getValue = contract.abi.FindMethod("getValue"); 58 | Assert.IsNotNull(getValue); 59 | 60 | vm = new TestVM(contract, storage, getValue); 61 | result = vm.Execute(); 62 | Assert.IsTrue(result == ExecutionState.Halt); 63 | 64 | Assert.IsTrue(storage.Count == 1); 65 | 66 | Assert.IsTrue(vm.Stack.Count == 1); 67 | 68 | var obj = vm.Stack.Pop(); 69 | var newVal = obj.AsEnum(); 70 | var expectedVal = MyEnum.B; 71 | 72 | Assert.IsTrue(newVal == expectedVal); 73 | } 74 | } -------------------------------------------------------------------------------- /Library/src/AST/Expressions/ArrayElementExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | 4 | using System; 5 | 6 | namespace Phantasma.Tomb.AST.Expressions 7 | { 8 | public class ArrayElementExpression : Expression 9 | { 10 | public VarDeclaration decl; 11 | public Expression indexExpression; 12 | 13 | public ArrayElementExpression(Scope parentScope, VarDeclaration declaration, Expression indexExpression) : base(parentScope) 14 | { 15 | this.decl = declaration; 16 | this.indexExpression = indexExpression; 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return $"{decl}[{indexExpression}]"; 22 | } 23 | 24 | public override Register GenerateCode(CodeGenerator output) 25 | { 26 | if (decl.Register == null) 27 | { 28 | throw new CompilerException(this, $"var not initialized:" + decl.Name); 29 | } 30 | 31 | var dstReg = Compiler.Instance.AllocRegister(output, this); 32 | var idxReg = indexExpression.GenerateCode(output); 33 | 34 | var reg = decl.Register; 35 | output.AppendLine(this, $"GET {reg} {dstReg} {idxReg}"); 36 | 37 | var arrayType = decl.Type as ArrayVarType; 38 | if (arrayType == null) 39 | { 40 | throw new CompilerException(this, $"expected array type:" + decl.Name); 41 | } 42 | this.CallNecessaryConstructors(output, arrayType.elementType, decl.Register); 43 | 44 | Compiler.Instance.DeallocRegister(ref idxReg); 45 | 46 | return dstReg; 47 | } 48 | 49 | public override void Visit(Action callback) 50 | { 51 | callback(this); 52 | decl.Visit(callback); 53 | indexExpression.Visit(callback); 54 | } 55 | 56 | public override bool IsNodeUsed(Node node) 57 | { 58 | return (node == this) || node == decl; 59 | } 60 | 61 | public override VarType ResultType => decl.Type is ArrayVarType ? ((ArrayVarType)decl.Type).elementType : VarType.Find(VarKind.Unknown); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Library/tests/Contracts/AddressTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Contract; 7 | using Phantasma.Core.Domain.Execution.Enums; 8 | using Phantasma.Core.Domain.VM; 9 | using Phantasma.Core.Utils; 10 | using Phantasma.Tomb.Compilers; 11 | 12 | namespace TOMBLib.Tests.Contracts; 13 | 14 | public class AddressTests 15 | { 16 | [Test] 17 | public void QueryMethodAddress() 18 | { 19 | string[] sourceCode = new string[] 20 | { 21 | "token TEST {", 22 | "property name:string = \"Unit test\";", 23 | " global _feesAddress:address;", 24 | $" property feesAddress:address = _feesAddress;", 25 | " constructor(owner:address) {", 26 | " _feesAddress = @P2KEYzWsbrMbPNtW1tBzzDKeYxYi4hjzpx4EfiyRyaoLkMM;", 27 | "}}" 28 | }; 29 | 30 | var parser = new TombLangCompiler(); 31 | var contract = parser.Process(sourceCode).First(); 32 | 33 | var storage = new Dictionary(new ByteArrayComparer()); 34 | 35 | TestVM vm; 36 | 37 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 38 | Assert.IsNotNull(constructor); 39 | 40 | var keys = PhantasmaKeys.Generate(); 41 | 42 | vm = new TestVM(contract, storage, constructor); 43 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 44 | var result = vm.Execute(); 45 | Assert.IsTrue(result == ExecutionState.Halt); 46 | 47 | Assert.IsTrue(storage.Count == 1); 48 | 49 | // call getFeesAddress 50 | var getValue = contract.abi.FindMethod("getFeesAddress"); 51 | Assert.IsNotNull(getValue); 52 | 53 | vm = new TestVM(contract, storage, getValue); 54 | result = vm.Execute(); 55 | Assert.IsTrue(result == ExecutionState.Halt); 56 | 57 | Assert.IsTrue(vm.Stack.Count == 1); 58 | 59 | var obj = vm.Stack.Pop(); 60 | var newVal = obj.AsString(); 61 | var expectedVal = "P2KEYzWsbrMbPNtW1tBzzDKeYxYi4hjzpx4EfiyRyaoLkMM"; 62 | 63 | Assert.IsTrue(newVal == expectedVal); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/StructDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST; 2 | using Phantasma.Tomb.AST.Declarations; 3 | using Phantasma.Tomb.Lexers; 4 | 5 | namespace TOMBLib.Tests.AST.Declaration; 6 | 7 | public class StructDeclarationTests 8 | { 9 | [SetUp] 10 | public void Setup() 11 | { 12 | TombLangLexer lexer = new TombLangLexer(); 13 | } 14 | 15 | [Test] 16 | public void Constructor_WithValidArguments_CreatesFieldsArray() 17 | { 18 | // Arrange 19 | var name = "MyStruct"; 20 | var fields = new List() 21 | { 22 | new StructField("myInt", VarType.Find(VarKind.Number)), 23 | new StructField("myFloat", VarType.Find(VarKind.Decimal,2)), 24 | new StructField("myString", VarType.Find(VarKind.String)), 25 | }; 26 | 27 | // Act 28 | var structDeclaration = new StructDeclaration(name, fields); 29 | 30 | // Assert 31 | Assert.That(structDeclaration.Name, Is.EqualTo(name)); 32 | Assert.That(structDeclaration.fields, Is.Not.Null); 33 | Assert.That(structDeclaration.fields.Length, Is.EqualTo(fields.Count)); 34 | for (int i = 0; i < fields.Count; i++) 35 | { 36 | Assert.That(structDeclaration.fields[i].name, Is.EqualTo(fields[i].name)); 37 | Assert.That(structDeclaration.fields[i].type, Is.EqualTo(fields[i].type)); 38 | } 39 | } 40 | 41 | [Test] 42 | public void IsNodeUsed_WithSameNode_ReturnsTrue() 43 | { 44 | // Arrange 45 | var structDeclaration = new StructDeclaration("MyStruct", new List()); 46 | 47 | // Act 48 | var isUsed = structDeclaration.IsNodeUsed(structDeclaration); 49 | 50 | // Assert 51 | Assert.That(isUsed, Is.True); 52 | } 53 | 54 | [Test] 55 | public void Visit_WithCallback_CallsCallbackWithThisNode() 56 | { 57 | // Arrange 58 | var structDeclaration = new StructDeclaration("MyStruct", new List()); 59 | bool isCallbackCalled = false; 60 | 61 | // Act 62 | structDeclaration.Visit(node => isCallbackCalled = (node == structDeclaration)); 63 | 64 | // Assert 65 | Assert.That(isCallbackCalled, Is.True); 66 | } 67 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/ForStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Statements 6 | { 7 | public class ForStatement : LoopStatement 8 | { 9 | public VarDeclaration loopVar; 10 | public Expression condition; 11 | public Statement initStatement; 12 | public Statement loopStatement; 13 | 14 | public StatementBlock body; 15 | public Scope Scope { get; } 16 | 17 | //private int label; 18 | 19 | public ForStatement(Scope parentScope) : base() 20 | { 21 | this.Scope = new Scope(parentScope, this.NodeID); 22 | //this.label = Parser.Instance.AllocateLabel(); 23 | } 24 | 25 | public override void Visit(Action callback) 26 | { 27 | callback(this); 28 | 29 | loopVar.Visit(callback); 30 | initStatement.Visit(callback); 31 | condition.Visit(callback); 32 | loopStatement.Visit(callback); 33 | body.Visit(callback); 34 | } 35 | 36 | public override bool IsNodeUsed(Node node) 37 | { 38 | return (node == this) || condition.IsNodeUsed(node) || body.IsNodeUsed(node) || loopVar.IsNodeUsed(node) || loopStatement.IsNodeUsed(node) || initStatement.IsNodeUsed(node); 39 | } 40 | 41 | public override void GenerateCode(CodeGenerator output) 42 | { 43 | initStatement.GenerateCode(output); 44 | 45 | Compiler.Instance.PushLoop(this); 46 | 47 | output.AppendLine(this, $"@loop_start_{this.NodeID}: NOP"); 48 | 49 | var reg = condition.GenerateCode(output); 50 | 51 | this.Scope.Enter(output); 52 | 53 | output.AppendLine(this, $"JMPNOT {reg} @loop_end_{this.NodeID}"); 54 | body.GenerateCode(output); 55 | loopStatement.GenerateCode(output); 56 | 57 | output.AppendLine(this, $"JMP @loop_start_{this.NodeID}"); 58 | output.AppendLine(this, $"@loop_end_{this.NodeID}: NOP"); 59 | 60 | this.Scope.Leave(output); 61 | 62 | Compiler.Instance.DeallocRegister(ref reg); 63 | Compiler.Instance.PopLoop(this); 64 | } 65 | } 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /Tests/AST/Declaration/StructDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | using Phantasma.Tomb.AST; 4 | using Phantasma.Tomb.AST.Declarations; 5 | using Phantasma.Tomb.Lexers; 6 | 7 | namespace Tests.AST.Declaration; 8 | 9 | public class StructDeclarationTests 10 | { 11 | [SetUp] 12 | public void Setup() 13 | { 14 | TombLangLexer lexer = new TombLangLexer(); 15 | } 16 | 17 | [Test] 18 | public void Constructor_WithValidArguments_CreatesFieldsArray() 19 | { 20 | // Arrange 21 | var name = "MyStruct"; 22 | var fields = new List() 23 | { 24 | new StructField("myInt", VarType.Find(VarKind.Number)), 25 | new StructField("myFloat", VarType.Find(VarKind.Decimal,2)), 26 | new StructField("myString", VarType.Find(VarKind.String)), 27 | }; 28 | 29 | // Act 30 | var structDeclaration = new StructDeclaration(name, fields); 31 | 32 | // Assert 33 | Assert.That(structDeclaration.Name, Is.EqualTo(name)); 34 | Assert.That(structDeclaration.fields, Is.Not.Null); 35 | Assert.That(structDeclaration.fields.Length, Is.EqualTo(fields.Count)); 36 | for (int i = 0; i < fields.Count; i++) 37 | { 38 | Assert.That(structDeclaration.fields[i].name, Is.EqualTo(fields[i].name)); 39 | Assert.That(structDeclaration.fields[i].type, Is.EqualTo(fields[i].type)); 40 | } 41 | } 42 | 43 | [Test] 44 | public void IsNodeUsed_WithSameNode_ReturnsTrue() 45 | { 46 | // Arrange 47 | var structDeclaration = new StructDeclaration("MyStruct", new List()); 48 | 49 | // Act 50 | var isUsed = structDeclaration.IsNodeUsed(structDeclaration); 51 | 52 | // Assert 53 | Assert.That(isUsed, Is.True); 54 | } 55 | 56 | [Test] 57 | public void Visit_WithCallback_CallsCallbackWithThisNode() 58 | { 59 | // Arrange 60 | var structDeclaration = new StructDeclaration("MyStruct", new List()); 61 | bool isCallbackCalled = false; 62 | 63 | // Act 64 | structDeclaration.Visit(node => isCallbackCalled = (node == structDeclaration)); 65 | 66 | // Assert 67 | Assert.That(isCallbackCalled, Is.True); 68 | } 69 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/EnumsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Contract; 7 | using Phantasma.Core.Domain.Execution.Enums; 8 | using Phantasma.Core.Domain.VM; 9 | using Phantasma.Core.Utils; 10 | using Phantasma.Tomb.Compilers; 11 | 12 | namespace TOMBLib.Tests.Contracts; 13 | 14 | public class EnumsTests 15 | { 16 | public enum MyEnum 17 | { 18 | A, 19 | B, 20 | C, 21 | } 22 | 23 | [Test] 24 | public void Enums() 25 | { 26 | string[] sourceCode = new string[] 27 | { 28 | "enum MyEnum { A, B, C}", 29 | "contract test{", 30 | $"global state: MyEnum;", 31 | "constructor(owner:address) {", 32 | "state = MyEnum.B;}", 33 | "public getValue():MyEnum {", 34 | "return state;}", 35 | "public isSet(val:MyEnum):bool {", 36 | "return state.isSet(val);}", 37 | "}" 38 | }; 39 | 40 | var parser = new TombLangCompiler(); 41 | var contract = parser.Process(sourceCode).First(); 42 | 43 | var storage = new Dictionary(new ByteArrayComparer()); 44 | 45 | TestVM vm; 46 | 47 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 48 | Assert.IsNotNull(constructor); 49 | 50 | var keys = PhantasmaKeys.Generate(); 51 | 52 | vm = new TestVM(contract, storage, constructor); 53 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 54 | var result = vm.Execute(); 55 | Assert.IsTrue(result == ExecutionState.Halt); 56 | 57 | Assert.IsTrue(storage.Count == 1); 58 | 59 | // call getVal 60 | var getValue = contract.abi.FindMethod("getValue"); 61 | Assert.IsNotNull(getValue); 62 | 63 | vm = new TestVM(contract, storage, getValue); 64 | result = vm.Execute(); 65 | Assert.IsTrue(result == ExecutionState.Halt); 66 | 67 | Assert.IsTrue(storage.Count == 1); 68 | 69 | Assert.IsTrue(vm.Stack.Count == 1); 70 | 71 | var obj = vm.Stack.Pop(); 72 | var newVal = obj.AsEnum(); 73 | var expectedVal = MyEnum.B; 74 | 75 | Assert.IsTrue(newVal == expectedVal); 76 | } 77 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/SwitchStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Expressions; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Phantasma.Tomb.AST.Statements 7 | { 8 | public class SwitchStatement : Statement 9 | { 10 | public VarExpression variable; 11 | public StatementBlock @default; 12 | public List cases = new List(); 13 | public Scope Scope { get; } 14 | 15 | //private int label; 16 | 17 | public SwitchStatement(Scope parentScope) : base() 18 | { 19 | this.Scope = new Scope(parentScope, this.NodeID); 20 | //this.label = Parser.Instance.AllocateLabel(); 21 | } 22 | 23 | public override void Visit(Action callback) 24 | { 25 | callback(this); 26 | 27 | variable.Visit(callback); 28 | foreach (var entry in cases) 29 | { 30 | entry.Visit(callback); 31 | } 32 | 33 | @default?.Visit(callback); 34 | } 35 | 36 | public override bool IsNodeUsed(Node node) 37 | { 38 | foreach (var entry in cases) 39 | { 40 | if (entry.IsNodeUsed(node)) 41 | { 42 | return true; 43 | } 44 | } 45 | 46 | if (@default != null && @default.IsNodeUsed(node)) 47 | { 48 | return true; 49 | } 50 | 51 | return (node == this); 52 | } 53 | 54 | public override void GenerateCode(CodeGenerator output) 55 | { 56 | var reg = variable.GenerateCode(output); 57 | var endLabel = $"@end_case_{this.NodeID}"; 58 | 59 | this.Scope.Enter(output); 60 | 61 | foreach (var entry in cases) 62 | { 63 | entry.variable = reg; 64 | entry.endLabel = endLabel; 65 | entry.GenerateCode(output); 66 | } 67 | 68 | if (@default != null) 69 | { 70 | @default.GenerateCode(output); 71 | } 72 | 73 | output.AppendLine(this, $"{endLabel}: NOP"); 74 | this.Scope.Leave(output); 75 | 76 | Compiler.Instance.DeallocRegister(ref reg); 77 | 78 | } 79 | } 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Tests/Contracts/VariableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb.Compilers; 8 | 9 | namespace Tests.Contracts; 10 | 11 | public class VariableTests 12 | { 13 | [Test] 14 | public void TypeOf() 15 | { 16 | var sourceCode = new string[] 17 | { 18 | "contract test{", 19 | "public returnType() : type {", 20 | "return $TYPE_OF(string);", 21 | "}}" 22 | }; 23 | 24 | var parser = new TombLangCompiler(); 25 | var contract = parser.Process(sourceCode).First(); 26 | 27 | var storage = new Dictionary(new ByteArrayComparer()); 28 | 29 | TestVM vm; 30 | 31 | var keys = PhantasmaKeys.Generate(); 32 | 33 | // call returnType 34 | var returnType = contract.abi.FindMethod("returnType"); 35 | Assert.IsNotNull(returnType); 36 | 37 | vm = new TestVM(contract, storage, returnType); 38 | var result = vm.Execute(); 39 | Assert.IsTrue(result == ExecutionState.Halt); 40 | 41 | Assert.IsTrue(storage.Count == 0); 42 | 43 | Assert.IsTrue(vm.Stack.Count == 1); 44 | 45 | var obj = vm.Stack.Pop(); 46 | var vmType = obj.AsEnum(); 47 | 48 | Assert.IsTrue(vmType == VMType.String); 49 | } 50 | 51 | [Test] 52 | public void TypeInferenceInVarDecls() 53 | { 54 | var sourceCode = 55 | @" 56 | contract test{ 57 | public calculate():string { 58 | local a = ""hello ""; 59 | local b = ""world""; 60 | return a + b; 61 | } 62 | }"; 63 | 64 | var parser = new TombLangCompiler(); 65 | var contract = parser.Process(sourceCode).First(); 66 | 67 | var storage = new Dictionary(new ByteArrayComparer()); 68 | 69 | TestVM vm; 70 | 71 | var method = contract.abi.FindMethod("calculate"); 72 | Assert.IsNotNull(method); 73 | 74 | vm = new TestVM(contract, storage, method); 75 | var result = vm.Execute(); 76 | Assert.IsTrue(result == ExecutionState.Halt); 77 | 78 | Assert.IsTrue(vm.Stack.Count == 1); 79 | 80 | var obj = vm.Stack.Pop(); 81 | var str = obj.AsString(); 82 | 83 | Assert.IsTrue(str == "hello world"); 84 | } 85 | } -------------------------------------------------------------------------------- /Library/src/AST/Statements/ReturnStatement.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST.Declarations; 2 | using Phantasma.Tomb.CodeGen; 3 | using System; 4 | using System.Linq.Expressions; 5 | 6 | namespace Phantasma.Tomb.AST.Statements 7 | { 8 | public class ReturnStatement : Statement 9 | { 10 | public Expression expression; 11 | 12 | public MethodDeclaration method; 13 | 14 | public ReturnStatement(MethodDeclaration method, Expression expression) : base() 15 | { 16 | this.expression = expression; 17 | this.method = method; 18 | } 19 | 20 | public override void Visit(Action callback) 21 | { 22 | callback(this); 23 | expression?.Visit(callback); 24 | } 25 | 26 | public override bool IsNodeUsed(Node node) 27 | { 28 | return (node == this) || (expression != null && expression.IsNodeUsed(node)); 29 | } 30 | 31 | public override void GenerateCode(CodeGenerator output) 32 | { 33 | var returnType = this.method.@interface.ReturnType; 34 | 35 | var simpleReturn = (this.method.ParentScope.Module is Script); 36 | var isMulti = this.method.@interface.IsMulti; 37 | 38 | if (expression != null) 39 | { 40 | if (returnType.Kind == VarKind.None) 41 | { 42 | throw new System.Exception($"unexpect return expression for void method: {method.Name}"); 43 | } 44 | 45 | this.expression = Expression.AutoCast(expression, returnType); 46 | 47 | var reg = this.expression.GenerateCode(output); 48 | output.AppendLine(this, $"PUSH {reg}"); 49 | Compiler.Instance.DeallocRegister(ref reg); 50 | } 51 | else 52 | if (returnType.Kind != VarKind.None && !isMulti) 53 | { 54 | throw new System.Exception($"expected return expression for non-void method: {method.Name}"); 55 | } 56 | 57 | if (isMulti && expression != null) 58 | { 59 | return; // for multi methods a return with expression is basically just a push, nothing more.. 60 | } 61 | 62 | if (simpleReturn) 63 | { 64 | output.AppendLine(this, "RET"); 65 | } 66 | else 67 | { 68 | output.AppendLine(this, "JMP @" + this.method.GetExitLabel()); 69 | } 70 | } 71 | } 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /Library/src/CodeGen/CodeGenerator.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | 5 | namespace Phantasma.Tomb.CodeGen 6 | { 7 | public class CodeGenerator 8 | { 9 | private StringBuilder _sb = new StringBuilder(); 10 | private StringBuilder _includedBuiltinCodeBuffer = new StringBuilder(); // used for builtins 11 | 12 | public static Scope currentScope = null; 13 | public static int currentSourceLine = 0; 14 | 15 | public int LineCount; 16 | 17 | private Dictionary _builtinMethodMap = new Dictionary(); 18 | 19 | public CodeGenerator() 20 | { 21 | } 22 | 23 | public static string Tabs(int n) 24 | { 25 | return new string('\t', n); 26 | } 27 | 28 | public void AppendLine(Node node, string line = "") 29 | { 30 | if (node.LineNumber <= 0) 31 | { 32 | throw new CompilerException("line number failed for " + node.GetType().Name); 33 | } 34 | 35 | while (currentSourceLine <= node.LineNumber) 36 | { 37 | if (currentSourceLine > 0) 38 | { 39 | var lineContent = Compiler.Instance.GetLine(currentSourceLine); 40 | _sb.AppendLine($"// Line {currentSourceLine}:" + lineContent); 41 | LineCount++; 42 | } 43 | currentSourceLine++; 44 | } 45 | 46 | line = Tabs(currentScope.Level) + line; 47 | _sb.AppendLine(line); 48 | LineCount++; 49 | } 50 | 51 | public void IncBuiltinReference(string builtinMethodName) 52 | { 53 | if (_builtinMethodMap.ContainsKey(builtinMethodName)) 54 | { 55 | return; 56 | } 57 | 58 | var builtin = Builtins.GetMethod(builtinMethodName); 59 | var code = builtin.Code; 60 | 61 | _builtinMethodMap[builtinMethodName] = code; 62 | 63 | if (_includedBuiltinCodeBuffer.Length == 0) 64 | { 65 | _includedBuiltinCodeBuffer.AppendLine(); 66 | _includedBuiltinCodeBuffer.AppendLine("// =======> BUILTINS SECTION"); 67 | } 68 | 69 | _includedBuiltinCodeBuffer.Append(code); 70 | } 71 | 72 | public override string ToString() 73 | { 74 | return _sb.ToString() + _includedBuiltinCodeBuffer.ToString(); 75 | } 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Library/tests/Contracts/VariableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Execution.Enums; 7 | using Phantasma.Core.Domain.VM.Enums; 8 | using Phantasma.Core.Utils; 9 | using Phantasma.Tomb.Compilers; 10 | 11 | namespace TOMBLib.Tests.Contracts; 12 | 13 | public class VariableTests 14 | { 15 | [Test] 16 | public void TypeOf() 17 | { 18 | var sourceCode = new string[] 19 | { 20 | "contract test{", 21 | "public returnType() : type {", 22 | "return $TYPE_OF(string);", 23 | "}}" 24 | }; 25 | 26 | var parser = new TombLangCompiler(); 27 | var contract = parser.Process(sourceCode).First(); 28 | 29 | var storage = new Dictionary(new ByteArrayComparer()); 30 | 31 | TestVM vm; 32 | 33 | var keys = PhantasmaKeys.Generate(); 34 | 35 | // call returnType 36 | var returnType = contract.abi.FindMethod("returnType"); 37 | Assert.IsNotNull(returnType); 38 | 39 | vm = new TestVM(contract, storage, returnType); 40 | var result = vm.Execute(); 41 | Assert.IsTrue(result == ExecutionState.Halt); 42 | 43 | Assert.IsTrue(storage.Count == 0); 44 | 45 | Assert.IsTrue(vm.Stack.Count == 1); 46 | 47 | var obj = vm.Stack.Pop(); 48 | var vmType = obj.AsEnum(); 49 | 50 | Assert.IsTrue(vmType == VMType.String); 51 | } 52 | 53 | [Test] 54 | public void TypeInferenceInVarDecls() 55 | { 56 | var sourceCode = 57 | @" 58 | contract test{ 59 | public calculate():string { 60 | local a = ""hello ""; 61 | local b = ""world""; 62 | return a + b; 63 | } 64 | }"; 65 | 66 | var parser = new TombLangCompiler(); 67 | var contract = parser.Process(sourceCode).First(); 68 | 69 | var storage = new Dictionary(new ByteArrayComparer()); 70 | 71 | TestVM vm; 72 | 73 | var method = contract.abi.FindMethod("calculate"); 74 | Assert.IsNotNull(method); 75 | 76 | vm = new TestVM(contract, storage, method); 77 | var result = vm.Execute(); 78 | Assert.IsTrue(result == ExecutionState.Halt); 79 | 80 | Assert.IsTrue(vm.Stack.Count == 1); 81 | 82 | var obj = vm.Stack.Pop(); 83 | var str = obj.AsString(); 84 | 85 | Assert.IsTrue(str == "hello world"); 86 | } 87 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/EnumDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using Phantasma.Tomb; 5 | using Phantasma.Tomb.AST.Declarations; 6 | using Phantasma.Tomb.Lexers; 7 | 8 | namespace Tests.AST.Declaration; 9 | 10 | public class EnumDeclarationTests 11 | { 12 | [SetUp] 13 | public void Setup() 14 | { 15 | TombLangLexer lexer = new TombLangLexer(); 16 | } 17 | 18 | [Test] 19 | public void Constructor_WithDuplicateEntryName_ThrowsCompilerException() 20 | { 21 | // Arrange 22 | var entries = new List() 23 | { 24 | new EnumEntry("Red", 1), 25 | new EnumEntry("Green", 2), 26 | new EnumEntry("Red", 3) 27 | }; 28 | 29 | // Act & Assert 30 | Assert.Throws(() => new EnumDeclaration("Colors", entries)); 31 | } 32 | 33 | [Test] 34 | public void Constructor_WithValidEntries_CreatesEntryNamesDictionary() 35 | { 36 | // Arrange 37 | var entries = new List() 38 | { 39 | new EnumEntry("Red", 1), 40 | new EnumEntry("Green", 2), 41 | new EnumEntry("Blue", 3) 42 | }; 43 | 44 | // Act 45 | var enumDeclaration = new EnumDeclaration("Colors", entries); 46 | 47 | 48 | // Assert 49 | Assert.That(enumDeclaration.entryNames, Is.Not.Null); 50 | Assert.That(enumDeclaration.entryNames.Count, Is.EqualTo(entries.Count)); 51 | foreach (var entry in entries) 52 | { 53 | Assert.That(enumDeclaration.entryNames.ContainsKey(entry.name), Is.True); 54 | Assert.That(enumDeclaration.entryNames[entry.name], Is.EqualTo(entry.value)); 55 | } 56 | } 57 | 58 | [Test] 59 | public void IsNodeUsed_WithSameNode_ReturnsTrue() 60 | { 61 | // Arrange 62 | var enumDeclaration = new EnumDeclaration("Colors", new List()); 63 | 64 | // Act 65 | var isUsed = enumDeclaration.IsNodeUsed(enumDeclaration); 66 | 67 | // Assert 68 | Assert.That(isUsed, Is.True); 69 | } 70 | 71 | [Test] 72 | public void Visit_WithCallback_CallsCallbackWithThisNode() 73 | { 74 | // Arrange 75 | var enumDeclaration = new EnumDeclaration("Colors", new List()); 76 | bool isCallbackCalled = false; 77 | 78 | // Act 79 | enumDeclaration.Visit(node => isCallbackCalled = (node == enumDeclaration)); 80 | 81 | // Assert 82 | Assert.That(isCallbackCalled, Is.True); 83 | } 84 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/EnumDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | using Phantasma.Tomb; 5 | using Phantasma.Tomb.AST.Declarations; 6 | using Phantasma.Tomb.Lexers; 7 | 8 | namespace TOMBLib.Tests.AST.Declaration; 9 | 10 | public class EnumDeclarationTests 11 | { 12 | [SetUp] 13 | public void Setup() 14 | { 15 | TombLangLexer lexer = new TombLangLexer(); 16 | } 17 | 18 | [Test] 19 | public void Constructor_WithDuplicateEntryName_ThrowsCompilerException() 20 | { 21 | // Arrange 22 | var entries = new List() 23 | { 24 | new EnumEntry("Red", 1), 25 | new EnumEntry("Green", 2), 26 | new EnumEntry("Red", 3) 27 | }; 28 | 29 | // Act & Assert 30 | Assert.Throws(() => new EnumDeclaration("Colors", entries)); 31 | } 32 | 33 | [Test] 34 | public void Constructor_WithValidEntries_CreatesEntryNamesDictionary() 35 | { 36 | // Arrange 37 | var entries = new List() 38 | { 39 | new EnumEntry("Red", 1), 40 | new EnumEntry("Green", 2), 41 | new EnumEntry("Blue", 3) 42 | }; 43 | 44 | // Act 45 | var enumDeclaration = new EnumDeclaration("Colors", entries); 46 | 47 | 48 | // Assert 49 | Assert.That(enumDeclaration.entryNames, Is.Not.Null); 50 | Assert.That(enumDeclaration.entryNames.Count, Is.EqualTo(entries.Count)); 51 | foreach (var entry in entries) 52 | { 53 | Assert.That(enumDeclaration.entryNames.ContainsKey(entry.name), Is.True); 54 | Assert.That(enumDeclaration.entryNames[entry.name], Is.EqualTo(entry.value)); 55 | } 56 | } 57 | 58 | [Test] 59 | public void IsNodeUsed_WithSameNode_ReturnsTrue() 60 | { 61 | // Arrange 62 | var enumDeclaration = new EnumDeclaration("Colors", new List()); 63 | 64 | // Act 65 | var isUsed = enumDeclaration.IsNodeUsed(enumDeclaration); 66 | 67 | // Assert 68 | Assert.That(isUsed, Is.True); 69 | } 70 | 71 | [Test] 72 | public void Visit_WithCallback_CallsCallbackWithThisNode() 73 | { 74 | // Arrange 75 | var enumDeclaration = new EnumDeclaration("Colors", new List()); 76 | bool isCallbackCalled = false; 77 | 78 | // Act 79 | enumDeclaration.Visit(node => isCallbackCalled = (node == enumDeclaration)); 80 | 81 | // Assert 82 | Assert.That(isCallbackCalled, Is.True); 83 | } 84 | } -------------------------------------------------------------------------------- /Samples/basic_token.sol: -------------------------------------------------------------------------------- 1 | /* 2 | Sample implementation of a pseudo-ERC20 token in Phantasma. 3 | Note that this contract is not compatible with normal Phantasma tokens yet (some details missing, TODO) 4 | */ 5 | 6 | pragma solidity ^0.4.19; 7 | 8 | import "Phantasma/Runtime.tomb"; 9 | 10 | contract ERC20Basic { 11 | 12 | string public constant name = "ERC20Basic"; 13 | string public constant symbol = "BSC"; 14 | uint8 public constant decimals = 18; 15 | 16 | // NOTE there is no need for a Phantsama token to emit those as they are automatically emmited 17 | //event Transfer(address indexed from, address indexed to, uint tokens); 18 | //event Approval(address indexed tokenOwner, address indexed spender, uint tokens); 19 | 20 | 21 | mapping(address => uint256) balances; 22 | 23 | //mapping(address => mapping (address => uint256)) allowed; 24 | 25 | uint256 totalSupply_; 26 | 27 | constructor(address owner) public { 28 | totalSupply_ = 10000000000; 29 | balances[owner] = totalSupply_; 30 | } 31 | 32 | function totalSupply() public view returns (uint256) { 33 | return totalSupply_; 34 | } 35 | 36 | function balanceOf(address tokenOwner) public view returns (uint) { 37 | return balances[tokenOwner]; 38 | } 39 | 40 | function transfer(address sender, address receiver, uint numTokens) public returns (bool) { 41 | require(Runtime.isWitness(sender)); 42 | require(numTokens <= balances[sender]); 43 | balances[sender] = balances[sender] - numTokens; 44 | balances[receiver] = balances[receiver] + numTokens; 45 | //emit Transfer(msg.sender, receiver, numTokens); 46 | return true; 47 | } 48 | 49 | /* NOTE the approve/allowance of ERC20 does not apply to Phantasma tokens 50 | 51 | function approve(address delegate, uint numTokens) public returns (bool) { 52 | allowed[msg.sender][delegate] = numTokens; 53 | //emit Approval(msg.sender, delegate, numTokens); 54 | return true; 55 | } 56 | 57 | function allowance(address owner, address delegate) public view returns (uint) { 58 | return allowed[owner][delegate]; 59 | } 60 | 61 | function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) { 62 | require(numTokens <= balances[owner]); 63 | require(numTokens <= allowed[owner][msg.sender]); 64 | 65 | balances[owner] = balances[owner].sub(numTokens); 66 | allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens); 67 | balances[buyer] = balances[buyer].add(numTokens); 68 | //emit Transfer(owner, buyer, numTokens); 69 | return true; 70 | }*/ 71 | } 72 | -------------------------------------------------------------------------------- /Library/src/CodeGen/NFT.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Phantasma.Business.Blockchain.Tokens; 3 | using Phantasma.Core.Domain; 4 | using Phantasma.Core.Domain.Contract; 5 | using Phantasma.Core.Domain.Contract.Structs; 6 | using Phantasma.Core.Domain.VM.Enums; 7 | using Phantasma.Tomb.AST; 8 | using Phantasma.Tomb.AST.Declarations; 9 | 10 | namespace Phantasma.Tomb.CodeGen 11 | { 12 | public class NFT : Contract 13 | { 14 | public VarType romType; 15 | public VarType ramType; 16 | 17 | public StructVarType nftType; 18 | 19 | public NFT(string name, VarType romType, VarType ramType, Module parent) : base(name, ModuleKind.NFT, parent) 20 | { 21 | this.romType = romType; 22 | this.ramType = ramType; 23 | 24 | this.nftType = (StructVarType)VarType.Find(VarKind.Struct, name); 25 | //this.nftType.decl = new StructDeclaration(name, new[] { new StructField("id", VarKind.Number), new StructField("owner", VarKind.Address), new StructField("chain", VarKind.String), new StructField("rom", romType), new StructField("ram", ramType) }); 26 | 27 | this.Scope.AddVariable(new VarDeclaration(this.Scope, "_tokenID", VarType.Find(VarKind.Number), VarStorage.NFT)); 28 | this.Scope.AddVariable(new VarDeclaration(this.Scope, "_seriesID", VarType.Find(VarKind.Number), VarStorage.NFT)); 29 | this.Scope.AddVariable(new VarDeclaration(this.Scope, "_mintID", VarType.Find(VarKind.Number), VarStorage.NFT)); 30 | this.Scope.AddVariable(new VarDeclaration(this.Scope, "_ROM", romType, VarStorage.NFT)); 31 | this.Scope.AddVariable(new VarDeclaration(this.Scope, "_RAM", ramType, VarStorage.NFT)); 32 | } 33 | 34 | public override ContractInterface GenerateCode(CodeGenerator output) 35 | { 36 | var abi = base.GenerateCode(output); 37 | 38 | var nftStandard = TokenUtils.GetNFTStandard(); 39 | 40 | // convert ABI parameters 41 | var methods = new List(); 42 | foreach (var method in abi.Methods) 43 | { 44 | if (nftStandard.HasMethod(method.name)) 45 | { 46 | var convertedMethod = new ContractMethod(method.name, method.returnType, method.offset, new[] { new ContractParameter("tokenID", VMType.Number) }); 47 | methods.Add(convertedMethod); 48 | } 49 | else 50 | { 51 | methods.Add(method); 52 | } 53 | } 54 | 55 | abi = new ContractInterface(methods, abi.Events); 56 | return abi; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/VarDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.AST; 2 | using Phantasma.Tomb.AST.Declarations; 3 | using Phantasma.Tomb.Lexers; 4 | 5 | namespace TOMBLib.Tests.AST.Declaration; 6 | 7 | public class VarDeclarationTests 8 | { 9 | [SetUp] 10 | public void Setup() 11 | { 12 | TombLangLexer lexer = new TombLangLexer(); 13 | } 14 | 15 | [Test] 16 | public void Constructor_WithValidArguments_SetsPropertiesCorrectly() 17 | { 18 | // Arrange 19 | Scope parentScope = null; 20 | var name = "myVar"; 21 | var type = VarType.Find(VarKind.Any); 22 | var storage = VarStorage.Global; 23 | 24 | // Act 25 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 26 | 27 | // Assert 28 | Assert.That(varDeclaration.ParentScope, Is.EqualTo(parentScope)); 29 | Assert.That(varDeclaration.Name, Is.EqualTo(name)); 30 | Assert.That(varDeclaration.Type, Is.EqualTo(type)); 31 | Assert.That(varDeclaration.Storage, Is.EqualTo(storage)); 32 | Assert.That(varDeclaration.Register, Is.Null); 33 | } 34 | 35 | [Test] 36 | public void ToString_WithValidVarDeclaration_ReturnsExpectedString() 37 | { 38 | // Arrange 39 | Scope parentScope = null; 40 | var name = "myVar"; 41 | var type = VarType.Find(VarKind.Bool); 42 | var storage = VarStorage.Local; 43 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 44 | 45 | // Act 46 | var result = varDeclaration.ToString(); 47 | 48 | // Assert 49 | Assert.That(result, Is.EqualTo("var myVar:Bool")); 50 | } 51 | 52 | [Test] 53 | public void Visit_WithCallback_CallsCallbackWithThisNode() 54 | { 55 | // Arrange 56 | Scope parentScope = null; 57 | var name = "myVar"; 58 | var type = VarType.Find(VarKind.Decimal, 2); 59 | var storage = VarStorage.Register; 60 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 61 | bool isCallbackCalled = false; 62 | 63 | // Act 64 | varDeclaration.Visit(node => isCallbackCalled = (node == varDeclaration)); 65 | 66 | // Assert 67 | Assert.That(isCallbackCalled, Is.True); 68 | } 69 | 70 | [Test] 71 | public void IsNodeUsed_WithSameNode_ReturnsTrue() 72 | { 73 | // Arrange 74 | Scope parentScope = null; 75 | var name = "myVar"; 76 | var type = VarType.Find(VarKind.String); 77 | var storage = VarStorage.Global; 78 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 79 | 80 | // Act 81 | var isUsed = varDeclaration.IsNodeUsed(varDeclaration); 82 | 83 | // Assert 84 | Assert.That(isUsed, Is.True); 85 | } 86 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/VarDeclarationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.Lexers; 5 | 6 | namespace Tests.AST.Declaration; 7 | 8 | public class VarDeclarationTests 9 | { 10 | [SetUp] 11 | public void Setup() 12 | { 13 | TombLangLexer lexer = new TombLangLexer(); 14 | } 15 | 16 | [Test] 17 | public void Constructor_WithValidArguments_SetsPropertiesCorrectly() 18 | { 19 | // Arrange 20 | Scope parentScope = null; 21 | var name = "myVar"; 22 | var type = VarType.Find(VarKind.Any); 23 | var storage = VarStorage.Global; 24 | 25 | // Act 26 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 27 | 28 | // Assert 29 | Assert.That(varDeclaration.ParentScope, Is.EqualTo(parentScope)); 30 | Assert.That(varDeclaration.Name, Is.EqualTo(name)); 31 | Assert.That(varDeclaration.Type, Is.EqualTo(type)); 32 | Assert.That(varDeclaration.Storage, Is.EqualTo(storage)); 33 | Assert.That(varDeclaration.Register, Is.Null); 34 | } 35 | 36 | [Test] 37 | public void ToString_WithValidVarDeclaration_ReturnsExpectedString() 38 | { 39 | // Arrange 40 | Scope parentScope = null; 41 | var name = "myVar"; 42 | var type = VarType.Find(VarKind.Bool); 43 | var storage = VarStorage.Local; 44 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 45 | 46 | // Act 47 | var result = varDeclaration.ToString(); 48 | 49 | // Assert 50 | Assert.That(result, Is.EqualTo("var myVar:Bool")); 51 | } 52 | 53 | [Test] 54 | public void Visit_WithCallback_CallsCallbackWithThisNode() 55 | { 56 | // Arrange 57 | Scope parentScope = null; 58 | var name = "myVar"; 59 | var type = VarType.Find(VarKind.Decimal, 2); 60 | var storage = VarStorage.Register; 61 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 62 | bool isCallbackCalled = false; 63 | 64 | // Act 65 | varDeclaration.Visit(node => isCallbackCalled = (node == varDeclaration)); 66 | 67 | // Assert 68 | Assert.That(isCallbackCalled, Is.True); 69 | } 70 | 71 | [Test] 72 | public void IsNodeUsed_WithSameNode_ReturnsTrue() 73 | { 74 | // Arrange 75 | Scope parentScope = null; 76 | var name = "myVar"; 77 | var type = VarType.Find(VarKind.String); 78 | var storage = VarStorage.Global; 79 | var varDeclaration = new VarDeclaration(parentScope, name, type, storage); 80 | 81 | // Act 82 | var isUsed = varDeclaration.IsNodeUsed(varDeclaration); 83 | 84 | // Assert 85 | Assert.That(isUsed, Is.True); 86 | } 87 | } -------------------------------------------------------------------------------- /Compiler/builtins.tomb: -------------------------------------------------------------------------------- 1 | contract builtins { 2 | import Array; 3 | import Runtime; 4 | import Storage; 5 | 6 | private tomb_math_sqrt(n:number) : number { 7 | local root:number = n / 2; 8 | while (n < root * root) { 9 | root += n / root; 10 | root /= 2; 11 | } 12 | 13 | return root; 14 | } 15 | 16 | private tomb_string_toUpper(s:string):string 17 | { 18 | local my_array: array; 19 | 20 | // extract chars from string into an array 21 | my_array = s.toArray(); 22 | 23 | local length :number = Array.length(my_array); 24 | local idx :number = 0; 25 | 26 | while (idx < length) { 27 | local ch : number = my_array[idx]; 28 | 29 | if (ch >= 97) { 30 | if (ch <= 122) { 31 | my_array[idx] = ch - 32; 32 | } 33 | } 34 | 35 | idx += 1; 36 | } 37 | 38 | // convert the array back into a unicode string 39 | local result:string = String.fromArray(my_array); 40 | return result; 41 | } 42 | 43 | private tomb_string_toLower(s:string):string 44 | { 45 | local my_array: array; 46 | 47 | // extract chars from string into an array 48 | my_array = s.toArray(); 49 | 50 | local length :number = Array.length(my_array); 51 | local idx :number = 0; 52 | 53 | while (idx < length) { 54 | local ch : number = my_array[idx]; 55 | 56 | if (ch >= 65) { 57 | if (ch <= 90) { 58 | my_array[idx] = ch + 32; 59 | } 60 | } 61 | 62 | idx += 1; 63 | } 64 | 65 | // convert the array back into a unicode string 66 | local result:string = String.fromArray(my_array); 67 | return result; 68 | } 69 | 70 | 71 | private tomb_string_indexOf(s:string, x:number):number 72 | { 73 | local my_array: array; 74 | 75 | // extract chars from string into an array 76 | my_array = s.toArray(); 77 | 78 | local length :number = Array.length(my_array); 79 | local idx :number = 0; 80 | 81 | while (idx < length) { 82 | local ch : number = my_array[idx]; 83 | 84 | if (ch == x) { 85 | // found, return index 86 | return idx; 87 | } 88 | 89 | idx += 1; 90 | } 91 | 92 | return -1; // not found 93 | } 94 | 95 | const RND_A:number = 16807; 96 | const RND_M:number = 2147483647; 97 | const RND_SEED_KEY:string = "tomb_rnd_seed"; 98 | 99 | private tomb_random_seed(seed:number) 100 | { 101 | Storage.write(RND_SEED_KEY, seed); 102 | } 103 | 104 | private tomb_random_generate(): number 105 | { 106 | local seed: number; 107 | local context:string = Runtime.context(); 108 | seed = Storage.read(context, RND_SEED_KEY); 109 | seed = (RND_A * seed) % RND_M; 110 | Storage.write(RND_SEED_KEY, seed); 111 | return seed; 112 | } 113 | } -------------------------------------------------------------------------------- /Tests/Contracts/IfTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Utils; 6 | using Phantasma.Tomb.Compilers; 7 | 8 | namespace Tests.Contracts; 9 | 10 | public class IfTests 11 | { 12 | [Test] 13 | public void IfChained() 14 | { 15 | var sourceCode = 16 | @" 17 | contract test { 18 | public sign(x:number): number { 19 | if (x > 0) 20 | { 21 | return 1; 22 | } 23 | else 24 | if (x < 0) 25 | { 26 | return -1; 27 | } 28 | else 29 | { 30 | return 0; 31 | } 32 | } 33 | }"; 34 | 35 | var parser = new TombLangCompiler(); 36 | var contract = parser.Process(sourceCode).First(); 37 | 38 | var storage = new Dictionary(new ByteArrayComparer()); 39 | 40 | var countStuff = contract.abi.FindMethod("sign"); 41 | Assert.IsNotNull(countStuff); 42 | 43 | var vm = new TestVM(contract, storage, countStuff); 44 | vm.Stack.Push(VMObject.FromObject(-3)); 45 | var result = vm.Execute(); 46 | Assert.IsTrue(result == ExecutionState.Halt); 47 | 48 | Assert.IsTrue(vm.Stack.Count == 1); 49 | var val = vm.Stack.Pop().AsNumber(); 50 | Assert.IsTrue(val == -1); 51 | } 52 | 53 | [Test] 54 | public void IfWithOr() 55 | { 56 | var sourceCode = 57 | @" 58 | contract test { 59 | public check(x:number, y:number): bool { 60 | return (x > 0) && (y < 0); 61 | } 62 | }"; 63 | 64 | var parser = new TombLangCompiler(); 65 | var contract = parser.Process(sourceCode).First(); 66 | 67 | var storage = new Dictionary(new ByteArrayComparer()); 68 | 69 | var countStuff = contract.abi.FindMethod("check"); 70 | Assert.IsNotNull(countStuff); 71 | 72 | var vm = new TestVM(contract, storage, countStuff); 73 | // NOTE - due to using a stack, we're pushing the second argument first (y), then the first argument (y) 74 | vm.Stack.Push(VMObject.FromObject(-3)); 75 | vm.Stack.Push(VMObject.FromObject(3)); 76 | var result = vm.Execute(); 77 | Assert.IsTrue(result == ExecutionState.Halt); 78 | 79 | Assert.IsTrue(vm.Stack.Count == 1); 80 | var val = vm.Stack.Pop().AsBool(); 81 | Assert.IsTrue(val); 82 | 83 | vm = new TestVM(contract, storage, countStuff); 84 | // NOTE - here we invert the order, in this case should fail due to the condition in the contract inside check() 85 | vm.Stack.Push(VMObject.FromObject(3)); 86 | vm.Stack.Push(VMObject.FromObject(-3)); 87 | result = vm.Execute(); 88 | Assert.IsTrue(result == ExecutionState.Halt); 89 | 90 | Assert.IsTrue(vm.Stack.Count == 1); 91 | val = vm.Stack.Pop().AsBool(); 92 | Assert.IsFalse(val); 93 | } 94 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/IfTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Domain.Execution.Enums; 6 | using Phantasma.Core.Domain.VM; 7 | using Phantasma.Core.Utils; 8 | using Phantasma.Tomb.Compilers; 9 | 10 | namespace TOMBLib.Tests.Contracts; 11 | 12 | public class IfTests 13 | { 14 | [Test] 15 | public void IfChained() 16 | { 17 | var sourceCode = 18 | @" 19 | contract test { 20 | public sign(x:number): number { 21 | if (x > 0) 22 | { 23 | return 1; 24 | } 25 | else 26 | if (x < 0) 27 | { 28 | return -1; 29 | } 30 | else 31 | { 32 | return 0; 33 | } 34 | } 35 | }"; 36 | 37 | var parser = new TombLangCompiler(); 38 | var contract = parser.Process(sourceCode).First(); 39 | 40 | var storage = new Dictionary(new ByteArrayComparer()); 41 | 42 | var countStuff = contract.abi.FindMethod("sign"); 43 | Assert.IsNotNull(countStuff); 44 | 45 | var vm = new TestVM(contract, storage, countStuff); 46 | vm.Stack.Push(VMObject.FromObject(-3)); 47 | var result = vm.Execute(); 48 | Assert.IsTrue(result == ExecutionState.Halt); 49 | 50 | Assert.IsTrue(vm.Stack.Count == 1); 51 | var val = vm.Stack.Pop().AsNumber(); 52 | Assert.IsTrue(val == -1); 53 | } 54 | 55 | [Test] 56 | public void IfWithOr() 57 | { 58 | var sourceCode = 59 | @" 60 | contract test { 61 | public check(x:number, y:number): bool { 62 | return (x > 0) && (y < 0); 63 | } 64 | }"; 65 | 66 | var parser = new TombLangCompiler(); 67 | var contract = parser.Process(sourceCode).First(); 68 | 69 | var storage = new Dictionary(new ByteArrayComparer()); 70 | 71 | var countStuff = contract.abi.FindMethod("check"); 72 | Assert.IsNotNull(countStuff); 73 | 74 | var vm = new TestVM(contract, storage, countStuff); 75 | // NOTE - due to using a stack, we're pushing the second argument first (y), then the first argument (y) 76 | vm.Stack.Push(VMObject.FromObject(-3)); 77 | vm.Stack.Push(VMObject.FromObject(3)); 78 | var result = vm.Execute(); 79 | Assert.IsTrue(result == ExecutionState.Halt); 80 | 81 | Assert.IsTrue(vm.Stack.Count == 1); 82 | var val = vm.Stack.Pop().AsBool(); 83 | Assert.IsTrue(val); 84 | 85 | vm = new TestVM(contract, storage, countStuff); 86 | // NOTE - here we invert the order, in this case should fail due to the condition in the contract inside check() 87 | vm.Stack.Push(VMObject.FromObject(3)); 88 | vm.Stack.Push(VMObject.FromObject(-3)); 89 | result = vm.Execute(); 90 | Assert.IsTrue(result == ExecutionState.Halt); 91 | 92 | Assert.IsTrue(vm.Stack.Count == 1); 93 | val = vm.Stack.Pop().AsBool(); 94 | Assert.IsFalse(val); 95 | } 96 | } -------------------------------------------------------------------------------- /Library/tests/CodeGen/CodeGeneratorTests.cs: -------------------------------------------------------------------------------- 1 | namespace TOMBLib.Tests.CodeGen; 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using NUnit.Framework; 6 | using Phantasma.Core.Cryptography.Structs; 7 | using Phantasma.Core.Domain; 8 | using Phantasma.Core.Domain.Execution.Enums; 9 | using Phantasma.Core.Domain.VM; 10 | using Phantasma.Core.Utils; 11 | using Phantasma.Tomb.Compilers; 12 | 13 | public class CodeGeneratorTests 14 | { 15 | [Test] 16 | public void AutoCasts() 17 | { 18 | var sourceCode = 19 | @" 20 | contract test { 21 | import Address; 22 | 23 | public castTest(x:address): string { 24 | return Address.toString(x); 25 | } 26 | }"; 27 | 28 | var parser = new TombLangCompiler(); 29 | var contract = parser.Process(sourceCode).First(); 30 | 31 | var storage = new Dictionary(new ByteArrayComparer()); 32 | 33 | var cast = contract.abi.FindMethod("castTest"); 34 | Assert.IsNotNull(cast); 35 | 36 | var expected = "P2K7GyVMC3f9XxKRji5gfg91WvutoHs2RyB6KzQxuaAUUeo"; 37 | var addr = Address.FromText(expected); 38 | 39 | var vm = new TestVM(contract, storage, cast); 40 | vm.Stack.Push(VMObject.FromObject(addr)); 41 | var result = vm.Execute(); 42 | Assert.IsTrue(result == ExecutionState.Halt); 43 | 44 | Assert.IsTrue(vm.Stack.Count == 1); 45 | var val = vm.Stack.Pop().AsString(); 46 | Assert.IsTrue(val == expected); 47 | } 48 | 49 | [Test] 50 | public void IfWithOr() 51 | { 52 | var sourceCode = 53 | @" 54 | contract test { 55 | public check(x:number, y:number): bool { 56 | return (x > 0) && (y < 0); 57 | } 58 | }"; 59 | 60 | var parser = new TombLangCompiler(); 61 | var contract = parser.Process(sourceCode).First(); 62 | 63 | var storage = new Dictionary(new ByteArrayComparer()); 64 | 65 | var countStuff = contract.abi.FindMethod("check"); 66 | Assert.IsNotNull(countStuff); 67 | 68 | var vm = new TestVM(contract, storage, countStuff); 69 | // NOTE - due to using a stack, we're pushing the second argument first (y), then the first argument (y) 70 | vm.Stack.Push(VMObject.FromObject(-3)); 71 | vm.Stack.Push(VMObject.FromObject(3)); 72 | var result = vm.Execute(); 73 | Assert.IsTrue(result == ExecutionState.Halt); 74 | 75 | Assert.IsTrue(vm.Stack.Count == 1); 76 | var val = vm.Stack.Pop().AsBool(); 77 | Assert.IsTrue(val); 78 | 79 | vm = new TestVM(contract, storage, countStuff); 80 | // NOTE - here we invert the order, in this case should fail due to the condition in the contract inside check() 81 | vm.Stack.Push(VMObject.FromObject(3)); 82 | vm.Stack.Push(VMObject.FromObject(-3)); 83 | result = vm.Execute(); 84 | Assert.IsTrue(result == ExecutionState.Halt); 85 | 86 | Assert.IsTrue(vm.Stack.Count == 1); 87 | val = vm.Stack.Pop().AsBool(); 88 | Assert.IsFalse(val); 89 | } 90 | } -------------------------------------------------------------------------------- /Library/src/AST/Expressions/CastExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Tomb.CodeGen; 2 | 3 | using System; 4 | 5 | namespace Phantasma.Tomb.AST.Expressions 6 | { 7 | public class CastExpression : Expression 8 | { 9 | public Expression expr; 10 | 11 | public override VarType ResultType { get; } 12 | 13 | public CastExpression(Scope parentScope, VarType resultType, Expression expr) : base(parentScope) 14 | { 15 | this.expr = expr; 16 | this.ResultType = resultType; 17 | } 18 | 19 | public override Register GenerateCode(CodeGenerator output) 20 | { 21 | var reg = expr.GenerateCode(output); 22 | 23 | switch (expr.ResultType.Kind) 24 | { 25 | case VarKind.Decimal: 26 | { 27 | switch (this.ResultType.Kind) 28 | { 29 | case VarKind.Number: 30 | return reg; 31 | 32 | case VarKind.Decimal: 33 | { 34 | var srcDecimals = ((DecimalVarType)expr.ResultType).decimals; 35 | var dstDecimals = ((DecimalVarType)this.ResultType).decimals; 36 | 37 | if (srcDecimals == dstDecimals) 38 | { 39 | return reg; 40 | } 41 | else 42 | if (srcDecimals < dstDecimals) 43 | { 44 | var diff = (dstDecimals - srcDecimals); 45 | var mult = (int)Math.Pow(10, diff); 46 | output.AppendLine(this, $"LOAD r0 {mult}"); 47 | output.AppendLine(this, $"MUL {reg} r0 {reg}"); 48 | return reg; 49 | } 50 | else 51 | { 52 | throw new CompilerException($"Decimal precision failure: {expr.ResultType} => {this.ResultType}"); 53 | } 54 | } 55 | 56 | default: 57 | throw new CompilerException($"Unsupported cast: {expr.ResultType} => {this.ResultType}"); 58 | } 59 | } 60 | } 61 | 62 | var vmType = MethodInterface.ConvertType(ResultType); 63 | output.AppendLine(this, $"CAST {reg} {reg} #{vmType}"); 64 | return reg; 65 | } 66 | 67 | 68 | public override void Visit(Action callback) 69 | { 70 | callback(this); 71 | expr.Visit(callback); 72 | } 73 | 74 | public override bool IsNodeUsed(Node node) 75 | { 76 | return (node == this) || expr.IsNodeUsed(node); 77 | } 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Samples/example8.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >0.4.10; 2 | 3 | 4 | //the very 8th example 5 | //this contract implements proof of ownership :) or at least it tries 6 | contract Example8 { 7 | 8 | address owner; 9 | 10 | event Stored( 11 | ); 12 | 13 | struct Document { 14 | address ownerAddress; 15 | string name; 16 | uint256 timestamp; 17 | uint256 modified; 18 | } 19 | mapping (bytes32 => Document) public documents; //every file hashed will belong to a address using the data type 32 bytes, size of a sha256 hash. 20 | mapping (uint => bytes32) public hashList; //every file hashed will belong to a address using the data type 32 bytes, size of a sha256 hash. 21 | uint public documentCount = 0; 22 | 23 | constructor() public { 24 | owner = msg.sender; 25 | } 26 | 27 | function amIMaster() public view returns (string memory) { 28 | if (msg.sender == owner) { 29 | return "Yes, master"; 30 | } 31 | return "No"; 32 | } 33 | function getBalance() public view returns (uint) { 34 | return msg.sender.balance; 35 | } 36 | 37 | function amIOwner(string memory file) public view returns (bool) { 38 | bytes32 fileHash = keccak256(abi.encodePacked(file)); 39 | 40 | if (msg.sender == documents[fileHash].ownerAddress) { 41 | return true; 42 | } 43 | return false; 44 | } 45 | function getHash(string memory file) public pure returns (bytes32) { 46 | return keccak256(abi.encodePacked(file)); 47 | } 48 | 49 | function changeOwner(string memory file,address newOwner) public returns (bool) { 50 | if (amIOwner(file)) { 51 | bytes32 fileHash = keccak256(abi.encodePacked(file)); 52 | documents[fileHash].ownerAddress = newOwner; 53 | documents[fileHash].modified = now; 54 | emit Stored(); 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | function store(string memory file,string memory name) public returns (bytes32) { 61 | bytes32 fileHash = keccak256(abi.encodePacked(file)); 62 | if (documents[fileHash].ownerAddress == 0x0000000000000000000000000000000000000000) { 63 | documents[fileHash].ownerAddress = msg.sender; //can save 64 | documents[fileHash].name = name; 65 | documents[fileHash].timestamp = now; 66 | hashList[documentCount] = fileHash; 67 | documentCount += 1; 68 | emit Stored(); 69 | return fileHash; 70 | } 71 | } 72 | 73 | function getDocumentCount() public view returns (uint) { 74 | return documentCount; 75 | } 76 | 77 | function getDocument(uint index) public view returns (bytes32 fileHash, address ownerAddress, string memory name, uint256 timestamp) { 78 | require(index > documentCount); 79 | 80 | fileHash = hashList[index]; 81 | ownerAddress = documents[fileHash].ownerAddress; 82 | name = documents[fileHash].name; 83 | timestamp = documents[fileHash].timestamp; 84 | 85 | } 86 | 87 | function hasOwner(string memory file) public view returns (address) { 88 | return documents[keccak256(abi.encodePacked(file))].ownerAddress; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Library/src/CodeGen/Script.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Statements; 4 | using Phantasma.Tomb.AST.Declarations; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Contract; 7 | 8 | namespace Phantasma.Tomb.CodeGen 9 | { 10 | public class Script: Module 11 | { 12 | public StatementBlock main; 13 | 14 | public MethodParameter[] Parameters { get; internal set; } 15 | public VarType ReturnType; 16 | 17 | public Script(string name, ModuleKind kind) : base(name, kind) 18 | { 19 | 20 | } 21 | 22 | public override MethodDeclaration FindMethod(string name) 23 | { 24 | return null; 25 | } 26 | 27 | public override bool IsNodeUsed(Node node) 28 | { 29 | if (node == this) 30 | { 31 | return true; 32 | } 33 | 34 | foreach (var lib in Libraries.Values) 35 | { 36 | if (lib.IsNodeUsed(node)) 37 | { 38 | return true; 39 | } 40 | } 41 | 42 | 43 | return main.IsNodeUsed(node); 44 | } 45 | 46 | public override void Visit(Action callback) 47 | { 48 | foreach (var lib in Libraries.Values) 49 | { 50 | lib.Visit(callback); 51 | } 52 | 53 | callback(this); 54 | main.Visit(callback); 55 | } 56 | 57 | 58 | public override ContractInterface GenerateCode(CodeGenerator output) 59 | { 60 | this.Scope.Enter(output); 61 | 62 | this.main.ParentScope.Enter(output); 63 | 64 | foreach (var parameter in this.Parameters) 65 | { 66 | var reg = Compiler.Instance.AllocRegister(output, this, parameter.Name); 67 | output.AppendLine(this, $"POP {reg}"); 68 | 69 | this.CallNecessaryConstructors(output, parameter.Type, reg); 70 | 71 | if (!this.main.ParentScope.Variables.ContainsKey(parameter.Name)) 72 | { 73 | throw new CompilerException("script parameter not initialized: " + parameter.Name); 74 | } 75 | 76 | var varDecl = this.main.ParentScope.Variables[parameter.Name]; 77 | varDecl.Register = reg; 78 | } 79 | 80 | this.main.GenerateCode(output); 81 | this.main.ParentScope.Leave(output); 82 | 83 | if (ReturnType.Kind == VarKind.None) 84 | { 85 | output.AppendLine(this, "RET"); 86 | } 87 | else 88 | { 89 | bool hasReturn = false; 90 | this.main.Visit((node) => 91 | { 92 | if (node is ReturnStatement) 93 | { 94 | hasReturn = true; 95 | } 96 | }); 97 | 98 | if (!hasReturn) 99 | { 100 | throw new Exception("Script is missing return statement"); 101 | } 102 | } 103 | 104 | this.Scope.Leave(output); 105 | 106 | return null; 107 | //return new ContractInterface(Enumerable.Empty(), Enumerable.Empty()); 108 | } 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Tests/Contracts/ArrayTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Utils; 6 | using Phantasma.Tomb.Compilers; 7 | 8 | namespace Tests.Contracts; 9 | 10 | public class ArrayTests 11 | { 12 | [Test] 13 | public void StringArray() 14 | { 15 | var str = "hello"; 16 | 17 | var sourceCode = 18 | @"contract test{ 19 | public getStrings(): array { 20 | local result:array = {""A"", ""B"", ""C""}; 21 | return result; 22 | }}"; 23 | 24 | var parser = new TombLangCompiler(); 25 | var contract = parser.Process(sourceCode).First(); 26 | 27 | var storage = new Dictionary(new ByteArrayComparer()); 28 | 29 | TestVM vm; 30 | 31 | var getStrings = contract.abi.FindMethod("getStrings"); 32 | Assert.IsNotNull(getStrings); 33 | 34 | vm = new TestVM(contract, storage, getStrings); 35 | var result = vm.Execute(); 36 | Assert.IsTrue(result == ExecutionState.Halt); 37 | 38 | Assert.IsTrue(vm.Stack.Count == 1); 39 | 40 | var obj = vm.Stack.Pop(); 41 | 42 | var array = obj.AsArray(VMType.String); 43 | Assert.IsTrue(array.Length == 3); 44 | } 45 | 46 | [Test] 47 | public void ArraySimple() 48 | { 49 | // TODO make other tests also use multiline strings for source code, much more readable... 50 | var sourceCode = @" 51 | contract arrays { 52 | import Array; 53 | 54 | public test(x:number):number { 55 | local my_array: array; 56 | my_array[1] = x; 57 | return Array.length(my_array); 58 | } 59 | } 60 | "; 61 | 62 | var parser = new TombLangCompiler(); 63 | var contract = parser.Process(sourceCode).First(); 64 | 65 | var storage = new Dictionary(new ByteArrayComparer()); 66 | 67 | TestVM vm; 68 | 69 | var test = contract.abi.FindMethod("test"); 70 | Assert.IsNotNull(test); 71 | 72 | vm = new TestVM(contract, storage, test); 73 | vm.Stack.Push(VMObject.FromObject(5)); 74 | var state = vm.Execute(); 75 | Assert.IsTrue(state == ExecutionState.Halt); 76 | 77 | var result = vm.Stack.Pop().AsNumber(); 78 | Assert.IsTrue(result == 1); 79 | } 80 | 81 | [Test] 82 | public void ArrayVariableIndex() 83 | { 84 | // TODO make other tests also use multiline strings for source code, much more readable... 85 | var sourceCode = @" 86 | contract arrays { 87 | public test(x:number, idx:number):number { 88 | local my_array: array; 89 | my_array[idx] = x; 90 | local num:number = my_array[idx]; 91 | return num + 1; 92 | } 93 | } 94 | "; 95 | 96 | var parser = new TombLangCompiler(); 97 | var contract = parser.Process(sourceCode).First(); 98 | 99 | var storage = new Dictionary(new ByteArrayComparer()); 100 | 101 | TestVM vm; 102 | 103 | var test = contract.abi.FindMethod("test"); 104 | Assert.IsNotNull(test); 105 | 106 | vm = new TestVM(contract, storage, test); 107 | vm.Stack.Push(VMObject.FromObject(2)); 108 | vm.Stack.Push(VMObject.FromObject(5)); 109 | var state = vm.Execute(); 110 | Assert.IsTrue(state == ExecutionState.Halt); 111 | 112 | var result = vm.Stack.Pop().AsNumber(); 113 | Assert.IsTrue(result == 6); 114 | } 115 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/ArrayTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Domain; 5 | using Phantasma.Core.Domain.Execution.Enums; 6 | using Phantasma.Core.Domain.VM; 7 | using Phantasma.Core.Domain.VM.Enums; 8 | using Phantasma.Core.Utils; 9 | using Phantasma.Tomb.Compilers; 10 | 11 | namespace TOMBLib.Tests.Contracts; 12 | 13 | public class ArrayTests 14 | { 15 | [Test] 16 | public void StringArray() 17 | { 18 | var str = "hello"; 19 | 20 | var sourceCode = 21 | @"contract test{ 22 | public getStrings(): array { 23 | local result:array = {""A"", ""B"", ""C""}; 24 | return result; 25 | }}"; 26 | 27 | var parser = new TombLangCompiler(); 28 | var contract = parser.Process(sourceCode).First(); 29 | 30 | var storage = new Dictionary(new ByteArrayComparer()); 31 | 32 | TestVM vm; 33 | 34 | var getStrings = contract.abi.FindMethod("getStrings"); 35 | Assert.IsNotNull(getStrings); 36 | 37 | vm = new TestVM(contract, storage, getStrings); 38 | var result = vm.Execute(); 39 | Assert.IsTrue(result == ExecutionState.Halt); 40 | 41 | Assert.IsTrue(vm.Stack.Count == 1); 42 | 43 | var obj = vm.Stack.Pop(); 44 | 45 | var array = obj.AsArray(VMType.String); 46 | Assert.IsTrue(array.Length == 3); 47 | } 48 | 49 | [Test] 50 | public void ArraySimple() 51 | { 52 | // TODO make other tests also use multiline strings for source code, much more readable... 53 | var sourceCode = @" 54 | contract arrays { 55 | import Array; 56 | 57 | public test(x:number):number { 58 | local my_array: array; 59 | my_array[1] = x; 60 | return Array.length(my_array); 61 | } 62 | } 63 | "; 64 | 65 | var parser = new TombLangCompiler(); 66 | var contract = parser.Process(sourceCode).First(); 67 | 68 | var storage = new Dictionary(new ByteArrayComparer()); 69 | 70 | TestVM vm; 71 | 72 | var test = contract.abi.FindMethod("test"); 73 | Assert.IsNotNull(test); 74 | 75 | vm = new TestVM(contract, storage, test); 76 | vm.Stack.Push(VMObject.FromObject(5)); 77 | var state = vm.Execute(); 78 | Assert.IsTrue(state == ExecutionState.Halt); 79 | 80 | var result = vm.Stack.Pop().AsNumber(); 81 | Assert.IsTrue(result == 1); 82 | } 83 | 84 | [Test] 85 | public void ArrayVariableIndex() 86 | { 87 | // TODO make other tests also use multiline strings for source code, much more readable... 88 | var sourceCode = @" 89 | contract arrays { 90 | public test(x:number, idx:number):number { 91 | local my_array: array; 92 | my_array[idx] = x; 93 | local num:number = my_array[idx]; 94 | return num + 1; 95 | } 96 | } 97 | "; 98 | 99 | var parser = new TombLangCompiler(); 100 | var contract = parser.Process(sourceCode).First(); 101 | 102 | var storage = new Dictionary(new ByteArrayComparer()); 103 | 104 | TestVM vm; 105 | 106 | var test = contract.abi.FindMethod("test"); 107 | Assert.IsNotNull(test); 108 | 109 | vm = new TestVM(contract, storage, test); 110 | vm.Stack.Push(VMObject.FromObject(2)); 111 | vm.Stack.Push(VMObject.FromObject(5)); 112 | var state = vm.Execute(); 113 | Assert.IsTrue(state == ExecutionState.Halt); 114 | 115 | var result = vm.Stack.Pop().AsNumber(); 116 | Assert.IsTrue(result == 6); 117 | } 118 | } -------------------------------------------------------------------------------- /Tests/Contracts/MethodTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Utils; 7 | using Phantasma.Tomb; 8 | using Phantasma.Tomb.Compilers; 9 | 10 | namespace Tests.Contracts; 11 | 12 | public class MethodTests 13 | { 14 | [Test] 15 | public void DuplicatedMethodNames() 16 | { 17 | var sourceCode = 18 | @" 19 | contract test { 20 | public testme(x:number): number { 21 | return 5; 22 | } 23 | 24 | public testme(x:number): string { 25 | return ""zero""; 26 | }}"; 27 | 28 | var parser = new TombLangCompiler(); 29 | 30 | Assert.Catch(() => 31 | { 32 | var contract = parser.Process(sourceCode).First(); 33 | }); 34 | } 35 | 36 | [Test] 37 | public void TooManyArgs() 38 | { 39 | var sourceCode = @" 40 | contract arrays { 41 | import Array; 42 | 43 | public mycall(x:number):number { 44 | return x+ 1; 45 | } 46 | 47 | public something():number { 48 | return this.mycall(2, 3); // extra argument here, should not compile 49 | } 50 | } 51 | "; 52 | 53 | var parser = new TombLangCompiler(); 54 | 55 | Assert.Catch(() => 56 | { 57 | var contract = parser.Process(sourceCode).First(); 58 | }); 59 | } 60 | 61 | [Test] 62 | public void TestLocalCallViaThis() 63 | { 64 | var sourceCode = 65 | @" 66 | contract test { 67 | private sum(x:number, y:number) : number 68 | { return x + y; } 69 | 70 | public fetch(val:number) : number 71 | { 72 | return this.sum(val, 1); 73 | } 74 | }"; 75 | 76 | var parser = new TombLangCompiler(); 77 | var contract = parser.Process(sourceCode).First(); 78 | 79 | var storage = new Dictionary(new ByteArrayComparer()); 80 | 81 | TestVM vm; 82 | 83 | var keys = PhantasmaKeys.Generate(); 84 | 85 | // call fetch 86 | var fetch = contract.abi.FindMethod("fetch"); 87 | Assert.IsNotNull(fetch); 88 | 89 | vm = new TestVM(contract, storage, fetch); 90 | vm.Stack.Push(VMObject.FromObject(10)); 91 | var state = vm.Execute(); 92 | Assert.IsTrue(state == ExecutionState.Halt); 93 | var result = vm.Stack.Pop().AsNumber(); 94 | 95 | Assert.IsTrue(result == 11); 96 | } 97 | 98 | [Test] 99 | public void TestContractCallViaCallMethod() 100 | { 101 | var sourceCode = 102 | @" 103 | contract test { 104 | import Call; 105 | 106 | private sum(x:number, y:number) : number 107 | { return x + y; } 108 | 109 | public fetch(val:number) : number 110 | { 111 | return Call.method(sum, val, 1); 112 | } 113 | }"; 114 | 115 | var parser = new TombLangCompiler(); 116 | var contract = parser.Process(sourceCode).First(); 117 | 118 | var storage = new Dictionary(new ByteArrayComparer()); 119 | 120 | TestVM vm; 121 | 122 | var keys = PhantasmaKeys.Generate(); 123 | 124 | // call fetch 125 | var fetch = contract.abi.FindMethod("fetch"); 126 | Assert.IsNotNull(fetch); 127 | 128 | vm = new TestVM(contract, storage, fetch); 129 | vm.Stack.Push(VMObject.FromObject(10)); 130 | var state = vm.Execute(); 131 | Assert.IsTrue(state == ExecutionState.Halt); 132 | var result = vm.Stack.Pop().AsNumber(); 133 | 134 | Assert.IsTrue(result == 11); 135 | } 136 | } -------------------------------------------------------------------------------- /Tests/Contracts/DecimalTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using Phantasma.Core.Cryptography; 6 | using Phantasma.Core.Domain; 7 | using Phantasma.Core.Numerics; 8 | using Phantasma.Core.Utils; 9 | using Phantasma.Tomb; 10 | using Phantasma.Tomb.Compilers; 11 | 12 | namespace Tests.Contracts; 13 | 14 | public class DecimalTests 15 | { 16 | [Test] 17 | public void DecimalsSimple() 18 | { 19 | var valStr = "2.4587"; 20 | var val = decimal.Parse(valStr, CultureInfo.InvariantCulture); 21 | var decimals = 8; 22 | 23 | var sourceCode = 24 | "contract test{\n" + 25 | $"global amount: decimal<{decimals}>;\n" + 26 | "constructor(owner:address) {\n" + 27 | "amount = " + valStr + ";\n}" + 28 | "public getValue():number {\n" + 29 | "return amount;\n}" + 30 | "public getLength():number {\n" + 31 | "return amount.decimals();\n}" + 32 | "}\n"; 33 | 34 | var parser = new TombLangCompiler(); 35 | var contract = parser.Process(sourceCode).First(); 36 | 37 | var storage = new Dictionary(new ByteArrayComparer()); 38 | 39 | TestVM vm; 40 | 41 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 42 | Assert.IsNotNull(constructor); 43 | 44 | var keys = PhantasmaKeys.Generate(); 45 | 46 | vm = new TestVM(contract, storage, constructor); 47 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 48 | var result = vm.Execute(); 49 | Assert.IsTrue(result == ExecutionState.Halt); 50 | 51 | Assert.IsTrue(storage.Count == 1); 52 | 53 | // call getVal 54 | var getValue = contract.abi.FindMethod("getValue"); 55 | Assert.IsNotNull(getValue); 56 | 57 | vm = new TestVM(contract, storage, getValue); 58 | result = vm.Execute(); 59 | Assert.IsTrue(result == ExecutionState.Halt); 60 | 61 | Assert.IsTrue(storage.Count == 1); 62 | 63 | Assert.IsTrue(vm.Stack.Count == 1); 64 | 65 | var obj = vm.Stack.Pop(); 66 | var newVal = obj.AsNumber(); 67 | var expectedVal = UnitConversion.ToBigInteger(val, decimals); 68 | 69 | Assert.IsTrue(newVal == expectedVal); 70 | 71 | // call getLength 72 | var getLength = contract.abi.FindMethod("getLength"); 73 | Assert.IsNotNull(getLength); 74 | 75 | vm = new TestVM(contract, storage, getLength); 76 | result = vm.Execute(); 77 | Assert.IsTrue(result == ExecutionState.Halt); 78 | 79 | Assert.IsTrue(storage.Count == 1); 80 | 81 | Assert.IsTrue(vm.Stack.Count == 1); 82 | 83 | obj = vm.Stack.Pop(); 84 | var len = obj.AsNumber(); 85 | 86 | Assert.IsTrue(len == decimals); 87 | } 88 | 89 | [Test] 90 | public void DecimalsPrecision() 91 | { 92 | var valStr = "2.4587"; 93 | var val = decimal.Parse(valStr, CultureInfo.InvariantCulture); 94 | 95 | var sourceCode = 96 | "contract test{\n" + 97 | $"global amount: decimal<3>;\n" + 98 | "constructor(owner:address) {\n" + 99 | "amount = " + valStr + ";\n}" + 100 | "}\n"; 101 | 102 | var parser = new TombLangCompiler(); 103 | 104 | try 105 | { 106 | var contract = parser.Process(sourceCode).First(); 107 | Assert.Fail("should have throw compile error"); 108 | } 109 | catch (CompilerException e) 110 | { 111 | Assert.IsTrue(e.Message.ToLower().Contains("precision")); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Tests/AST/Declaration/ConstDeclarationTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.CodeGen; 5 | using Phantasma.Tomb.Lexers; 6 | 7 | namespace Tests.AST.Declaration; 8 | 9 | public class ConstDeclarationTest 10 | { 11 | [SetUp] 12 | public void Setup() 13 | { 14 | TombLangLexer lexer = new TombLangLexer(); 15 | } 16 | 17 | [Test] 18 | public void ConstDeclaration_Constructor_SetsProperties() 19 | { 20 | // Arrange 21 | var module = new Contract("myself", ModuleKind.Contract); 22 | var parentScope = new Scope(module); 23 | var name = "MyConst"; 24 | var type = VarType.Find(VarKind.Number); 25 | var value = "42"; 26 | 27 | // Act 28 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 29 | 30 | // Assert 31 | Assert.AreEqual(parentScope, constDeclaration.ParentScope); 32 | Assert.AreEqual(name, constDeclaration.Name); 33 | Assert.AreEqual(type, constDeclaration.Type); 34 | Assert.AreEqual(value, constDeclaration.Value); 35 | } 36 | 37 | [Test] 38 | public void ConstDeclaration_ToString_ReturnsCorrectString() 39 | { 40 | // Arrange 41 | var module = new Contract("myself", ModuleKind.Contract); 42 | var parentScope = new Scope(module); 43 | var name = "MyConst"; 44 | var type = VarType.Find(VarKind.Number); 45 | var value = "42"; 46 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 47 | 48 | // Act 49 | var result = constDeclaration.ToString(); 50 | 51 | // Assert 52 | Assert.AreEqual("const MyConst:Number", result); 53 | } 54 | 55 | [Test] 56 | public void ConstDeclaration_Visit_ExecutesCallback() 57 | { 58 | // Arrange 59 | var module = new Contract("myself", ModuleKind.Contract); 60 | var parentScope = new Scope(module); 61 | var name = "MyConst"; 62 | var type = VarType.Find(VarKind.Number); 63 | var value = "42"; 64 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 65 | var visited = false; 66 | 67 | // Act 68 | constDeclaration.Visit(node => visited = true); 69 | 70 | // Assert 71 | Assert.True(visited); 72 | } 73 | 74 | [Test] 75 | public void ConstDeclaration_IsNodeUsed_ReturnsTrue() 76 | { 77 | // Arrange 78 | var module = new Contract("myself", ModuleKind.Contract); 79 | var parentScope = new Scope(module); 80 | var name = "MyConst"; 81 | var type = VarType.Find(VarKind.Number); 82 | var value = "42"; 83 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 84 | 85 | // Act 86 | var result = constDeclaration.IsNodeUsed(constDeclaration); 87 | 88 | // Assert 89 | Assert.True(result); 90 | } 91 | 92 | [Test] 93 | public void ConstDeclaration_IsNodeUsed_ReturnsFalse() 94 | { 95 | // Arrange 96 | var module = new Contract("myself", ModuleKind.Contract); 97 | var parentScope = new Scope(module); 98 | var name = "MyConst"; 99 | var type = VarType.Find(VarKind.Number); 100 | var value = "42"; 101 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 102 | var otherNode = new ConstDeclaration(parentScope, "OtherConst", VarType.Find(VarKind.Decimal, 2), "3.12"); 103 | 104 | // Act 105 | var result = constDeclaration.IsNodeUsed(otherNode); 106 | 107 | // Assert 108 | Assert.False(result); 109 | } 110 | } -------------------------------------------------------------------------------- /Library/tests/AST/Declaration/ConstDeclarationTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Phantasma.Tomb.AST; 3 | using Phantasma.Tomb.AST.Declarations; 4 | using Phantasma.Tomb.CodeGen; 5 | using Phantasma.Tomb.Lexers; 6 | 7 | namespace TOMBLib.Tests.AST.Declaration; 8 | 9 | public class ConstDeclarationTest 10 | { 11 | [SetUp] 12 | public void Setup() 13 | { 14 | TombLangLexer lexer = new TombLangLexer(); 15 | } 16 | 17 | [Test] 18 | public void ConstDeclaration_Constructor_SetsProperties() 19 | { 20 | // Arrange 21 | var module = new Contract("myself", ModuleKind.Contract); 22 | var parentScope = new Scope(module); 23 | var name = "MyConst"; 24 | var type = VarType.Find(VarKind.Number); 25 | var value = "42"; 26 | 27 | // Act 28 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 29 | 30 | // Assert 31 | Assert.AreEqual(parentScope, constDeclaration.ParentScope); 32 | Assert.AreEqual(name, constDeclaration.Name); 33 | Assert.AreEqual(type, constDeclaration.Type); 34 | Assert.AreEqual(value, constDeclaration.Value); 35 | } 36 | 37 | [Test] 38 | public void ConstDeclaration_ToString_ReturnsCorrectString() 39 | { 40 | // Arrange 41 | var module = new Contract("myself", ModuleKind.Contract); 42 | var parentScope = new Scope(module); 43 | var name = "MyConst"; 44 | var type = VarType.Find(VarKind.Number); 45 | var value = "42"; 46 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 47 | 48 | // Act 49 | var result = constDeclaration.ToString(); 50 | 51 | // Assert 52 | Assert.AreEqual("const MyConst:Number", result); 53 | } 54 | 55 | [Test] 56 | public void ConstDeclaration_Visit_ExecutesCallback() 57 | { 58 | // Arrange 59 | var module = new Contract("myself", ModuleKind.Contract); 60 | var parentScope = new Scope(module); 61 | var name = "MyConst"; 62 | var type = VarType.Find(VarKind.Number); 63 | var value = "42"; 64 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 65 | var visited = false; 66 | 67 | // Act 68 | constDeclaration.Visit(node => visited = true); 69 | 70 | // Assert 71 | Assert.True(visited); 72 | } 73 | 74 | [Test] 75 | public void ConstDeclaration_IsNodeUsed_ReturnsTrue() 76 | { 77 | // Arrange 78 | var module = new Contract("myself", ModuleKind.Contract); 79 | var parentScope = new Scope(module); 80 | var name = "MyConst"; 81 | var type = VarType.Find(VarKind.Number); 82 | var value = "42"; 83 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 84 | 85 | // Act 86 | var result = constDeclaration.IsNodeUsed(constDeclaration); 87 | 88 | // Assert 89 | Assert.True(result); 90 | } 91 | 92 | [Test] 93 | public void ConstDeclaration_IsNodeUsed_ReturnsFalse() 94 | { 95 | // Arrange 96 | var module = new Contract("myself", ModuleKind.Contract); 97 | var parentScope = new Scope(module); 98 | var name = "MyConst"; 99 | var type = VarType.Find(VarKind.Number); 100 | var value = "42"; 101 | var constDeclaration = new ConstDeclaration(parentScope, name, type, value); 102 | var otherNode = new ConstDeclaration(parentScope, "OtherConst", VarType.Find(VarKind.Decimal, 2), "3.12"); 103 | 104 | // Act 105 | var result = constDeclaration.IsNodeUsed(otherNode); 106 | 107 | // Assert 108 | Assert.False(result); 109 | } 110 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/MethodTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | using Phantasma.Core.Cryptography; 5 | using Phantasma.Core.Domain; 6 | using Phantasma.Core.Domain.Execution.Enums; 7 | using Phantasma.Core.Domain.VM; 8 | using Phantasma.Core.Utils; 9 | using Phantasma.Tomb; 10 | using Phantasma.Tomb.Compilers; 11 | 12 | namespace TOMBLib.Tests.Contracts; 13 | 14 | public class MethodTests 15 | { 16 | [Test] 17 | public void DuplicatedMethodNames() 18 | { 19 | var sourceCode = 20 | @" 21 | contract test { 22 | public testme(x:number): number { 23 | return 5; 24 | } 25 | 26 | public testme(x:number): string { 27 | return ""zero""; 28 | }}"; 29 | 30 | var parser = new TombLangCompiler(); 31 | 32 | Assert.Catch(() => 33 | { 34 | var contract = parser.Process(sourceCode).First(); 35 | }); 36 | } 37 | 38 | [Test] 39 | public void TooManyArgs() 40 | { 41 | var sourceCode = @" 42 | contract arrays { 43 | import Array; 44 | 45 | public mycall(x:number):number { 46 | return x+ 1; 47 | } 48 | 49 | public something():number { 50 | return this.mycall(2, 3); // extra argument here, should not compile 51 | } 52 | } 53 | "; 54 | 55 | var parser = new TombLangCompiler(); 56 | 57 | Assert.Catch(() => 58 | { 59 | var contract = parser.Process(sourceCode).First(); 60 | }); 61 | } 62 | 63 | [Test] 64 | public void TestLocalCallViaThis() 65 | { 66 | var sourceCode = 67 | @" 68 | contract test { 69 | private sum(x:number, y:number) : number 70 | { return x + y; } 71 | 72 | public fetch(val:number) : number 73 | { 74 | return this.sum(val, 1); 75 | } 76 | }"; 77 | 78 | var parser = new TombLangCompiler(); 79 | var contract = parser.Process(sourceCode).First(); 80 | 81 | var storage = new Dictionary(new ByteArrayComparer()); 82 | 83 | TestVM vm; 84 | 85 | var keys = PhantasmaKeys.Generate(); 86 | 87 | // call fetch 88 | var fetch = contract.abi.FindMethod("fetch"); 89 | Assert.IsNotNull(fetch); 90 | 91 | vm = new TestVM(contract, storage, fetch); 92 | vm.Stack.Push(VMObject.FromObject(10)); 93 | var state = vm.Execute(); 94 | Assert.IsTrue(state == ExecutionState.Halt); 95 | var result = vm.Stack.Pop().AsNumber(); 96 | 97 | Assert.IsTrue(result == 11); 98 | } 99 | 100 | [Test] 101 | public void TestContractCallViaCallMethod() 102 | { 103 | var sourceCode = 104 | @" 105 | contract test { 106 | import Call; 107 | 108 | private sum(x:number, y:number) : number 109 | { return x + y; } 110 | 111 | public fetch(val:number) : number 112 | { 113 | return Call.method(sum, val, 1); 114 | } 115 | }"; 116 | 117 | var parser = new TombLangCompiler(); 118 | var contract = parser.Process(sourceCode).First(); 119 | 120 | var storage = new Dictionary(new ByteArrayComparer()); 121 | 122 | TestVM vm; 123 | 124 | var keys = PhantasmaKeys.Generate(); 125 | 126 | // call fetch 127 | var fetch = contract.abi.FindMethod("fetch"); 128 | Assert.IsNotNull(fetch); 129 | 130 | vm = new TestVM(contract, storage, fetch); 131 | vm.Stack.Push(VMObject.FromObject(10)); 132 | var state = vm.Execute(); 133 | Assert.IsTrue(state == ExecutionState.Halt); 134 | var result = vm.Stack.Pop().AsNumber(); 135 | 136 | Assert.IsTrue(result == 11); 137 | } 138 | } -------------------------------------------------------------------------------- /Library/tests/Contracts/DecimalTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | using Phantasma.Core.Cryptography; 6 | using Phantasma.Core.Domain; 7 | using Phantasma.Core.Domain.Contract; 8 | using Phantasma.Core.Domain.Execution.Enums; 9 | using Phantasma.Core.Domain.VM; 10 | using Phantasma.Core.Numerics; 11 | using Phantasma.Core.Utils; 12 | using Phantasma.Tomb; 13 | using Phantasma.Tomb.Compilers; 14 | 15 | namespace TOMBLib.Tests.Contracts; 16 | 17 | public class DecimalTests 18 | { 19 | [Test] 20 | public void DecimalsSimple() 21 | { 22 | var valStr = "2.4587"; 23 | var val = decimal.Parse(valStr, CultureInfo.InvariantCulture); 24 | var decimals = 8; 25 | 26 | var sourceCode = 27 | "contract test{\n" + 28 | $"global amount: decimal<{decimals}>;\n" + 29 | "constructor(owner:address) {\n" + 30 | "amount = " + valStr + ";\n}" + 31 | "public getValue():number {\n" + 32 | "return amount;\n}" + 33 | "public getLength():number {\n" + 34 | "return amount.decimals();\n}" + 35 | "}\n"; 36 | 37 | var parser = new TombLangCompiler(); 38 | var contract = parser.Process(sourceCode).First(); 39 | 40 | var storage = new Dictionary(new ByteArrayComparer()); 41 | 42 | TestVM vm; 43 | 44 | var constructor = contract.abi.FindMethod(SmartContract.ConstructorName); 45 | Assert.IsNotNull(constructor); 46 | 47 | var keys = PhantasmaKeys.Generate(); 48 | 49 | vm = new TestVM(contract, storage, constructor); 50 | vm.Stack.Push(VMObject.FromObject(keys.Address)); 51 | var result = vm.Execute(); 52 | Assert.IsTrue(result == ExecutionState.Halt); 53 | 54 | Assert.IsTrue(storage.Count == 1); 55 | 56 | // call getVal 57 | var getValue = contract.abi.FindMethod("getValue"); 58 | Assert.IsNotNull(getValue); 59 | 60 | vm = new TestVM(contract, storage, getValue); 61 | result = vm.Execute(); 62 | Assert.IsTrue(result == ExecutionState.Halt); 63 | 64 | Assert.IsTrue(storage.Count == 1); 65 | 66 | Assert.IsTrue(vm.Stack.Count == 1); 67 | 68 | var obj = vm.Stack.Pop(); 69 | var newVal = obj.AsNumber(); 70 | var expectedVal = UnitConversion.ToBigInteger(val, decimals); 71 | 72 | Assert.IsTrue(newVal == expectedVal); 73 | 74 | // call getLength 75 | var getLength = contract.abi.FindMethod("getLength"); 76 | Assert.IsNotNull(getLength); 77 | 78 | vm = new TestVM(contract, storage, getLength); 79 | result = vm.Execute(); 80 | Assert.IsTrue(result == ExecutionState.Halt); 81 | 82 | Assert.IsTrue(storage.Count == 1); 83 | 84 | Assert.IsTrue(vm.Stack.Count == 1); 85 | 86 | obj = vm.Stack.Pop(); 87 | var len = obj.AsNumber(); 88 | 89 | Assert.IsTrue(len == decimals); 90 | } 91 | 92 | [Test] 93 | public void DecimalsPrecision() 94 | { 95 | var valStr = "2.4587"; 96 | var val = decimal.Parse(valStr, CultureInfo.InvariantCulture); 97 | 98 | var sourceCode = 99 | "contract test{\n" + 100 | $"global amount: decimal<3>;\n" + 101 | "constructor(owner:address) {\n" + 102 | "amount = " + valStr + ";\n}" + 103 | "}\n"; 104 | 105 | var parser = new TombLangCompiler(); 106 | 107 | try 108 | { 109 | var contract = parser.Process(sourceCode).First(); 110 | Assert.Fail("should have throw compile error"); 111 | } 112 | catch (CompilerException e) 113 | { 114 | Assert.IsTrue(e.Message.ToLower().Contains("precision")); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /Library/src/AST/Expressions/MacroExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Core.Domain; 2 | using Phantasma.Core.Domain.Contract; 3 | using Phantasma.Core.Numerics; 4 | using Phantasma.Tomb.CodeGen; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | 10 | namespace Phantasma.Tomb.AST.Expressions 11 | { 12 | public class MacroExpression : Expression 13 | { 14 | public string value; 15 | public string[] args; 16 | 17 | public MacroExpression(Scope parentScope, string value, IEnumerable args) : base(parentScope) 18 | { 19 | this.value = value; 20 | this.args = args.ToArray(); 21 | } 22 | 23 | public override string ToString() 24 | { 25 | return "macro: " + value; 26 | } 27 | 28 | public override Register GenerateCode(CodeGenerator output) 29 | { 30 | throw new System.Exception($"macro {value} was not unfolded!"); 31 | } 32 | 33 | public override void Visit(Action callback) 34 | { 35 | callback(this); 36 | } 37 | 38 | public override bool IsNodeUsed(Node node) 39 | { 40 | return (node == this); 41 | } 42 | 43 | public LiteralExpression Unfold(Scope scope) 44 | { 45 | switch (value) 46 | { 47 | case "THIS_ADDRESS": 48 | { 49 | var addr = SmartContract.GetAddressFromContractName(scope.Module.Name); 50 | var hex = Base16.Encode(addr.ToByteArray()); 51 | return new LiteralExpression(scope, "0x" + hex, VarType.Find(VarKind.Address)); 52 | } 53 | 54 | case "THIS_SYMBOL": 55 | { 56 | var module = scope.Module; 57 | 58 | while (module.Kind != ModuleKind.Token && module.Parent != null) 59 | { 60 | module = module.Parent; 61 | } 62 | 63 | if (module.Kind == ModuleKind.Token) 64 | { 65 | return new LiteralExpression(scope, '\"' + module.Name + '\"', VarType.Find(VarKind.String)); 66 | } 67 | 68 | throw new CompilerException($"macro {value} is not available here"); 69 | } 70 | 71 | case "TYPE_OF": 72 | { 73 | if (args.Length != 1) 74 | { 75 | throw new CompilerException($"macro {value} requires 1 argument"); 76 | } 77 | 78 | var target = args[0]; 79 | 80 | VarKind kind; 81 | if (!Enum.TryParse(target, true, out kind)) 82 | { 83 | var decl = scope.FindVariable(target, false); 84 | if (decl == null) 85 | { 86 | throw new CompilerException($"unknown identifier: {target}"); 87 | } 88 | 89 | kind = decl.Type.Kind; 90 | } 91 | 92 | return new LiteralExpression(scope, kind.ToString(), VarType.Find(VarKind.Type)); 93 | } 94 | 95 | default: 96 | { 97 | var macro = Compiler.Instance.ResolveMacro(value); 98 | 99 | if (macro != null) 100 | { 101 | return new LiteralExpression(scope, macro.GetValue(), macro.type); 102 | } 103 | else 104 | { 105 | throw new CompilerException($"unknown compile time macro: {value}"); 106 | } 107 | } 108 | } 109 | } 110 | 111 | public override VarType ResultType => VarType.Find(VarKind.Unknown); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /Library/src/AST/Expressions/BinaryExpression.cs: -------------------------------------------------------------------------------- 1 | using Phantasma.Core.Domain; 2 | using Phantasma.Core.Domain.VM.Enums; 3 | using Phantasma.Tomb.CodeGen; 4 | 5 | using System; 6 | 7 | namespace Phantasma.Tomb.AST.Expressions 8 | { 9 | public class BinaryExpression : Expression 10 | { 11 | private OperatorKind op; 12 | private Expression left; 13 | private Expression right; 14 | 15 | public override VarType ResultType => op.IsLogicalOperator() ? VarType.Find(VarKind.Bool) : left.ResultType; 16 | 17 | public BinaryExpression(Scope parentScope, OperatorKind op, Expression leftSide, Expression rightSide) : base(parentScope) 18 | { 19 | if (op == OperatorKind.Unknown) 20 | { 21 | throw new CompilerException("implementation failure"); 22 | } 23 | 24 | this.op = op; 25 | this.left = leftSide; 26 | this.right = rightSide; 27 | } 28 | 29 | public override T AsLiteral() 30 | { 31 | if (op == OperatorKind.Addition) 32 | { 33 | if (typeof(T) == typeof(string) && ResultType.Kind == VarKind.String) 34 | return (T)(object)(left.AsLiteral() + right.AsLiteral()); 35 | 36 | if (typeof(T) == typeof(int) && ResultType.Kind == VarKind.Number) 37 | return (T)(object)(left.AsLiteral() + right.AsLiteral()); 38 | } 39 | 40 | 41 | return base.AsLiteral(); 42 | } 43 | 44 | public override bool IsNodeUsed(Node node) 45 | { 46 | return (node == this) || left.IsNodeUsed(node) || right.IsNodeUsed(node); 47 | } 48 | 49 | public override void Visit(Action callback) 50 | { 51 | callback(this); 52 | left.Visit(callback); 53 | right.Visit(callback); 54 | } 55 | 56 | public override Register GenerateCode(CodeGenerator output) 57 | { 58 | if (this.op == OperatorKind.Addition && left.ResultType.Kind == VarKind.String && right.ResultType.Kind != VarKind.String) 59 | { 60 | this.right = new CastExpression(this.ParentScope, VarType.Find(VarKind.String), right); 61 | } 62 | 63 | var regLeft = left.GenerateCode(output); 64 | var regRight = right.GenerateCode(output); 65 | var regResult = Compiler.Instance.AllocRegister(output, this); 66 | 67 | Opcode opcode; 68 | switch (this.op) 69 | { 70 | case OperatorKind.Addition: opcode = Opcode.ADD; break; 71 | case OperatorKind.Subtraction: opcode = Opcode.SUB; break; 72 | case OperatorKind.Multiplication: opcode = Opcode.MUL; break; 73 | case OperatorKind.Division: opcode = Opcode.DIV; break; 74 | case OperatorKind.Modulus: opcode = Opcode.MOD; break; 75 | case OperatorKind.Power: opcode = Opcode.POW; break; 76 | 77 | case OperatorKind.Equal: opcode = Opcode.EQUAL; break; 78 | case OperatorKind.Less: opcode = Opcode.LT; break; 79 | case OperatorKind.LessOrEqual: opcode = Opcode.LTE; break; 80 | case OperatorKind.Greater: opcode = Opcode.GT; break; 81 | case OperatorKind.GreaterOrEqual: opcode = Opcode.GTE; break; 82 | 83 | case OperatorKind.ShiftRight: opcode = Opcode.SHR; break; 84 | case OperatorKind.ShiftLeft: opcode = Opcode.SHL; break; 85 | 86 | case OperatorKind.Or: opcode = Opcode.OR; break; 87 | case OperatorKind.And: opcode = Opcode.AND; break; 88 | case OperatorKind.Xor: opcode = Opcode.XOR; break; 89 | 90 | default: 91 | throw new CompilerException("not implemented vmopcode for " + op); 92 | } 93 | 94 | output.AppendLine(this, $"{opcode} {regLeft} {regRight} {regResult}"); 95 | 96 | Compiler.Instance.DeallocRegister(ref regRight); 97 | Compiler.Instance.DeallocRegister(ref regLeft); 98 | 99 | return regResult; 100 | } 101 | 102 | public override string ToString() 103 | { 104 | return $"{left} {op} {right}"; 105 | } 106 | } 107 | 108 | } 109 | --------------------------------------------------------------------------------