This commit is contained in:
Rohan Singh
2019-01-23 22:52:45 -05:00
parent 3f69ec7987
commit ad50a19057
103 changed files with 5734 additions and 1 deletions

View File

@@ -1,2 +1,6 @@
# SWG-ScriptConverter
Some tool I made in 2015 to convert the original Star Wars Galaxies server scripts to Java.
In 2015 someone asked me to make a tool to convert the original Star Wars Galaxies server scripts to Java. I believe it resulted in [this project](https://github.com/SWG-Source) as they have the scripts in Java form over there. That seems to be a fork though so it's probably in other places too.
The source they have on [their repo](https://github.com/SWG-Source/dsrc/tree/master/sku.0/sys.server/compiled/game/script) is missing all the fun comments that were in the original scripts. I didn't bother preserving them in the output from this tool.
Also I only got this to the point where you could compile the resulting Java source into class files. There is almost certainly a version out there that has additional fixes to this code.

22
ScriptConverter.sln Normal file
View File

@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptConverter", "ScriptConverter\ScriptConverter.csproj", "{3F560E64-ACCE-4520-B3E7-EA6502E20F49}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3F560E64-ACCE-4520-B3E7-EA6502E20F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F560E64-ACCE-4520-B3E7-EA6502E20F49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F560E64-ACCE-4520-B3E7-EA6502E20F49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F560E64-ACCE-4520-B3E7-EA6502E20F49}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

View File

@@ -0,0 +1,24 @@
using System;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Declarations
{
abstract class Declaration
{
public readonly ScriptToken Start;
public readonly ScriptToken End;
protected Declaration(ScriptToken start, ScriptToken end = null)
{
if (start == null)
throw new ArgumentNullException("start");
Start = start;
End = end ?? start;
}
public abstract TDecl Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor);
public abstract void SetParent();
}
}

View File

@@ -0,0 +1,34 @@
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Declarations
{
class FieldDeclaration : Declaration
{
public ScriptType Type { get; private set; }
public string Name { get; private set; }
public Expression Value { get; private set; }
public bool IsConstant { get; private set; }
public bool IsPublic { get; private set; }
public FieldDeclaration(ScriptToken start, ScriptToken end, ScriptType type, string name, Expression value, bool isConst, bool isPublic)
: base(start, end)
{
Type = type;
Name = name;
Value = value;
IsConstant = isConst;
IsPublic = isPublic;
}
public override TDecl Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent()
{
Value.SetParent(null);
}
}
}

View File

@@ -0,0 +1,25 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Declarations
{
class IncludeDeclaration : Declaration
{
public string Name { get; private set; }
public IncludeDeclaration(ScriptToken start, ScriptToken end, string name)
: base(start, end)
{
Name = name;
}
public override TDecl Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent()
{
}
}
}

View File

@@ -0,0 +1,25 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Declarations
{
class InheritsDeclaration : Declaration
{
public string Name { get; private set; }
public InheritsDeclaration(ScriptToken start, ScriptToken end, string name)
: base(start, end)
{
Name = name;
}
public override TDecl Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent()
{
}
}
}

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Ast.Statements;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Declarations
{
class MethodDeclaration : Declaration
{
public class Parameter
{
public ScriptType Type { get; private set; }
public string Name { get; private set; }
public Parameter(ScriptType type, string name)
{
Type = type;
Name = name;
}
}
public ScriptType ReturnType { get; private set; }
public string Name { get; private set; }
public ReadOnlyCollection<Parameter> Parameters { get; private set; }
public BlockStatement Block { get; private set; }
public MethodDeclaration(ScriptToken start, ScriptToken end, ScriptType returnType, string name, List<Parameter> parameters, BlockStatement block)
: base(start, end)
{
ReturnType = returnType;
Name = name;
Parameters = parameters.AsReadOnly();
Block = block;
}
public override TDecl Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent()
{
Block.SetParent(null);
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Ast
{
class Document
{
public ReadOnlyCollection<Declaration> Declarations { get; private set; }
public Document(IEnumerable<Declaration> declarations)
{
Declarations = declarations
.ToList()
.AsReadOnly();
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class ArrayInitializerExpression : Expression
{
public ReadOnlyCollection<Expression> Values { get; private set; }
public ArrayInitializerExpression(ScriptToken start, ScriptToken end, List<Expression> values)
: base(start, end)
{
Values = values.AsReadOnly();
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
foreach (var expression in Values)
{
expression.SetParent(this);
}
}
}
}

View File

@@ -0,0 +1,32 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class BinaryOperatorExpression : Expression
{
public ScriptTokenType Operation { get; private set; }
public Expression Left { get; private set; }
public Expression Right { get; private set; }
public BinaryOperatorExpression(ScriptTokenType op, Expression left, Expression right)
: base(left.Start, right.End)
{
Operation = op;
Left = left;
Right = right;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Left.SetParent(this);
Right.SetParent(this);
}
}
}

View File

@@ -0,0 +1,25 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class BoolExpression : Expression
{
public bool Value { get; private set; }
public BoolExpression(ScriptToken token, bool value)
: base(token)
{
Value = value;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class CallExpression : Expression
{
public Expression Left { get; private set; }
public ReadOnlyCollection<Expression> Parameters { get; private set; }
public CallExpression(ScriptToken end, Expression left, List<Expression> parameters)
: base(left.Start, end)
{
Left = left;
Parameters = parameters.AsReadOnly();
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Left.SetParent(this);
foreach (var expression in Parameters)
{
expression.SetParent(this);
}
}
}
}

View File

@@ -0,0 +1,29 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class CastExpression : Expression
{
public ScriptType Type { get; private set; }
public Expression Value { get; private set; }
public CastExpression(ScriptToken start, ScriptType type, Expression value)
: base(start, value.End)
{
Type = type;
Value = value;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Value.SetParent(this);
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
abstract class Expression
{
public readonly ScriptToken Start;
public readonly ScriptToken End;
public Expression Parent { get; protected set; }
protected Expression(ScriptToken start, ScriptToken end = null)
{
if (start == null)
throw new ArgumentNullException("start");
Start = start;
End = end ?? start;
}
public abstract TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor);
public abstract void SetParent(Expression parent);
}
}

View File

@@ -0,0 +1,29 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class FieldExpression : Expression
{
public Expression Left { get; private set; }
public string Name { get; private set; }
public FieldExpression(Expression left, ScriptToken name)
: base(left.Start, name)
{
Left = left;
Name = name.Contents;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Left.SetParent(this);
}
}
}

View File

@@ -0,0 +1,25 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class IdentifierExpression : Expression
{
public string Name { get; private set; }
public IdentifierExpression(ScriptToken token)
: base(token)
{
Name = token.Contents;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,30 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class IndexerExpression : Expression
{
public Expression Left { get; private set; }
public Expression Index { get; private set; }
public IndexerExpression(ScriptToken end, Expression left, Expression index)
: base(left.Start, end)
{
Left = left;
Index = index;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Left.SetParent(this);
Index.SetParent(this);
}
}
}

View File

@@ -0,0 +1,29 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class InstanceOfExpression : Expression
{
public Expression Left { get; private set; }
public ScriptType Type { get; private set; }
public InstanceOfExpression(ScriptToken end, Expression left, ScriptType type)
: base(left.Start, end)
{
Left = left;
Type = type;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Left.SetParent(this);
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class NewExpression : Expression
{
public string Type { get; private set; }
public bool IsArray { get { return ArrayDimensions > 0; } }
public int ArrayDimensions { get; private set; }
public ReadOnlyCollection<Expression> Parameters { get; private set; }
public ReadOnlyCollection<Expression> Initializer { get; private set; }
public NewExpression(ScriptToken start, ScriptToken end, string type, int arrayDimensions, List<Expression> parameters, List<Expression> initializer = null)
: base(start, end)
{
Type = type;
ArrayDimensions = arrayDimensions;
Parameters = parameters.AsReadOnly();
Initializer = initializer != null ? initializer.AsReadOnly() : null;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
foreach (var expression in Parameters)
{
expression.SetParent(this);
}
if (Initializer != null)
{
foreach (var expression in Initializer)
{
expression.SetParent(this);
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class NullExpression : Expression
{
public NullExpression(ScriptToken token)
: base(token)
{
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,25 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class NumberExpression : Expression
{
public string Value { get; private set; }
public NumberExpression(ScriptToken token)
: base(token)
{
Value = token.Contents;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,29 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class PostfixOperatorExpression : Expression
{
public ScriptTokenType Operation { get; private set; }
public Expression Left { get; private set; }
public PostfixOperatorExpression(ScriptToken end, Expression left)
: base(left.Start, end)
{
Operation = end.Type;
Left = left;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Left.SetParent(this);
}
}
}

View File

@@ -0,0 +1,29 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class PrefixOperatorExpression : Expression
{
public ScriptTokenType Operation { get; private set; }
public Expression Right { get; private set; }
public PrefixOperatorExpression(ScriptToken start, Expression right)
: base(start, right.End)
{
Operation = start.Type;
Right = right;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Right.SetParent(this);
}
}
}

View File

@@ -0,0 +1,27 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Expressions
{
class StringExpression : Expression
{
public string Value { get; private set; }
public bool IsSingleQuote { get; private set; }
public StringExpression(ScriptToken token, bool singleQuote)
: base(token)
{
Value = token.Contents;
IsSingleQuote = singleQuote;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,31 @@
namespace ScriptConverter.Ast.Expressions
{
class TernaryExpression : Expression
{
public Expression Condition { get; private set; }
public Expression TrueExpression { get; private set; }
public Expression FalseExpression { get; private set; }
public TernaryExpression(Expression condition, Expression trueExpression, Expression falseExpression)
: base(condition.Start, falseExpression.End)
{
Condition = condition;
TrueExpression = trueExpression;
FalseExpression = falseExpression;
}
public override TExpr Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Expression parent)
{
Parent = parent;
Condition.SetParent(this);
TrueExpression.SetParent(this);
FalseExpression.SetParent(this);
}
}
}

View File

@@ -0,0 +1,48 @@
using ScriptConverter.Ast.Declarations;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Ast
{
interface IAstVisitor<out TDoc, out TDecl, out TStmt, out TExpr>
{
TDoc Visit(Document document);
TDecl Visit(FieldDeclaration declaration);
TDecl Visit(IncludeDeclaration declaration);
TDecl Visit(InheritsDeclaration declaration);
TDecl Visit(MethodDeclaration declaration);
TStmt Visit(BlockStatement statement);
TStmt Visit(BreakStatement statement);
TStmt Visit(ContinueStatement statement);
TStmt Visit(DoStatement statement);
TStmt Visit(EmptyStatement statement);
TStmt Visit(ForStatement statement);
TStmt Visit(IfStatement statement);
TStmt Visit(NakedStatement statement);
TStmt Visit(ReturnStatement statement);
TStmt Visit(SwitchStatement statement);
TStmt Visit(ThrowStatement statement);
TStmt Visit(TryStatement statement);
TStmt Visit(VariableStatement statement);
TStmt Visit(WhileStatement statement);
TExpr Visit(ArrayInitializerExpression expression);
TExpr Visit(BinaryOperatorExpression expression);
TExpr Visit(BoolExpression expression);
TExpr Visit(CallExpression expression);
TExpr Visit(CastExpression expression);
TExpr Visit(FieldExpression expression);
TExpr Visit(IdentifierExpression expression);
TExpr Visit(IndexerExpression expression);
TExpr Visit(InstanceOfExpression expression);
TExpr Visit(NewExpression expression);
TExpr Visit(NullExpression expression);
TExpr Visit(NumberExpression expression);
TExpr Visit(PostfixOperatorExpression expression);
TExpr Visit(PrefixOperatorExpression expression);
TExpr Visit(StringExpression expression);
TExpr Visit(TernaryExpression expression);
}
}

View File

@@ -0,0 +1,48 @@
using System.Linq;
namespace ScriptConverter.Ast
{
class ScriptType
{
public string Name { get; private set; }
public bool IsArray { get { return ArrayDimensions > 0; } }
public bool IsResizable { get; private set; }
public int ArrayDimensions { get; private set; }
public ScriptType(string name, int arrayDimensions = 0, bool isResizable = false)
{
if (name == "unknown")
name = "Object";
if (name == "string")
name = "String";
if (name == "modifyable_int")
name = "modifiable_int";
if (name == "modifyable _float")
name = "modifiable_float";
if (name == "modifyable_string_id")
name = "modifiable_string_id";
if (isResizable)
arrayDimensions = 1;
Name = name;
IsResizable = isResizable;
ArrayDimensions = arrayDimensions;
}
public override string ToString()
{
if (IsResizable)
return "Vector";
if (!IsArray)
return Name;
return Name + string.Join("", Enumerable.Repeat("[]", ArrayDimensions));
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class BlockStatement : Statement
{
public ReadOnlyCollection<Statement> Statements { get; private set; }
public bool IsSwitch { get; private set; }
public BlockStatement(ScriptToken start, ScriptToken end, List<Statement> statements, bool isSwitch = false)
: base(start, end)
{
if (statements == null)
throw new ArgumentNullException("statements");
Statements = statements.AsReadOnly();
IsSwitch = isSwitch;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
foreach (var statement in Statements)
{
statement.SetParent(this);
}
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class BreakStatement : Statement
{
public BreakStatement(ScriptToken token)
: base(token)
{
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class ContinueStatement : Statement
{
public ContinueStatement(ScriptToken token)
: base(token)
{
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,31 @@
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class DoStatement : Statement
{
public Expression Condition { get; private set; }
public BlockStatement Block { get; private set; }
public DoStatement(ScriptToken start, ScriptToken end, Expression condition, BlockStatement block)
: base(start, end)
{
Condition = condition;
Block = block;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
Condition.SetParent(null);
Block.SetParent(this);
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class EmptyStatement : Statement
{
public EmptyStatement(ScriptToken token)
: base(token)
{
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class ForStatement : Statement
{
public ReadOnlyCollection<Statement> Initializers { get; private set; }
public Expression Condition { get; private set; }
public ReadOnlyCollection<Expression> Increment { get; private set; }
public BlockStatement Block { get; private set; }
public ForStatement(ScriptToken start, ScriptToken end, List<Statement> initializers, Expression condition, List<Expression> increment, BlockStatement block)
: base(start, end)
{
Initializers = initializers != null ? initializers.AsReadOnly() : null;
Condition = condition;
Increment = increment != null ? increment.AsReadOnly() : null;
Block = block;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
if (Initializers != null)
{
foreach (var statement in Initializers)
{
statement.SetParent(this);
}
}
if (Condition != null)
Condition.SetParent(null);
if (Increment != null)
{
foreach (var expression in Increment)
{
expression.SetParent(null);
}
}
Block.SetParent(this);
}
}
}

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class IfStatement : Statement
{
public class Branch
{
public Expression Condition { get; private set; }
public BlockStatement Block { get; private set; }
public Branch(Expression condition, BlockStatement block)
{
Condition = condition;
Block = block;
}
}
public ReadOnlyCollection<Branch> Branches { get; private set; }
public Branch DefaultBranch { get; private set; }
public IfStatement(ScriptToken start, ScriptToken end, List<Branch> branches, Branch defaultBranch)
: base(start, end)
{
Branches = branches.AsReadOnly();
DefaultBranch = defaultBranch;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
foreach (var branch in Branches)
{
branch.Condition.SetParent(null);
branch.Block.SetParent(this);
}
if (DefaultBranch != null)
{
DefaultBranch.Block.SetParent(this);
}
}
}
}

View File

@@ -0,0 +1,27 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Ast.Statements
{
class NakedStatement : Statement
{
public Expression Expression { get; private set; }
public NakedStatement(Expression expression)
: base(expression.Start, expression.End)
{
Expression = expression;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
Expression.SetParent(null);
}
}
}

View File

@@ -0,0 +1,29 @@
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class ReturnStatement : Statement
{
public Expression Value { get; private set; }
public ReturnStatement(ScriptToken start, ScriptToken end, Expression value)
: base(start, end)
{
Value = value;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
if (Value != null)
Value.SetParent(null);
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
abstract class Statement
{
public readonly ScriptToken Start;
public readonly ScriptToken End;
public Statement Parent { get; protected set; }
protected Statement(ScriptToken start, ScriptToken end = null)
{
if (start == null)
throw new ArgumentNullException("start");
Start = start;
End = end ?? start;
}
public abstract TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor);
public abstract void SetParent(Statement parent);
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class SwitchStatement : Statement
{
public class Branch
{
public ReadOnlyCollection<Expression> Conditions { get; private set; }
public BlockStatement Block { get; private set; }
public Branch(List<Expression> conditions, BlockStatement block)
{
Conditions = conditions.AsReadOnly();
Block = block;
}
}
public Expression Expression { get; private set; }
public ReadOnlyCollection<Branch> Branches { get; private set; }
public SwitchStatement(ScriptToken start, ScriptToken end, Expression expression, List<Branch> branches)
: base(start, end)
{
Expression = expression;
Branches = branches.AsReadOnly();
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
Expression.SetParent(null);
foreach (var branch in Branches)
{
foreach (var expression in branch.Conditions)
{
if (expression != null)
expression.SetParent(null);
}
branch.Block.SetParent(this);
}
}
}
}

View File

@@ -0,0 +1,28 @@
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class ThrowStatement : Statement
{
public Expression Value { get; private set; }
public ThrowStatement(ScriptToken start, Expression value)
: base(start, value.End)
{
Value = value;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
Value.SetParent(null);
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class TryStatement : Statement
{
public class Branch
{
public ScriptType Type { get; private set; }
public string Name { get; private set; }
public BlockStatement Block { get; private set; }
public Branch(ScriptType type, string name, BlockStatement block)
{
Type = type;
Name = name;
Block = block;
}
}
public BlockStatement Block { get; private set; }
public ReadOnlyCollection<Branch> Branches { get; private set; }
public TryStatement(ScriptToken start, ScriptToken end, BlockStatement block, List<Branch> branches) : base(start, end)
{
if (branches.Count < 1)
throw new ArgumentException("need at least 1 branch", "branches");
Block = block;
Branches = branches.AsReadOnly();
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
Block.SetParent(this);
foreach (var branch in Branches)
{
branch.Block.SetParent(this);
}
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class VariableStatement : Statement
{
public class Definition
{
public ScriptType Type { get; private set; }
public string Name { get; private set; }
public Expression Value { get; private set; }
public Definition(ScriptType type, string name, Expression value)
{
Type = type;
Name = name;
Value = value;
}
}
public ScriptType BaseType { get; private set; }
public bool Final { get; private set; }
public ReadOnlyCollection<Definition> Definitions { get; private set; }
public VariableStatement(ScriptToken start, ScriptToken end, ScriptType baseType, bool final, List<Definition> definitions)
: base(start, end)
{
if (definitions.Count < 1)
throw new ArgumentException("need at least 1 definition", "definitions");
BaseType = baseType;
Final = final;
Definitions = definitions.AsReadOnly();
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
foreach (var definition in Definitions)
{
if (definition.Value != null)
definition.Value.SetParent(null);
}
}
}
}

View File

@@ -0,0 +1,31 @@
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Parser;
namespace ScriptConverter.Ast.Statements
{
class WhileStatement : Statement
{
public Expression Condition { get; private set; }
public BlockStatement Block { get; private set; }
public WhileStatement(ScriptToken start, ScriptToken end, Expression condition, BlockStatement block)
: base(start, end)
{
Condition = condition;
Block = block;
}
public override TStmt Accept<TDoc, TDecl, TStmt, TExpr>(IAstVisitor<TDoc, TDecl, TStmt, TExpr> visitor)
{
return visitor.Visit(this);
}
public override void SetParent(Statement parent)
{
Parent = parent;
Condition.SetParent(null);
Block.SetParent(this);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,406 @@
/*
* Copyright 2007-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace JetBrains.Annotations
{
/// <summary>
/// Indicates that marked element should be localized or not.
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public sealed class LocalizationRequiredAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationRequiredAttribute"/> class.
/// </summary>
/// <param name="required"><c>true</c> if a element should be localized; otherwise, <c>false</c>.</param>
public LocalizationRequiredAttribute(bool required)
{
Required = required;
}
/// <summary>
/// Gets a value indicating whether a element should be localized.
/// <value><c>true</c> if a element should be localized; otherwise, <c>false</c>.</value>
/// </summary>
public bool Required { get; set; }
/// <summary>
/// Returns whether the value of the given object is equal to the current <see cref="LocalizationRequiredAttribute"/>.
/// </summary>
/// <param name="obj">The object to test the value equality of. </param>
/// <returns>
/// <c>true</c> if the value of the given object is equal to that of the current; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
var attribute = obj as LocalizationRequiredAttribute;
return attribute != null && attribute.Required == Required;
}
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>A hash code for the current <see cref="LocalizationRequiredAttribute"/>.</returns>
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// Indicates that marked method builds string by format pattern and (optional) arguments.
/// Parameter, which contains format string, should be given in constructor.
/// The format string should be in <see cref="string.Format(IFormatProvider,string,object[])"/> -like form
/// </summary>
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class StringFormatMethodAttribute : Attribute
{
private readonly string myFormatParameterName;
/// <summary>
/// Initializes new instance of StringFormatMethodAttribute
/// </summary>
/// <param name="formatParameterName">Specifies which parameter of an annotated method should be treated as format-string</param>
public StringFormatMethodAttribute(string formatParameterName)
{
myFormatParameterName = formatParameterName;
}
/// <summary>
/// Gets format parameter name
/// </summary>
public string FormatParameterName
{
get { return myFormatParameterName; }
}
}
/// <summary>
/// Indicates that the function argument should be string literal and match one of the parameters of the caller function.
/// For example, <see cref="ArgumentNullException"/> has such parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class InvokerParameterNameAttribute : Attribute
{
}
/// <summary>
/// Indicates that the marked method is assertion method, i.e. it halts control flow if one of the conditions is satisfied.
/// To set the condition, mark one of the parameters with <see cref="AssertionConditionAttribute"/> attribute
/// </summary>
/// <seealso cref="AssertionConditionAttribute"/>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class AssertionMethodAttribute : Attribute
{
}
/// <summary>
/// Indicates the condition parameter of the assertion method.
/// The method itself should be marked by <see cref="AssertionMethodAttribute"/> attribute.
/// The mandatory argument of the attribute is the assertion type.
/// </summary>
/// <seealso cref="AssertionConditionType"/>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class AssertionConditionAttribute : Attribute
{
private readonly AssertionConditionType myConditionType;
/// <summary>
/// Initializes new instance of AssertionConditionAttribute
/// </summary>
/// <param name="conditionType">Specifies condition type</param>
public AssertionConditionAttribute(AssertionConditionType conditionType)
{
myConditionType = conditionType;
}
/// <summary>
/// Gets condition type
/// </summary>
public AssertionConditionType ConditionType
{
get { return myConditionType; }
}
}
/// <summary>
/// Specifies assertion type. If the assertion method argument satisifes the condition, then the execution continues.
/// Otherwise, execution is assumed to be halted
/// </summary>
public enum AssertionConditionType
{
/// <summary>
/// Indicates that the marked parameter should be evaluated to true
/// </summary>
IS_TRUE = 0,
/// <summary>
/// Indicates that the marked parameter should be evaluated to false
/// </summary>
IS_FALSE = 1,
/// <summary>
/// Indicates that the marked parameter should be evaluated to null value
/// </summary>
IS_NULL = 2,
/// <summary>
/// Indicates that the marked parameter should be evaluated to not null value
/// </summary>
IS_NOT_NULL = 3,
}
/// <summary>
/// Indicates that the marked method unconditionally terminates control flow execution.
/// For example, it could unconditionally throw exception
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class TerminatesProgramAttribute : Attribute
{
}
/// <summary>
/// Indicates that the value of marked element could be <c>null</c> sometimes, so the check for <c>null</c> is necessary before its usage
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class CanBeNullAttribute : Attribute
{
}
/// <summary>
/// Indicates that the value of marked element could never be <c>null</c>
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class NotNullAttribute : Attribute
{
}
/// <summary>
/// Indicates that the value of marked type (or its derivatives) cannot be compared using '==' or '!=' operators.
/// There is only exception to compare with <c>null</c>, it is permitted
/// </summary>
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public sealed class CannotApplyEqualityOperatorAttribute : Attribute
{
}
/// <summary>
/// When applied to target attribute, specifies a requirement for any type which is marked with
/// target attribute to implement or inherit specific type or types
/// </summary>
/// <example>
/// <code>
/// [BaseTypeRequired(typeof(IComponent)] // Specify requirement
/// public class ComponentAttribute : Attribute
/// {}
///
/// [Component] // ComponentAttribute requires implementing IComponent interface
/// public class MyComponent : IComponent
/// {}
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
[BaseTypeRequired(typeof(Attribute))]
public sealed class BaseTypeRequiredAttribute : Attribute
{
private readonly Type[] myBaseTypes;
/// <summary>
/// Initializes new instance of BaseTypeRequiredAttribute
/// </summary>
/// <param name="baseType">Specifies which types are required</param>
public BaseTypeRequiredAttribute(Type baseType)
{
myBaseTypes = new[] { baseType };
}
/// <summary>
/// Gets enumerations of specified base types
/// </summary>
public IEnumerable<Type> BaseTypes
{
get { return myBaseTypes; }
}
}
/// <summary>
/// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library),
/// so this symbol will not be marked as unused (as well as by other usage inspections)
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public sealed class UsedImplicitlyAttribute : Attribute
{
[UsedImplicitly]
public UsedImplicitlyAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
{
}
[UsedImplicitly]
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
[UsedImplicitly]
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default)
{
}
[UsedImplicitly]
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags)
{
}
[UsedImplicitly]
public ImplicitUseKindFlags UseKindFlags { get; private set; }
/// <summary>
/// Gets value indicating what is meant to be used
/// </summary>
[UsedImplicitly]
public ImplicitUseTargetFlags TargetFlags { get; private set; }
}
/// <summary>
/// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes as unused (as well as by other usage inspections)
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class MeansImplicitUseAttribute : Attribute
{
[UsedImplicitly]
public MeansImplicitUseAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default)
{
}
[UsedImplicitly]
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
[UsedImplicitly]
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default)
{
}
[UsedImplicitly]
public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags)
{
}
[UsedImplicitly]
public ImplicitUseKindFlags UseKindFlags { get; private set; }
/// <summary>
/// Gets value indicating what is meant to be used
/// </summary>
[UsedImplicitly]
public ImplicitUseTargetFlags TargetFlags { get; private set; }
}
[Flags]
public enum ImplicitUseKindFlags
{
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
/// <summary>
/// Only entity marked with attribute considered used
/// </summary>
Access = 1,
/// <summary>
/// Indicates implicit assignment to a member
/// </summary>
Assign = 2,
/// <summary>
/// Indicates implicit instantiation of a type with fixed constructor signature.
/// That means any unused constructor parameters won't be reported as such.
/// </summary>
InstantiatedWithFixedConstructorSignature = 4,
/// <summary>
/// Indicates implicit instantiation of a type
/// </summary>
InstantiatedNoFixedConstructorSignature = 8,
}
/// <summary>
/// Specify what is considered used implicitly when marked with <see cref="MeansImplicitUseAttribute"/> or <see cref="UsedImplicitlyAttribute"/>
/// </summary>
[Flags]
public enum ImplicitUseTargetFlags
{
Default = Itself,
Itself = 1,
/// <summary>
/// Members of entity marked with attribute are considered used
/// </summary>
Members = 2,
/// <summary>
/// Entity marked with attribute and all its members considered used
/// </summary>
WithMembers = Itself | Members
}
/// <summary>
/// This attribute is intended to mark publicly available API which should not be removed and so is treated as used.
/// </summary>
[MeansImplicitUse]
public sealed class PublicAPIAttribute : Attribute
{
public PublicAPIAttribute()
{
}
// ReSharper disable UnusedParameter.Local
public PublicAPIAttribute(string comment)
// ReSharper restore UnusedParameter.Local
{
}
}
/// <summary>
/// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack.
/// If the parameter is delegate, indicates that delegate is executed while the method is executed.
/// If the parameter is enumerable, indicates that it is enumerated while the method is executed.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, Inherited = true)]
public sealed class InstantHandleAttribute : Attribute
{
}
/// <summary>
/// Indicates that method doesn't contain observable side effects.
/// The same as <see cref="System.Diagnostics.Contracts.PureAttribute"/>
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public sealed class PureAttribute : Attribute { }
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
namespace ScriptConverter
{
public class GenericComparer<T> : IComparer<T>
{
private Func<T, T, int> _comparer;
public GenericComparer(Func<T, T, int> comparer)
{
_comparer = comparer;
}
public int Compare(T x, T y)
{
return _comparer(x, y);
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Globalization;
using System.IO;
using System.Text;
namespace ScriptConverter
{
public class IndentTextWriter : TextWriter
{
private readonly TextWriter _writer;
private readonly string _indentStr;
private bool _shouldIndent;
public int Indent { get; set; }
public IndentTextWriter(TextWriter writer, string indentStr = " ")
{
_writer = writer;
_indentStr = indentStr;
_shouldIndent = false;
}
public override Encoding Encoding
{
get { return Encoding.Unicode; }
}
public override IFormatProvider FormatProvider
{
get { return CultureInfo.InvariantCulture; }
}
public override void Write(char value)
{
if (_shouldIndent)
{
_shouldIndent = false; // shouldIndent must be cleared first
WriteIndent();
}
_writer.Write(value);
}
public override void WriteLine()
{
base.WriteLine();
_shouldIndent = true; // defer indenting until the next Write
}
public override void WriteLine(string value)
{
Write(value);
WriteLine();
}
public void WriteIndent()
{
for (var i = 0; i < Indent; i++)
Write(_indentStr);
}
}
}

View File

@@ -0,0 +1,13 @@
namespace ScriptConverter.Parser
{
static class CompilerError
{
public const string UnterminatedString = "Unterminated string";
public const string UnexpectedEofString = "Unexpected end of file (bad escape sequence)";
public const string InvalidEscapeSequence = "Invalid escape sequence '{0}'";
public const string CharLiteralLength = "Character literals must be one byte long";
public const string InvalidNumber = "Invalid {0} number '{1}'";
public const string ExpectedButFound = "Expected {0} but found {1}";
}
}

View File

@@ -0,0 +1,21 @@
using System;
using JetBrains.Annotations;
namespace ScriptConverter.Parser
{
public class CompilerException : Exception
{
protected CompilerException(string message)
: base(message)
{
}
[StringFormatMethod("format")]
internal CompilerException(ScriptToken token, string format, params object[] args)
: base(string.Format("{0}({1}): {2}", token.FileName ?? "null", token.RangeString, string.Format(format, args)))
{
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace ScriptConverter.Parser
{
abstract partial class Lexer<TToken, TTokenType>
{
protected static TToken EofToken;
static Lexer()
{
EofToken = null;
}
protected class OperatorDictionary<T> : IEnumerable<object>
{
private readonly GenericComparer<Tuple<string, T>> _comparer;
private Dictionary<char, List<Tuple<string, T>>> _operatorDictionary;
public OperatorDictionary()
{
_comparer = new GenericComparer<Tuple<string, T>>((a, b) => b.Item1.Length - a.Item1.Length);
_operatorDictionary = new Dictionary<char, List<Tuple<string, T>>>();
}
public void Add(string op, T type)
{
List<Tuple<string, T>> list;
if (!_operatorDictionary.TryGetValue(op[0], out list))
{
list = new List<Tuple<string, T>>();
_operatorDictionary.Add(op[0], list);
}
list.Add(Tuple.Create(op, type));
list.Sort(_comparer);
}
public IEnumerable<Tuple<string, T>> Lookup(char ch)
{
List<Tuple<string, T>> list;
if (!_operatorDictionary.TryGetValue(ch, out list))
return null;
return list;
}
public IEnumerator<object> GetEnumerator()
{
throw new NotSupportedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
}

View File

@@ -0,0 +1,224 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
namespace ScriptConverter.Parser
{
abstract partial class Lexer<TToken, TTokenType> : IEnumerable<TToken>
where TToken : Token<TTokenType>
{
protected delegate bool LexerRule(char ch, out TToken token);
private readonly IEnumerable<char> _sourceEnumerable;
private IEnumerator<char> _source;
private int _length;
private List<char> _read;
private int _index;
protected string FileName { get; private set; }
protected int Line { get; private set; }
protected int Column { get; private set; }
protected int StartLine { get; private set; }
protected int StartColumn { get; private set; }
protected List<LexerRule> Rules { get; set; }
protected bool AtEof
{
get { return _index >= _length; }
}
protected Lexer(IEnumerable<char> source, string fileName = null)
{
if (source == null)
throw new ArgumentNullException("source");
FileName = fileName;
_sourceEnumerable = source;
}
public IEnumerator<TToken> GetEnumerator()
{
if (Rules == null)
throw new Exception("Rules must be set");
if (EofToken == null)
throw new Exception("EofToken must be set");
_length = int.MaxValue;
_source = _sourceEnumerable.GetEnumerator();
_read = new List<char>(16);
_index = 0;
Line = 1;
Column = 1;
while (_index < _length)
{
SkipWhiteSpace();
if (SkipComment())
continue;
if (_index >= _length)
break;
StartLine = Line;
StartColumn = Column;
var ch = PeekChar();
TToken token = null;
if (!Rules.Any(rule => rule(ch, out token)))
throw Error("Unexpected character '{0}'", ch);
yield return token;
}
while (true)
yield return EofToken;
}
protected virtual bool SkipComment()
{
return false;
}
protected void SkipWhiteSpace()
{
while (_index < _length)
{
var ch = PeekChar();
if (!char.IsWhiteSpace(ch))
break;
TakeChar();
}
}
protected bool TakeIfNext(string value)
{
if (!IsNext(value))
return false;
for (var i = 0; i < value.Length; i++)
TakeChar();
return true;
}
protected bool IsNext(string value)
{
if (_index + value.Length > _length)
return false;
return !value.Where((t, i) => PeekChar(i) != t).Any();
}
protected string TakeWhile(Func<char, bool> condition)
{
var sb = new StringBuilder();
while (_index < _length)
{
var ch = PeekChar();
if (!condition(ch))
break;
sb.Append(TakeChar());
}
return sb.ToString();
}
protected char TakeChar()
{
var result = TakeCharImpl();
if (result == '\n')
{
Line++;
Column = 0;
}
if (result == '\r')
{
if (PeekChar() == '\n')
TakeCharImpl();
Line++;
Column = 0;
}
Column++;
return result;
}
private char TakeCharImpl()
{
PeekChar();
var result = _read[0];
_read.RemoveAt(0);
_index++;
return result;
}
protected string PeekString(int length)
{
if (length <= 0)
throw new ArgumentOutOfRangeException("length", "distance must be at least 1");
var sb = new StringBuilder(length);
for (var i = 0; i < length; i++)
{
sb.Append(PeekChar(i));
}
return sb.ToString();
}
protected char PeekChar(int distance = 0)
{
if (distance < 0)
throw new ArgumentOutOfRangeException("distance", "distance can't be negative");
while (_read.Count <= distance)
{
var success = _source.MoveNext();
_read.Add(success ? _source.Current : '\0');
if (!success)
_length = _index + _read.Count - 1;
}
return _read[distance];
}
[StringFormatMethod("format")]
protected Exception Error(string format, params object[] args)
{
return new LexerException(FileName, new SourcePosition(Line, Column), format, args);
}
[StringFormatMethod("format")]
protected Exception ErrorStart(string format, params object[] args)
{
return new LexerException(FileName, new SourcePosition(StartLine, StartColumn), format, args);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using JetBrains.Annotations;
namespace ScriptConverter.Parser
{
class LexerException : Exception
{
[StringFormatMethod("format")]
internal LexerException(string fileName, SourcePosition pos, string format, params object[] args)
: base(string.Format("{0}({1}): {2}", fileName ?? "null", pos, string.Format(format, args)))
{
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Ast;
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Parser.Parselets.Declarations
{
class ConstParselet : IDeclarationParselet
{
public Declaration Parse(ScriptParser parser, ScriptToken token)
{
ScriptType type;
string name;
parser.ParseNamedType(out type, out name);
parser.Take(ScriptTokenType.Assign);
var value = parser.ParseExpression();
parser.Take(ScriptTokenType.Semicolon);
return new FieldDeclaration(token, parser.Previous, type, name, value, true, true);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Linq;
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Parser.Parselets.Declarations
{
class IncludeParselet : IDeclarationParselet
{
public Declaration Parse(ScriptParser parser, ScriptToken token)
{
var done = false;
var tokens = parser.ParseSeparatedBy(ScriptTokenType.Dot, (_, first) =>
{
if (done)
return null;
if (parser.Match(ScriptTokenType.Multiply))
{
done = true;
return parser.Take(ScriptTokenType.Multiply);
}
return parser.Take(ScriptTokenType.Identifier);
});
var name = string.Join(".", tokens.Select(t => t.Contents));
if (!parser.MatchAndTake(ScriptTokenType.Semicolon))
Console.WriteLine("JFjngkjasnholjhnaskl");
return new IncludeDeclaration(token, parser.Previous, name);
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Linq;
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Parser.Parselets.Declarations
{
class InheritsParselet : IDeclarationParselet
{
public Declaration Parse(ScriptParser parser, ScriptToken token)
{
var tokens = parser.ParseSeparatedBy(ScriptTokenType.Dot, (_, first) => parser.Take(ScriptTokenType.Identifier));
var name = string.Join(".", tokens.Select(t => t.Contents));
if (!parser.MatchAndTake(ScriptTokenType.Semicolon))
Console.WriteLine("fkshgnbkashnkhnasb");
return new InheritsDeclaration(token, parser.Previous, name);
}
}
}

View File

@@ -0,0 +1,43 @@
using System.Linq;
using ScriptConverter.Ast;
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Parser.Parselets.Declarations
{
class MethodParselet : IDeclarationParselet
{
public Declaration Parse(ScriptParser parser, ScriptToken token)
{
var returnType = parser.ParseType(token);
var name = parser.Take(ScriptTokenType.Identifier).Contents;
if (parser.MatchAndTake(ScriptTokenType.Assign))
{
var value = parser.ParseExpression();
parser.Take(ScriptTokenType.Semicolon);
return new FieldDeclaration(token, parser.Previous, returnType, name, value, false, false);
}
parser.Take(ScriptTokenType.LeftParen);
var parameters = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
if (parser.Match(ScriptTokenType.RightParen))
return null;
ScriptType paramType;
string paramName;
parser.ParseNamedType(out paramType, out paramName);
return new MethodDeclaration.Parameter(paramType, paramName);
}).ToList();
parser.Take(ScriptTokenType.RightParen);
var block = parser.ParseBlock(false);
return new MethodDeclaration(token, parser.Previous, returnType, name, parameters, block);
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Ast;
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Parser.Parselets.Declarations
{
class PublicParselet : IDeclarationParselet
{
public Declaration Parse(ScriptParser parser, ScriptToken token)
{
ScriptType type;
string name;
parser.ParseNamedType(out type, out name);
parser.Take(ScriptTokenType.Assign);
var value = parser.ParseExpression();
parser.Take(ScriptTokenType.Semicolon);
return new FieldDeclaration(token, parser.Previous, type, name, value, false, true);
}
}
}

View File

@@ -0,0 +1,22 @@
using System.Linq;
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class ArrayInitializerParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
var values = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
if (parser.Match(ScriptTokenType.RightBrace))
return null;
return parser.ParseExpression();
}).ToList();
parser.Take(ScriptTokenType.RightBrace);
return new ArrayInitializerExpression(token, parser.Previous, values);
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class BinaryOperatorParselet : IInfixParselet
{
private readonly bool _isRight;
public BinaryOperatorParselet(PrecedenceValue precedence, bool isRight)
{
_isRight = isRight;
Precedence = (int)precedence;
}
public int Precedence { get; private set; }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
var right = parser.ParseExpression(Precedence - (_isRight ? 1 : 0));
return new BinaryOperatorExpression(token.Type, left, right);
}
}
}

View File

@@ -0,0 +1,19 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class BoolParselet : IPrefixParselet
{
private readonly bool _value;
public BoolParselet(bool value)
{
_value = value;
}
public Expression Parse(ScriptParser parser, ScriptToken token)
{
return new BoolExpression(token, _value);
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Linq;
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class CallParselet : IInfixParselet
{
public int Precedence { get { return (int)PrecedenceValue.Suffix; } }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
var parameters = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
if (first && parser.Match(ScriptTokenType.RightParen))
return null;
return parser.ParseExpression();
}).ToList();
parser.Take(ScriptTokenType.RightParen);
return new CallExpression(parser.Previous, left, parameters);
}
}
}

View File

@@ -0,0 +1,15 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class FieldParselet : IInfixParselet
{
public int Precedence { get { return (int)PrecedenceValue.Suffix; } }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
var name = parser.Take(ScriptTokenType.Identifier);
return new FieldExpression(left, name);
}
}
}

View File

@@ -0,0 +1,54 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class GroupParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
if (IsCast(parser))
{
var type = parser.ParseType();
parser.Take(ScriptTokenType.RightParen);
var value = parser.ParseExpression((int)PrecedenceValue.Cast - 1);
return new CastExpression(token, type, value);
}
var expression = parser.ParseExpression();
parser.Take(ScriptTokenType.RightParen);
return expression;
}
private static bool IsCast(ScriptParser parser)
{
var token = parser.Peek();
if (token.Type != ScriptTokenType.Identifier)
return false;
if (token.Contents == "resizeable")
return true;
var i = 1;
while (parser.Match(ScriptTokenType.Dot, i) && parser.Match(ScriptTokenType.Identifier, i + 1))
{
i += 2;
}
var next1Type = parser.Peek(i).Type;
var next2Type = parser.Peek(i + 1).Type;
return (next1Type == ScriptTokenType.RightParen && IsCastable(next2Type)) ||
(next1Type == ScriptTokenType.LeftSquare && next2Type == ScriptTokenType.RightSquare);
}
private static bool IsCastable(ScriptTokenType token)
{
return token == ScriptTokenType.Identifier ||
token == ScriptTokenType.Number ||
token == ScriptTokenType.Null ||
token == ScriptTokenType.LeftParen ||
token == ScriptTokenType.SingleString ||
token == ScriptTokenType.String;
}
}
}

View File

@@ -0,0 +1,86 @@
using System.Linq;
using System.Text;
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class HashParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
var str = parser.Take(ScriptTokenType.String);
var crc = (int)StringtoCrc(str.Contents);
var crcStr = "0x" + crc.ToString("X8");
return new NumberExpression(new ScriptToken(str, ScriptTokenType.Number, crcStr));
}
private static uint StringtoCrc(string input)
{
byte[] bytes = Encoding.ASCII.GetBytes(input.ToArray());
uint crc = 0xFFFFFFFF;
for (int i = 0; i < bytes.Length; ++i)
{
crc = _crcTable[(bytes[i] ^ (crc >> 24)) & 0x000000FF] ^ (crc << 8);
}
return ~crc;
}
private static uint[] _crcTable =
{
0x0000000,
0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B,
0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6,
0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC,
0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F,
0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A,
0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58,
0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033,
0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE,
0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4,
0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0,
0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5,
0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07,
0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C,
0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1,
0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B,
0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698,
0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D,
0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F,
0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34,
0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80,
0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A,
0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629,
0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C,
0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E,
0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65,
0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8,
0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2,
0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71,
0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74,
0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21,
0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A,
0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087,
0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D,
0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE,
0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB,
0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09,
0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662,
0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF,
0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
};
}
}

View File

@@ -0,0 +1,12 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class IdentifierParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
return new IdentifierExpression(token);
}
}
}

View File

@@ -0,0 +1,18 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class IndexerParselet : IInfixParselet
{
public int Precedence { get { return (int)PrecedenceValue.Suffix; } }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
var index = parser.ParseExpression();
parser.Take(ScriptTokenType.RightSquare);
return new IndexerExpression(parser.Previous, left, index);
}
}
}

View File

@@ -0,0 +1,15 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class InstanceOfParselet : IInfixParselet
{
public int Precedence { get { return (int)PrecedenceValue.Relational; } }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
var type = parser.ParseType();
return new InstanceOfExpression(parser.Previous, left, type);
}
}
}

View File

@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class NewParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
var type = string.Join(".", parser.ParseSeparatedBy(ScriptTokenType.Dot, (_, first) => parser.Take(ScriptTokenType.Identifier).Contents));
if (type == "string")
type = "String";
var isArray = parser.Match(ScriptTokenType.LeftSquare);
parser.Take(isArray ? ScriptTokenType.LeftSquare : ScriptTokenType.LeftParen);
var parameters = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
if (first && parser.Match(isArray ? ScriptTokenType.RightSquare : ScriptTokenType.RightParen))
return null;
return parser.ParseExpression();
}).ToList();
parser.Take(isArray ? ScriptTokenType.RightSquare : ScriptTokenType.RightParen);
List<Expression> values = null;
if (isArray && parser.MatchAndTake(ScriptTokenType.LeftBrace))
{
values = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
if (parser.Match(ScriptTokenType.RightBrace))
return null;
return parser.ParseExpression();
}).ToList();
parser.MatchAndTake(ScriptTokenType.RightBrace);
}
int arrayDimensions = isArray ? 1 : 0;
if (isArray)
{
while (parser.Match(ScriptTokenType.LeftSquare) && parser.Match(ScriptTokenType.RightSquare, 1))
{
parser.Take(ScriptTokenType.LeftSquare);
parser.Take(ScriptTokenType.RightSquare);
arrayDimensions++;
}
}
return new NewExpression(token, parser.Previous, type, arrayDimensions, parameters, values);
}
}
}

View File

@@ -0,0 +1,12 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class NullParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
return new NullExpression(token);
}
}
}

View File

@@ -0,0 +1,12 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class NumberParselet : IPrefixParselet
{
public Expression Parse(ScriptParser parser, ScriptToken token)
{
return new NumberExpression(token);
}
}
}

View File

@@ -0,0 +1,19 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class PostfixOperatorParselet : IInfixParselet
{
public PostfixOperatorParselet(PrecedenceValue precedence)
{
Precedence = (int)precedence;
}
public int Precedence { get; private set; }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
return new PostfixOperatorExpression(token, left);
}
}
}

View File

@@ -0,0 +1,20 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class PrefixOperatorParselet : IPrefixParselet
{
private readonly int _precedence;
public PrefixOperatorParselet(PrecedenceValue precedence)
{
_precedence = (int)precedence;
}
public Expression Parse(ScriptParser parser, ScriptToken token)
{
var right = parser.ParseExpression(_precedence);
return new PrefixOperatorExpression(token, right);
}
}
}

View File

@@ -0,0 +1,19 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class StringParselet : IPrefixParselet
{
private readonly bool _singleQuote;
public StringParselet(bool singleQuote)
{
_singleQuote = singleQuote;
}
public Expression Parse(ScriptParser parser, ScriptToken token)
{
return new StringExpression(token, _singleQuote);
}
}
}

View File

@@ -0,0 +1,18 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets.Expressions
{
class TernaryParselet : IInfixParselet
{
public int Precedence { get { return (int)PrecedenceValue.Ternary; } }
public Expression Parse(ScriptParser parser, Expression left, ScriptToken token)
{
var trueExpr = parser.ParseExpression();
parser.Take(ScriptTokenType.Colon);
var falseExpr = parser.ParseExpression();
return new TernaryExpression(left, trueExpr, falseExpr);
}
}
}

View File

@@ -0,0 +1,9 @@
using ScriptConverter.Ast.Declarations;
namespace ScriptConverter.Parser.Parselets
{
interface IDeclarationParselet
{
Declaration Parse(ScriptParser parser, ScriptToken token);
}
}

View File

@@ -0,0 +1,11 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets
{
interface IInfixParselet
{
int Precedence { get; }
Expression Parse(ScriptParser parser, Expression left, ScriptToken token);
}
}

View File

@@ -0,0 +1,9 @@
using ScriptConverter.Ast.Expressions;
namespace ScriptConverter.Parser.Parselets
{
interface IPrefixParselet
{
Expression Parse(ScriptParser parser, ScriptToken token);
}
}

View File

@@ -0,0 +1,9 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets
{
interface IStatementParselet
{
Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon);
}
}

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class BlockParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
var statements = new List<Statement>();
while (!parser.Match(ScriptTokenType.RightBrace))
{
statements.Add(parser.ParseStatement());
}
parser.Take(ScriptTokenType.RightBrace);
return new BlockStatement(token, parser.Previous, statements);
}
}
}

View File

@@ -0,0 +1,13 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class BreakParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
return new BreakStatement(token);
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using ScriptConverter.Ast;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class ConstVariableParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
ScriptType type;
string name;
parser.ParseNamedType(out type, out name);
parser.Take(ScriptTokenType.Assign);
var value = parser.ParseExpression();
var definitions = new List<VariableStatement.Definition>()
{
new VariableStatement.Definition(type, name, value)
};
return new VariableStatement(token, parser.Previous, type, true, definitions);
}
}
}

View File

@@ -0,0 +1,13 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class ContinueParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
return new ContinueStatement(token);
}
}
}

View File

@@ -0,0 +1,23 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class DoParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
var block = parser.ParseBlock();
parser.Take(ScriptTokenType.While);
parser.Take(ScriptTokenType.LeftParen);
var condition = parser.ParseExpression();
parser.Take(ScriptTokenType.RightParen);
return new DoStatement(token, parser.Previous, condition, block);
}
}
}

View File

@@ -0,0 +1,13 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class EmptyParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
return new EmptyStatement(token);
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class ForParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
parser.Take(ScriptTokenType.LeftParen);
List<Statement> initializers = null;
if (!parser.Match(ScriptTokenType.Semicolon))
{
initializers = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
var stmt = parser.ParseStatement(false);
if (!(stmt is VariableStatement) && !(stmt is NakedStatement))
throw new CompilerException(token, "bad for loop initializer");
return stmt;
}).ToList();
}
parser.Take(ScriptTokenType.Semicolon);
Expression condition = null;
if (!parser.Match(ScriptTokenType.Semicolon))
condition = parser.ParseExpression();
parser.Take(ScriptTokenType.Semicolon);
List<Expression> increment = null;
if (!parser.Match(ScriptTokenType.RightParen))
increment = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) => parser.ParseExpression()).ToList();
parser.Take(ScriptTokenType.RightParen);
var block = parser.ParseBlock();
return new ForStatement(token, parser.Previous, initializers, condition, increment, block);
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class IfParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
var first = true;
var branches = new List<IfStatement.Branch>();
IfStatement.Branch defaultBranch = null;
do
{
var isDefaultBranch = !first && !parser.MatchAndTake(ScriptTokenType.If);
first = false;
Expression condition = null;
if (!isDefaultBranch)
{
parser.Take(ScriptTokenType.LeftParen);
condition = parser.ParseExpression();
parser.Take(ScriptTokenType.RightParen);
}
var block = parser.ParseBlock();
var branch = new IfStatement.Branch(condition, block);
if (isDefaultBranch)
defaultBranch = branch;
else
branches.Add(branch);
if (isDefaultBranch)
break;
} while (parser.MatchAndTake(ScriptTokenType.Else));
return new IfStatement(token, parser.Previous, branches, defaultBranch);
}
}
}

View File

@@ -0,0 +1,18 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class ReturnParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
if (parser.Match(ScriptTokenType.Semicolon))
return new ReturnStatement(token, parser.Previous, null);
var value = parser.ParseExpression();
return new ReturnStatement(token, parser.Previous, value);
}
}
}

View File

@@ -0,0 +1,84 @@
using System.Collections.Generic;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class SwitchParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
parser.Take(ScriptTokenType.LeftParen);
var expression = parser.ParseExpression();
parser.Take(ScriptTokenType.RightParen);
parser.Take(ScriptTokenType.LeftBrace);
var hasDefault = false;
var branches = new List<SwitchStatement.Branch>();
while (!parser.Match(ScriptTokenType.RightBrace))
{
var conditions = new List<Expression>();
while (true)
{
if (parser.MatchAndTake(ScriptTokenType.Case))
{
var condition = parser.ParseExpression();
conditions.Add(condition);
parser.Take(ScriptTokenType.Colon);
continue;
}
if (!parser.Match(ScriptTokenType.Default))
break;
var defaultToken = parser.Take(ScriptTokenType.Default);
if (hasDefault)
throw new CompilerException(defaultToken, "Multiple default labels");
conditions.Add(null); // special default condition
hasDefault = true;
parser.Take(ScriptTokenType.Colon);
}
if (conditions.Count > 0)
{
var block = ParseBlock(parser);
var branch = new SwitchStatement.Branch(conditions, block);
branches.Add(branch);
continue;
}
var errorToken = parser.Peek();
throw new CompilerException(errorToken, "Expected Case or Default but found {0}", errorToken);
}
parser.Take(ScriptTokenType.RightBrace);
return new SwitchStatement(token, parser.Previous, expression, branches);
}
private static BlockStatement ParseBlock(ScriptParser parser)
{
var statements = new List<Statement>();
var start = parser.Peek();
while (!parser.Match(ScriptTokenType.Case) &&
!parser.Match(ScriptTokenType.Default) &&
!parser.Match(ScriptTokenType.RightBrace))
{
statements.Add(parser.ParseStatement());
}
return new BlockStatement(start, parser.Previous, statements, true);
}
}
}

View File

@@ -0,0 +1,15 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class ThrowParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
var value = parser.ParseExpression();
return new ThrowStatement(token, value);
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using ScriptConverter.Ast;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class TryParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
var mainBlock = parser.ParseBlock(false);
var catches = new List<TryStatement.Branch>(2);
do
{
parser.Take(ScriptTokenType.Catch);
parser.Take(ScriptTokenType.LeftParen);
ScriptType type;
string name;
parser.ParseNamedType(out type, out name);
parser.Take(ScriptTokenType.RightParen);
var block = parser.ParseBlock(false);
catches.Add(new TryStatement.Branch(type, name, block));
} while (parser.Match(ScriptTokenType.Catch));
return new TryStatement(token, parser.Previous, mainBlock, catches);
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Linq;
using ScriptConverter.Ast;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class VariableParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = true;
var baseType = parser.ParseType(token);
var definitions = parser.ParseSeparatedBy(ScriptTokenType.Comma, (_, first) =>
{
var name = parser.Take(ScriptTokenType.Identifier).Contents;
var arrayDims = 0;
while (parser.MatchAndTake(ScriptTokenType.LeftSquare))
{
parser.Take(ScriptTokenType.RightSquare);
arrayDims++;
}
var type = baseType;
if (arrayDims > 0)
type = new ScriptType(baseType.Name, baseType.ArrayDimensions + arrayDims, baseType.IsResizable);
Expression value = null;
if (parser.MatchAndTake(ScriptTokenType.Assign))
value = parser.ParseExpression();
return new VariableStatement.Definition(type, name, value);
}).ToList();
return new VariableStatement(token, parser.Previous, baseType, false, definitions);
}
}
}

View File

@@ -0,0 +1,20 @@
using ScriptConverter.Ast.Statements;
namespace ScriptConverter.Parser.Parselets.Statements
{
class WhileParselet : IStatementParselet
{
public Statement Parse(ScriptParser parser, ScriptToken token, out bool trailingSemicolon)
{
trailingSemicolon = false;
parser.Take(ScriptTokenType.LeftParen);
var condition = parser.ParseExpression();
parser.Take(ScriptTokenType.RightParen);
var block = parser.ParseBlock();
return new WhileStatement(token, parser.Previous, condition, block);
}
}
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
namespace ScriptConverter.Parser
{
abstract class Parser
{
private IEnumerator<ScriptToken> _tokens;
private List<ScriptToken> _read;
protected Parser(IEnumerable<ScriptToken> tokens)
{
_tokens = tokens.GetEnumerator();
_read = new List<ScriptToken>(8);
}
/// <summary>
/// Returns the token that was most recently taken.
/// </summary>
public ScriptToken Previous { get; private set; }
public IEnumerable<T> ParseSeparatedBy<T>(ScriptTokenType separator, Func<Parser, bool, T> parseFunc) where T : class
{
var first = parseFunc(this, true);
if (first == null)
yield break;
yield return first;
while (MatchAndTake(separator))
{
var next = parseFunc(this, false);
if (next == null)
yield break;
yield return next;
}
}
/// <summary>
/// Check if the next token matches the given type. If they match, take the token.
/// </summary>
public bool MatchAndTake(ScriptTokenType type)
{
var isMatch = Match(type);
if (isMatch)
Take();
return isMatch;
}
/// <summary>
/// Check if the next token matches the given type.
/// </summary>
public bool Match(ScriptTokenType type, int distance = 0)
{
return Peek(distance).Type == type;
}
/// <summary>
/// Take a token from the stream. Throws an exception if the given type does not match the token type.
/// </summary>
public ScriptToken Take(ScriptTokenType type)
{
var token = Take();
if (token.Type != type)
throw new CompilerException(token, CompilerError.ExpectedButFound, type, token);
return token;
}
/// <summary>
/// Take a token from the stream.
/// </summary>
public ScriptToken Take()
{
Peek();
var result = _read[0];
_read.RemoveAt(0);
Previous = result;
return result;
}
/// <summary>
/// Peek at future tokens in the stream. Distance is the number of tokens from the current one.
/// </summary>
public ScriptToken Peek(int distance = 0)
{
if (distance < 0)
throw new ArgumentOutOfRangeException("distance", "distance can't be negative");
while (_read.Count <= distance)
{
_tokens.MoveNext();
_read.Add(_tokens.Current);
//Console.WriteLine(_tokens.Current.Type);
}
return _read[distance];
}
}
}

View File

@@ -0,0 +1,22 @@
namespace ScriptConverter.Parser
{
enum PrecedenceValue
{
Invalid,
Assignment,
Ternary,
LogicalOr,
LogicalAnd,
BitwiseOr,
BitwiseXor,
BitwiseAnd,
Equality,
Relational,
BitwiseShift,
Additive,
Multiplicative,
Cast,
Prefix,
Suffix
}
}

View File

@@ -0,0 +1,105 @@
using System.Collections.Generic;
namespace ScriptConverter.Parser
{
partial class ScriptLexer
{
private static OperatorDictionary<ScriptTokenType> _operators;
private static Dictionary<string, ScriptTokenType> _keywords;
static ScriptLexer()
{
EofToken = new ScriptToken(null, default(SourcePosition), default(SourcePosition), ScriptTokenType.Eof, null);
_operators = new OperatorDictionary<ScriptTokenType>
{
{ ";", ScriptTokenType.Semicolon },
{ ",", ScriptTokenType.Comma },
{ ".", ScriptTokenType.Dot },
{ "=", ScriptTokenType.Assign },
{ "?", ScriptTokenType.QuestionMark },
{ ":", ScriptTokenType.Colon },
{ "(", ScriptTokenType.LeftParen },
{ ")", ScriptTokenType.RightParen },
{ "{", ScriptTokenType.LeftBrace },
{ "}", ScriptTokenType.RightBrace },
{ "[", ScriptTokenType.LeftSquare },
{ "]", ScriptTokenType.RightSquare },
{ "+", ScriptTokenType.Add },
{ "+=", ScriptTokenType.AddAssign },
{ "-", ScriptTokenType.Subtract },
{ "-=", ScriptTokenType.SubtractAssign },
{ "*", ScriptTokenType.Multiply },
{ "*=", ScriptTokenType.MultiplyAssign },
{ "/", ScriptTokenType.Divide },
{ "/=", ScriptTokenType.DivideAssign },
{ "%", ScriptTokenType.Remainder },
{ "%=", ScriptTokenType.RemainderAssign },
{ "++", ScriptTokenType.Increment },
{ "--", ScriptTokenType.Decrement },
{ "==", ScriptTokenType.EqualTo },
{ "!=", ScriptTokenType.NotEqualTo },
{ ">", ScriptTokenType.GreaterThan },
{ ">=", ScriptTokenType.GreaterThanOrEqual },
{ "<", ScriptTokenType.LessThan },
{ "<=", ScriptTokenType.LessThanOrEqual },
{ "&&", ScriptTokenType.LogicalAnd },
{ "||", ScriptTokenType.LogicalOr },
{ "!", ScriptTokenType.LogicalNot },
{ "~", ScriptTokenType.BitwiseNot },
{ "&", ScriptTokenType.BitwiseAnd },
{ "&=", ScriptTokenType.BitwiseAndAssign },
{ "|", ScriptTokenType.BitwiseOr },
{ "|=", ScriptTokenType.BitwiseOrAssign },
{ "^", ScriptTokenType.BitwiseXor },
{ "^=", ScriptTokenType.BitwiseXorAssign },
{ "<<", ScriptTokenType.BitwiseShiftLeft },
{ "<<=", ScriptTokenType.BitwiseShiftLeftAssign },
{ ">>", ScriptTokenType.BitwiseShiftRight },
{ ">>=", ScriptTokenType.BitwiseShiftRightAssign },
{ "##", ScriptTokenType.Hash },
{ "#define", ScriptTokenType.PreprocessDefine },
{ "#ifdef", ScriptTokenType.PreprocessIfDef },
{ "#endif", ScriptTokenType.PreprocessEndIf },
};
_keywords = new Dictionary<string, ScriptTokenType>
{
{ "null", ScriptTokenType.Null },
{ "true", ScriptTokenType.True },
{ "false", ScriptTokenType.False },
{ "return", ScriptTokenType.Return },
{ "if", ScriptTokenType.If },
{ "else", ScriptTokenType.Else },
{ "for", ScriptTokenType.For },
{ "while", ScriptTokenType.While },
{ "do", ScriptTokenType.Do },
{ "break", ScriptTokenType.Break },
{ "continue", ScriptTokenType.Continue },
{ "switch", ScriptTokenType.Switch },
{ "case", ScriptTokenType.Case },
{ "default", ScriptTokenType.Default },
{ "inherits", ScriptTokenType.Inherits },
{ "include", ScriptTokenType.Include },
{ "const", ScriptTokenType.Const },
{ "public", ScriptTokenType.Public },
{ "new", ScriptTokenType.New },
{ "instanceof", ScriptTokenType.InstanceOf },
{ "try", ScriptTokenType.Try },
{ "catch", ScriptTokenType.Catch },
{ "throw", ScriptTokenType.Throw },
};
}
}
}

View File

@@ -0,0 +1,254 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ScriptConverter.Parser
{
partial class ScriptLexer : Lexer<ScriptToken, ScriptTokenType>
{
public ScriptLexer(IEnumerable<char> source, string fileName = null)
: base(source, fileName)
{
Rules = new List<LexerRule>
{
TryLexNumber,
TryLexOperator,
TryLexString,
TryLexWord,
};
}
protected override bool SkipComment()
{
if (TakeIfNext("//"))
{
while (true)
{
var ch = TakeChar();
if (ch == '\0' || ch == '\n' || ch == '\r')
return true;
}
}
if (TakeIfNext("/*"))
{
while (true)
{
if (TakeIfNext("*/"))
return true;
var ch = TakeChar();
if (ch == '\0')
return true;
}
}
return false;
}
private bool TryLexOperator(char ch, out ScriptToken token)
{
var opList = _operators.Lookup(ch);
if (opList != null)
{
var op = opList.FirstOrDefault(o => TakeIfNext(o.Item1));
if (op != null)
{
token = Token(op.Item2, op.Item1);
return true;
}
}
token = null;
return false;
}
private bool TryLexString(char ch, out ScriptToken token)
{
if (ch == '"' || ch == '\'')
{
TakeChar();
var stringTerminator = ch;
var stringContentsBuilder = new StringBuilder();
while (true)
{
if (AtEof)
throw ErrorStart(CompilerError.UnterminatedString);
ch = TakeChar();
if (ch == stringTerminator)
break;
if (ch != '\\')
{
switch (ch)
{
case '\n':
stringContentsBuilder.Append("\\n");
continue;
case '\r':
stringContentsBuilder.Append("\\r");
continue;
}
if (ch < ' ' || ch > '~')
{
stringContentsBuilder.AppendFormat("\\u{0:X4}", (int)ch);
continue;
}
stringContentsBuilder.Append(ch);
continue;
}
// escape sequence
ch = TakeChar();
if (AtEof)
throw Error(CompilerError.UnexpectedEofString);
switch (ch)
{
case '\\':
stringContentsBuilder.Append("\\\\");
break;
case '"':
stringContentsBuilder.Append("\\\"");
break;
case '\'':
stringContentsBuilder.Append("\\'");
break;
case 'r':
stringContentsBuilder.Append("\\r");
break;
case 'n':
stringContentsBuilder.Append("\\n");
break;
case 't':
stringContentsBuilder.Append("\\t");
break;
case '0':
stringContentsBuilder.Append("\\0");
break;
// TODO: more escape sequences
default:
throw Error(CompilerError.InvalidEscapeSequence, ch);
}
}
var stringContents = stringContentsBuilder.ToString();
if (stringTerminator == '\'')
{
token = Token(ScriptTokenType.SingleString, stringContents);
return true;
}
token = Token(ScriptTokenType.String, stringContents);
return true;
}
token = null;
return false;
}
private bool TryLexWord(char ch, out ScriptToken token)
{
if (char.IsLetter(ch) || ch == '_')
{
var wordContents = TakeWhile(c => char.IsLetterOrDigit(c) || c == '_');
ScriptTokenType keywordType;
var isKeyword = _keywords.TryGetValue(wordContents, out keywordType);
// HACK: allow using enum as an identifier
if (!isKeyword && wordContents == "enum")
wordContents = "enum_";
token = Token(isKeyword ? keywordType : ScriptTokenType.Identifier, wordContents);
return true;
}
token = null;
return false;
}
private bool TryLexNumber(char ch, out ScriptToken token)
{
if (char.IsDigit(ch) || (ch == '.' && char.IsDigit(PeekChar(1))))
{
var format = NumberFormat.Decimal;
var hasDecimal = false;
var prefix = "";
if (ch == '0')
{
var nextChar = PeekChar(1);
if (nextChar == 'x' || nextChar == 'X')
format = NumberFormat.Hexadecimal;
if (nextChar == 'b' || nextChar == 'B')
format = NumberFormat.Binary;
if (format != NumberFormat.Decimal)
{
prefix = "" + TakeChar() + TakeChar();
}
}
if (ch == '.')
{
hasDecimal = true;
prefix = "" + TakeChar();
}
Func<char, bool> isHexLetter = c => (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
Func<char, bool> isDigit = c => char.IsDigit(c) || (format == NumberFormat.Hexadecimal && isHexLetter(c));
var numberContents = TakeWhile(c =>
{
if (format == NumberFormat.Decimal && c == '.' && isDigit(PeekChar(1)) && !hasDecimal)
{
hasDecimal = true;
return true;
}
if (format == NumberFormat.Decimal && (c == 'f' || c == 'd' || c == 'L') && !isDigit(PeekChar(1)))
return true;
return isDigit(c);
});
token = Token(ScriptTokenType.Number, prefix + numberContents);
return true;
}
token = null;
return false;
}
enum NumberFormat
{
Decimal, Hexadecimal, Binary
}
private ScriptToken Token(ScriptTokenType type, string contents)
{
return new ScriptToken(FileName, new SourcePosition(StartLine, StartColumn), new SourcePosition(Line, Column - 1), type, contents);
}
}
}

View File

@@ -0,0 +1,122 @@
using System.Collections.Generic;
using ScriptConverter.Parser.Parselets;
using ScriptConverter.Parser.Parselets.Declarations;
using ScriptConverter.Parser.Parselets.Expressions;
using ScriptConverter.Parser.Parselets.Statements;
namespace ScriptConverter.Parser
{
partial class ScriptParser
{
private static Dictionary<ScriptTokenType, IDeclarationParselet> _declarationParselets;
private static Dictionary<ScriptTokenType, IStatementParselet> _statementParselets;
private static Dictionary<ScriptTokenType, IPrefixParselet> _prefixParselets;
private static Dictionary<ScriptTokenType, IInfixParselet> _infixParselets;
static ScriptParser()
{
_declarationParselets = new Dictionary<ScriptTokenType, IDeclarationParselet>();
_statementParselets = new Dictionary<ScriptTokenType, IStatementParselet>();
_prefixParselets = new Dictionary<ScriptTokenType, IPrefixParselet>();
_infixParselets = new Dictionary<ScriptTokenType, IInfixParselet>();
// declarations
RegisterDeclaration(ScriptTokenType.Const, new ConstParselet());
RegisterDeclaration(ScriptTokenType.Include, new IncludeParselet());
RegisterDeclaration(ScriptTokenType.Inherits, new InheritsParselet());
RegisterDeclaration(ScriptTokenType.Identifier, new MethodParselet());
RegisterDeclaration(ScriptTokenType.Public, new PublicParselet());
// statements
RegisterStatement(ScriptTokenType.LeftBrace, new BlockParselet());
RegisterStatement(ScriptTokenType.Break, new BreakParselet());
RegisterStatement(ScriptTokenType.Const, new ConstVariableParselet());
RegisterStatement(ScriptTokenType.Continue, new ContinueParselet());
RegisterStatement(ScriptTokenType.Do, new DoParselet());
RegisterStatement(ScriptTokenType.Semicolon, new EmptyParselet());
RegisterStatement(ScriptTokenType.For, new ForParselet());
RegisterStatement(ScriptTokenType.If, new IfParselet());
RegisterStatement(ScriptTokenType.Return, new ReturnParselet());
RegisterStatement(ScriptTokenType.Switch, new SwitchParselet());
RegisterStatement(ScriptTokenType.Throw, new ThrowParselet());
RegisterStatement(ScriptTokenType.Try, new TryParselet());
RegisterStatement(ScriptTokenType.Identifier, new VariableParselet());
RegisterStatement(ScriptTokenType.While, new WhileParselet());
// expressions
RegisterPrefix(ScriptTokenType.LeftBrace, new ArrayInitializerParselet());
RegisterInfix(ScriptTokenType.Add, new BinaryOperatorParselet(PrecedenceValue.Additive, false));
RegisterInfix(ScriptTokenType.Subtract, new BinaryOperatorParselet(PrecedenceValue.Additive, false));
RegisterInfix(ScriptTokenType.Multiply, new BinaryOperatorParselet(PrecedenceValue.Multiplicative, false));
RegisterInfix(ScriptTokenType.Divide, new BinaryOperatorParselet(PrecedenceValue.Multiplicative, false));
RegisterInfix(ScriptTokenType.Remainder, new BinaryOperatorParselet(PrecedenceValue.Multiplicative, false));
RegisterInfix(ScriptTokenType.BitwiseShiftLeft, new BinaryOperatorParselet(PrecedenceValue.BitwiseShift, false));
RegisterInfix(ScriptTokenType.BitwiseShiftRight, new BinaryOperatorParselet(PrecedenceValue.BitwiseShift, false));
RegisterInfix(ScriptTokenType.BitwiseAnd, new BinaryOperatorParselet(PrecedenceValue.BitwiseAnd, false));
RegisterInfix(ScriptTokenType.BitwiseOr, new BinaryOperatorParselet(PrecedenceValue.BitwiseOr, false));
RegisterInfix(ScriptTokenType.BitwiseXor, new BinaryOperatorParselet(PrecedenceValue.BitwiseXor, false));
RegisterInfix(ScriptTokenType.LogicalAnd, new BinaryOperatorParselet(PrecedenceValue.LogicalAnd, false));
RegisterInfix(ScriptTokenType.LogicalOr, new BinaryOperatorParselet(PrecedenceValue.LogicalOr, false));
RegisterInfix(ScriptTokenType.EqualTo, new BinaryOperatorParselet(PrecedenceValue.Equality, false));
RegisterInfix(ScriptTokenType.NotEqualTo, new BinaryOperatorParselet(PrecedenceValue.Equality, false));
RegisterInfix(ScriptTokenType.GreaterThan, new BinaryOperatorParselet(PrecedenceValue.Relational, false));
RegisterInfix(ScriptTokenType.GreaterThanOrEqual, new BinaryOperatorParselet(PrecedenceValue.Relational, false));
RegisterInfix(ScriptTokenType.LessThan, new BinaryOperatorParselet(PrecedenceValue.Relational, false));
RegisterInfix(ScriptTokenType.LessThanOrEqual, new BinaryOperatorParselet(PrecedenceValue.Relational, false));
RegisterInfix(ScriptTokenType.Assign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.AddAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.SubtractAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.MultiplyAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.DivideAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.RemainderAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.BitwiseShiftLeftAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.BitwiseShiftRightAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.BitwiseAndAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.BitwiseOrAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterInfix(ScriptTokenType.BitwiseXorAssign, new BinaryOperatorParselet(PrecedenceValue.Assignment, true));
RegisterPrefix(ScriptTokenType.True, new BoolParselet(true));
RegisterPrefix(ScriptTokenType.False, new BoolParselet(false));
RegisterInfix(ScriptTokenType.LeftParen, new CallParselet());
RegisterInfix(ScriptTokenType.Dot, new FieldParselet());
RegisterPrefix(ScriptTokenType.LeftParen, new GroupParselet());
RegisterPrefix(ScriptTokenType.Hash, new HashParselet());
RegisterPrefix(ScriptTokenType.Identifier, new IdentifierParselet());
RegisterInfix(ScriptTokenType.LeftSquare, new IndexerParselet());
RegisterInfix(ScriptTokenType.InstanceOf, new InstanceOfParselet());
RegisterPrefix(ScriptTokenType.New, new NewParselet());
RegisterPrefix(ScriptTokenType.Null, new NullParselet());
RegisterPrefix(ScriptTokenType.Number, new NumberParselet());
RegisterInfix(ScriptTokenType.Increment, new PostfixOperatorParselet(PrecedenceValue.Suffix));
RegisterInfix(ScriptTokenType.Decrement, new PostfixOperatorParselet(PrecedenceValue.Suffix));
RegisterPrefix(ScriptTokenType.Add, new PrefixOperatorParselet(PrecedenceValue.Prefix));
RegisterPrefix(ScriptTokenType.Subtract, new PrefixOperatorParselet(PrecedenceValue.Prefix));
RegisterPrefix(ScriptTokenType.BitwiseNot, new PrefixOperatorParselet(PrecedenceValue.Prefix));
RegisterPrefix(ScriptTokenType.LogicalNot, new PrefixOperatorParselet(PrecedenceValue.Prefix));
RegisterPrefix(ScriptTokenType.Increment, new PrefixOperatorParselet(PrecedenceValue.Prefix));
RegisterPrefix(ScriptTokenType.Decrement, new PrefixOperatorParselet(PrecedenceValue.Prefix));
RegisterPrefix(ScriptTokenType.String, new StringParselet(false));
RegisterPrefix(ScriptTokenType.SingleString, new StringParselet(true));
RegisterInfix(ScriptTokenType.QuestionMark, new TernaryParselet());
}
static void RegisterDeclaration(ScriptTokenType type, IDeclarationParselet parselet)
{
_declarationParselets.Add(type, parselet);
}
static void RegisterStatement(ScriptTokenType type, IStatementParselet parselet)
{
_statementParselets.Add(type, parselet);
}
static void RegisterPrefix(ScriptTokenType type, IPrefixParselet parselet)
{
_prefixParselets.Add(type, parselet);
}
static void RegisterInfix(ScriptTokenType type, IInfixParselet parselet)
{
_infixParselets.Add(type, parselet);
}
}
}

View File

@@ -0,0 +1,237 @@
using System;
using System.Collections.Generic;
using ScriptConverter.Ast;
using ScriptConverter.Ast.Declarations;
using ScriptConverter.Ast.Expressions;
using ScriptConverter.Ast.Statements;
using ScriptConverter.Parser.Parselets;
namespace ScriptConverter.Parser
{
partial class ScriptParser : Parser
{
public ScriptParser(IEnumerable<ScriptToken> tokens)
: base(tokens)
{
}
public void ParseNamedType(out ScriptType type, out string name)
{
ParseNamedType(Take(), out type, out name);
}
public void ParseNamedType(ScriptToken token, out ScriptType type, out string name)
{
type = ParseType(token);
name = Take(ScriptTokenType.Identifier).Contents;
var arrayDims = 0;
while (MatchAndTake(ScriptTokenType.LeftSquare))
{
Take(ScriptTokenType.RightSquare);
arrayDims++;
}
if (arrayDims == 0)
return;
type = new ScriptType(type.Name, type.ArrayDimensions + arrayDims, type.IsResizable);
}
public ScriptType ParseType()
{
return ParseType(Take());
}
public ScriptType ParseType(ScriptToken token)
{
if (token.Type != ScriptTokenType.Identifier)
throw new CompilerException(token, CompilerError.ExpectedButFound, ScriptTokenType.Identifier, token);
var isResizable = false;
var arrayDims = 0;
if (token.Contents == "resizeable") // yes
{
isResizable = true;
token = Take(ScriptTokenType.Identifier);
}
var typeName = token.Contents;
if (Match(ScriptTokenType.Dot))
{
typeName += "." + string.Join(".", ParseSeparatedBy(ScriptTokenType.Dot, (_, first) =>
{
if (first)
Take(ScriptTokenType.Dot);
return Take(ScriptTokenType.Identifier).Contents;
}));
}
if (isResizable)
{
Take(ScriptTokenType.LeftSquare);
Take(ScriptTokenType.RightSquare);
}
else
{
while (MatchAndTake(ScriptTokenType.LeftSquare))
{
Take(ScriptTokenType.RightSquare);
arrayDims++;
}
}
return new ScriptType(typeName, arrayDims, isResizable);
}
/// <summary>
/// Parse an expression into an expression tree. You can think of expressions as sub-statements.
/// </summary>
public Expression ParseExpression(int precendence = 0)
{
var token = Take();
IPrefixParselet prefixParselet;
_prefixParselets.TryGetValue(token.Type, out prefixParselet);
if (prefixParselet == null)
throw new CompilerException(token, CompilerError.ExpectedButFound, "Expression", token);
var left = prefixParselet.Parse(this, token);
while (GetPrecedence() > precendence)
{
token = Take();
IInfixParselet infixParselet;
_infixParselets.TryGetValue(token.Type, out infixParselet);
if (infixParselet == null)
throw new Exception("probably can't happen");
left = infixParselet.Parse(this, left, token);
}
return left;
}
/// <summary>
/// Parse a statement into an expression tree.
/// </summary>
public Statement ParseStatement(bool takeTrailingSemicolon = true)
{
var token = Peek();
IStatementParselet statementParselet;
_statementParselets.TryGetValue(token.Type, out statementParselet);
// HACK: workaround for variables
if (token.Type == ScriptTokenType.Identifier && !IsVariableStatement())
statementParselet = null;
if (statementParselet == null)
{
var expr = ParseExpression();
if (takeTrailingSemicolon)
Take(ScriptTokenType.Semicolon);
return new NakedStatement(expr);
}
token = Take();
bool hasTrailingSemicolon;
var result = statementParselet.Parse(this, token, out hasTrailingSemicolon);
if (takeTrailingSemicolon && hasTrailingSemicolon)
Take(ScriptTokenType.Semicolon);
return result;
}
/// <summary>
/// Parse a block of code into an expression tree. Blocks can either be a single statement or
/// multiple surrounded by braces.
/// </summary>
public BlockStatement ParseBlock(bool allowSingle = true)
{
ScriptToken start;
ScriptToken end;
var statements = new List<Statement>();
if (allowSingle && !Match(ScriptTokenType.LeftBrace))
{
start = Peek();
statements.Add(ParseStatement());
end = Previous;
return new BlockStatement(start, end, statements);
}
start = Take(ScriptTokenType.LeftBrace);
while (!Match(ScriptTokenType.RightBrace))
{
statements.Add(ParseStatement());
}
end = Take(ScriptTokenType.RightBrace);
return new BlockStatement(start, end, statements);
}
/// <summary>
/// Parses declarations until there are no more tokens available.
/// </summary>
public Document ParseAll()
{
var declarations = new List<Declaration>();
while (!Match(ScriptTokenType.Eof))
{
var token = Take();
if (token.Type == ScriptTokenType.Semicolon)
continue;
IDeclarationParselet declarationParselet;
if (!_declarationParselets.TryGetValue(token.Type, out declarationParselet))
throw new CompilerException(token, "expected declaration"); // TODO
declarations.Add(declarationParselet.Parse(this, token));
}
return new Document(declarations);
}
private bool IsVariableStatement()
{
if (!Match(ScriptTokenType.Identifier))
return false;
var i = 1;
while (Match(ScriptTokenType.Dot, i) && Match(ScriptTokenType.Identifier, i + 1))
{
i += 2;
}
return Match(ScriptTokenType.Identifier, i) ||
(Match(ScriptTokenType.LeftSquare, i) && Match(ScriptTokenType.RightSquare, i + 1));
}
private int GetPrecedence()
{
IInfixParselet infixParselet;
_infixParselets.TryGetValue(Peek().Type, out infixParselet);
return infixParselet != null ? infixParselet.Precedence : 0;
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Globalization;
using JetBrains.Annotations;
namespace ScriptConverter.Parser
{
public struct SourcePosition
{
public readonly int Line;
public readonly int Column;
public SourcePosition(int line, int column = -1)
{
Line = line;
Column = column;
}
[Pure]
public override string ToString()
{
if (Column <= 0)
return Line.ToString("G", CultureInfo.InvariantCulture);
return string.Format(CultureInfo.InvariantCulture, "{0:G}:{1:G}", Line, Column);
}
[Pure]
public string ToRangeString(SourcePosition end)
{
if (Line == end.Line && Column == end.Column)
return ToString();
return string.Format(CultureInfo.InvariantCulture, "{0}-{1}", this, end);
}
}
}

View File

@@ -0,0 +1,172 @@
namespace ScriptConverter.Parser
{
abstract class Token<T>
{
public readonly string FileName;
public readonly SourcePosition Start;
public readonly SourcePosition End;
public readonly T Type;
public readonly string Contents;
protected Token(string fileName, SourcePosition start, SourcePosition end, T type, string contents)
{
FileName = fileName;
Start = start;
End = end;
Type = type;
Contents = contents;
}
protected Token(Token<T> token, T type, string contents)
: this(token.FileName, token.Start, token.End, type, contents)
{
}
protected Token(T type, string contents)
: this(null, new SourcePosition(-1), new SourcePosition(-1), type, contents)
{
}
public string RangeString
{
get { return Start.ToRangeString(End); }
}
}
enum ScriptTokenType
{
Identifier,
Number,
SingleString,
String,
Null,
True,
False,
Return,
If,
Else,
For,
While,
Do,
Break,
Continue,
Switch,
Case,
Default,
Hash,
Inherits,
Include,
Const,
Public,
New,
InstanceOf,
Try,
Catch,
Throw,
Semicolon,
Comma,
Dot,
Assign,
QuestionMark,
Colon,
LeftParen,
RightParen,
LeftBrace,
RightBrace,
LeftSquare,
RightSquare,
Add,
AddAssign,
Subtract,
SubtractAssign,
Multiply,
MultiplyAssign,
Divide,
DivideAssign,
Remainder,
RemainderAssign,
Increment,
Decrement,
EqualTo,
NotEqualTo,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
LogicalAnd,
LogicalOr,
LogicalNot,
BitwiseNot,
BitwiseAnd,
BitwiseAndAssign,
BitwiseOr,
BitwiseOrAssign,
BitwiseXor,
BitwiseXorAssign,
BitwiseShiftLeft,
BitwiseShiftLeftAssign,
BitwiseShiftRight,
BitwiseShiftRightAssign,
PreprocessDefine,
PreprocessIfDef,
PreprocessEndIf,
Eof
}
class ScriptToken : Token<ScriptTokenType>
{
public ScriptToken(string fileName, SourcePosition start, SourcePosition end, ScriptTokenType type, string contents)
: base(fileName, start, end, type, contents)
{
}
public ScriptToken(Token<ScriptTokenType> token, ScriptTokenType type, string contents)
: base(token, type, contents)
{
}
public ScriptToken(ScriptTokenType type, string contents)
: base(type, contents)
{
}
public override string ToString()
{
switch (Type)
{
case ScriptTokenType.Identifier:
case ScriptTokenType.Number:
case ScriptTokenType.String:
var contentsStr = Contents;
if (contentsStr.Length > 16)
contentsStr = contentsStr.Substring(0, 13) + "...";
return string.Format("{0}('{1}')", Type, contentsStr);
default:
return Type.ToString();
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More