Skip to content
46 changes: 46 additions & 0 deletions src/WattleScript.Interpreter/DataTypes/Module.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.IO;

namespace WattleScript.Interpreter
{
public class Module
{
public static Module LocalModule = new Module(true);

/// <summary>
/// Gets/sets the Wattlescript source code of this module
/// </summary>
public string Code { get; set; }

/// <summary>
/// Gets/sets the Wattlescript bytecode of this module. If not null, this has priority over <see cref="Code"/>
/// </summary>
public byte[] Bytecode { get; set; }

/// <summary>
/// Gets/sets the Stream with Wattlescript bytecode of this module. If not null, this has priority over <see cref="Bytecode"/>
/// </summary>
public Stream Stream { get; set; }

internal bool IsEntryRef { get; set; }

internal Module(bool isEntryRef)
{
IsEntryRef = isEntryRef;
}

public Module(string code)
{
Code = code;
}

public Module(byte[] bytecode)
{
Bytecode = bytecode;
}

public Module(Stream stream)
{
Stream = stream;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
case OpCode.PushInt:
case OpCode.BaseChk:
case OpCode.SetMetaTab:
case OpCode.PrepNmspc:
return InstructionFieldUsage.NumVal;
case OpCode.Call:
case OpCode.ThisCall:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public void SetHasVarArgs()
m_Frames.Last().HasVarArgs = true;
}

public void PushBlock(string nmspc)
{
m_Frames.Last().PushBlock(nmspc);
}

public void PushBlock()
{
m_Frames.Last().PushBlock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,28 @@ internal class BuildTimeScopeBlock
internal BuildTimeScopeBlock Parent { get; private set; }
internal List<BuildTimeScopeBlock> ChildNodes { get; private set; }
internal RuntimeScopeBlock ScopeBlock { get; private set; }
internal string Namespace { get; private set; }

Dictionary<string, SymbolRef> m_DefinedNames = new Dictionary<string, SymbolRef>();

internal void Rename(string name)
{
SymbolRef sref = m_DefinedNames[name];
m_DefinedNames.Remove(name);
m_DefinedNames.Add(string.Format("@{0}_{1}", name, Guid.NewGuid().ToString("N")), sref);
}

internal BuildTimeScopeBlock(BuildTimeScopeBlock parent)
internal BuildTimeScopeBlock(BuildTimeScopeBlock parent, string nmspc = "")
{
Parent = parent;
ChildNodes = new List<BuildTimeScopeBlock>();
ScopeBlock = new RuntimeScopeBlock();
Namespace = nmspc;
}


internal BuildTimeScopeBlock AddChild()
internal BuildTimeScopeBlock AddChild(string nmspc = "")
{
BuildTimeScopeBlock block = new BuildTimeScopeBlock(this);
BuildTimeScopeBlock block = new BuildTimeScopeBlock(this, nmspc);
ChildNodes.Add(block);
return block;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ internal BuildTimeScopeFrame(bool isConstructor)
m_ScopeTreeHead = m_ScopeTreeRoot = new BuildTimeScopeBlock(null);
}

internal void PushBlock(string nmspc)
{
m_ScopeTreeHead = m_ScopeTreeHead.AddChild(nmspc);
}

internal void PushBlock()
{
m_ScopeTreeHead = m_ScopeTreeHead.AddChild();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ScriptLoadingContext
public Lexer Lexer { get; set; }

public ScriptSyntax Syntax { get; set; }
public Linker Linker { get; set; }

//Compiler state
internal List<Annotation> ChunkAnnotations { get; set; } = new List<Annotation>();
Expand Down
19 changes: 19 additions & 0 deletions src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Text;
using System.Xml.Schema;
using WattleScript.Interpreter.Debugging;
using WattleScript.Interpreter.Tree;

namespace WattleScript.Interpreter.Execution.VM
{
Expand Down Expand Up @@ -421,6 +422,24 @@ public int Emit_MixInit(string mixinName)
return AppendInstruction(new Instruction(OpCode.MixInit, StringArg(mixinName)));
}

public int Emit_PrepNmspc(List<Token> namespaceComponents)
{
if (namespaceComponents == null)
{
throw new InternalErrorException("List of namespace components cannot be null");
}

int parts = 0;

foreach (Token token in namespaceComponents.Where(x => x.Type == TokenType.Name))
{
parts++;
Emit_IndexSet(0, 0, token.Text, true);
}

return AppendInstruction(new Instruction(OpCode.PrepNmspc, parts));
}

public int Emit_NewCall(int argCount, string className)
{
return AppendInstruction(new Instruction(OpCode.NewCall, argCount) {NumValB = (uint) StringArg(className)});
Expand Down
29 changes: 15 additions & 14 deletions src/WattleScript.Interpreter/Execution/VM/OpCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,21 @@ internal enum OpCode
// OOP
// AnnotX instructions, add annotation to table
//NumValB = annotation name string
AnnotI, //NumVal = int
AnnotN, //NumVal = number
AnnotS, //NumVal = string or nil
AnnotB, //NumVal = bool
AnnotT, //pop table from v-stack
LoopChk, //Checks if local in NumVal is < threshold. If not, throw error using NumValB as the class name
BaseChk, //Checks if v-stack top is a class. If not, throw error using NumVal as the base class name
NewCall, //Calls the new() function stored in table at v-stack offset NumVal with NumVal arguments.
//Throws error using class name in NumValB if type check fails
MixInit, //Checks type of mixin on v-stack top, stores init to v-stack + 1, adds functions to v-stack + 2, pops top
//Error check uses NumVal for mixin name
SetFlags, //Sets WattleFieldsInfo for NumVal keys, using modifier in NumVal2 (pops NumVal Items)
MergeFlags, //Merges the WattleFieldsInfo of v-stack(NumVal) into v-stack(NumVal2)
CopyFlags, //Copies the WattleFieldsInfo of v-stack top into v-stack +1, pops 1 value
AnnotI, // NumVal = int
AnnotN, // NumVal = number
AnnotS, // NumVal = string or nil
AnnotB, // NumVal = bool
AnnotT, // pop table from v-stack
LoopChk, // Checks if local in NumVal is < threshold. If not, throw error using NumValB as the class name
BaseChk, // Checks if v-stack top is a class. If not, throw error using NumVal as the base class name
NewCall, // Calls the new() function stored in table at v-stack offset NumVal with NumVal arguments.
// Throws error using class name in NumValB if type check fails
MixInit, // Checks type of mixin on v-stack top, stores init to v-stack + 1, adds functions to v-stack + 2, pops top
// Error check uses NumVal for mixin name
SetFlags, // Sets WattleFieldsInfo for NumVal keys, using modifier in NumVal2 (pops NumVal Items)
MergeFlags, // Merges the WattleFieldsInfo of v-stack(NumVal) into v-stack(NumVal2)
CopyFlags, // Copies the WattleFieldsInfo of v-stack top into v-stack +1, pops 1 value
PrepNmspc, // Pop next NumVal values from v-stack (namespace components), next [todo] NewCall/../.. will use this for namespace resolution. This should cover static class access eg. val = myNamespace.myStaticClass.staticField or myNamespace.myStaticClass.staticMethod()
// Meta
Invalid, // Crashes the executor with an unrecoverable NotImplementedException. This MUST always be the last opcode in enum
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,9 @@ private DynValue Processing_Loop(int instructionPtr, bool canAwait = false)
}
break;
}
case OpCode.PrepNmspc:
ExecPrepNamespace(i);
break;
case OpCode.Invalid:
throw new NotImplementedException($"Invalid opcode {i.OpCode}");
default:
Expand Down Expand Up @@ -661,6 +664,14 @@ private void ExecMergeFlags(Instruction i)
dest.Table.Members.Merge(src.Table.Members);
}
}

private void ExecPrepNamespace(Instruction i)
{
for (int j = 0; j < i.NumVal; j++)
{
DynValue component = m_ValueStack.Pop();
}
}

private void ExecIterPrep()
{
Expand Down
6 changes: 6 additions & 0 deletions src/WattleScript.Interpreter/Loaders/IScriptLoader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

namespace WattleScript.Interpreter.Loaders
{
Expand Down Expand Up @@ -36,5 +37,10 @@ public interface IScriptLoader
/// <param name="globalContext">The global context.</param>
/// <returns></returns>
string ResolveModuleName(string modname, Table globalContext);
/// <summary>
/// Resolves the name of a module imported by "using" statement
/// </summary>
/// <returns></returns>
public Func<string, Module> UsingResolver { get; set; }
}
}
4 changes: 4 additions & 0 deletions src/WattleScript.Interpreter/Loaders/InvalidScriptLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ internal InvalidScriptLoader(string frameworkname)
@"Loading scripts from files is not automatically supported on {0}.
Please implement your own IScriptLoader (possibly, extending ScriptLoaderBase for easier implementation),
use a preexisting loader like EmbeddedResourcesScriptLoader or UnityAssetsScriptLoader or load scripts from strings.", frameworkname);

UsingResolver = s => throw new PlatformNotSupportedException(m_Error);
}

public object LoadFile(string file, Table globalContext)
Expand All @@ -31,5 +33,7 @@ public string ResolveModuleName(string modname, Table globalContext)
{
throw new PlatformNotSupportedException(m_Error);
}

public Func<string, Module> UsingResolver { get; set; }
}
}
6 changes: 6 additions & 0 deletions src/WattleScript.Interpreter/Loaders/ScriptLoaderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ public virtual string ResolveModuleName(string modname, Table globalContext)
return ResolveModuleName(modname, this.ModulePaths);
}

/// <summary>
/// Resolves the name of a module imported by "using" statement
/// </summary>
/// <returns></returns>
public Func<string, Module> UsingResolver { get; set; }

/// <summary>
/// Gets or sets the modules paths used by the "require" function. If null, the default paths are used (using
/// environment variables etc.).
Expand Down
3 changes: 1 addition & 2 deletions src/WattleScript.Interpreter/Script.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,14 @@ public Script()
/// <param name="coreModules">The core modules to be pre-registered in the default global table.</param>
public Script(CoreModules coreModules)
{
Options = new ScriptOptions(DefaultOptions);
Options = new ScriptOptions(DefaultOptions) { CoreModules = coreModules };
PerformanceStats = new PerformanceStatistics();
Registry = new Table(this);

m_MainProcessor = new Processor(this, m_GlobalTable);
m_GlobalTable = new Table(this).RegisterCoreModules(coreModules);
}


/// <summary>
/// Gets or sets the script loader which will be used as the value of the
/// ScriptLoader property for all newly created scripts.
Expand Down
5 changes: 5 additions & 0 deletions src/WattleScript.Interpreter/ScriptOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,5 +152,10 @@ public enum ParserErrorModes
/// If set to 0, this property is ignored (no limit is applied).
/// </summary>
public ulong InstructionLimit { get; set; } = 0;

/// <summary>
/// Gets/sets CoreModules used in script.
/// </summary>
internal CoreModules CoreModules { get; set; }
}
}
32 changes: 24 additions & 8 deletions src/WattleScript.Interpreter/Tree/Expressions/NewExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,30 @@ class NewExpression : Expression
private SymbolRefExpression classRef;
private List<Expression> arguments;
private string className;
private List<Token> namespaceQualifier = null;
private bool referencesNamespace => namespaceQualifier != null;

public NewExpression(ScriptLoadingContext lcontext) : base(lcontext)
{
lcontext.Lexer.Next(); //lexer at "new" token
var classTok = CheckTokenType(lcontext, TokenType.Name);
className = classTok.Text;
CheckTokenType(lcontext, TokenType.Brk_Open_Round);
if (lcontext.Lexer.Current.Type == TokenType.Brk_Close_Round)
{
arguments = new List<Expression>();
}
else

if (lcontext.Lexer.Current.Type == TokenType.Dot) // possible namespace qualifier
{
arguments = ExprList(lcontext);
namespaceQualifier = ParseNamespace(lcontext, true);
namespaceQualifier.Insert(0, classTok);

if (namespaceQualifier.Count < 2) // at least ident-dot
{
throw new SyntaxErrorException(namespaceQualifier[namespaceQualifier.Count - 1], $"Unexpected token '{namespaceQualifier[namespaceQualifier.Count - 1].Text}' while parsing namespace in 'new' expresson");
}

classTok = CheckTokenType(lcontext, TokenType.Name);
}

className = classTok.Text;
CheckTokenType(lcontext, TokenType.Brk_Open_Round);
arguments = lcontext.Lexer.Current.Type == TokenType.Brk_Close_Round ? new List<Expression>() : ExprList(lcontext);
var end = CheckTokenType(lcontext, TokenType.Brk_Close_Round);
SourceRef = classTok.GetSourceRef(end);
}
Expand All @@ -38,6 +47,13 @@ public override void Compile(FunctionBuilder bc)
classRef.Compile(bc);
foreach(var a in arguments)
a.CompilePossibleLiteral(bc);

if (referencesNamespace)
{
// [todo] update indexing for fully qualified access
// bc.Emit_PrepNmspc(namespaceQualifier);
}

bc.Emit_NewCall(arguments.Count, className);
bc.PopSourceRef();
}
Expand Down
Loading