Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linker #61

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
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