Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
Relfos committed Feb 23, 2022
2 parents bc37cfd + 1a53bd5 commit c8e86c5
Show file tree
Hide file tree
Showing 62 changed files with 5,105 additions and 3,681 deletions.
14 changes: 14 additions & 0 deletions Compiler/AST/Declaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Phantasma.Tomb.AST
{
public abstract class Declaration: Node
{
public readonly string Name;
public Scope ParentScope { get; }

protected Declaration(Scope parentScope, string name)
{
Name = name;
ParentScope = parentScope;
}
}
}
38 changes: 38 additions & 0 deletions Compiler/AST/Declarations/ConstDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Phantasma.Tomb.CodeGen;

using System;

namespace Phantasma.Tomb.AST.Declarations
{
public class ConstDeclaration : Declaration
{
public VarType Type;
public string Value;

public ConstDeclaration(Scope parentScope, string name, VarType kind, string value) : base(parentScope, name)
{
this.Type = kind;
this.Value = value;
}

public void GenerateCode(CodeGenerator output)
{
// DO NOTHING
}

public override string ToString()
{
return $"const {Name}:{Type}";
}

public override void Visit(Action<Node> callback)
{
callback(this);
}

public override bool IsNodeUsed(Node node)
{
return node == this;
}
}
}
12 changes: 12 additions & 0 deletions Compiler/AST/Declarations/DecimalDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Phantasma.Tomb.AST.Declarations
{
public class DecimalDeclaration : VarDeclaration
{
public readonly int Decimals;

public DecimalDeclaration(Scope parentScope, string name, int decimals, VarStorage storage) : base(parentScope, name, VarType.Find(VarKind.Decimal), storage)
{
this.Decimals = decimals;
}
}
}
48 changes: 48 additions & 0 deletions Compiler/AST/Declarations/EnumDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

using System;
using System.Collections.Generic;

namespace Phantasma.Tomb.AST.Declarations
{
public struct EnumEntry
{
public readonly string name;
public readonly uint value;

public EnumEntry(string name, uint value)
{
this.name = name;
this.value = value;
}
}

public class EnumDeclaration : Declaration
{
public Dictionary<string, uint> entryNames;

public EnumDeclaration(string name, IEnumerable<EnumEntry> entries) : base(null, name)
{
entryNames = new Dictionary<string, uint>();

foreach (var entry in entries)
{
if (entryNames.ContainsKey(entry.name))
{
throw new CompilerException($"Duplicated entry {entry.value} in enum {name}");
}

entryNames[entry.name] = entry.value;
}
}

public override bool IsNodeUsed(Node node)
{
return (node == this);
}

public override void Visit(Action<Node> callback)
{
callback(this);
}
}
}
283 changes: 283 additions & 0 deletions Compiler/AST/Declarations/EventDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
using Phantasma.Blockchain;
using Phantasma.CodeGen.Assembler;
using Phantasma.Cryptography;
using Phantasma.Domain;
using Phantasma.Numerics;
using Phantasma.Tomb.CodeGen;
using Phantasma.VM;

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Phantasma.Tomb.AST.Declarations
{
public class EventDeclaration : Declaration
{
public readonly Scope scope;
public readonly byte value;
public readonly VarType returnType;
public readonly byte[] descriptionScript;

struct StringToken
{
public readonly bool dynamic;
public readonly string value;

public StringToken(bool dynamic, string value)
{
this.dynamic = dynamic;
this.value = value;
}

public override string ToString()
{
return value;
}
}

public EventDeclaration(Scope scope, string name, byte value, VarType returnType, byte[] description) : base(scope.Parent, name)
{
this.scope = scope;
this.value = value;
this.returnType = returnType;
this.descriptionScript = description;

}

public static byte[] GenerateScriptFromString(VarType type, string src)
{
src = src.Substring(1, src.Length - 2); // remove "" delimiters

var tokens = new List<StringToken>();

var sb = new StringBuilder();

bool insideTags = false;
for (int i = 0; i < src.Length; i++)
{
var ch = src[i];

switch (ch)
{
case '{':
if (insideTags)
{
throw new CompilerException("Open declaration tag mismatch");
}

if (sb.Length > 0)
{
tokens.Add(new StringToken(false, sb.ToString()));
sb.Clear();
}
insideTags = true;
break;

case '}':
if (!insideTags)
{
throw new CompilerException("Close declaration tag mismatch");
}

if (sb.Length == 0)
{
throw new CompilerException("Empty declaration tag");
}
insideTags = false;
tokens.Add(new StringToken(true, sb.ToString()));
sb.Clear();
break;

default:
sb.Append(ch);
break;
}
}

if (sb.Length > 0)
{
tokens.Add(new StringToken(false, sb.ToString()));
}

sb.Clear();
sb.AppendLine("POP r2"); // address
sb.AppendLine("POP r3"); // data
sb.AppendLine("LOAD r0 \"\"");
foreach (var token in tokens)
{
if (token.dynamic) {
if (token.value == "address")
{
sb.AppendLine($"CAST r2 r1 #String");
}
else
if (token.value == "data")
{
if (type.Kind == VarKind.Struct)
{
throw new CompilerException($"struct fields not specified");
}
else
{
sb.AppendLine($"CAST r3 r1 #String");
}
}
else
if (token.value.StartsWith("data."))
{
throw new CompilerException($"Struct tags not implemented");
}
else
{
throw new CompilerException($"Invalid declaration tag: {token.value}");
}
}
else
{
sb.AppendLine($"LOAD r1 \"{token.value}\"");
}
sb.AppendLine("ADD r0 r1 r0");
}
sb.AppendLine("PUSH r0"); // return result
sb.AppendLine("RET");

var asm = sb.ToString();
var script = AssemblerUtils.BuildScript(asm);


return script;
}

public override void Visit(Action<Node> callback)
{
callback(this);
}

public override bool IsNodeUsed(Node node)
{
return (node == this);
}

public void GenerateCode(CodeGenerator output)
{
// do nothing
}

private VMObject GenerateTestObject(VarType type)
{
VMObject obj;

switch (type.Kind)
{
case VarKind.Number:
obj = VMObject.FromObject(new BigInteger(123));
break;

case VarKind.String:
obj = VMObject.FromObject("test");
break;

case VarKind.Bool:
obj = VMObject.FromObject(true);
break;

case VarKind.Struct:
var fields = new Dictionary<VMObject, VMObject>();

var structInfo = type as StructVarType;
foreach (var field in structInfo.decl.fields)
{
fields[VMObject.FromObject(field.name)] = GenerateTestObject(field.type);
}

obj = new VMObject();
obj.SetValue(fields);

using (var stream = new MemoryStream())
{
using (var writer = new BinaryWriter(stream))
{
obj.SerializeData(writer);
}

var bytes = stream.ToArray();
obj.SetValue(bytes);
}

break;


default:
throw new CompilerException($"Can't initialize test object with type: {type}");
}

return obj;
}

private class CustomDescriptionVM : DescriptionVM
{
public CustomDescriptionVM(byte[] script, uint offset) : base(script, offset)
{
}

public override IToken FetchToken(string symbol)
{
return new Blockchain.Tokens.TokenInfo(symbol, symbol, Address.FromHash("test"), 0, 8, TokenFlags.Fungible | TokenFlags.Divisible, new byte[] { (byte)Opcode.RET}, ContractInterface.Empty);
}

public override string OutputAddress(Address address)
{
return address.Text;
}

public override string OutputSymbol(string symbol)
{
return symbol;
}
}

public void Validate()
{
try
{
var vm = new CustomDescriptionVM(this.descriptionScript, 0);

var obj = GenerateTestObject(this.returnType);
vm.Stack.Push(obj);
vm.Stack.Push(VMObject.FromObject(Address.FromText("S3dApERMJUMRYECjyKLJioz2PCBUY6HBnktmC9u1obhDAgm")));
var state = vm.Execute();

if (state != ExecutionState.Halt)
{
throw new CompilerException("description script did not execute correctly");
}

if (vm.Stack.Count > 0)
{
var result = vm.Stack.Pop();
}
else
{
throw new CompilerException("description script did not return a result");
}
}
catch (Exception e)
{
if (e is CompilerException)
{
throw e;
}

throw new CompilerException($"Error validating description script. {e.Message}");
}
}

internal ContractEvent GetABI()
{
var type = MethodInterface.ConvertType(this.returnType);
return new ContractEvent(this.value, this.Name, type, descriptionScript);
}
}
}
Loading

0 comments on commit c8e86c5

Please sign in to comment.