Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace WattleScript.Interpreter
{
[Flags]
internal enum MemberModifierFlags
public enum MemberModifierFlags
{
None = 0,
Static = 1
Expand Down
6 changes: 6 additions & 0 deletions src/WattleScript.Interpreter/DataTypes/Table.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public Table(Script owner, params DynValue[] arrayValues)
/// This is only for metadata purposes, and does not affect execution.
/// </summary>
public TableKind Kind { get; set; }

/// <summary>
/// Gets/sets the modifiers of table.
/// This is only for metadata purposes, and does not affect execution.
/// </summary>
public MemberModifierFlags ModifierFlags { get; set; }

/// <summary>
/// Removes all items from the Table.
Expand Down
26 changes: 26 additions & 0 deletions src/WattleScript.Interpreter/Errors/ScriptRuntimeException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ public static ScriptRuntimeException NotAClass(string name, DynValue value)
throw new ScriptRuntimeException($"'{name}' is invalid class (expected class, got {ExtTypeStr(value)})");
}

/// <summary>
/// Creates a ScriptRuntimeException with a predefined error message specifying that
/// a specified value is not a class
/// </summary>
/// <param name="modifier">The incompatible modifier</param>
/// <param name="name">The name the value was indexed from</param>
/// <param name="value">The referencing type</param>
/// <returns>The exception to be raised</returns>
public static ScriptRuntimeException NewCallIncompatibleModifier(string modifier, string name, DynValue value)
{
throw new ScriptRuntimeException($"{modifier} {ExtTypeStr(value)} '{name}' cannot be instantiated.");
}

/// <summary>
/// Creates a ScriptRuntimeException with a predefined error message specifying that
/// a specified value is not a class
/// </summary>
/// <param name="modifier">The incompatible modifier</param>
/// <param name="ancestorName">The name of child class</param>
/// <param name="descendantName">The name of parent class</param>
/// <returns>The exception to be raised</returns>
public static ScriptRuntimeException BaseInvalidModifier(string modifier, string ancestorName, string descendantName)
{
throw new ScriptRuntimeException($"class '{descendantName}' cannot inherit from {modifier} class '{ancestorName}'.");
}

/// <summary>
/// Creates a ScriptRuntimeException with a predefined error message specifying that
/// a specified value is not a mixin
Expand Down
11 changes: 6 additions & 5 deletions src/WattleScript.Interpreter/Execution/InstructionFieldUsage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
case OpCode.PushNil:
case OpCode.PushTrue:
case OpCode.PushFalse:
case OpCode.SetMetaTab:
return InstructionFieldUsage.None;
case OpCode.Pop:
case OpCode.Copy:
Expand Down Expand Up @@ -68,7 +67,6 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
case OpCode.CopyValue:
case OpCode.JLclInit:
case OpCode.Args:
case OpCode.TabProps:
case OpCode.TblInitN:
return InstructionFieldUsage.NumVal | InstructionFieldUsage.NumVal2;
case OpCode.Local:
Expand All @@ -81,7 +79,8 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
case OpCode.StoreUpv:
case OpCode.NewRange:
case OpCode.TblInitI:
return InstructionFieldUsage.NumVal3 | InstructionFieldUsage.NumVal | InstructionFieldUsage.NumVal2;
case OpCode.TabProps:
return InstructionFieldUsage.NumVal | InstructionFieldUsage.NumVal2 | InstructionFieldUsage.NumVal3;
case OpCode.Index:
case OpCode.IndexL:
case OpCode.IndexN:
Expand All @@ -93,9 +92,10 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
case OpCode.Invalid:
case OpCode.MixInit:
case OpCode.PushString:
case OpCode.PushInt:
return InstructionFieldUsage.NumVal;
case OpCode.PushNumber:
case OpCode.PushInt:
case OpCode.BaseChk:
case OpCode.SetMetaTab:
return InstructionFieldUsage.NumVal;
case OpCode.Call:
case OpCode.ThisCall:
Expand All @@ -111,6 +111,7 @@ internal static InstructionFieldUsage GetFieldUsage(this OpCode op)
case OpCode.AnnotB:
case OpCode.AnnotS:
case OpCode.AnnotN:
case OpCode.LoopChk:
return InstructionFieldUsage.NumVal | InstructionFieldUsage.NumValB;
case OpCode.AnnotT:
return InstructionFieldUsage.NumValB;
Expand Down
9 changes: 7 additions & 2 deletions src/WattleScript.Interpreter/Execution/VM/FunctionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ public int Emit_Invalid(string type)
return AppendInstruction(new Instruction(OpCode.Invalid));
}

public int Emit_TabProps(TableKind kind, bool isReadOnly)
public int Emit_TabProps(TableKind kind, MemberModifierFlags flags, bool isReadOnly)
{
return AppendInstruction(new Instruction(OpCode.TabProps, (int) kind, isReadOnly ? 1 : 0));
return AppendInstruction(new Instruction(OpCode.TabProps, (int) kind, (int)flags, isReadOnly ? (uint)1 : 0));
}


Expand Down Expand Up @@ -571,6 +571,11 @@ public int Emit_SetMetaTab()
{
return AppendInstruction(new Instruction(OpCode.SetMetaTab));
}

public int Emit_SetMetaTab(string name)
{
return AppendInstruction(new Instruction(OpCode.SetMetaTab, StringArg(name)));
}

public int Emit_TblInitI(bool lastpos, int count, int create)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ private DynValue Processing_Loop(int instructionPtr, bool canAwait = false)
ref var top = ref m_ValueStack.Peek();
if (top.Type != DataType.Table) throw new InternalErrorException("v-stack top NOT table");
top.Table.Kind = (TableKind)i.NumVal;
top.Table.ReadOnly = i.NumVal2 != 0;
top.Table.ReadOnly = i.NumVal3 != 0;
top.Table.ModifierFlags = (MemberModifierFlags)i.NumVal2;
break;
}
case OpCode.SetMetaTab:
Expand All @@ -285,6 +286,10 @@ private DynValue Processing_Loop(int instructionPtr, bool canAwait = false)
ref var tab = ref m_ValueStack.Peek();
if (top.Type != DataType.Table) throw new InternalErrorException("v-stack top NOT table");
if (tab.Type != DataType.Table) throw new InternalErrorException("v-stack tab NOT table");
if (top.Table.ModifierFlags.HasFlag(MemberModifierFlags.Static))
{
throw ScriptRuntimeException.BaseInvalidModifier(MemberModifierFlags.Static.ToString().ToLowerInvariant(), top.Table.Get("Name").String ?? "(null)", currentFrame.Function.strings[i.NumVal]);
}
tab.Table.MetaTable = top.Table;
break;
}
Expand Down Expand Up @@ -1603,6 +1608,8 @@ private int ExecNewCall(Instruction i, int instructionPtr, bool canAwait)
if (cls.Type != DataType.Table ||
cls.Table.Kind != TableKind.Class)
throw ScriptRuntimeException.NotAClass(GetString((int) i.NumValB), cls);
if (cls.Table.ModifierFlags.HasFlag(MemberModifierFlags.Static))
throw ScriptRuntimeException.NewCallIncompatibleModifier(MemberModifierFlags.Static.ToString().ToLowerInvariant(), GetString((int) i.NumValB), cls);
cls = cls.Table.Get("new");
return Internal_ExecCall(canAwait, i.NumVal, instructionPtr);
}
Expand Down Expand Up @@ -1788,8 +1795,7 @@ private int ExecIndex(Instruction i, int instructionPtr)

// stack: base - index
bool isNameIndex = i.OpCode == OpCode.IndexN;

bool isMultiIndex = (i.OpCode == OpCode.IndexL);
bool isMultiIndex = i.OpCode == OpCode.IndexL;

string i_str = GetString(i.NumVal);
DynValue originalIdx = i_str != null ? DynValue.NewString(i_str) : m_ValueStack.Pop();
Expand Down
2 changes: 2 additions & 0 deletions src/WattleScript.Interpreter/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using WattleScript.Interpreter.Debugging;

namespace WattleScript.Interpreter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ namespace WattleScript.Interpreter.Tree.Expressions
{
class NewExpression : Expression
{
internal SourceRef SourceRef;

private SymbolRefExpression classRef;
private SourceRef sourceRef;
private List<Expression> arguments;
private string className;

Expand All @@ -28,12 +29,12 @@ public NewExpression(ScriptLoadingContext lcontext) : base(lcontext)
arguments = ExprList(lcontext);
}
var end = CheckTokenType(lcontext, TokenType.Brk_Close_Round);
sourceRef = classTok.GetSourceRef(end);
SourceRef = classTok.GetSourceRef(end);
}

public override void Compile(FunctionBuilder bc)
{
bc.PushSourceRef(sourceRef);
bc.PushSourceRef(SourceRef);
classRef.Compile(bc);
foreach(var a in arguments)
a.CompilePossibleLiteral(bc);
Expand Down
2 changes: 1 addition & 1 deletion src/WattleScript.Interpreter/Tree/GeneratedClosure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ public void ResolveScope(ScriptLoadingContext lcontext, Action<ScriptLoadingCont
{
referenced[s] = new SymbolRefExpression(lcontext, lcontext.Scope.DefineLocal(s));
}
foreach(var e in toResolve) e.ResolveScope(lcontext);
extra?.Invoke(lcontext);
foreach(var e in toResolve) e.ResolveScope(lcontext);
frame = lcontext.Scope.PopFunction();
}

Expand Down
15 changes: 15 additions & 0 deletions src/WattleScript.Interpreter/Tree/Lexer/Token.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using WattleScript.Interpreter.Tree.Statements;

namespace WattleScript.Interpreter.Tree
{
Expand Down Expand Up @@ -128,6 +129,20 @@ public bool IsUnaryOperator()
Type == TokenType.Op_Inc || Type == TokenType.Op_Dec || Type == TokenType.Op_Not;
}

public bool IsMemberModifier()
{
return Type == TokenType.Static;
}

public MemberModifierFlags ToMemberModiferFlag()
{
return Type switch
{
TokenType.Static => MemberModifierFlags.Static,
_ => MemberModifierFlags.None
};
}

public bool IsBinaryOperator()
{
switch (Type)
Expand Down
41 changes: 37 additions & 4 deletions src/WattleScript.Interpreter/Tree/Statement.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using WattleScript.Interpreter.Execution;
using WattleScript.Interpreter.Tree.Expressions;
using WattleScript.Interpreter.Tree.Statements;
Expand Down Expand Up @@ -162,6 +164,8 @@ protected static void ParseAnnotations(ScriptLoadingContext lcontext)
}

private const string ANNOTATION_ERROR = @"annotations may only be applied to function, class, mixin or enum declarations";
private const string UNEXPECTED_MODIFIER_ERROR = "'{0}' modifier{1} can be only applied to class or mixin";
private const string DUPLICATE_MODIFIER_ERROR = "duplicate modifier '{0}' encountered";
static bool AnnotationsAllowed(TokenType type)
{
switch (type)
Expand Down Expand Up @@ -241,6 +245,35 @@ protected static Statement CreateStatement(ScriptLoadingContext lcontext, out bo
return new ClassDefinitionStatement(lcontext);
case TokenType.Mixin:
return new MixinDefinitionStatement(lcontext);
// all member modifier keywords
case TokenType.Static:
lcontext.Lexer.SavePos();
HashSet<string> flags = new HashSet<string>();

while (lcontext.Lexer.Current.IsMemberModifier())
{
if (!flags.Contains(lcontext.Lexer.Current.Text))
{
flags.Add(lcontext.Lexer.Current.Text);
}
else
{
throw new SyntaxErrorException(tkn, string.Format(DUPLICATE_MODIFIER_ERROR, lcontext.Lexer.Current.Text));
}

lcontext.Lexer.Next();
}

Token lastToken = lcontext.Lexer.Current;
lcontext.Lexer.RestorePos();

if (lastToken.Type == TokenType.Class)
{
return new ClassDefinitionStatement(lcontext);
}
// [todo] mixins

throw new SyntaxErrorException(lastToken, string.Format(UNEXPECTED_MODIFIER_ERROR, string.Join(" ", flags), flags.Count > 1 ? "s" : ""));
default:
{
//Check for labels in CLike mode
Expand All @@ -257,11 +290,11 @@ protected static Statement CreateStatement(ScriptLoadingContext lcontext, out bo
lcontext.Lexer.RestorePos();
//Regular expression
Expression exp = Expression.PrimaryExp(lcontext, false);
FunctionCallExpression fnexp = exp as FunctionCallExpression;
if (fnexp != null)
if (exp is FunctionCallExpression fnexp)
return new FunctionCallStatement(lcontext, fnexp);
else
return new AssignmentStatement(lcontext, exp, l);
if (exp is NewExpression ne)
return new NewCallStatement(lcontext, ne);
return new AssignmentStatement(lcontext, exp, l);
}
}
}
Expand Down
Loading