├── .gitignore ├── Interpreter ├── Interpreter.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── Samples │ ├── anton.lang │ └── run.lang ├── Lang.Tests ├── Lang.Tests.csproj ├── LangTests.cs └── app.config ├── Lang.sln ├── Lang ├── AST │ ├── ArrayDeclrAst.cs │ ├── ArrayIndexAst.cs │ ├── Ast.cs │ ├── AstTypes.cs │ ├── ClassAst.cs │ ├── ClassReference.cs │ ├── Conditional.cs │ ├── Expr.cs │ ├── ForLoop.cs │ ├── FuncInvoke.cs │ ├── LambdaDeclr.cs │ ├── MethodDeclr.cs │ ├── NewArrayAst.cs │ ├── NewAst.cs │ ├── PrintAst.cs │ ├── ReturnAst.cs │ ├── ScopeDeclr.cs │ ├── TryCatchAst.cs │ ├── VarDeclrAst.cs │ └── WhileLoop.cs ├── Data │ ├── SpecialNames.cs │ ├── Token.cs │ ├── TokenType.cs │ └── ValueMemory.cs ├── Exceptions │ ├── InvalidSyntax.cs │ ├── ReturnException.cs │ └── UndefinedElementException.cs ├── Lang.csproj ├── Lexers │ ├── Lexer.cs │ ├── TokenizableStreamBase.cs │ └── Tokenizer.cs ├── Matches │ ├── IMatcher.cs │ ├── MatchKeyword.cs │ ├── MatchNumber.cs │ ├── MatchString.cs │ ├── MatchWhiteSpace.cs │ ├── MatchWord.cs │ └── MatcherBase.cs ├── Parser │ ├── LanguageParser.cs │ └── ParseableTokenStream.cs ├── Spaces │ ├── IScopeable.cs │ ├── MemorySpace.cs │ ├── Scope.cs │ ├── ScopeContainer.cs │ └── ScopeStack.cs ├── Symbols │ ├── BuiltInType.cs │ ├── ClassSymbol.cs │ ├── ExpressionTypes.cs │ ├── IType.cs │ ├── MethodSymbol.cs │ ├── StructSymbol.cs │ ├── Symbol.cs │ └── UserDefinedType.cs ├── Utils │ ├── CollectionUtil.cs │ ├── ExtensionMethods.cs │ ├── Maybe.cs │ ├── NullTester.cs │ ├── ScopeUtil.cs │ └── TokenUtil.cs ├── Visitors │ ├── IAcceptVisitor.cs │ ├── IAstVisitor.cs │ ├── InterpretorVisitor.cs │ ├── PrintAstVisitor.cs │ └── ScopeBuilderVisitor.cs ├── app.config └── packages.config ├── License ├── README.md └── packages ├── NUnit.2.6.2 ├── NUnit.2.6.2.nupkg ├── lib │ ├── nunit.framework.dll │ └── nunit.framework.xml └── license.txt └── repositories.config /.gitignore: -------------------------------------------------------------------------------- 1 | # git ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | bin/ 8 | _Resharper* 9 | Debug/ 10 | Release/ 11 | obj/ 12 | *.suo 13 | *.ReSharper.user 14 | .swf 15 | *.idea* 16 | *.lib 17 | *.ipch 18 | *.filters 19 | *.user 20 | *.sdf 21 | *.opensdf 22 | !WinPCap/Lib/* -------------------------------------------------------------------------------- /Interpreter/Interpreter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {B311E47E-EC9D-4B07-BC63-84C602FB88DE} 9 | Exe 10 | Properties 11 | Interpreter 12 | Interpreter 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | x86 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {37F13707-0783-424E-8D35-7518377A3A7A} 52 | Lang 53 | 54 | 55 | 56 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Interpreter/Program.cs: -------------------------------------------------------------------------------- 1 | using Lang.Lexers; 2 | using Lang.Parser; 3 | using Lang.Visitors; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.IO; 7 | using System; 8 | 9 | namespace Interpreter 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | var files = new List (args).Where (File.Exists); 16 | 17 | var str = files.Select (File.ReadAllText) 18 | .Aggregate (string.Empty, (acc, item) => acc + Environment.NewLine + item); 19 | 20 | if (String.IsNullOrWhiteSpace (str)) { 21 | Console.WriteLine ("No available files for compilation"); 22 | 23 | return; 24 | } 25 | 26 | var ast = new LanguageParser(new Lexer(str)).Parse(); 27 | 28 | new InterpretorVisitor().Start(ast); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Interpreter/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Interpreter")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Interpreter")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7323e733-92ef-4700-8eb0-2caeb6843fc0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Interpreter/Samples/anton.lang: -------------------------------------------------------------------------------- 1 | class anton{ 2 | int x = 1; 3 | int y = 2; 4 | 5 | void foo(){ 6 | print x; 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Interpreter/Samples/run.lang: -------------------------------------------------------------------------------- 1 | var ant = new anton(); 2 | var foo = new anton(); 3 | 4 | foo.x = 2; 5 | 6 | ant.foo(); 7 | 8 | foo.foo(); 9 | 10 | foo.x = 10; 11 | 12 | foo.foo(); 13 | -------------------------------------------------------------------------------- /Lang.Tests/Lang.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {BA58B05D-8EEE-4FF3-B644-46528525C0A8} 9 | Library 10 | Properties 11 | Lang.Tests 12 | Lang.Tests 13 | v4.0 14 | 15 | 16 | 512 17 | 18 | 19 | x86 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | x86 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | False 46 | ..\packages\NUnit.2.6.2\lib\nunit.framework.dll 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {37F13707-0783-424E-8D35-7518377A3A7A} 56 | Lang 57 | 58 | 59 | 60 | 67 | -------------------------------------------------------------------------------- /Lang.Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Lang.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lang", "Lang\Lang.csproj", "{37F13707-0783-424E-8D35-7518377A3A7A}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lang.Tests", "Lang.Tests\Lang.Tests.csproj", "{BA58B05D-8EEE-4FF3-B644-46528525C0A8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Interpreter", "Interpreter\Interpreter.csproj", "{B311E47E-EC9D-4B07-BC63-84C602FB88DE}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x86 = Debug|x86 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {37F13707-0783-424E-8D35-7518377A3A7A}.Debug|x86.ActiveCfg = Debug|x86 17 | {37F13707-0783-424E-8D35-7518377A3A7A}.Debug|x86.Build.0 = Debug|x86 18 | {37F13707-0783-424E-8D35-7518377A3A7A}.Release|x86.ActiveCfg = Release|x86 19 | {37F13707-0783-424E-8D35-7518377A3A7A}.Release|x86.Build.0 = Release|x86 20 | {BA58B05D-8EEE-4FF3-B644-46528525C0A8}.Debug|x86.ActiveCfg = Debug|x86 21 | {BA58B05D-8EEE-4FF3-B644-46528525C0A8}.Debug|x86.Build.0 = Debug|x86 22 | {BA58B05D-8EEE-4FF3-B644-46528525C0A8}.Release|x86.ActiveCfg = Release|x86 23 | {BA58B05D-8EEE-4FF3-B644-46528525C0A8}.Release|x86.Build.0 = Release|x86 24 | {B311E47E-EC9D-4B07-BC63-84C602FB88DE}.Debug|x86.ActiveCfg = Debug|x86 25 | {B311E47E-EC9D-4B07-BC63-84C602FB88DE}.Debug|x86.Build.0 = Debug|x86 26 | {B311E47E-EC9D-4B07-BC63-84C602FB88DE}.Release|x86.ActiveCfg = Release|x86 27 | {B311E47E-EC9D-4B07-BC63-84C602FB88DE}.Release|x86.Build.0 = Release|x86 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /Lang/AST/ArrayDeclrAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | 7 | namespace Lang.AST 8 | { 9 | class ArrayDeclrAst : VarDeclrAst 10 | { 11 | protected ArrayDeclrAst(Token token) : base(token) 12 | { 13 | IsArray = true; 14 | } 15 | 16 | public ArrayDeclrAst(Token declType, Token name) : base(declType, name) 17 | { 18 | IsArray = true; 19 | } 20 | 21 | public ArrayDeclrAst(Token declType, Token name, Ast value) : base(declType, name, value) 22 | { 23 | IsArray = true; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Lang/AST/ArrayIndexAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class ArrayIndexAst : Ast 11 | { 12 | public Ast Name { get; set; } 13 | 14 | public Ast Index { get; set; } 15 | 16 | public ArrayIndexAst(Ast name, Ast index) : base(name.Token) 17 | { 18 | Name = name; 19 | Index = index; 20 | } 21 | 22 | public override void Visit(IAstVisitor visitor) 23 | { 24 | visitor.Visit(this); 25 | } 26 | 27 | public override AstTypes AstType 28 | { 29 | get { return AstTypes.ArrayIndex; } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Lang/AST/Ast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lang.Spaces; 5 | using Lang.Data; 6 | using Lang.Symbols; 7 | using Lang.Visitors; 8 | 9 | namespace Lang.AST 10 | { 11 | public abstract class Ast : IAcceptVisitor 12 | { 13 | public MemorySpace CallingMemory { get; set; } 14 | 15 | public Scope CallingScope { get; set; } 16 | 17 | public Scope CurrentScope { get; set; } 18 | 19 | public Scope Global { get; set; } 20 | 21 | public Token Token { get; set; } 22 | 23 | public IType AstSymbolType { get; set; } 24 | 25 | public List Children { get; private set; } 26 | 27 | public Ast ConvertedExpression { get; set; } 28 | 29 | public bool IsLink { get; set; } 30 | 31 | public Ast(Token token) 32 | { 33 | Token = token; 34 | Children = new List(); 35 | } 36 | 37 | public void AddChild(Ast child) 38 | { 39 | if (child != null) 40 | { 41 | Children.Add(child); 42 | } 43 | } 44 | 45 | public override string ToString() 46 | { 47 | return Token.TokenType + " " + Children.Aggregate("", (acc, ast) => acc + " " + ast); 48 | } 49 | 50 | public abstract void Visit(IAstVisitor visitor); 51 | 52 | /// 53 | /// Used instead of reflection to determine the syntax tree type 54 | /// 55 | public abstract AstTypes AstType { get; } 56 | 57 | /// 58 | /// Pure dynamic's are resolved only at runtime 59 | /// 60 | public bool IsPureDynamic { get; set; } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Lang/AST/AstTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Lang.AST 8 | { 9 | public enum AstTypes 10 | { 11 | Expression, 12 | For, 13 | FunctionInvoke, 14 | MethodDeclr, 15 | Return, 16 | VarDeclr, 17 | While, 18 | Conditional, 19 | ScopeDeclr, 20 | Class, 21 | Print, 22 | ClassRef, 23 | New, 24 | TryCatch, 25 | ArrayIndex 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Lang/AST/ClassAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class ClassAst : Ast 11 | { 12 | public MethodDeclr Constructor { get; set; } 13 | 14 | public ScopeDeclr Body { get; set; } 15 | 16 | public ClassAst(Token token, ScopeDeclr body) : base(token) 17 | { 18 | Body = body; 19 | } 20 | 21 | public override void Visit(IAstVisitor visitor) 22 | { 23 | visitor.Visit(this); 24 | } 25 | 26 | public override AstTypes AstType 27 | { 28 | get { return AstTypes.Class; } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Lang/AST/ClassReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class ClassReference : Ast 11 | { 12 | public Ast ClassInstance { get; set; } 13 | 14 | public List Deferences { get; set; } 15 | 16 | public ClassReference(Ast classInstance, List deferences) 17 | : base(classInstance.Token) 18 | { 19 | ClassInstance = classInstance; 20 | Deferences = deferences; 21 | } 22 | 23 | 24 | public override void Visit(IAstVisitor visitor) 25 | { 26 | visitor.Visit(this); 27 | } 28 | 29 | public override AstTypes AstType 30 | { 31 | get { return AstTypes.ClassRef; } 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return "[( " + ClassInstance + ")" + Deferences.Aggregate("", (acc, item) => acc + ". (" + item + ")") + "]"; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Lang/AST/Conditional.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class Conditional : Ast 11 | { 12 | public Ast Predicate { get; set; } 13 | 14 | public ScopeDeclr Body { get; set; } 15 | 16 | public Conditional Alternate { get; set; } 17 | 18 | public Conditional(Token token) : base(token) 19 | { 20 | } 21 | 22 | public Conditional(Token conditionalType, Ast predicate, ScopeDeclr body, Conditional alternate = null) 23 | : this(conditionalType) 24 | { 25 | Predicate = predicate; 26 | Body = body; 27 | Alternate = alternate; 28 | } 29 | 30 | public override void Visit(IAstVisitor visitor) 31 | { 32 | visitor.Visit(this); 33 | } 34 | 35 | public override AstTypes AstType 36 | { 37 | get { return AstTypes.Conditional; } 38 | } 39 | 40 | public override string ToString() 41 | { 42 | return "(" + Token + "(" + Predicate + ") then " + Body + (Alternate != null ? " else " + Alternate : ""); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Lang/AST/Expr.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class Expr : Ast 11 | { 12 | public Ast Left { get; private set; } 13 | 14 | public Ast Right { get; private set; } 15 | 16 | public Expr(Token token) 17 | : base(token) 18 | { 19 | } 20 | 21 | public Expr(Ast left, Token token, Ast right) 22 | : base(token) 23 | { 24 | Left = left; 25 | Right = right; 26 | } 27 | 28 | 29 | public override void Visit(IAstVisitor visitor) 30 | { 31 | visitor.Visit(this); 32 | } 33 | 34 | public override AstTypes AstType 35 | { 36 | get { return AstTypes.Expression; } 37 | } 38 | 39 | public override string ToString() 40 | { 41 | if (Left == null && Right == null) 42 | { 43 | return Token.ToString(); 44 | } 45 | 46 | return "(" + Left + " " + Token + " " + Right + ")"; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Lang/AST/ForLoop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class ForLoop : Ast 11 | { 12 | public Ast Setup { get; private set; } 13 | 14 | public Ast Predicate { get; private set; } 15 | 16 | public Ast Update { get; private set; } 17 | 18 | public ScopeDeclr Body { get; private set; } 19 | 20 | public ForLoop(Ast init, Ast stop, Ast modify, ScopeDeclr body) 21 | : base(new Token(TokenType.For)) 22 | { 23 | Setup = init; 24 | 25 | Predicate = stop; 26 | 27 | Update = modify; 28 | 29 | Body = body; 30 | } 31 | 32 | public override void Visit(IAstVisitor visitor) 33 | { 34 | visitor.Visit(this); 35 | } 36 | 37 | public override AstTypes AstType 38 | { 39 | get { return AstTypes.For; } 40 | } 41 | 42 | public override string ToString() 43 | { 44 | return "(" + Token + "(" + Setup + ") (" + Predicate + ")" + "(" + Update +"){" + Body + "}"; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Lang/AST/FuncInvoke.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Symbols; 7 | using Lang.Visitors; 8 | 9 | namespace Lang.AST 10 | { 11 | public class FuncInvoke : Ast 12 | { 13 | public List Arguments { get; private set; } 14 | 15 | public Ast FunctionName { get; set; } 16 | 17 | public IType ReturnType { get; set; } 18 | 19 | public FuncInvoke(Token token, List args) : base(token) 20 | { 21 | FunctionName = new Expr(token); 22 | 23 | Arguments = args; 24 | } 25 | 26 | 27 | public override void Visit(IAstVisitor visitor) 28 | { 29 | visitor.Visit(this); 30 | } 31 | 32 | public override AstTypes AstType 33 | { 34 | get { return AstTypes.FunctionInvoke; } 35 | } 36 | 37 | public override string ToString() 38 | { 39 | return "call " + FunctionName + " with args " + Arguments.Aggregate("", (acc, item) => acc + item + ","); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Lang/AST/LambdaDeclr.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | 7 | namespace Lang.AST 8 | { 9 | public class LambdaDeclr : MethodDeclr 10 | { 11 | public static int LambdaCount { get; set; } 12 | 13 | private static Random _rand = new Random(); 14 | 15 | public LambdaDeclr(List arguments, ScopeDeclr body ) 16 | : base(new Token(TokenType.Infer), new Token(TokenType.Word, AnonymousFunctionName), arguments, body, true) 17 | { 18 | } 19 | 20 | private static string AnonymousFunctionName 21 | { 22 | get 23 | { 24 | LambdaCount++; 25 | return "anonymous" + LambdaCount; 26 | } 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Lang/AST/MethodDeclr.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class MethodDeclr : Ast 11 | { 12 | public Ast MethodName { get; private set; } 13 | 14 | /// 15 | /// An expression representing the return type declared for the method 16 | /// 17 | public Ast MethodReturnType { get; private set; } 18 | 19 | public List Arguments { get; private set; } 20 | 21 | public ScopeDeclr Body { get; private set; } 22 | 23 | public Boolean IsAnonymous { get; set; } 24 | 25 | public ReturnAst ReturnAst { get; set; } 26 | 27 | public MethodDeclr(Token returnType, Token funcName, List arguments, ScopeDeclr body, bool isAnon = false) 28 | : base(funcName) 29 | { 30 | MethodReturnType = new Expr(returnType); 31 | 32 | MethodName = new Expr(funcName); 33 | 34 | Arguments = arguments; 35 | 36 | Body = body; 37 | 38 | IsAnonymous = isAnon; 39 | } 40 | 41 | 42 | public override void Visit(IAstVisitor visitor) 43 | { 44 | visitor.Visit(this); 45 | } 46 | 47 | public override AstTypes AstType 48 | { 49 | get { return AstTypes.MethodDeclr; } 50 | } 51 | 52 | public override string ToString() 53 | { 54 | return "Declare " + MethodName + " ret: " + MethodReturnType + ", args " + Arguments.Aggregate("", (a, b) => a + b + ",") + " with body " + Body.ScopedStatements.Aggregate("", (acc, item) => acc + item + ","); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Lang/AST/NewArrayAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.AST 7 | { 8 | class NewArrayAst : NewAst 9 | { 10 | public NewArrayAst(Ast name, Ast size) : base(name, new List{size}) 11 | { 12 | IsArray = true; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Lang/AST/NewAst.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Lang.Data; 4 | using Lang.Visitors; 5 | 6 | namespace Lang.AST 7 | { 8 | public class NewAst : Ast 9 | { 10 | public bool IsArray { get; set; } 11 | 12 | public NewAst(Ast name, List args) : base(name.Token) 13 | { 14 | Args = args; 15 | Name = name; 16 | } 17 | 18 | public List Args { get; set; } 19 | 20 | public Ast Name { get; set; } 21 | 22 | public override void Visit(IAstVisitor visitor) 23 | { 24 | visitor.Visit(this); 25 | } 26 | 27 | public override AstTypes AstType 28 | { 29 | get { return AstTypes.New; } 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return string.Format("new {0} with args {1}", Name, 35 | CollectionUtil.IsNullOrEmpty(Args) ? "n/a" : Args.Aggregate(")", (item, acc) => item + acc + "\n")); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Lang/AST/PrintAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class PrintAst : Ast 11 | { 12 | public Ast Expression { get; private set; } 13 | public PrintAst(Ast expression) : base(new Token(TokenType.Print)) 14 | { 15 | Expression = expression; 16 | } 17 | 18 | public override void Visit(IAstVisitor visitor) 19 | { 20 | visitor.Visit(this); 21 | } 22 | 23 | public override AstTypes AstType 24 | { 25 | get { return AstTypes.Print; } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Lang/AST/ReturnAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class ReturnAst : Ast 11 | { 12 | public Ast ReturnExpression { get; private set; } 13 | public ReturnAst(Ast expression) : base(new Token(TokenType.Return)) 14 | { 15 | ReturnExpression = expression; 16 | } 17 | 18 | public ReturnAst() 19 | : base(new Token(TokenType.Return)) 20 | { 21 | 22 | } 23 | 24 | public override void Visit(IAstVisitor visitor) 25 | { 26 | visitor.Visit(this); 27 | } 28 | 29 | public override AstTypes AstType 30 | { 31 | get { return AstTypes.Return; } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Lang/AST/ScopeDeclr.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class ScopeDeclr : Ast 11 | { 12 | public List ScopedStatements { get; private set; } 13 | 14 | public ScopeDeclr(List statements) : base(new Token(TokenType.ScopeStart)) 15 | { 16 | ScopedStatements = statements; 17 | } 18 | 19 | 20 | public override void Visit(IAstVisitor visitor) 21 | { 22 | visitor.Visit(this); 23 | } 24 | 25 | public override AstTypes AstType 26 | { 27 | get { return AstTypes.ScopeDeclr; } 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return "SCOPE: \n" + ScopedStatements.Aggregate("", (item, acc) => item + acc + "\n"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Lang/AST/TryCatchAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class TryCatchAst : Ast 11 | { 12 | public ScopeDeclr TryBody { get; set; } 13 | public ScopeDeclr CatchBody { get; set; } 14 | 15 | public TryCatchAst(ScopeDeclr tryBody, ScopeDeclr catchBody) : base(new Token(TokenType.Try)) 16 | { 17 | TryBody = tryBody; 18 | CatchBody = catchBody; 19 | } 20 | 21 | public override void Visit(IAstVisitor visitor) 22 | { 23 | visitor.Visit(this); 24 | } 25 | 26 | public override AstTypes AstType 27 | { 28 | get { return AstTypes.TryCatch; } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Lang/AST/VarDeclrAst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class VarDeclrAst : Ast 11 | { 12 | public bool IsArray { get; set; } 13 | 14 | public Ast DeclarationType { get; private set; } 15 | 16 | public Ast VariableValue { get; private set; } 17 | 18 | public Ast VariableName { get; private set; } 19 | 20 | protected VarDeclrAst(Token token) : base(token) 21 | { 22 | } 23 | 24 | public VarDeclrAst(Token declType, Token name) 25 | : base(name) 26 | { 27 | DeclarationType = new Expr(declType); 28 | 29 | VariableName = new Expr(name); 30 | } 31 | 32 | public VarDeclrAst(Token declType, Token name, Ast value) 33 | : base(name) 34 | { 35 | DeclarationType = new Expr(declType); 36 | 37 | VariableValue = value; 38 | 39 | VariableName = new Expr(name); 40 | } 41 | 42 | 43 | public override void Visit(IAstVisitor visitor) 44 | { 45 | visitor.Visit(this); 46 | } 47 | 48 | public override AstTypes AstType 49 | { 50 | get { return AstTypes.VarDeclr; } 51 | } 52 | 53 | public override string ToString() 54 | { 55 | return String.Format("Declare {0} as {1} with value {2}", 56 | VariableName, DeclarationType, VariableValue); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Lang/AST/WhileLoop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Visitors; 7 | 8 | namespace Lang.AST 9 | { 10 | public class WhileLoop : Ast 11 | { 12 | public Ast Predicate { get; private set; } 13 | 14 | public ScopeDeclr Body { get; private set; } 15 | 16 | public WhileLoop(Token token) : base(token) 17 | { 18 | } 19 | 20 | public WhileLoop(Ast predicate, ScopeDeclr body) 21 | : this(new Token(TokenType.While)) 22 | { 23 | Predicate = predicate; 24 | Body = body; 25 | } 26 | 27 | 28 | public override void Visit(IAstVisitor visitor) 29 | { 30 | visitor.Visit(this); 31 | } 32 | 33 | public override AstTypes AstType 34 | { 35 | get { return AstTypes.While; } 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return "While (" + Predicate + ") do " + Body.ScopedStatements.Aggregate("", (acc, item) => acc + item + ","); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Lang/Data/SpecialNames.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Data 7 | { 8 | public static class SpecialNames 9 | { 10 | public const string CONSTRUCTOR_NAME = "init"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Lang/Data/Token.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Data 7 | { 8 | public class Token 9 | { 10 | public TokenType TokenType { get; private set; } 11 | 12 | public String TokenValue { get; private set; } 13 | 14 | public Token(TokenType tokenType, String token) 15 | { 16 | TokenType = tokenType; 17 | TokenValue = token; 18 | } 19 | 20 | public Token(TokenType tokenType) 21 | { 22 | TokenValue = null; 23 | TokenType = tokenType; 24 | } 25 | 26 | public override string ToString() 27 | { 28 | return TokenType + ": " + TokenValue; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Lang/Data/TokenType.cs: -------------------------------------------------------------------------------- 1 | namespace Lang.Data 2 | { 3 | public enum TokenType 4 | { 5 | Infer, 6 | Void, 7 | WhiteSpace, 8 | LBracket, 9 | RBracket, 10 | Plus, 11 | Minus, 12 | Equals, 13 | HashTag, 14 | QuotedString, 15 | Word, 16 | Comma, 17 | OpenParenth, 18 | CloseParenth, 19 | Asterix, 20 | Slash, 21 | Carat, 22 | DeRef, 23 | Ampersand, 24 | Fun, 25 | GreaterThan, 26 | LessThan, 27 | SemiColon, 28 | If, 29 | Return, 30 | While, 31 | Else, 32 | ScopeStart, 33 | EOF, 34 | For, 35 | Float, 36 | Print, 37 | Dot, 38 | True, 39 | False, 40 | Boolean, 41 | Or, 42 | Int, 43 | Double, 44 | String, 45 | Method, 46 | Class, 47 | New, 48 | Compare, 49 | Nil, 50 | NotCompare, 51 | Try, 52 | Catch, 53 | LSquareBracket, 54 | RSquareBracket 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Lang/Data/ValueMemory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Symbols; 6 | 7 | namespace Lang.Data 8 | { 9 | class ValueMemory 10 | { 11 | public dynamic Value { get; set; } 12 | public MemorySpace Memory { get; set; } 13 | 14 | public ValueMemory(dynamic value, MemorySpace memory) 15 | { 16 | Value = value; 17 | Memory = memory; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Lang/Exceptions/InvalidSyntax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Exceptions 7 | { 8 | public class InvalidSyntax : Exception 9 | { 10 | public InvalidSyntax(string format) : base(format) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Lang/Exceptions/ReturnException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Exceptions 7 | { 8 | public class ReturnException : Exception 9 | { 10 | public dynamic Value { get; private set; } 11 | 12 | public ReturnException(dynamic value) 13 | { 14 | Value = value; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Lang/Exceptions/UndefinedElementException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Exceptions 7 | { 8 | public class UndefinedElementException : Exception 9 | { 10 | public UndefinedElementException(string msg, params string[] param) : base(String.Format(msg, param)) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Lang/Lang.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {37F13707-0783-424E-8D35-7518377A3A7A} 9 | Library 10 | Properties 11 | Lang 12 | Lang 13 | v4.0 14 | 15 | 16 | 512 17 | 18 | 19 | x86 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | x86 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | False 44 | ..\packages\NUnit.2.6.2\lib\nunit.framework.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 124 | -------------------------------------------------------------------------------- /Lang/Lexers/Lexer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lang.Data; 5 | using Lang.Matches; 6 | 7 | namespace Lang.Lexers 8 | { 9 | public class Lexer 10 | { 11 | private Tokenizer Tokenizer { get; set; } 12 | 13 | private List Matchers { get; set; } 14 | 15 | public Lexer(String source) 16 | { 17 | Tokenizer = new Tokenizer(source); 18 | } 19 | 20 | public IEnumerable Lex() 21 | { 22 | Matchers = InitializeMatchList(); 23 | 24 | var current = Next(); 25 | 26 | while (current != null && current.TokenType != TokenType.EOF) 27 | { 28 | // skip whitespace 29 | if (current.TokenType != TokenType.WhiteSpace) 30 | { 31 | yield return current; 32 | } 33 | 34 | current = Next(); 35 | } 36 | } 37 | 38 | private List InitializeMatchList() 39 | { 40 | // the order here matters because it defines token precedence 41 | 42 | var matchers = new List(64); 43 | 44 | var keywordmatchers = new List 45 | { 46 | new MatchKeyword(TokenType.Void, "void"), 47 | new MatchKeyword(TokenType.Int, "int"), 48 | new MatchKeyword(TokenType.Fun, "fun"), 49 | new MatchKeyword(TokenType.If, "if"), 50 | new MatchKeyword(TokenType.Infer, "var"), 51 | new MatchKeyword(TokenType.Else, "else"), 52 | new MatchKeyword(TokenType.While, "while"), 53 | new MatchKeyword(TokenType.For, "for"), 54 | new MatchKeyword(TokenType.Return, "return"), 55 | new MatchKeyword(TokenType.Print, "print"), 56 | new MatchKeyword(TokenType.True, "true"), 57 | new MatchKeyword(TokenType.False, "false"), 58 | new MatchKeyword(TokenType.Boolean, "bool"), 59 | new MatchKeyword(TokenType.String, "string"), 60 | new MatchKeyword(TokenType.Method, "method"), 61 | new MatchKeyword(TokenType.Class, "class"), 62 | new MatchKeyword(TokenType.New, "new"), 63 | new MatchKeyword(TokenType.Nil, "nil"), 64 | new MatchKeyword(TokenType.Try, "try"), 65 | new MatchKeyword(TokenType.Catch, "catch") 66 | }; 67 | 68 | 69 | var specialCharacters = new List 70 | { 71 | new MatchKeyword(TokenType.DeRef, "->"), 72 | new MatchKeyword(TokenType.LBracket, "{"), 73 | new MatchKeyword(TokenType.RBracket, "}"), 74 | new MatchKeyword(TokenType.LSquareBracket, "["), 75 | new MatchKeyword(TokenType.RSquareBracket, "]"), 76 | new MatchKeyword(TokenType.Plus, "+"), 77 | new MatchKeyword(TokenType.Minus, "-"), 78 | new MatchKeyword(TokenType.NotCompare, "!="), 79 | new MatchKeyword(TokenType.Compare, "=="), 80 | new MatchKeyword(TokenType.Equals, "="), 81 | new MatchKeyword(TokenType.HashTag, "#"), 82 | new MatchKeyword(TokenType.Comma, ","), 83 | new MatchKeyword(TokenType.OpenParenth, "("), 84 | new MatchKeyword(TokenType.CloseParenth, ")"), 85 | new MatchKeyword(TokenType.Asterix, "*"), 86 | new MatchKeyword(TokenType.Slash, "/"), 87 | new MatchKeyword(TokenType.Carat, "^"), 88 | new MatchKeyword(TokenType.Ampersand, "&"), 89 | new MatchKeyword(TokenType.GreaterThan, ">"), 90 | new MatchKeyword(TokenType.LessThan, "<"), 91 | new MatchKeyword(TokenType.Or, "||"), 92 | new MatchKeyword(TokenType.SemiColon, ";"), 93 | new MatchKeyword(TokenType.Dot, "."), 94 | }; 95 | 96 | // give each keyword the list of possible delimiters and not allow them to be 97 | // substrings of other words, i.e. token fun should not be found in string "function" 98 | keywordmatchers.ForEach(keyword => 99 | { 100 | var current = (keyword as MatchKeyword); 101 | current.AllowAsSubString = false; 102 | current.SpecialCharacters = specialCharacters.Select(i => i as MatchKeyword).ToList(); 103 | }); 104 | 105 | matchers.Add(new MatchString(MatchString.QUOTE)); 106 | matchers.Add(new MatchString(MatchString.TIC)); 107 | matchers.AddRange(specialCharacters); 108 | matchers.AddRange(keywordmatchers); 109 | matchers.AddRange(new List 110 | { 111 | new MatchWhiteSpace(), 112 | new MatchNumber(), 113 | new MatchWord(specialCharacters) 114 | }); 115 | 116 | return matchers; 117 | } 118 | 119 | private Token Next() 120 | { 121 | if (Tokenizer.End()) 122 | { 123 | return new Token(TokenType.EOF); 124 | } 125 | 126 | return 127 | (from match in Matchers 128 | let token = match.IsMatch(Tokenizer) 129 | where token != null 130 | select token).FirstOrDefault(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Lang/Lexers/TokenizableStreamBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Lang.Lexers 5 | { 6 | public class TokenizableStreamBase where T : class 7 | { 8 | public TokenizableStreamBase(Func> extractor) 9 | { 10 | Index = 0; 11 | 12 | Items = extractor(); 13 | 14 | SnapshotIndexes = new Stack(); 15 | } 16 | 17 | private List Items { get; set; } 18 | 19 | protected int Index { get; set; } 20 | 21 | private Stack SnapshotIndexes { get; set; } 22 | 23 | public virtual T Current 24 | { 25 | get 26 | { 27 | if (EOF(0)) 28 | { 29 | return null; 30 | } 31 | 32 | return Items[Index]; 33 | } 34 | } 35 | 36 | public void Consume() 37 | { 38 | Index++; 39 | } 40 | 41 | private Boolean EOF(int lookahead) 42 | { 43 | if (Index + lookahead >= Items.Count) 44 | { 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | public Boolean End() 52 | { 53 | return EOF(0); 54 | } 55 | 56 | public virtual T Peek(int lookahead) 57 | { 58 | if (EOF(lookahead)) 59 | { 60 | return null; 61 | } 62 | 63 | return Items[Index + lookahead]; 64 | } 65 | 66 | public void TakeSnapshot() 67 | { 68 | SnapshotIndexes.Push(Index); 69 | } 70 | 71 | public void RollbackSnapshot() 72 | { 73 | Index = SnapshotIndexes.Pop(); 74 | } 75 | 76 | public void CommitSnapshot() 77 | { 78 | SnapshotIndexes.Pop(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Lang/Lexers/Tokenizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | 5 | namespace Lang.Lexers 6 | { 7 | public class Tokenizer : TokenizableStreamBase 8 | { 9 | public Tokenizer(String source) 10 | : base(() => source.ToCharArray().Select(i => i.ToString(CultureInfo.InvariantCulture)).ToList()) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Lang/Matches/IMatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Lexers; 7 | 8 | namespace Lang.Matches 9 | { 10 | public interface IMatcher 11 | { 12 | Token IsMatch(Tokenizer tokenizer); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Lang/Matches/MatchKeyword.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using Lang.Data; 6 | using Lang.Lexers; 7 | 8 | namespace Lang.Matches 9 | { 10 | public class MatchKeyword : MatcherBase 11 | { 12 | public string Match { get; set; } 13 | 14 | private TokenType TokenType { get; set; } 15 | 16 | 17 | /// 18 | /// If true then matching on { in a string like "{test" will match the first cahracter 19 | /// because it is not space delimited. If false it must be space or special character delimited 20 | /// 21 | public Boolean AllowAsSubString { get; set; } 22 | 23 | public List SpecialCharacters { get; set; } 24 | 25 | public MatchKeyword(TokenType type, String match) 26 | { 27 | Match = match; 28 | TokenType = type; 29 | AllowAsSubString = true; 30 | } 31 | 32 | protected override Token IsMatchImpl(Tokenizer tokenizer) 33 | { 34 | foreach (var character in Match) 35 | { 36 | if (tokenizer.Current == character.ToString(CultureInfo.InvariantCulture)) 37 | { 38 | tokenizer.Consume(); 39 | } 40 | else 41 | { 42 | return null; 43 | } 44 | } 45 | 46 | bool found; 47 | 48 | if (!AllowAsSubString) 49 | { 50 | var next = tokenizer.Current; 51 | 52 | found = String.IsNullOrWhiteSpace(next) || SpecialCharacters.Any(character => character.Match == next); 53 | } 54 | else 55 | { 56 | found = true; 57 | } 58 | 59 | if (found) 60 | { 61 | return new Token(TokenType, Match); 62 | } 63 | 64 | return null; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Lang/Matches/MatchNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using Lang.Data; 7 | using Lang.Lexers; 8 | 9 | namespace Lang.Matches 10 | { 11 | public class MatchNumber : MatcherBase 12 | { 13 | protected override Token IsMatchImpl(Tokenizer tokenizer) 14 | { 15 | 16 | var leftOperand = GetIntegers(tokenizer); 17 | 18 | if (leftOperand != null) 19 | { 20 | if (tokenizer.Current == ".") 21 | { 22 | tokenizer.Consume(); 23 | 24 | var rightOperand = GetIntegers(tokenizer); 25 | 26 | // found a float 27 | if (rightOperand != null) 28 | { 29 | return new Token(TokenType.Float, leftOperand + "." + rightOperand); 30 | } 31 | } 32 | 33 | return new Token(TokenType.Int, leftOperand); 34 | } 35 | 36 | return null; 37 | } 38 | 39 | private String GetIntegers(Tokenizer tokenizer) 40 | { 41 | var regex = new Regex("[0-9]"); 42 | 43 | String num = null; 44 | 45 | while (tokenizer.Current != null && regex.IsMatch(tokenizer.Current)) 46 | { 47 | num += tokenizer.Current; 48 | tokenizer.Consume(); 49 | } 50 | 51 | if (num != null) 52 | { 53 | return num; 54 | } 55 | 56 | return null; 57 | 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Lang/Matches/MatchString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Lexers; 7 | 8 | namespace Lang.Matches 9 | { 10 | public class MatchString : MatcherBase 11 | { 12 | public const string QUOTE = "\""; 13 | 14 | public const string TIC = "'"; 15 | 16 | private String StringDelim { get; set; } 17 | 18 | public MatchString(String delim) 19 | { 20 | StringDelim = delim; 21 | } 22 | 23 | protected override Token IsMatchImpl(Tokenizer tokenizer) 24 | { 25 | var str = new StringBuilder(); 26 | 27 | if (tokenizer.Current == StringDelim) 28 | { 29 | tokenizer.Consume(); 30 | 31 | while (!tokenizer.End() && tokenizer.Current != StringDelim) 32 | { 33 | str.Append(tokenizer.Current); 34 | tokenizer.Consume(); 35 | } 36 | 37 | if (tokenizer.Current == StringDelim) 38 | { 39 | tokenizer.Consume(); 40 | } 41 | } 42 | 43 | if (str.Length > 0) 44 | { 45 | return new Token(TokenType.QuotedString, str.ToString()); 46 | } 47 | 48 | return null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Lang/Matches/MatchWhiteSpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Lexers; 7 | 8 | namespace Lang.Matches 9 | { 10 | class MatchWhiteSpace : MatcherBase 11 | { 12 | protected override Token IsMatchImpl(Tokenizer tokenizer) 13 | { 14 | bool foundWhiteSpace = false; 15 | 16 | while (!tokenizer.End() && String.IsNullOrWhiteSpace(tokenizer.Current)) 17 | { 18 | foundWhiteSpace = true; 19 | 20 | tokenizer.Consume(); 21 | } 22 | 23 | if (foundWhiteSpace) 24 | { 25 | return new Token(TokenType.WhiteSpace); 26 | } 27 | 28 | return null; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Lang/Matches/MatchWord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Exceptions; 7 | using Lang.Lexers; 8 | 9 | namespace Lang.Matches 10 | { 11 | public class MatchWord : MatcherBase 12 | { 13 | private List SpecialCharacters { get; set; } 14 | public MatchWord(IEnumerable keywordMatchers) 15 | { 16 | SpecialCharacters = keywordMatchers.Select(i=>i as MatchKeyword).Where(i=> i != null).ToList(); 17 | } 18 | 19 | protected override Token IsMatchImpl(Tokenizer tokenizer) 20 | { 21 | String current = null; 22 | 23 | while (!tokenizer.End() && !String.IsNullOrWhiteSpace(tokenizer.Current) && SpecialCharacters.All(m => m.Match != tokenizer.Current)) 24 | { 25 | current += tokenizer.Current; 26 | tokenizer.Consume(); 27 | } 28 | 29 | if (current == null) 30 | { 31 | return null; 32 | } 33 | 34 | // can't start a word with a special character 35 | if (SpecialCharacters.Any(c => current.StartsWith(c.Match))) 36 | { 37 | throw new InvalidSyntax(String.Format("Cannot start a word with a special character {0}", current)); 38 | } 39 | 40 | return new Token(TokenType.Word, current); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Lang/Matches/MatcherBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Lexers; 7 | 8 | namespace Lang.Matches 9 | { 10 | public abstract class MatcherBase : IMatcher 11 | { 12 | public Token IsMatch(Tokenizer tokenizer) 13 | { 14 | if (tokenizer.End()) 15 | { 16 | return new Token(TokenType.EOF); 17 | } 18 | 19 | tokenizer.TakeSnapshot(); 20 | 21 | var match = IsMatchImpl(tokenizer); 22 | 23 | if (match == null) 24 | { 25 | tokenizer.RollbackSnapshot(); 26 | } 27 | else 28 | { 29 | tokenizer.CommitSnapshot(); 30 | } 31 | 32 | return match; 33 | } 34 | 35 | protected abstract Token IsMatchImpl(Tokenizer tokenizer); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Lang/Parser/LanguageParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lang.AST; 5 | using Lang.Data; 6 | using Lang.Exceptions; 7 | using Lang.Utils; 8 | 9 | namespace Lang.Parser 10 | { 11 | public class LanguageParser 12 | { 13 | private ParseableTokenStream TokenStream { get; set; } 14 | 15 | public LanguageParser(Lexers.Lexer lexer) 16 | { 17 | TokenStream = new ParseableTokenStream(lexer); 18 | 19 | // we'll tag all lambdas we find starting from 1000 here 20 | // later when we iterate over scope and create anonnymous lambdas 21 | // we need the lambdas to have the SAME name even if we iterate over 22 | // the syntax tree multiple times. this is hacky, i know. 23 | // curried functions will be labeled from 0 to 1000 24 | LambdaDeclr.LambdaCount = 1000; 25 | } 26 | 27 | public Ast Parse() 28 | { 29 | var statements = new List(1024); 30 | 31 | while (TokenStream.Current.TokenType != TokenType.EOF) 32 | { 33 | statements.Add(ScopeStart().Or(Statement)); 34 | } 35 | 36 | return new ScopeDeclr(statements); 37 | } 38 | 39 | 40 | #region Statement Parsers 41 | 42 | private Ast Class() 43 | { 44 | Func classTaker = () => 45 | { 46 | if (TokenStream.Current.TokenType == TokenType.Class) 47 | { 48 | TokenStream.Take(TokenType.Class); 49 | 50 | var className = TokenStream.Take(TokenType.Word); 51 | 52 | var body = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket, Statement, false); 53 | 54 | return new ClassAst(className, body); 55 | } 56 | 57 | return null; 58 | }; 59 | 60 | return TokenStream.Capture(classTaker); 61 | } 62 | 63 | #region Single statement 64 | 65 | /// 66 | /// Class, method declaration or inner statements 67 | /// 68 | /// 69 | private Ast Statement() 70 | { 71 | var ast = TokenStream.Capture(Class) 72 | .Or(() => TokenStream.Capture(MethodDeclaration)); 73 | 74 | if (ast != null) 75 | { 76 | return ast; 77 | } 78 | 79 | // must be an inner statement if the other two didn't pass 80 | // these are statements that can be inside of scopes such as classes 81 | // methods, or just global scope 82 | var statement = InnerStatement(); 83 | 84 | if (TokenStream.Current.TokenType == TokenType.SemiColon) 85 | { 86 | TokenStream.Take(TokenType.SemiColon); 87 | } 88 | 89 | return statement; 90 | } 91 | 92 | 93 | /// 94 | /// A statement inside of a valid scope 95 | /// 96 | /// 97 | private Ast InnerStatement() 98 | { 99 | // ordering here matters since it resolves to precedence 100 | var ast = TryCatch().Or(ScopeStart) 101 | .Or(LambdaStatement) 102 | .Or(VariableDeclWithAssignStatement) 103 | .Or(VariableDeclrStatement) 104 | .Or(GetIf) 105 | .Or(GetWhile) 106 | .Or(GetFor) 107 | .Or(GetReturn) 108 | .Or(PrintStatement) 109 | .Or(ArrayIndex) 110 | .Or(Expression) 111 | .Or(New); 112 | 113 | if (ast != null) 114 | { 115 | return ast; 116 | } 117 | 118 | throw new InvalidSyntax(String.Format("Unknown expression type {0} - {1}", TokenStream.Current.TokenType, TokenStream.Current.TokenValue)); 119 | } 120 | 121 | private Ast TryCatch() 122 | { 123 | if (TokenStream.Current.TokenType == TokenType.Try) 124 | { 125 | TokenStream.Take(TokenType.Try); 126 | 127 | var tryBody = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 128 | 129 | ScopeDeclr catchBody = null; 130 | if (TokenStream.Current.TokenType == TokenType.Catch) 131 | { 132 | TokenStream.Take(TokenType.Catch); 133 | 134 | catchBody = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 135 | } 136 | 137 | return new TryCatchAst(tryBody, catchBody); 138 | } 139 | 140 | return null; 141 | } 142 | 143 | private Ast New() 144 | { 145 | Func op = () => 146 | { 147 | if (TokenStream.Current.TokenType == TokenType.New) 148 | { 149 | TokenStream.Take(TokenType.New); 150 | 151 | if (ValidNewable()) 152 | { 153 | var name = new Expr(TokenStream.Take(TokenStream.Current.TokenType)); 154 | 155 | if (TokenStream.Current.TokenType == TokenType.LSquareBracket) 156 | { 157 | TokenStream.Take(TokenType.LSquareBracket); 158 | 159 | var size = Expression(); 160 | 161 | TokenStream.Take(TokenType.RSquareBracket); 162 | 163 | return new NewArrayAst(name, size); 164 | } 165 | 166 | var args = GetArgumentList(); 167 | 168 | return new NewAst(name, args); 169 | } 170 | } 171 | 172 | if (TokenStream.Current.TokenType == TokenType.OpenParenth && 173 | TokenStream.Peek(1).TokenType == TokenType.New) 174 | { 175 | TokenStream.Take(TokenType.OpenParenth); 176 | 177 | var item = New(); 178 | 179 | TokenStream.Take(TokenType.CloseParenth); 180 | 181 | return item; 182 | } 183 | 184 | return null; 185 | }; 186 | 187 | return TokenStream.Capture(op); 188 | } 189 | 190 | private Ast ClassReferenceStatement() 191 | { 192 | Func reference = () => 193 | { 194 | var references = new List(); 195 | 196 | var classInstance = New().Or(() => new Expr(TokenStream.Take(TokenType.Word))); 197 | 198 | while (true) 199 | { 200 | if (TokenStream.Current.TokenType == TokenType.Dot) 201 | { 202 | TokenStream.Take(TokenType.Dot); 203 | } 204 | else 205 | { 206 | if (references.Count == 0) 207 | { 208 | return null; 209 | } 210 | 211 | if (references.Count > 0) 212 | { 213 | return new ClassReference(classInstance, references); 214 | } 215 | } 216 | 217 | var deref = FunctionCallStatement().Or(() => TokenStream.Current.TokenType == TokenType.Word ? new Expr(TokenStream.Take(TokenType.Word)) : null); 218 | 219 | references.Add(deref); 220 | } 221 | }; 222 | 223 | return TokenStream.Capture(reference); 224 | } 225 | 226 | private Ast ScopeStart() 227 | { 228 | if (TokenStream.Current.TokenType == TokenType.LBracket) 229 | { 230 | var statements = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 231 | 232 | return statements; 233 | } 234 | 235 | return null; 236 | } 237 | 238 | #endregion 239 | 240 | #region Print 241 | 242 | private Ast PrintStatement() 243 | { 244 | Func op = () => 245 | { 246 | TokenStream.Take(TokenType.Print); 247 | 248 | var expr = InnerStatement(); 249 | 250 | if (expr != null) 251 | { 252 | return new PrintAst(expr); 253 | } 254 | 255 | return null; 256 | }; 257 | 258 | if (TokenStream.Alt(op)) 259 | { 260 | return TokenStream.Get(op); 261 | } 262 | 263 | return null; 264 | } 265 | 266 | #endregion 267 | 268 | 269 | #region Expressions of single items or expr op expr 270 | 271 | private Ast Expression() 272 | { 273 | if (IsValidOperand() || TokenStream.Current.TokenType == TokenType.New) 274 | { 275 | return ParseExpression(); 276 | } 277 | 278 | switch (TokenStream.Current.TokenType) 279 | { 280 | case TokenType.OpenParenth: 281 | 282 | Func basicOp = () => 283 | { 284 | TokenStream.Take(TokenType.OpenParenth); 285 | 286 | var expr = Expression(); 287 | 288 | TokenStream.Take(TokenType.CloseParenth); 289 | 290 | return expr; 291 | }; 292 | 293 | Func doubleOp = () => 294 | { 295 | var op1 = basicOp(); 296 | 297 | var op = Operator(); 298 | 299 | var expr = Expression(); 300 | 301 | return new Expr(op1, op, expr); 302 | }; 303 | 304 | return TokenStream.Capture(doubleOp) 305 | .Or(() => TokenStream.Capture(basicOp)); 306 | 307 | default: 308 | return null; 309 | } 310 | } 311 | 312 | private Ast ParseExpression() 313 | { 314 | Func, Func, Ast> op = (leftFunc, rightFunc) => 315 | { 316 | var left = leftFunc(); 317 | 318 | if (left == null) 319 | { 320 | return null; 321 | } 322 | 323 | var opType = Operator(); 324 | 325 | var right = rightFunc(); 326 | 327 | if (right == null) 328 | { 329 | return null; 330 | } 331 | 332 | return new Expr(left, opType, right); 333 | }; 334 | 335 | Func leftOp = () => op(ExpressionTerminal, Expression); 336 | 337 | return TokenStream.Capture(leftOp) 338 | .Or(() => TokenStream.Capture(ExpressionTerminal)); 339 | } 340 | 341 | #endregion 342 | 343 | #region Return 344 | 345 | private Ast GetReturn() 346 | { 347 | if (TokenStream.Current.TokenType == TokenType.Return && TokenStream.Alt(ParseReturn)) 348 | { 349 | return TokenStream.Get(ParseReturn); 350 | } 351 | 352 | return null; 353 | } 354 | 355 | private ReturnAst ParseReturn() 356 | { 357 | TokenStream.Take(TokenType.Return); 358 | 359 | if (TokenStream.Current.TokenType == TokenType.SemiColon) 360 | { 361 | return new ReturnAst(); 362 | } 363 | 364 | return new ReturnAst(InnerStatement()); 365 | } 366 | 367 | 368 | #endregion 369 | 370 | #region Conditionals and Loops 371 | 372 | private Ast GetWhile() 373 | { 374 | if (TokenStream.Current.TokenType == TokenType.While) 375 | { 376 | Func op = () => 377 | { 378 | var predicateAndStatements = GetPredicateAndStatements(TokenType.While); 379 | 380 | var predicate = predicateAndStatements.Item1; 381 | 382 | var statements = predicateAndStatements.Item2; 383 | 384 | return new WhileLoop(predicate, statements); 385 | }; 386 | 387 | return TokenStream.Capture(op); 388 | } 389 | 390 | return null; 391 | } 392 | 393 | private Ast GetIf() 394 | { 395 | if (TokenStream.Current.TokenType == TokenType.If) 396 | { 397 | return TokenStream.Capture(ParseIf); 398 | } 399 | 400 | return null; 401 | } 402 | 403 | 404 | private Ast GetFor() 405 | { 406 | if (TokenStream.Current.TokenType == TokenType.For && TokenStream.Alt(ParseFor)) 407 | { 408 | return TokenStream.Get(ParseFor); 409 | } 410 | 411 | return null; 412 | } 413 | 414 | private ForLoop ParseFor() 415 | { 416 | TokenStream.Take(TokenType.For); 417 | 418 | var args = GetArgumentList(); 419 | 420 | var init = args[0]; 421 | 422 | var condition = args[1]; 423 | 424 | var modify = args[2]; 425 | 426 | var body = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 427 | 428 | return new ForLoop(init, condition, modify, body); 429 | } 430 | 431 | private Conditional ParseIf() 432 | { 433 | var predicateAndExpressions = GetPredicateAndStatements(TokenType.If); 434 | 435 | var predicate = predicateAndExpressions.Item1; 436 | var statements = predicateAndExpressions.Item2; 437 | 438 | // no else following, then just basic if statement 439 | if (TokenStream.Current.TokenType != TokenType.Else) 440 | { 441 | return new Conditional(new Token(TokenType.If), predicate, statements); 442 | } 443 | 444 | // we found an else if scenario 445 | if (TokenStream.Peek(1).TokenType == TokenType.If) 446 | { 447 | TokenStream.Take(TokenType.Else); 448 | 449 | var alternate = ParseIf(); 450 | 451 | return new Conditional(new Token(TokenType.If), predicate, statements, alternate); 452 | } 453 | 454 | // found a trailing else 455 | return new Conditional(new Token(TokenType.If), predicate, statements, ParseTrailingElse()); 456 | } 457 | 458 | private Conditional ParseTrailingElse() 459 | { 460 | TokenStream.Take(TokenType.Else); 461 | 462 | var statements = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 463 | 464 | return new Conditional(new Token(TokenType.Else), null, statements); 465 | } 466 | 467 | #endregion 468 | 469 | #region Function parsing (lambdas, declarations, arguments) 470 | 471 | private Ast FunctionCall() 472 | { 473 | var name = TokenStream.Take(TokenType.Word); 474 | 475 | var args = GetArgumentList(); 476 | 477 | return new FuncInvoke(name, args); 478 | } 479 | 480 | 481 | private Ast Lambda() 482 | { 483 | TokenStream.Take(TokenType.Fun); 484 | 485 | var arguments = GetArgumentList(true); 486 | 487 | TokenStream.Take(TokenType.DeRef); 488 | 489 | var lines = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 490 | 491 | var method = new LambdaDeclr(arguments, lines); 492 | 493 | return method; 494 | } 495 | 496 | 497 | private Ast MethodDeclaration() 498 | { 499 | if (!IsValidMethodReturnType()) 500 | { 501 | throw new InvalidSyntax("Invalid syntax"); 502 | } 503 | 504 | // return type 505 | var returnType = TokenStream.Take(TokenStream.Current.TokenType); 506 | 507 | // func name 508 | var funcName = TokenStream.Take(TokenType.Word); 509 | 510 | var argList = GetArgumentList(true); 511 | 512 | var innerExpressions = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 513 | 514 | return new MethodDeclr(returnType, funcName, argList, innerExpressions); 515 | } 516 | 517 | private List GetArgumentList(bool includeType = false) 518 | { 519 | TokenStream.Take(TokenType.OpenParenth); 520 | 521 | var args = new List(64); 522 | 523 | while (TokenStream.Current.TokenType != TokenType.CloseParenth) 524 | { 525 | var argument = includeType ? VariableDeclaration() : InnerStatement(); 526 | 527 | args.Add(argument); 528 | 529 | if (TokenStream.Current.TokenType == TokenType.Comma || TokenStream.Current.TokenType == TokenType.SemiColon) 530 | { 531 | TokenStream.Take(TokenStream.Current.TokenType); 532 | } 533 | } 534 | 535 | TokenStream.Take(TokenType.CloseParenth); 536 | 537 | return args; 538 | } 539 | 540 | #endregion 541 | 542 | #region Variable Declrations and Assignments 543 | 544 | private Ast VariableDeclarationAndAssignment() 545 | { 546 | var isVar = TokenStream.Current.TokenType == TokenType.Infer; 547 | 548 | if ((isVar || IsValidMethodReturnType()) && IsValidVariableName(TokenStream.Peek(1))) 549 | { 550 | var type = TokenStream.Take(TokenStream.Current.TokenType); 551 | 552 | var name = TokenStream.Take(TokenType.Word); 553 | 554 | TokenStream.Take(TokenType.Equals); 555 | 556 | bool isLink = false; 557 | if (TokenStream.Current.TokenType == TokenType.Ampersand) 558 | { 559 | isLink = true; 560 | TokenStream.Take(TokenType.Ampersand); 561 | } 562 | 563 | var expr = InnerStatement(); 564 | 565 | expr.IsLink = isLink; 566 | 567 | return new VarDeclrAst(type, name, expr); 568 | } 569 | 570 | return null; 571 | } 572 | 573 | private Ast VariableDeclaration() 574 | { 575 | if (IsValidMethodReturnType() && IsValidVariableName(TokenStream.Peek(1))) 576 | { 577 | var type = TokenStream.Take(TokenStream.Current.TokenType); 578 | 579 | var name = TokenStream.Take(TokenType.Word); 580 | 581 | if (TokenStream.Current.TokenType != TokenType.LSquareBracket) 582 | { 583 | return new VarDeclrAst(type, name); 584 | } 585 | 586 | TokenStream.Take(TokenType.LSquareBracket); 587 | 588 | TokenStream.Take(TokenType.RSquareBracket); 589 | 590 | return new ArrayDeclrAst(type, name); 591 | } 592 | 593 | return null; 594 | } 595 | 596 | private Ast VariableAssignment() 597 | { 598 | var name = TokenStream.Take(TokenType.Word); 599 | 600 | var equals = TokenStream.Take(TokenType.Equals); 601 | 602 | return new Expr(new Expr(name), equals, InnerStatement()); 603 | } 604 | 605 | #endregion 606 | 607 | #region Single Expressions or Tokens 608 | 609 | private Ast ExpressionTerminal() 610 | { 611 | return ClassReferenceStatement().Or(FunctionCallStatement) 612 | .Or(VariableAssignmentStatement) 613 | .Or(New) 614 | .Or(SingleToken); 615 | } 616 | 617 | private Ast ArrayIndex() 618 | { 619 | Func op = () => 620 | { 621 | var id = Expression(); 622 | 623 | TokenStream.Take(TokenType.LSquareBracket); 624 | 625 | var expr = Expression(); 626 | 627 | TokenStream.Take(TokenType.RSquareBracket); 628 | 629 | return new ArrayIndexAst(id, expr); 630 | }; 631 | 632 | return TokenStream.Capture(op); 633 | } 634 | 635 | private Ast SingleToken() 636 | { 637 | if (IsValidOperand()) 638 | { 639 | var token = new Expr(TokenStream.Take(TokenStream.Current.TokenType)); 640 | 641 | return token; 642 | } 643 | 644 | return null; 645 | } 646 | 647 | private Token Operator() 648 | { 649 | if (TokenUtil.IsOperator(TokenStream.Current)) 650 | { 651 | return TokenStream.Take(TokenStream.Current.TokenType); 652 | } 653 | 654 | throw new InvalidSyntax(String.Format("Invalid token found. Expected operator but found {0} - {1}", TokenStream.Current.TokenType, TokenStream.Current.TokenValue)); 655 | } 656 | 657 | #endregion 658 | 659 | #region Helpers 660 | 661 | private Tuple GetPredicateAndStatements(TokenType type) 662 | { 663 | TokenStream.Take(type); 664 | 665 | TokenStream.Take(TokenType.OpenParenth); 666 | 667 | var predicate = Expression(); 668 | 669 | TokenStream.Take(TokenType.CloseParenth); 670 | 671 | var statements = GetStatementsInScope(TokenType.LBracket, TokenType.RBracket); 672 | 673 | return new Tuple(predicate, statements); 674 | } 675 | 676 | private ScopeDeclr GetStatementsInScope(TokenType open, TokenType close, bool expectSemicolon = true) 677 | { 678 | return GetStatementsInScope(open, close, InnerStatement, expectSemicolon); 679 | } 680 | 681 | private ScopeDeclr GetStatementsInScope(TokenType open, TokenType close, Func getter, bool expectSemicolon = true) 682 | { 683 | TokenStream.Take(open); 684 | var lines = new List(); 685 | while (TokenStream.Current.TokenType != close) 686 | { 687 | var statement = getter(); 688 | 689 | lines.Add(statement); 690 | 691 | if (expectSemicolon && StatementExpectsSemiColon(statement)) 692 | { 693 | TokenStream.Take(TokenType.SemiColon); 694 | } 695 | } 696 | 697 | TokenStream.Take(close); 698 | 699 | return new ScopeDeclr(lines); 700 | } 701 | 702 | 703 | private bool StatementExpectsSemiColon(Ast statement) 704 | { 705 | return !(statement is MethodDeclr || 706 | statement is Conditional || 707 | statement is WhileLoop || 708 | statement is TryCatchAst || 709 | statement is ForLoop); 710 | } 711 | 712 | #endregion 713 | 714 | #endregion 715 | 716 | #region TokenStream.Alternative Route Testers 717 | 718 | private Ast VariableDeclWithAssignStatement() 719 | { 720 | return TokenStream.Capture(VariableDeclarationAndAssignment); 721 | } 722 | 723 | private Ast VariableAssignmentStatement() 724 | { 725 | return TokenStream.Capture(VariableAssignment); 726 | } 727 | 728 | private Ast VariableDeclrStatement() 729 | { 730 | return TokenStream.Capture(VariableDeclaration); 731 | } 732 | 733 | private Ast FunctionCallStatement() 734 | { 735 | return TokenStream.Capture(FunctionCall); 736 | } 737 | 738 | private Ast LambdaStatement() 739 | { 740 | return TokenStream.Capture(Lambda); 741 | } 742 | 743 | private bool IsValidMethodReturnType() 744 | { 745 | switch (TokenStream.Current.TokenType) 746 | { 747 | case TokenType.Void: 748 | case TokenType.Word: 749 | case TokenType.Int: 750 | case TokenType.String: 751 | case TokenType.Infer: 752 | case TokenType.Method: 753 | case TokenType.Boolean: 754 | return true; 755 | } 756 | return false; 757 | } 758 | 759 | private bool IsValidOperand() 760 | { 761 | switch (TokenStream.Current.TokenType) 762 | { 763 | case TokenType.Int: 764 | case TokenType.QuotedString: 765 | case TokenType.Word: 766 | case TokenType.True: 767 | case TokenType.Float: 768 | case TokenType.Nil: 769 | case TokenType.False: 770 | return true; 771 | } 772 | return false; 773 | } 774 | 775 | private bool ValidNewable() 776 | { 777 | switch (TokenStream.Current.TokenType) 778 | { 779 | case TokenType.Int: 780 | case TokenType.String: 781 | case TokenType.Word: 782 | case TokenType.Float: 783 | case TokenType.Boolean: 784 | return true; 785 | } 786 | return false; 787 | } 788 | 789 | private bool IsValidVariableName(Token item) 790 | { 791 | switch (item.TokenType) 792 | { 793 | case TokenType.Word: 794 | return true; 795 | } 796 | return false; 797 | } 798 | 799 | 800 | 801 | #endregion 802 | } 803 | } 804 | -------------------------------------------------------------------------------- /Lang/Parser/ParseableTokenStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lang.AST; 5 | using Lang.Data; 6 | using Lang.Exceptions; 7 | using Lang.Lexers; 8 | 9 | namespace Lang.Parser 10 | { 11 | internal class Memo 12 | { 13 | public Ast Ast { get; set; } 14 | public int NextIndex { get; set; } 15 | } 16 | 17 | public class ParseableTokenStream : TokenizableStreamBase 18 | { 19 | public ParseableTokenStream(Lexer lexer) : base (() => lexer.Lex().ToList()) 20 | { 21 | } 22 | 23 | private Dictionary CachedAst = new Dictionary(); 24 | 25 | public Boolean IsMatch(TokenType type) 26 | { 27 | if (Current.TokenType == type) 28 | { 29 | return true; 30 | } 31 | 32 | return false; 33 | } 34 | 35 | public Ast Capture(Func ast) 36 | { 37 | if (Alt(ast)) 38 | { 39 | return Get(ast); 40 | } 41 | 42 | return null; 43 | } 44 | 45 | /// 46 | /// Retrieves a cached version if it was found during any alternate route 47 | /// otherwise executes it 48 | /// 49 | /// 50 | /// 51 | public Ast Get(Func getter) 52 | { 53 | Memo memo; 54 | if (!CachedAst.TryGetValue(Index, out memo)) 55 | { 56 | return getter(); 57 | } 58 | 59 | Index = memo.NextIndex; 60 | 61 | //Console.WriteLine("Returning type {0} from index {1} as memo", memo.Ast, Index); 62 | return memo.Ast; 63 | } 64 | 65 | public Token Take(TokenType type) 66 | { 67 | if (IsMatch(type)) 68 | { 69 | var current = Current; 70 | 71 | Consume(); 72 | 73 | return current; 74 | } 75 | 76 | throw new InvalidSyntax(String.Format("Invalid Syntax. Expecting {0} but got {1}", type, Current.TokenType)); 77 | } 78 | 79 | 80 | 81 | 82 | public Boolean Alt(Func action) 83 | { 84 | TakeSnapshot(); 85 | 86 | Boolean found = false; 87 | 88 | try 89 | { 90 | var currentIndex = Index; 91 | 92 | var ast = action(); 93 | 94 | if (ast != null) 95 | { 96 | found = true; 97 | 98 | CachedAst[currentIndex] = new Memo 99 | { 100 | Ast = ast, 101 | NextIndex = Index 102 | }; 103 | } 104 | } 105 | catch 106 | { 107 | 108 | } 109 | 110 | RollbackSnapshot(); 111 | 112 | return found; 113 | } 114 | 115 | public override Token Peek(int lookahead) 116 | { 117 | var peeker = base.Peek(lookahead); 118 | 119 | if (peeker == null) 120 | { 121 | return new Token(TokenType.EOF); 122 | } 123 | 124 | return peeker; 125 | } 126 | 127 | public override Token Current 128 | { 129 | get 130 | { 131 | var current = base.Current; 132 | if (current == null) 133 | { 134 | return new Token(TokenType.EOF); 135 | } 136 | return current; 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Lang/Spaces/IScopeable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Spaces 7 | { 8 | public interface IScopeable where T : class, new() 9 | { 10 | void SetParentScope(T scope); 11 | List> ChildScopes { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Lang/Spaces/MemorySpace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lang.Data; 3 | using System.Collections.Generic; 4 | using Lang.Spaces; 5 | 6 | namespace Lang.Symbols 7 | { 8 | public class MemorySpace : IScopeable 9 | { 10 | public Dictionary Values { get; set; } 11 | 12 | public Dictionary Links { get; set; } 13 | 14 | public MemorySpace EnclosingSpace { get; private set; } 15 | 16 | public MemorySpace() 17 | { 18 | Values = new Dictionary(); 19 | 20 | ChildScopes = new List>(64); 21 | 22 | Links = new Dictionary(); 23 | } 24 | 25 | public void Define(string name, object value) 26 | { 27 | Values[name] = value; 28 | } 29 | 30 | public void Link(string target, string source) 31 | { 32 | Links[target] = source; 33 | } 34 | 35 | public void Assign(string name, object value) 36 | { 37 | string link; 38 | while (Links.TryGetValue(name, out link)) 39 | { 40 | name = link; 41 | } 42 | 43 | if (Values.ContainsKey(name)) 44 | { 45 | Values[name] = value; 46 | 47 | return; 48 | } 49 | 50 | if (EnclosingSpace != null) 51 | { 52 | EnclosingSpace.Assign(name, value); 53 | } 54 | else 55 | { 56 | throw new Exception( 57 | String.Format( 58 | "Attempting to update variable {0} with value {1} but varialbe isn't defined in any memory scope", 59 | name, value)); 60 | } 61 | } 62 | 63 | public object Get(string name, bool local = false) 64 | { 65 | string link; 66 | 67 | if (Links.TryGetValue(name, out link)) 68 | { 69 | return Get(link); 70 | } 71 | 72 | object o; 73 | if (Values.TryGetValue(name, out o)) 74 | { 75 | return o; 76 | } 77 | 78 | if (EnclosingSpace != null && local == false) 79 | { 80 | return EnclosingSpace.Get(name); 81 | } 82 | 83 | return null; 84 | } 85 | 86 | public void SetParentScope(MemorySpace scope) 87 | { 88 | EnclosingSpace = scope; 89 | } 90 | 91 | public List> ChildScopes { get; private set; } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Lang/Spaces/Scope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Lang.AST; 4 | using Lang.Symbols; 5 | 6 | namespace Lang.Spaces 7 | { 8 | [Serializable] 9 | public class Scope : IScopeable 10 | { 11 | public Dictionary Symbols { get; set; } 12 | 13 | public Scope EnclosingScope { get; private set; } 14 | 15 | public Boolean AllowAllForwardReferences { get; set; } 16 | 17 | public void SetParentScope(Scope scope) 18 | { 19 | EnclosingScope = scope; 20 | } 21 | 22 | public List> ChildScopes { get; private set; } 23 | 24 | public Scope() 25 | { 26 | Symbols = new Dictionary(); 27 | 28 | ChildScopes = new List>(64); 29 | 30 | AllowAllForwardReferences = false; 31 | } 32 | 33 | public String ScopeName { get; set; } 34 | 35 | public void Define(Symbol symbol) 36 | { 37 | Symbols[symbol.Name] = symbol; 38 | } 39 | 40 | public Boolean AllowedForwardReferences(Ast ast) 41 | { 42 | if (Symbols.ContainsKey(ast.Token.TokenValue)) 43 | { 44 | return AllowAllForwardReferences; 45 | } 46 | 47 | if (EnclosingScope == null) 48 | { 49 | return false; 50 | } 51 | 52 | return EnclosingScope.AllowedForwardReferences(ast); 53 | } 54 | 55 | public Symbol Resolve(Ast ast) 56 | { 57 | return Resolve(ast.Token.TokenValue); 58 | } 59 | 60 | public Symbol Resolve(String name) 61 | { 62 | Symbol o; 63 | if (Symbols.TryGetValue(name, out o)) 64 | { 65 | return o; 66 | } 67 | 68 | if (EnclosingScope == null) 69 | { 70 | return null; 71 | } 72 | 73 | return EnclosingScope.Resolve(name); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Lang/Spaces/ScopeContainer.cs: -------------------------------------------------------------------------------- 1 | namespace Lang.Spaces 2 | { 3 | public enum ScopeType 4 | { 5 | Class, 6 | Global 7 | } 8 | public class ScopeContainer 9 | { 10 | public ScopeStack Global { get; set; } 11 | 12 | public ScopeStack Class { get; set; } 13 | 14 | public ScopeContainer() 15 | { 16 | Global = new ScopeStack(); 17 | 18 | Class = new ScopeStack(); 19 | 20 | CurrentScopeType = ScopeType.Global; 21 | } 22 | 23 | public ScopeType CurrentScopeType { get; set; } 24 | 25 | public ScopeStack CurrentScopeStack 26 | { 27 | get 28 | { 29 | switch (CurrentScopeType) 30 | { 31 | case ScopeType.Class: 32 | return Class; 33 | case ScopeType.Global: 34 | return Global; 35 | } 36 | 37 | return null; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Lang/Spaces/ScopeStack.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Lang.Spaces 4 | { 5 | public class ScopeStack where T : class, IScopeable, new() 6 | { 7 | private Stack Stack { get; set; } 8 | 9 | public T Current { get; set; } 10 | 11 | public ScopeStack() 12 | { 13 | Stack = new Stack(); 14 | } 15 | 16 | public void CreateScope() 17 | { 18 | var parentScope = Current; 19 | 20 | if (Current != null) 21 | { 22 | Stack.Push(Current); 23 | } 24 | 25 | Current = new T(); 26 | 27 | Current.SetParentScope(parentScope); 28 | 29 | if (parentScope != null) 30 | { 31 | parentScope.ChildScopes.Add(Current); 32 | } 33 | } 34 | 35 | public void PopScope() 36 | { 37 | if (Stack.Count > 0) 38 | { 39 | Current = Stack.Pop(); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Lang/Symbols/BuiltInType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lang.AST; 3 | 4 | namespace Lang.Symbols 5 | { 6 | [Serializable] 7 | public class BuiltInType : Symbol, IType 8 | { 9 | public BuiltInType(ExpressionTypes type, Ast src = null) 10 | : base(type.ToString()) 11 | { 12 | ExpressionType = type; 13 | 14 | Src = src; 15 | } 16 | 17 | public string TypeName 18 | { 19 | get { return Name; } 20 | } 21 | 22 | public ExpressionTypes ExpressionType { get; set; } 23 | 24 | public Ast Src { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Lang/Symbols/ClassSymbol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.AST; 6 | 7 | namespace Lang.Symbols 8 | { 9 | [Serializable] 10 | public class ClassSymbol : Symbol, IType 11 | { 12 | public ClassSymbol(string name) 13 | : base(name, new UserDefinedType(name)) 14 | { 15 | ExpressionType = ExpressionTypes.UserDefined; 16 | } 17 | 18 | public string TypeName 19 | { 20 | get { return Name; } 21 | } 22 | 23 | public ExpressionTypes ExpressionType { get; set; } 24 | 25 | public Ast Src { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Lang/Symbols/ExpressionTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Symbols 7 | { 8 | public enum ExpressionTypes 9 | { 10 | Int, 11 | String, 12 | UserDefined, 13 | Void, 14 | Boolean, 15 | Float, 16 | Inferred, 17 | Method, 18 | Nil 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Lang/Symbols/IType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.AST; 6 | 7 | namespace Lang.Symbols 8 | { 9 | public interface IType 10 | { 11 | String TypeName { get; } 12 | ExpressionTypes ExpressionType { get; } 13 | Ast Src { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Lang/Symbols/MethodSymbol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.AST; 6 | 7 | namespace Lang.Symbols 8 | { 9 | [Serializable] 10 | public class MethodSymbol : Symbol 11 | { 12 | public MethodDeclr MethodDeclr { get; private set; } 13 | 14 | public MemorySpace Environment { get; set; } 15 | 16 | public MethodSymbol(string name, IType returnType, MethodDeclr declr) 17 | : base(name, returnType) 18 | { 19 | MethodDeclr = declr; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Lang/Symbols/StructSymbol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Symbols.Symbosl2; 6 | 7 | namespace Lang.Symbols 8 | { 9 | public class StructSymbol : ISymbol 10 | { 11 | public String Name { get; set; } 12 | 13 | public Dictionary Symbols { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Lang/Symbols/Symbol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lang.Spaces; 3 | 4 | namespace Lang.Symbols 5 | { 6 | [Serializable] 7 | public class Symbol : Scope 8 | { 9 | public String Name { get; private set; } 10 | public IType Type { get; private set; } 11 | 12 | public bool IsArray { get; set; } 13 | 14 | public Symbol(String name, IType type) 15 | { 16 | Name = name; 17 | Type = type; 18 | } 19 | 20 | public Symbol(String name) 21 | { 22 | Name = name; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Lang/Symbols/UserDefinedType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.AST; 6 | 7 | namespace Lang.Symbols 8 | { 9 | [Serializable] 10 | public class UserDefinedType : Symbol, IType 11 | { 12 | public UserDefinedType(string name) : base(name) 13 | { 14 | ExpressionType = ExpressionTypes.UserDefined; 15 | } 16 | 17 | public string TypeName 18 | { 19 | get { return Name; } 20 | } 21 | 22 | public ExpressionTypes ExpressionType { get; set; } 23 | public Ast Src { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Lang/Utils/CollectionUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang 7 | { 8 | public static class CollectionUtil 9 | { 10 | public static void ForEach(this IEnumerable list, Action action) 11 | { 12 | foreach (var i in list) 13 | { 14 | action(i); 15 | } 16 | } 17 | 18 | public static Boolean IsNullOrEmpty(ICollection collection) 19 | { 20 | return collection == null || collection.Count == 0; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Lang/Utils/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.Serialization.Formatters.Binary; 6 | using System.Text; 7 | 8 | namespace Lang.Utils 9 | { 10 | public static class ExtensionMethods 11 | { 12 | // Deep clone 13 | public static T DeepClone(this T a) 14 | { 15 | using (MemoryStream stream = new MemoryStream()) 16 | { 17 | BinaryFormatter formatter = new BinaryFormatter(); 18 | formatter.Serialize(stream, a); 19 | stream.Position = 0; 20 | return (T)formatter.Deserialize(stream); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Lang/Utils/Maybe.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Utils 7 | { 8 | public static class Maybe 9 | { 10 | public static TInput Or(this TInput input, Func evaluator) 11 | where TInput : class 12 | { 13 | if (input != null) 14 | { 15 | return input; 16 | } 17 | 18 | return evaluator(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Lang/Utils/NullTester.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Lang.Data; 3 | 4 | namespace Lang.Utils 5 | { 6 | public static class NullTester 7 | { 8 | public static bool NullEqual(dynamic left, dynamic right) 9 | { 10 | if (left is TokenType && right is TokenType) 11 | { 12 | return left == TokenType.Nil && right == TokenType.Nil; 13 | } 14 | 15 | return false; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lang/Utils/ScopeUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.AST; 6 | using Lang.Data; 7 | using Lang.Symbols; 8 | 9 | namespace Lang.Utils 10 | { 11 | class ScopeUtil 12 | { 13 | /// 14 | /// Determines user type 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | public static IType GetExpressionType(Ast left, Ast right, Token token) 21 | { 22 | switch (token.TokenType) 23 | { 24 | case TokenType.Ampersand: 25 | case TokenType.Or: 26 | case TokenType.GreaterThan: 27 | case TokenType.LessThan: 28 | return new BuiltInType(ExpressionTypes.Boolean); 29 | 30 | case TokenType.Infer: 31 | return right.AstSymbolType; 32 | } 33 | 34 | if (left.AstSymbolType.ExpressionType != right.AstSymbolType.ExpressionType) 35 | { 36 | throw new Exception("Mismatched types"); 37 | } 38 | 39 | return left.AstSymbolType; 40 | } 41 | 42 | public static IType CreateSymbolType(Ast astType) 43 | { 44 | if (astType == null) 45 | { 46 | return null; 47 | } 48 | 49 | Func op = () => 50 | { 51 | switch (astType.Token.TokenType) 52 | { 53 | case TokenType.Int: 54 | return new BuiltInType(ExpressionTypes.Int); 55 | case TokenType.Float: 56 | return new BuiltInType(ExpressionTypes.Float); 57 | case TokenType.Void: 58 | return new BuiltInType(ExpressionTypes.Void); 59 | case TokenType.Infer: 60 | return new BuiltInType(ExpressionTypes.Inferred); 61 | case TokenType.QuotedString: 62 | case TokenType.String: 63 | return new BuiltInType(ExpressionTypes.String); 64 | case TokenType.Word: 65 | return new UserDefinedType(astType.Token.TokenValue); 66 | case TokenType.Boolean: 67 | case TokenType.False: 68 | case TokenType.True: 69 | return new BuiltInType(ExpressionTypes.Boolean); 70 | case TokenType.Method: 71 | return new BuiltInType(ExpressionTypes.Method); 72 | } 73 | return null; 74 | }; 75 | 76 | var type = op(); 77 | 78 | if (type != null) 79 | { 80 | type.Src = astType; 81 | } 82 | 83 | return type; 84 | } 85 | 86 | public static Symbol DefineUserSymbol(Ast ast, Ast name) 87 | { 88 | IType type = CreateSymbolType(ast); 89 | 90 | return new Symbol(name.Token.TokenValue, type); 91 | } 92 | 93 | public static Symbol DefineUserSymbol(IType type, Ast name) 94 | { 95 | return new Symbol(name.Token.TokenValue, type); 96 | } 97 | 98 | public static Symbol DefineMethod(MethodDeclr method) 99 | { 100 | IType returnType = CreateSymbolType(method.MethodReturnType); 101 | 102 | return new MethodSymbol(method.Token.TokenValue, returnType, method); 103 | } 104 | 105 | public static Symbol DefineClassSymbol(ClassAst ast) 106 | { 107 | return new ClassSymbol(ast.Token.TokenValue) 108 | { 109 | Src = ast, 110 | ScopeName = ast.Token.TokenValue 111 | }; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Lang/Utils/TokenUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.Data; 6 | using Lang.Symbols; 7 | 8 | namespace Lang.Utils 9 | { 10 | public static class TokenUtil 11 | { 12 | public static Boolean EqualOrPromotable(IType item1, IType item2) 13 | { 14 | return EqualOrPromotable(item1.ExpressionType, item2.ExpressionType); 15 | } 16 | 17 | public static Boolean IsOperator(Token item) 18 | { 19 | switch (item.TokenType) 20 | { 21 | case TokenType.Equals: 22 | case TokenType.Plus: 23 | case TokenType.Minus: 24 | case TokenType.Asterix: 25 | case TokenType.Carat: 26 | case TokenType.GreaterThan: 27 | case TokenType.LessThan: 28 | case TokenType.Compare: 29 | case TokenType.NotCompare: 30 | case TokenType.Ampersand: 31 | case TokenType.Or: 32 | case TokenType.Slash: 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | public static bool EqualOrPromotable(ExpressionTypes item1, ExpressionTypes item2) 39 | { 40 | return item1 == item2 || 41 | item1 == ExpressionTypes.Method || 42 | item2 == ExpressionTypes.Method || 43 | item1 == ExpressionTypes.Inferred || 44 | item2 == ExpressionTypes.Inferred || 45 | item2 == ExpressionTypes.Nil || item1 == ExpressionTypes.Nil; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Lang/Visitors/IAcceptVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Lang.Visitors 7 | { 8 | interface IAcceptVisitor 9 | { 10 | void Visit(IAstVisitor visitor); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Lang/Visitors/IAstVisitor.cs: -------------------------------------------------------------------------------- 1 | using Lang.AST; 2 | 3 | namespace Lang.Visitors 4 | { 5 | public interface IAstVisitor 6 | { 7 | void Visit(Conditional ast); 8 | void Visit(Expr ast); 9 | void Visit(FuncInvoke ast); 10 | void Visit(VarDeclrAst ast); 11 | void Visit(MethodDeclr ast); 12 | void Visit(WhileLoop ast); 13 | void Visit(ScopeDeclr ast); 14 | void Visit(ForLoop ast); 15 | void Visit(ReturnAst ast); 16 | void Visit(PrintAst ast); 17 | 18 | void Start(Ast ast); 19 | void Visit(ClassAst ast); 20 | void Visit(ClassReference ast); 21 | void Visit(NewAst ast); 22 | void Visit(TryCatchAst ast); 23 | void Visit(ArrayIndexAst ast); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Lang/Visitors/InterpretorVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lang.AST; 5 | using Lang.Data; 6 | using Lang.Exceptions; 7 | using Lang.Spaces; 8 | using Lang.Symbols; 9 | using Lang.Utils; 10 | 11 | namespace Lang.Visitors 12 | { 13 | public class InterpretorVisitor : IAstVisitor 14 | { 15 | private ScopeStack MemorySpaces { get; set; } 16 | 17 | public Stack Environment { get; set; } 18 | 19 | public MemorySpace Global { get; set; } 20 | 21 | public InterpretorVisitor() 22 | { 23 | Environment = new Stack(); 24 | MemorySpaces = new ScopeStack(); 25 | } 26 | 27 | 28 | public void Visit(Conditional ast) 29 | { 30 | Exec(ast); 31 | } 32 | 33 | public void Visit(Expr ast) 34 | { 35 | Exec(ast); 36 | } 37 | 38 | public void Visit(FuncInvoke ast) 39 | { 40 | Exec(ast); 41 | } 42 | 43 | public void Visit(VarDeclrAst ast) 44 | { 45 | Exec(ast); 46 | } 47 | 48 | public void Visit(MethodDeclr ast) 49 | { 50 | Exec(ast); 51 | } 52 | 53 | public void Visit(WhileLoop ast) 54 | { 55 | Exec(ast); 56 | } 57 | 58 | public void Visit(ScopeDeclr ast) 59 | { 60 | Exec(ast); 61 | } 62 | 63 | public void Visit(ForLoop ast) 64 | { 65 | Exec(ast); 66 | } 67 | 68 | public void Visit(ReturnAst ast) 69 | { 70 | Exec(ast); 71 | } 72 | 73 | public void Visit(PrintAst ast) 74 | { 75 | Exec(ast); 76 | } 77 | 78 | public void Start(Ast ast) 79 | { 80 | var scopeBuilder = new ScopeBuilderVisitor(); 81 | 82 | var resolver = new ScopeBuilderVisitor(true); 83 | 84 | scopeBuilder.Start(ast); 85 | 86 | resolver.Start(ast); 87 | 88 | Global = MemorySpaces.Current; 89 | 90 | ast.Visit(this); 91 | } 92 | 93 | public void Visit(ClassAst ast) 94 | { 95 | Exec(ast); 96 | } 97 | 98 | public void Visit(ClassReference ast) 99 | { 100 | Exec(ast); 101 | } 102 | 103 | public void Visit(NewAst ast) 104 | { 105 | Exec(ast); 106 | } 107 | 108 | public void Visit(TryCatchAst ast) 109 | { 110 | try 111 | { 112 | Exec(ast.TryBody); 113 | } 114 | catch (ReturnException ex) 115 | { 116 | throw; 117 | } 118 | catch (Exception ex) 119 | { 120 | Exec(ast.CatchBody); 121 | } 122 | } 123 | 124 | public void Visit(ArrayIndexAst ast) 125 | { 126 | throw new NotImplementedException(); 127 | } 128 | 129 | private dynamic Exec(Ast ast) 130 | { 131 | try 132 | { 133 | if (ast == null) 134 | { 135 | return null; 136 | } 137 | 138 | if (ast.ConvertedExpression != null) 139 | { 140 | return Exec(ast.ConvertedExpression); 141 | } 142 | 143 | switch (ast.AstType) 144 | { 145 | case AstTypes.ScopeDeclr: 146 | ScopeDelcaration(ast as ScopeDeclr); 147 | break; 148 | case AstTypes.VarDeclr: 149 | VariableDeclaration(ast as VarDeclrAst); 150 | break; 151 | 152 | case AstTypes.Expression: 153 | var ret = Expression(ast as Expr); 154 | if (ret != null) 155 | { 156 | return ret; 157 | } 158 | break; 159 | case AstTypes.Print: 160 | Print(ast as PrintAst); 161 | break; 162 | case AstTypes.FunctionInvoke: 163 | return InvokeFunction(ast as FuncInvoke); 164 | case AstTypes.Conditional: 165 | ConditionalDo(ast as Conditional); 166 | break; 167 | case AstTypes.MethodDeclr: 168 | var methodDeclr = ast as MethodDeclr; 169 | return new MethodSymbol(methodDeclr.Token.TokenValue, ScopeUtil.CreateSymbolType(methodDeclr.ReturnAst), methodDeclr); 170 | 171 | case AstTypes.While: 172 | WhileDo(ast as WhileLoop); 173 | break; 174 | case AstTypes.Return: 175 | ReturnDo(ast as ReturnAst); 176 | break; 177 | case AstTypes.For: 178 | ForDo(ast as ForLoop); 179 | break; 180 | case AstTypes.Class: 181 | ClassDo(ast as ClassAst); 182 | break; 183 | case AstTypes.ClassRef: 184 | return ClassRefDo(ast as ClassReference); 185 | break; 186 | case AstTypes.New: 187 | return NewDo(ast as NewAst); 188 | break; 189 | } 190 | } 191 | catch (ReturnException ex) 192 | { 193 | // let the return value bubble up through all the exectuions until we 194 | // get to the source syntax tree that invoked it. at that point safely return the value 195 | if (ast.AstType == AstTypes.FunctionInvoke) 196 | { 197 | return ex.Value; 198 | } 199 | 200 | throw; 201 | } 202 | 203 | return null; 204 | } 205 | 206 | private object NewDo(NewAst ast) 207 | { 208 | var className = Resolve(ast); 209 | 210 | var classType = (className as ClassSymbol).Src as ClassAst; 211 | 212 | var space = new MemorySpace(); 213 | 214 | var oldSpace = MemorySpaces.Current; 215 | 216 | MemorySpaces.Current = space; 217 | 218 | foreach (var symbol in classType.Body.ScopedStatements) 219 | { 220 | Exec(symbol); 221 | } 222 | 223 | if (classType.Constructor != null) 224 | { 225 | var funcInvoke = new FuncInvoke(classType.Constructor.Token, ast.Args); 226 | 227 | funcInvoke.CurrentScope = classType.Body.CurrentScope; 228 | 229 | Exec(funcInvoke); 230 | } 231 | 232 | MemorySpaces.Current = oldSpace; 233 | 234 | return space; 235 | } 236 | 237 | private dynamic ClassRefDo(ClassReference classReference) 238 | { 239 | var oldSpace = MemorySpaces.Current; 240 | 241 | var memorySpace = Get(classReference.ClassInstance).Value as MemorySpace; 242 | 243 | MemorySpaces.Current = memorySpace; 244 | 245 | try 246 | { 247 | if (classReference.Deferences.Count == 0) 248 | { 249 | return memorySpace; 250 | } 251 | 252 | foreach (var deref in classReference.Deferences) 253 | { 254 | // make sure that the last dereference knows how to pull 255 | // its arguments, which are from the original memory space and not 256 | // the relative class space. i.e. A.b.foo(x), x is loaded from the 257 | // space that contains A, not from the space of b. 258 | 259 | if (deref == classReference.Deferences.Last()) 260 | { 261 | deref.CallingMemory = oldSpace; 262 | } 263 | 264 | var newSpace = GetValue(Exec(deref)); 265 | 266 | if (deref == classReference.Deferences.Last()) 267 | { 268 | return newSpace; 269 | } 270 | 271 | MemorySpaces.Current = newSpace; 272 | } 273 | } 274 | finally 275 | { 276 | MemorySpaces.Current = oldSpace; 277 | } 278 | 279 | return null; 280 | } 281 | 282 | private void ClassDo(ClassAst classAst) 283 | { 284 | // nothing to do here 285 | } 286 | 287 | private void ForDo(ForLoop forLoop) 288 | { 289 | MemorySpaces.CreateScope(); 290 | 291 | Exec(forLoop.Setup); 292 | 293 | while (GetValue(Exec(forLoop.Predicate))) 294 | { 295 | Exec(forLoop.Update); 296 | 297 | Exec(forLoop.Body); 298 | } 299 | 300 | MemorySpaces.PopScope(); 301 | } 302 | 303 | private void ReturnDo(ReturnAst returnAst) 304 | { 305 | if (returnAst.ReturnExpression != null) 306 | { 307 | var value = Exec(returnAst.ReturnExpression); 308 | 309 | throw new ReturnException(value); 310 | } 311 | 312 | throw new ReturnException(null); 313 | } 314 | 315 | private void WhileDo(WhileLoop whileLoop) 316 | { 317 | MemorySpaces.CreateScope(); 318 | 319 | while (GetValue(Exec(whileLoop.Predicate))) 320 | { 321 | whileLoop.Body.ScopedStatements.ForEach(statement => Exec(statement)); 322 | } 323 | 324 | MemorySpaces.PopScope(); 325 | } 326 | 327 | private void ConditionalDo(Conditional conditional) 328 | { 329 | // else has no predicate 330 | if (conditional.Predicate == null) 331 | { 332 | Exec(conditional.Body); 333 | return; 334 | } 335 | 336 | MemorySpaces.CreateScope(); 337 | 338 | var success = Convert.ToBoolean(Exec(conditional.Predicate)); 339 | 340 | if(success) 341 | { 342 | Exec(conditional.Body); 343 | 344 | MemorySpaces.PopScope(); 345 | } 346 | else 347 | { 348 | MemorySpaces.PopScope(); 349 | 350 | Exec(conditional.Alternate); 351 | } 352 | 353 | } 354 | 355 | private object InvokeFunction(FuncInvoke funcInvoke) 356 | { 357 | var method = Resolve(funcInvoke); 358 | 359 | if (method != null) 360 | { 361 | var invoker = method as MethodSymbol; 362 | 363 | if (invoker == null) 364 | { 365 | invoker = MemorySpaces.Current.Get(method.Name) as MethodSymbol; 366 | } 367 | 368 | // arguments should always be resolved from the current calling space 369 | // so make sure the invoking function knows which space it comes from 370 | if (funcInvoke.CallingMemory == null) 371 | { 372 | funcInvoke.CallingMemory = MemorySpaces.Current; 373 | } 374 | 375 | if (funcInvoke.CallingMemory != null && !CollectionUtil.IsNullOrEmpty(funcInvoke.Arguments)) 376 | { 377 | funcInvoke.Arguments.ForEach(arg => arg.CallingMemory = funcInvoke.CallingMemory); 378 | } 379 | 380 | var oldMemory = MemorySpaces.Current; 381 | 382 | try 383 | { 384 | // if we're a lambda and we have some sort of closure 385 | // set our working space to be that. 386 | if (invoker.Environment != null) 387 | { 388 | MemorySpaces.Current = invoker.Environment; 389 | } 390 | 391 | var value = InvokeMethodSymbol(invoker, funcInvoke.Arguments); 392 | 393 | return value; 394 | } 395 | finally 396 | { 397 | MemorySpaces.Current = oldMemory; 398 | } 399 | } 400 | 401 | throw new UndefinedElementException("Undefined method"); 402 | } 403 | 404 | private dynamic GetValue(dynamic value) 405 | { 406 | return value is ValueMemory ? (value as ValueMemory).Value : value; 407 | } 408 | 409 | private object InvokeMethodSymbol(MethodSymbol method, List args) 410 | { 411 | // create a new memory scope. this is where arguments will get defined 412 | // we wont overwrite any memory values since they will all be local here 413 | MemorySpaces.CreateScope(); 414 | 415 | var count = 0; 416 | 417 | if (method.MethodDeclr.Arguments.Count != args.Count) 418 | { 419 | throw new InvalidSyntax(String.Format("Wrong number of arguments passed to method {0}. Got {1}, expected {2}", 420 | method.MethodDeclr.MethodName.Token.TokenValue, 421 | args.Count, method.MethodDeclr.Arguments.Count)); 422 | } 423 | 424 | foreach (VarDeclrAst expectedArgument in method.MethodDeclr.Arguments) 425 | { 426 | var currentArgument = args[count]; 427 | 428 | var oldmemory = MemorySpaces.Current; 429 | 430 | // if the argument is coming from somewhere else, make sure to be able to 431 | // load the argument from its preferred space. 432 | if (currentArgument.CallingMemory != null) 433 | { 434 | MemorySpaces.Current = currentArgument.CallingMemory; 435 | } 436 | 437 | var value = GetValue(Exec(currentArgument)); 438 | 439 | // since we were just loading values from the argument space 440 | // switch back to the current space so we can assign the argument value 441 | // into our local working memory 442 | MemorySpaces.Current = oldmemory; 443 | 444 | if (expectedArgument.VariableValue == null) 445 | { 446 | MemorySpaces.Current.Define(expectedArgument.Token.TokenValue, value); 447 | } 448 | else 449 | { 450 | MemorySpaces.Current.Assign(expectedArgument.Token.TokenValue, value); 451 | } 452 | 453 | // if the passed in argument is a word and not a literal (like string or bool) then 454 | // pull its value from memory so we can match type to the target type 455 | var resolvedSymbol = (currentArgument.Token.TokenType == TokenType.Word ? 456 | MemorySpaces.Current.Get(currentArgument.Token.TokenValue) 457 | : args[count]) as Symbol; 458 | 459 | var resolvedType = resolvedSymbol != null ? resolvedSymbol.Type : currentArgument.AstSymbolType; 460 | 461 | if (currentArgument is MethodDeclr) 462 | { 463 | resolvedType = new BuiltInType(ExpressionTypes.Method); 464 | } 465 | 466 | if (!TokenUtil.EqualOrPromotable(expectedArgument.AstSymbolType, resolvedType)) 467 | { 468 | throw new InvalidSyntax(String.Format("Cannot pass argument {0} of type {1} to function {2} as argument {3} of type {4}", 469 | currentArgument.Token.TokenValue, 470 | currentArgument.AstSymbolType.TypeName, 471 | method.MethodDeclr.MethodName.Token.TokenValue, 472 | expectedArgument.VariableName.Token.TokenValue, 473 | expectedArgument.AstSymbolType.TypeName)); 474 | } 475 | 476 | count++; 477 | } 478 | 479 | var val = Exec(method.MethodDeclr.Body); 480 | 481 | MemorySpaces.PopScope(); 482 | 483 | return val; 484 | } 485 | 486 | private void VariableDeclaration(VarDeclrAst varDeclrAst) 487 | { 488 | if (varDeclrAst.VariableValue == null) 489 | { 490 | var symbol = varDeclrAst.CurrentScope.Resolve(varDeclrAst.VariableName.Token.TokenValue); 491 | 492 | var space = MemorySpaces.Current; 493 | 494 | space.Define(symbol.Name, TokenType.Nil); 495 | 496 | return; 497 | } 498 | 499 | var variableValue = varDeclrAst.VariableValue.ConvertedExpression ?? varDeclrAst.VariableValue; 500 | 501 | // if the rhs of a variable is not a method, then execute it, 502 | if (variableValue.AstType != AstTypes.MethodDeclr) 503 | { 504 | var value = GetValue(Exec(variableValue)); 505 | 506 | if (value != null) 507 | { 508 | var symbol = varDeclrAst.CurrentScope.Resolve(varDeclrAst.VariableName.Token.TokenValue); 509 | 510 | var space = MemorySpaces.Current; 511 | 512 | if (variableValue.IsLink) 513 | { 514 | space.Link(symbol.Name, variableValue.Token.TokenValue); 515 | } 516 | else 517 | { 518 | space.Define(symbol.Name, value); 519 | } 520 | } 521 | } 522 | else 523 | { 524 | var symbol = varDeclrAst.CurrentScope.Resolve(varDeclrAst.VariableName.Token.TokenValue); 525 | 526 | var resolvedMethod = varDeclrAst.CurrentScope.Resolve(variableValue.Token.TokenValue) as MethodSymbol; 527 | 528 | // make sure to create a NEW method symbol. this way each time we declare this item 529 | // it will create a local copy and get its own memory space for closures. 530 | // if we shared the same method symbol then all instances of the same declaration would share the memory space, 531 | // which may not be what we want given class instances having their own spaces 532 | 533 | var localMethodCopy = new MethodSymbol(resolvedMethod.Name, resolvedMethod.Type, 534 | resolvedMethod.MethodDeclr); 535 | 536 | var space = MemorySpaces.Current; 537 | 538 | if (variableValue is LambdaDeclr) 539 | { 540 | localMethodCopy.Environment = space; 541 | } 542 | 543 | space.Define(symbol.Name, localMethodCopy); 544 | } 545 | } 546 | 547 | private void Print(PrintAst ast) 548 | { 549 | var expression = GetValue(Exec(ast.Expression)); 550 | 551 | Console.WriteLine(expression); 552 | } 553 | 554 | private void Assign(Ast ast, dynamic value, MemorySpace space = null) 555 | { 556 | if (value is ValueMemory) 557 | { 558 | var tup = value as ValueMemory; 559 | 560 | tup.Memory.Assign(ast.Token.TokenValue, tup.Value); 561 | 562 | return; 563 | } 564 | 565 | if (space != null) 566 | { 567 | space.Assign(ast.Token.TokenValue, value); 568 | return; 569 | } 570 | 571 | MemorySpaces.Current.Assign(ast.Token.TokenValue, value); 572 | } 573 | 574 | private ValueMemory Get(Ast ast) 575 | { 576 | object item; 577 | 578 | if (Environment.Count > 0) 579 | { 580 | foreach (var env in Environment) 581 | { 582 | item = env.Get(ast.Token.TokenValue, true); 583 | if (item != null) 584 | { 585 | //return item; 586 | return new ValueMemory(item, env); 587 | } 588 | } 589 | } 590 | 591 | item = MemorySpaces.Current.Get(ast.Token.TokenValue); 592 | 593 | if (item != null) 594 | { 595 | return new ValueMemory(item, MemorySpaces.Current); 596 | } 597 | 598 | if (ast.CallingMemory != null) 599 | { 600 | return new ValueMemory(ast.CallingMemory.Get(ast.Token.TokenValue), ast.CallingMemory); 601 | } 602 | 603 | 604 | 605 | 606 | return null; 607 | } 608 | 609 | private dynamic Expression(Expr ast) 610 | { 611 | var lhs = ast.Left; 612 | var rhs = ast.Right; 613 | 614 | switch (ast.Token.TokenType) 615 | { 616 | case TokenType.Equals: 617 | if (lhs.AstType == AstTypes.ClassRef) 618 | { 619 | // a litle trickery here. create a copy of the class reference 620 | // with everytihng up to the second to last item. this gives you 621 | // the workign memory space that the very last item should sit in 622 | // then lets execute this as if we are asking for the memory space 623 | // and finally assign the very last symbol to the calculated memory 624 | // space we got 625 | 626 | var classRef = (lhs as ClassReference); 627 | 628 | var lastItem = classRef.Deferences.Last(); 629 | 630 | var fakeRef = new ClassReference(classRef.ClassInstance, 631 | classRef.Deferences.Take(classRef.Deferences.Count - 1) 632 | .ToList()); 633 | 634 | var space = GetValue(Exec(fakeRef)); 635 | 636 | Assign(lastItem, Exec(rhs), space); 637 | } 638 | 639 | else 640 | { 641 | ValueMemory itemSpace = Get(lhs); 642 | 643 | Assign(lhs, Exec(rhs), itemSpace != null ? itemSpace.Memory : null); 644 | } 645 | return null; 646 | 647 | case TokenType.Word: 648 | return Get(ast); 649 | 650 | case TokenType.Int: 651 | return Convert.ToInt32(ast.Token.TokenValue); 652 | 653 | case TokenType.Float: 654 | return Convert.ToDouble(ast.Token.TokenValue); 655 | 656 | case TokenType.QuotedString: 657 | return ast.Token.TokenValue; 658 | 659 | case TokenType.Nil: 660 | return TokenType.Nil; 661 | 662 | case TokenType.True: 663 | return true; 664 | 665 | case TokenType.False: 666 | return false; 667 | } 668 | 669 | if (TokenUtil.IsOperator(ast.Token)) 670 | { 671 | return ApplyOperation(ast); 672 | } 673 | 674 | return null; 675 | } 676 | 677 | 678 | private object ApplyOperation(Expr ast) 679 | { 680 | dynamic leftExec = Exec(ast.Left); 681 | 682 | var left = leftExec is ValueMemory ? (leftExec as ValueMemory).Value : leftExec; 683 | // short circuit 684 | if (ast.Token.TokenType == TokenType.Or) 685 | { 686 | if (left is bool && left == true) 687 | { 688 | return true; 689 | } 690 | } 691 | 692 | if (ast.Token.TokenType == TokenType.Compare) 693 | { 694 | if (left is bool && left == false) 695 | { 696 | return false; 697 | } 698 | } 699 | 700 | dynamic rightExec = Exec(ast.Right); 701 | 702 | 703 | var right = rightExec is ValueMemory ? (rightExec as ValueMemory).Value : rightExec; 704 | 705 | switch (ast.Token.TokenType) 706 | { 707 | case TokenType.Compare: 708 | if (left is TokenType || right is TokenType) 709 | { 710 | return NullTester.NullEqual(left, right); 711 | } 712 | 713 | return left == right; 714 | case TokenType.NotCompare: 715 | if (left is TokenType || right is TokenType) 716 | { 717 | return !NullTester.NullEqual(left, right); 718 | } 719 | return left != right; 720 | 721 | case TokenType.GreaterThan: 722 | return left > right; 723 | case TokenType.LessThan: 724 | return left < right; 725 | case TokenType.Plus: 726 | return left + right; 727 | case TokenType.Minus: 728 | return left - right; 729 | case TokenType.Slash: 730 | return left/right; 731 | case TokenType.Carat: 732 | return left ^ right; 733 | case TokenType.Ampersand: 734 | return left && right; 735 | case TokenType.Or: 736 | return left || right; 737 | } 738 | 739 | return null; 740 | } 741 | 742 | 743 | private void ScopeDelcaration(ScopeDeclr ast) 744 | { 745 | MemorySpaces.CreateScope(); 746 | 747 | ast.ScopedStatements.ForEach(statement => statement.Visit(this)); 748 | 749 | MemorySpaces.PopScope(); 750 | } 751 | 752 | private Symbol Resolve(Ast ast) 753 | { 754 | var resolved = ast.CurrentScope.Resolve(ast.Token.TokenValue); 755 | 756 | if (resolved == null) 757 | { 758 | resolved = ast.Global.Resolve(ast.Token.TokenValue); 759 | 760 | if (resolved == null) 761 | { 762 | var msg = String.Format("Trying to access undefined function {0}", ast.Token.TokenValue); 763 | 764 | Console.WriteLine(msg); 765 | 766 | throw new UndefinedElementException(msg); 767 | } 768 | } 769 | 770 | return resolved; 771 | } 772 | } 773 | } 774 | -------------------------------------------------------------------------------- /Lang/Visitors/PrintAstVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Lang.AST; 6 | using Lang.Data; 7 | 8 | namespace Lang.Visitors 9 | { 10 | public class PrintAstVisitor : IAstVisitor 11 | { 12 | public void Visit(Conditional ast) 13 | { 14 | Console.Write(ast.Token); 15 | 16 | if (ast.Predicate != null) 17 | { 18 | PrintWrap("Predicate", () => ast.Predicate.Visit(this)); 19 | } 20 | 21 | ast.Body.Visit(this); 22 | 23 | if (ast.Alternate != null) 24 | { 25 | if (ast.Alternate.Token.TokenType == TokenType.If) 26 | { 27 | Console.Write("Else"); 28 | } 29 | 30 | ast.Alternate.Visit(this); 31 | } 32 | } 33 | 34 | public void Visit(Expr ast) 35 | { 36 | if (ast.Left != null) 37 | { 38 | ast.Left.Visit(this); 39 | } 40 | 41 | Console.Write(" " + ast.Token); 42 | 43 | if (ast.Right != null) 44 | { 45 | Console.Write(" "); 46 | 47 | ast.Right.Visit(this); 48 | 49 | Console.WriteLine(); 50 | } 51 | } 52 | 53 | public void Visit(FuncInvoke ast) 54 | { 55 | ast.FunctionName.Visit(this); 56 | 57 | ast.Arguments.ForEach(arg => arg.Visit(this)); 58 | } 59 | 60 | public void Visit(VarDeclrAst ast) 61 | { 62 | if (ast.DeclarationType != null) 63 | { 64 | ast.DeclarationType.Visit(this); 65 | } 66 | 67 | ast.VariableName.Visit(this); 68 | 69 | if (ast.VariableValue != null) 70 | { 71 | Console.WriteLine("Equals"); 72 | 73 | ast.VariableValue.Visit(this); 74 | } 75 | } 76 | 77 | public void Visit(MethodDeclr ast) 78 | { 79 | PrintWrap("MethodDeclaration", () => 80 | { 81 | PrintWrap("Return type", () => ast.MethodReturnType.Visit(this)); 82 | 83 | PrintWrap("FunctionName", () => ast.MethodName.Visit(this)); 84 | 85 | PrintWrap("Arguments", () => ast.Arguments.ForEach(arg => arg.Visit(this))); 86 | 87 | PrintWrap("Body", () => ast.Body.Visit(this)); 88 | }); 89 | } 90 | 91 | public void Visit(WhileLoop ast) 92 | { 93 | Console.Write(ast.Token); 94 | 95 | PrintWrap("Predicate", () => ast.Predicate.Visit(this)); 96 | 97 | ast.Body.Visit(this); 98 | } 99 | 100 | public void Visit(ScopeDeclr ast) 101 | { 102 | PrintWrap("Scope", () => ast.ScopedStatements.ForEach(statement => statement.Visit(this)), true); 103 | } 104 | 105 | public void Visit(ForLoop ast) 106 | { 107 | PrintWrap("ForLoop", () => 108 | { 109 | PrintWrap("For", () => ast.Setup.Visit(this)); 110 | PrintWrap("Until", () => ast.Predicate.Visit(this)); 111 | PrintWrap("Modify", () => ast.Update.Visit(this)); 112 | 113 | ast.Body.Visit(this); 114 | }); 115 | } 116 | 117 | public void Visit(ReturnAst ast) 118 | { 119 | PrintWrap("Return", () => 120 | { 121 | if (ast.ReturnExpression != null) 122 | { 123 | ast.ReturnExpression.Visit(this); 124 | } 125 | }); 126 | } 127 | 128 | public void Visit(PrintAst ast) 129 | { 130 | PrintWrap("Print", () => ast.Expression.Visit(this)); 131 | } 132 | 133 | public void Start(Ast ast) 134 | { 135 | ast.Visit(this); 136 | } 137 | 138 | public void Visit(ClassAst ast) 139 | { 140 | throw new NotImplementedException(); 141 | } 142 | 143 | public void Visit(ClassReference ast) 144 | { 145 | throw new NotImplementedException(); 146 | } 147 | 148 | public void Visit(NewAst ast) 149 | { 150 | throw new NotImplementedException(); 151 | } 152 | 153 | public void Visit(TryCatchAst ast) 154 | { 155 | throw new NotImplementedException(); 156 | } 157 | 158 | public void Visit(ArrayIndexAst ast) 159 | { 160 | throw new NotImplementedException(); 161 | } 162 | 163 | private void PrintWrap(string name, Action action, bool newLine = false) 164 | { 165 | if (newLine) 166 | { 167 | Console.WriteLine(name + " ("); 168 | } 169 | else 170 | { 171 | Console.Write(name + " ("); 172 | } 173 | 174 | 175 | action(); 176 | 177 | Console.WriteLine(" )"); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /Lang/Visitors/ScopeBuilderVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Lang.AST; 5 | using Lang.Data; 6 | using Lang.Exceptions; 7 | using Lang.Spaces; 8 | using Lang.Symbols; 9 | using Lang.Utils; 10 | 11 | namespace Lang.Visitors 12 | { 13 | public class ScopeBuilderVisitor : IAstVisitor 14 | { 15 | private void SetScopeType(ScopeType scopeType) 16 | { 17 | ScopeContainer.CurrentScopeType = scopeType; 18 | } 19 | 20 | private Scope Global { get; set; } 21 | 22 | private Scope Current 23 | { 24 | get { return ScopeTree.Current; } 25 | set { ScopeContainer.CurrentScopeStack.Current = value; } 26 | } 27 | 28 | private ScopeStack ScopeTree { get { return ScopeContainer.CurrentScopeStack; } } 29 | 30 | private MethodDeclr CurrentMethod { get; set; } 31 | 32 | private Boolean ResolvingTypes { get; set; } 33 | 34 | private ScopeContainer ScopeContainer { get; set; } 35 | 36 | public ScopeBuilderVisitor(bool resolvingTypes = false) 37 | { 38 | ResolvingTypes = resolvingTypes; 39 | ScopeContainer = new ScopeContainer(); 40 | } 41 | 42 | public void Visit(Conditional ast) 43 | { 44 | if (ast.Predicate != null) 45 | { 46 | ast.Predicate.Visit(this); 47 | } 48 | 49 | ast.Body.Visit(this); 50 | 51 | if (ast.Alternate != null) 52 | { 53 | ast.Alternate.Visit(this); 54 | } 55 | 56 | SetScope(ast); 57 | } 58 | 59 | public void Visit(Expr ast) 60 | { 61 | if (ast.Left != null) 62 | { 63 | ast.Left.Visit(this); 64 | } 65 | 66 | if (ast.Right != null) 67 | { 68 | ast.Right.Visit(this); 69 | } 70 | 71 | SetScope(ast); 72 | 73 | if (ast.Left == null && ast.Right == null) 74 | { 75 | ast.AstSymbolType = ResolveOrDefine(ast); 76 | } 77 | else 78 | { 79 | if (ResolvingTypes) 80 | { 81 | ast.AstSymbolType = GetExpressionType(ast.Left, ast.Right, ast.Token); 82 | } 83 | } 84 | } 85 | 86 | /// 87 | /// Creates a type for built in types or resolves user defined types 88 | /// 89 | /// 90 | /// 91 | private IType ResolveOrDefine(Expr ast) 92 | { 93 | if (ast == null) 94 | { 95 | return null; 96 | } 97 | 98 | switch (ast.Token.TokenType) 99 | { 100 | case TokenType.Word: return ResolveType(ast); 101 | } 102 | 103 | return ScopeUtil.CreateSymbolType(ast); 104 | } 105 | 106 | /// 107 | /// Determines user type 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | private IType GetExpressionType(Ast left, Ast right, Token token) 114 | { 115 | switch (token.TokenType) 116 | { 117 | case TokenType.Ampersand: 118 | case TokenType.Or: 119 | case TokenType.GreaterThan: 120 | case TokenType.Compare: 121 | case TokenType.LessThan: 122 | case TokenType.NotCompare: 123 | return new BuiltInType(ExpressionTypes.Boolean); 124 | 125 | case TokenType.Method: 126 | case TokenType.Infer: 127 | if (right is MethodDeclr) 128 | { 129 | return new BuiltInType(ExpressionTypes.Method, right); 130 | } 131 | 132 | return right.AstSymbolType; 133 | } 134 | 135 | if (!ResolvingTypes && (left.AstSymbolType == null || right.AstSymbolType == null)) 136 | { 137 | return null; 138 | } 139 | 140 | if (!TokenUtil.EqualOrPromotable(left.AstSymbolType.ExpressionType, right.AstSymbolType.ExpressionType)) 141 | { 142 | throw new Exception("Mismatched types"); 143 | } 144 | 145 | return left.AstSymbolType; 146 | } 147 | 148 | public void Visit(FuncInvoke ast) 149 | { 150 | if (ast.CallingScope != null) 151 | { 152 | ast.Arguments.ForEach(arg => arg.CallingScope = ast.CallingScope); 153 | } 154 | 155 | ast.Arguments.ForEach(arg => arg.Visit(this)); 156 | 157 | SetScope(ast); 158 | 159 | var functionType = Resolve(ast.FunctionName) as MethodSymbol; 160 | 161 | if (functionType != null && ast.Arguments.Count < functionType.MethodDeclr.Arguments.Count) 162 | { 163 | var curriedMethod = CreateCurriedMethod(ast, functionType); 164 | 165 | curriedMethod.Visit(this); 166 | 167 | var methodSymbol = ScopeUtil.DefineMethod(curriedMethod); 168 | 169 | Current.Define(methodSymbol); 170 | 171 | ast.ConvertedExpression = curriedMethod; 172 | } 173 | else if(ResolvingTypes) 174 | { 175 | ast.AstSymbolType = ResolveType(ast.FunctionName, ast.CurrentScope); 176 | } 177 | } 178 | 179 | private LambdaDeclr CreateCurriedMethod(FuncInvoke ast, MethodSymbol functionType) 180 | { 181 | var srcMethod = functionType.MethodDeclr; 182 | 183 | var fixedAssignments = new List(); 184 | 185 | var count = 0; 186 | foreach (var argValue in ast.Arguments) 187 | { 188 | var srcArg = srcMethod.Arguments[count] as VarDeclrAst; 189 | 190 | var token = new Token(srcArg.DeclarationType.Token.TokenType, argValue.Token.TokenValue); 191 | 192 | var declr = new VarDeclrAst(token, srcArg.Token, new Expr(argValue.Token)); 193 | 194 | // if we're creating a curry using a variable then we need to resolve the variable type 195 | // otherwise we can make a symbol for the literal 196 | var newArgType = argValue.Token.TokenType == TokenType.Word ? 197 | ast.CurrentScope.Resolve(argValue).Type 198 | : ScopeUtil.CreateSymbolType(argValue); 199 | 200 | // create a symbol type for the target we're invoking on so we can do type checking 201 | var targetArgType = ScopeUtil.CreateSymbolType(srcArg.DeclarationType); 202 | 203 | if (!TokenUtil.EqualOrPromotable(newArgType, targetArgType)) 204 | { 205 | throw new InvalidSyntax(String.Format("Cannot pass argument {0} of type {1} to partial function {2} as argument {3} of type {4}", 206 | argValue.Token.TokenValue, 207 | newArgType.TypeName, 208 | srcMethod.MethodName.Token.TokenValue, 209 | srcArg.VariableName.Token.TokenValue, 210 | targetArgType.TypeName)); 211 | } 212 | 213 | fixedAssignments.Add(declr); 214 | 215 | count++; 216 | } 217 | 218 | var newBody = fixedAssignments.Concat(srcMethod.Body.ScopedStatements).ToList(); 219 | 220 | var curriedMethod = new LambdaDeclr(srcMethod.Arguments.Skip(ast.Arguments.Count).ToList(), new ScopeDeclr(newBody)); 221 | 222 | SetScope(curriedMethod); 223 | 224 | return curriedMethod; 225 | } 226 | 227 | /// 228 | /// Resolve the target ast type from the current scope, OR give it a scope to use. 229 | /// Since things can be resolved in two passes (initial scope and forward reference scope) 230 | /// we want to be able to pass in a scope override. The second value is usually only ever used 231 | /// on the second pass when determining forward references 232 | /// 233 | /// 234 | /// 235 | /// 236 | private IType ResolveType(Ast ast, Scope currentScope = null) 237 | { 238 | var scopeTrys = new List { currentScope, ast.CurrentScope }; 239 | 240 | try 241 | { 242 | return Current.Resolve(ast).Type; 243 | } 244 | catch (Exception ex) 245 | { 246 | try 247 | { 248 | return ast.CallingScope.Resolve(ast).Type; 249 | } 250 | catch 251 | { 252 | foreach (var scopeTry in scopeTrys) 253 | { 254 | try 255 | { 256 | if (scopeTry == null) 257 | { 258 | continue; 259 | } 260 | 261 | var resolvedType = scopeTry.Resolve(ast); 262 | 263 | var allowedFwdReferences = scopeTry.AllowedForwardReferences(ast); 264 | 265 | if (allowedFwdReferences || 266 | scopeTry.AllowAllForwardReferences || 267 | resolvedType is ClassSymbol || 268 | resolvedType is MethodSymbol) 269 | { 270 | return resolvedType.Type; 271 | } 272 | } 273 | catch 274 | { 275 | 276 | } 277 | } 278 | } 279 | } 280 | 281 | if (ResolvingTypes) 282 | { 283 | if (ast.IsPureDynamic) 284 | { 285 | return new BuiltInType(ExpressionTypes.Inferred); 286 | } 287 | 288 | throw new UndefinedElementException(String.Format("Undefined element {0}", 289 | ast.Token.TokenValue)); 290 | } 291 | 292 | return null; 293 | } 294 | 295 | 296 | private Symbol Resolve(String name) 297 | { 298 | try 299 | { 300 | return Current.Resolve(name); 301 | } 302 | catch (Exception ex) 303 | { 304 | try 305 | { 306 | return Global.Resolve(name); 307 | } 308 | catch 309 | { 310 | if (ResolvingTypes) 311 | { 312 | return null; 313 | } 314 | } 315 | 316 | throw; 317 | } 318 | } 319 | 320 | private Symbol Resolve(Ast ast) 321 | { 322 | return Resolve(ast.Token.TokenValue); 323 | } 324 | 325 | public void Visit(VarDeclrAst ast) 326 | { 327 | var isVar = ast.DeclarationType.Token.TokenType == TokenType.Infer; 328 | 329 | if (ast.DeclarationType != null && !isVar) 330 | { 331 | var symbol = ScopeUtil.DefineUserSymbol(ast.DeclarationType, ast.VariableName); 332 | 333 | symbol.IsArray = ast is ArrayDeclrAst; 334 | 335 | DefineToScope(ast, symbol); 336 | 337 | ast.AstSymbolType = symbol.Type; 338 | } 339 | 340 | if (ast.VariableValue == null && isVar) 341 | { 342 | var symbol = ScopeUtil.DefineUserSymbol(ast.DeclarationType, ast.VariableName); 343 | 344 | DefineToScope(ast, symbol); 345 | 346 | ast.AstSymbolType = symbol.Type; 347 | } 348 | 349 | else if (ast.VariableValue != null) 350 | { 351 | ast.VariableValue.Visit(this); 352 | 353 | // if its type inferred, determine the declaration by the value's type 354 | if (isVar) 355 | { 356 | // if the right hand side is a method declaration, make sure to track the source value 357 | // this way we can reference it later to determine not only that this is a method type, but what 358 | // is the expected return value for static type checking later 359 | 360 | var val = ast.VariableValue.ConvertedExpression ?? ast.VariableValue; 361 | 362 | ast.AstSymbolType = val is MethodDeclr 363 | ? new BuiltInType(ExpressionTypes.Method, val) 364 | : val.AstSymbolType; 365 | 366 | var symbol = ScopeUtil.DefineUserSymbol(ast.AstSymbolType, ast.VariableName); 367 | 368 | symbol.IsArray = ast is ArrayDeclrAst; 369 | 370 | DefineToScope(ast, symbol); 371 | } 372 | else if (ResolvingTypes) 373 | { 374 | var declaredType = ScopeUtil.CreateSymbolType(ast.DeclarationType); 375 | 376 | var value = ast.VariableValue.ConvertedExpression ?? ast.VariableValue; 377 | 378 | ReturnAst returnType = null; 379 | 380 | // when we're resolving types check if the rhs is a function invoke. if it is, see 381 | // what the return value of the src expression is so we can make sure that the 382 | // lhs and the rhs match. 383 | try 384 | { 385 | returnType = 386 | value is FuncInvoke 387 | ? ((value as FuncInvoke).AstSymbolType) != null 388 | ? ((value as FuncInvoke).AstSymbolType.Src as MethodDeclr).ReturnAst 389 | : null 390 | : null; 391 | 392 | } 393 | catch 394 | { 395 | } 396 | 397 | value = returnType != null ? returnType.ReturnExpression : value; 398 | 399 | if (!TokenUtil.EqualOrPromotable(value.AstSymbolType.ExpressionType, declaredType.ExpressionType)) 400 | { 401 | throw new InvalidSyntax(String.Format("Cannot assign {0} of type {1} to {2}", ast.VariableValue, 402 | value.AstSymbolType.ExpressionType, 403 | declaredType.ExpressionType)); 404 | } 405 | 406 | } 407 | } 408 | 409 | SetScope(ast); 410 | } 411 | 412 | private void DefineToScope(Ast ast, Symbol symbol) 413 | { 414 | if (ast.CurrentScope != null && ast.CurrentScope.Symbols.ContainsKey(symbol.Name)) 415 | { 416 | Symbol old = ast.CurrentScope.Resolve(symbol.Name); 417 | if (old.Type == null) 418 | { 419 | ast.CurrentScope.Define(symbol); 420 | } 421 | } 422 | 423 | Current.Define(symbol); 424 | } 425 | 426 | public void Visit(MethodDeclr ast) 427 | { 428 | var previousMethod = CurrentMethod; 429 | 430 | CurrentMethod = ast; 431 | 432 | var symbol = ScopeUtil.DefineMethod(ast); 433 | 434 | Current.Define(symbol); 435 | 436 | ScopeTree.CreateScope(); 437 | 438 | ast.Arguments.ForEach(arg => arg.Visit(this)); 439 | 440 | ast.Body.Visit(this); 441 | 442 | SetScope(ast); 443 | 444 | if (symbol.Type.ExpressionType == ExpressionTypes.Inferred) 445 | { 446 | if (ast.ReturnAst == null) 447 | { 448 | ast.AstSymbolType = new BuiltInType(ExpressionTypes.Void); 449 | } 450 | else 451 | { 452 | ast.AstSymbolType = ast.ReturnAst.AstSymbolType; 453 | } 454 | } 455 | else 456 | { 457 | ast.AstSymbolType = symbol.Type; 458 | } 459 | 460 | ValidateReturnStatementType(ast, symbol); 461 | 462 | ScopeTree.PopScope(); 463 | 464 | CurrentMethod = previousMethod; 465 | } 466 | 467 | private void ValidateReturnStatementType(MethodDeclr ast, Symbol symbol) 468 | { 469 | if (!ResolvingTypes) 470 | { 471 | return; 472 | } 473 | 474 | IType returnStatementType; 475 | 476 | // no return found 477 | if (ast.ReturnAst == null) 478 | { 479 | returnStatementType = new BuiltInType(ExpressionTypes.Void); 480 | } 481 | else 482 | { 483 | returnStatementType = ast.ReturnAst.AstSymbolType; 484 | } 485 | 486 | var delcaredSymbol = ScopeUtil.CreateSymbolType(ast.MethodReturnType); 487 | 488 | // if its inferred, just use whatever the return statement i 489 | if (delcaredSymbol.ExpressionType == ExpressionTypes.Inferred) 490 | { 491 | return; 492 | } 493 | 494 | if (!TokenUtil.EqualOrPromotable(returnStatementType.ExpressionType, delcaredSymbol.ExpressionType)) 495 | { 496 | throw new InvalidSyntax(String.Format("Return type {0} for function {1} is not of the same type of declared method (type {2})", 497 | returnStatementType.ExpressionType, symbol.Name, delcaredSymbol.ExpressionType)); 498 | } 499 | } 500 | 501 | public void Visit(WhileLoop ast) 502 | { 503 | ast.Predicate.Visit(this); 504 | 505 | ast.Body.Visit(this); 506 | 507 | SetScope(ast); 508 | } 509 | 510 | public void Visit(ScopeDeclr ast) 511 | { 512 | ScopeTree.CreateScope(); 513 | 514 | ast.ScopedStatements.ForEach(statement => statement.Visit(this)); 515 | 516 | SetScope(ast); 517 | 518 | ScopeTree.PopScope(); 519 | } 520 | 521 | private void SetScope(Ast ast) 522 | { 523 | if (ast.CurrentScope == null) 524 | { 525 | ast.CurrentScope = Current; 526 | 527 | ast.Global = Global; 528 | } 529 | 530 | if (ast.CurrentScope != null && ast.CurrentScope.Symbols.Count < Current.Symbols.Count) 531 | { 532 | ast.CurrentScope = Current; 533 | } 534 | 535 | if (ast.Global != null && ast.Global.Symbols.Count < Global.Symbols.Count) 536 | { 537 | ast.Global = Global; 538 | } 539 | } 540 | 541 | public void Visit(ForLoop ast) 542 | { 543 | ast.Setup.Visit(this); 544 | 545 | ast.Predicate.Visit(this); 546 | 547 | if (ResolvingTypes && ast.Predicate.AstSymbolType.ExpressionType != ExpressionTypes.Boolean) 548 | { 549 | throw new InvalidSyntax("For loop predicate has to evaluate to a boolean"); 550 | } 551 | 552 | ast.Update.Visit(this); 553 | 554 | ast.Body.Visit(this); 555 | 556 | SetScope(ast); 557 | } 558 | 559 | public void Visit(ReturnAst ast) 560 | { 561 | if (ast.ReturnExpression != null) 562 | { 563 | ast.ReturnExpression.Visit(this); 564 | 565 | ast.AstSymbolType = ast.ReturnExpression.AstSymbolType; 566 | 567 | CurrentMethod.ReturnAst = ast; 568 | } 569 | } 570 | 571 | public void Visit(PrintAst ast) 572 | { 573 | ast.Expression.Visit(this); 574 | 575 | if (ResolvingTypes) 576 | { 577 | if (ast.Expression.AstSymbolType == null) 578 | { 579 | throw new InvalidSyntax("Undefined expression in print statement"); 580 | } 581 | 582 | if (ast.Expression.AstSymbolType.ExpressionType == ExpressionTypes.Void) 583 | { 584 | throw new InvalidSyntax("Cannot print a void expression"); 585 | } 586 | 587 | if (ast.Expression.AstSymbolType.ExpressionType == ExpressionTypes.Method) 588 | { 589 | var returnAst = (ast.Expression.AstSymbolType.Src as MethodDeclr); 590 | 591 | if (returnAst != null) 592 | { 593 | var retStatement = returnAst.ReturnAst; 594 | 595 | if (retStatement == null) 596 | { 597 | throw new InvalidSyntax("Cannot print a void expression"); 598 | } 599 | } 600 | 601 | } 602 | } 603 | } 604 | 605 | public void Start(Ast ast) 606 | { 607 | LambdaDeclr.LambdaCount = 0; 608 | 609 | if (ast.Global != null) 610 | { 611 | Global = ast.Global; 612 | } 613 | 614 | ast.Visit(this); 615 | } 616 | 617 | public void Visit(ClassAst ast) 618 | { 619 | if (Global == null) 620 | { 621 | Global = Current; 622 | } 623 | 624 | var classSymbol = ScopeUtil.DefineClassSymbol(ast); 625 | 626 | Current.Define(classSymbol); 627 | 628 | SetScopeType(ScopeType.Class); 629 | 630 | SetScopeSource(classSymbol); 631 | 632 | ScopeTree.CreateScope(); 633 | 634 | ast.Body.Visit(this); 635 | 636 | classSymbol.Symbols = ast.Body.CurrentScope.Symbols; 637 | 638 | //redefine the class symbol now with actual symbols 639 | Current.Define(classSymbol); 640 | 641 | ast.Body.CurrentScope.AllowAllForwardReferences = true; 642 | 643 | ScopeTree.PopScope(); 644 | 645 | if (ast.Global == null) 646 | { 647 | ast.Global = Global; 648 | } 649 | 650 | SetScopeType(ScopeType.Global); 651 | } 652 | 653 | public static MethodDeclr GetConstructorForClass(ClassAst ast) 654 | { 655 | return 656 | ast.Body.ScopedStatements.Where(i => i is MethodDeclr) 657 | .FirstOrDefault(i => (i as MethodDeclr).MethodName.Token.TokenValue == SpecialNames.CONSTRUCTOR_NAME) as MethodDeclr; 658 | } 659 | 660 | private void SetScopeSource(Symbol classSymbol) 661 | { 662 | Current = classSymbol; 663 | } 664 | 665 | public void Visit(ClassReference ast) 666 | { 667 | if (!ResolvingTypes) 668 | { 669 | return; 670 | } 671 | 672 | var declaredSymbol = Resolve(ast.ClassInstance); 673 | 674 | if (declaredSymbol == null) 675 | { 676 | throw new UndefinedElementException(string.Format("Class instance '{0}' does not exist in current scope", ast.ClassInstance.Token.TokenValue)); 677 | } 678 | 679 | var classScope = Resolve(declaredSymbol.Type.TypeName) as ClassSymbol; 680 | 681 | if (classScope == null) 682 | { 683 | classScope = Global.Resolve(declaredSymbol.Type.TypeName) as ClassSymbol; 684 | } 685 | 686 | var oldScope = Current; 687 | 688 | Current = classScope; 689 | 690 | foreach (var reference in ast.Deferences) 691 | { 692 | if (reference == ast.Deferences.Last()) 693 | { 694 | reference.CallingScope = oldScope; 695 | } 696 | 697 | reference.CurrentScope = Current; 698 | 699 | reference.IsPureDynamic = Current == null; 700 | 701 | reference.Visit(this); 702 | 703 | var field = Resolve(reference); 704 | 705 | 706 | if (field == null && !reference.IsPureDynamic) 707 | { 708 | throw new InvalidSyntax(String.Format("Class {0} has no field named {1}", declaredSymbol.Type.TypeName, reference.Token.TokenValue)); 709 | } 710 | 711 | if (field != null && field.Type.ExpressionType == ExpressionTypes.UserDefined) 712 | { 713 | Current = Global.Resolve(field.Type.TypeName) as ClassSymbol; 714 | } 715 | } 716 | 717 | Current = oldScope; 718 | 719 | ast.AstSymbolType = ast.Deferences.Last().AstSymbolType; 720 | 721 | if (ast.AstSymbolType.ExpressionType == ExpressionTypes.Method) 722 | { 723 | try 724 | { 725 | ast.AstSymbolType = (ast.AstSymbolType.Src as MethodDeclr).ReturnAst.AstSymbolType; 726 | } 727 | catch (Exception ex) 728 | { 729 | 730 | } 731 | } 732 | 733 | SetScope(ast); 734 | 735 | SetScope(ast.ClassInstance); 736 | } 737 | 738 | public void Visit(NewAst ast) 739 | { 740 | ast.Args.ForEach(arg => arg.Visit(this)); 741 | 742 | if (ResolvingTypes) 743 | { 744 | if (ast.Name.Token.TokenType == TokenType.Word && !ast.IsArray) 745 | { 746 | var className = Resolve(ast.Name) as ClassSymbol; 747 | 748 | if (className == null) 749 | { 750 | className = ast.Global.Resolve(ast.Name) as ClassSymbol; 751 | 752 | if (className == null) 753 | { 754 | throw new InvalidSyntax(String.Format("Class {0} is undefined", ast.Name.Token.TokenValue)); 755 | } 756 | } 757 | 758 | ValidateClassConstructorArgs(ast, className); 759 | 760 | ast.AstSymbolType = className.Type; 761 | } 762 | else if (ast.IsArray) 763 | { 764 | 765 | } 766 | else 767 | { 768 | throw new InvalidSyntax("Cannot new type of " + ast.Name); 769 | } 770 | } 771 | 772 | SetScope(ast); 773 | } 774 | 775 | public void Visit(TryCatchAst ast) 776 | { 777 | ast.TryBody.Visit(this); 778 | 779 | if (ast.CatchBody != null) 780 | { 781 | ast.CatchBody.Visit(this); 782 | } 783 | } 784 | 785 | public void Visit(ArrayIndexAst ast) 786 | { 787 | ast.Name.Visit(this); 788 | 789 | ast.Index.Visit(this); 790 | 791 | if (ResolvingTypes) 792 | { 793 | var symbol = Resolve(ast.Name); 794 | 795 | if (!symbol.IsArray) 796 | { 797 | throw new InvalidSyntax("Trying to index a non array"); 798 | } 799 | 800 | if (ast.Index.AstSymbolType.ExpressionType != ExpressionTypes.Int) 801 | { 802 | throw new InvalidSyntax("Cannot index an array with a non integer type: " + ast.Index.AstSymbolType.ExpressionType); 803 | } 804 | } 805 | } 806 | 807 | private void ValidateClassConstructorArgs(NewAst ast, ClassSymbol classSource) 808 | { 809 | if (!ResolvingTypes) 810 | { 811 | return; 812 | } 813 | 814 | if (classSource == null) 815 | { 816 | throw new ArgumentException("classSource"); 817 | } 818 | 819 | var constructor = GetConstructorForClass(classSource.Src as ClassAst); 820 | 821 | if (constructor != null) 822 | { 823 | if (CollectionUtil.IsNullOrEmpty(ast.Args) && !CollectionUtil.IsNullOrEmpty(constructor.Arguments)) 824 | { 825 | throw new InvalidSyntax("Not enough arguments for constructor arguments"); 826 | } 827 | 828 | if (ast.Args.Count != constructor.Arguments.Count) 829 | { 830 | throw new InvalidSyntax("Not enough arguments for constructor arguments"); 831 | } 832 | 833 | (classSource.Src as ClassAst).Constructor = constructor; 834 | } 835 | } 836 | } 837 | } 838 | 839 | -------------------------------------------------------------------------------- /Lang/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Lang/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Anton Kropp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LanguageCreator 2 | =============== 3 | 4 | A place to practice language creation mechanisms. Currently can execute a weird minimal language inspired by c# and f#. It properly contructs an AST, validates scoping rules, infer types, and properly uses encapsulated memory spaces. 5 | 6 | Supported Constructs 7 | === 8 | 9 | The language supports: 10 | 11 | - variable assignment 12 | - if/else conditionals declaration 13 | - function declaration with variable arguments 14 | - anonymous functions 15 | - quoted strings 16 | - simple type inference 17 | - basic classes (no inheritance) 18 | - exception handling 19 | - nil support 20 | - partial function creation and application 21 | 22 | Supported built in types are: 23 | 24 | - int 25 | - float 26 | - string 27 | - void 28 | - bool (Booleans are `true` and `false`) 29 | 30 | Operations are: 31 | 32 | - `+` 33 | - `-` 34 | - `&` (and) 35 | - `||` (or) 36 | - `/` 37 | - `^` 38 | 39 | Regarding type inference: 40 | - Anonymous lambdas can take arguments and return values when stored in a type inferred "var" variable 41 | - Regular functions can also be declared as "var" and type inferred from their return types 42 | - You can enforce static typing rules by giving a function a proper type, otherwise it'll just use the return type 43 | - If no return statement exists it'll default to void. 44 | 45 | Closures can reference their parent's memory space but not their callers memory space. 46 | 47 | If a class has a function called `init` then that is treated as the constructor and is called when a class is created. 48 | 49 | Class instantiation is scala style, it is just top down. If a statement isn't enclosed in a function it will be executed before calling the `init` function. 50 | 51 | Example1 52 | ==== 53 | 54 | ```csharp 55 | void foo(int x){ 56 | if(x > 2){ 57 | print ((x + 1) + 2); 58 | } 59 | else{ 60 | print (x); 61 | } 62 | } 63 | foo(1); 64 | foo(100); 65 | ``` 66 | 67 | 68 | ``` 69 | 1 70 | 103 71 | ``` 72 | 73 | Example2 74 | ==== 75 | ```csharp 76 | var x = fun(int arg) -> { 77 | int g = arg; 78 | while(g > 0){ 79 | print g; 80 | g = g - 1; 81 | } 82 | print "done!"; 83 | } 84 | 85 | var y = x; 86 | 87 | var z = y; 88 | 89 | z(5); 90 | 91 | print "lambda assigments work!"; 92 | 93 | z(3); 94 | 95 | int a = 1; 96 | 97 | int b = a; 98 | 99 | int c = b; 100 | 101 | print c; 102 | ``` 103 | 104 | 105 | ``` 106 | 5 107 | 4 108 | 3 109 | 2 110 | 1 111 | done! 112 | lambda assigments work! 113 | 3 114 | 2 115 | 1 116 | done! 117 | 1 118 | ``` 119 | 120 | Example3 121 | ==== 122 | 123 | ```csharp 124 | var foo(string t){ 125 | var x = "test"; 126 | return x + t; 127 | } 128 | 129 | print foo("pong"); 130 | ``` 131 | 132 | 133 | 134 | ``` 135 | testpong 136 | ``` 137 | 138 | Example4 (currying) 139 | === 140 | 141 | ```csharp 142 | var func(string printer, int x){ 143 | print printer; 144 | print x; 145 | } 146 | 147 | var curry = func("anton"); 148 | 149 | curry(1); 150 | 151 | curry(2); 152 | 153 | var otherCurry = func("test"); 154 | 155 | otherCurry(3); 156 | ``` 157 | 158 | ``` 159 | anton 160 | 1 161 | anton 162 | 2 163 | test 164 | 3 165 | ``` 166 | 167 | Example 5 (classes) 168 | === 169 | 170 | ```csharp 171 | class anton{ 172 | int x = 1; 173 | int y = 2; 174 | 175 | void foo(){ 176 | print x; 177 | } 178 | 179 | } 180 | 181 | var ant = new anton(); 182 | var foo = new anton(); 183 | 184 | foo.x = 2; 185 | 186 | ant.foo(); 187 | 188 | foo.foo(); 189 | 190 | foo.x = 10; 191 | 192 | foo.foo(); 193 | ``` 194 | 195 | ``` 196 | 1 197 | 2 198 | 10 199 | ``` 200 | 201 | Example 6 202 | === 203 | ```csharp 204 | class bob{ 205 | var z = 1; 206 | } 207 | 208 | class anton{ 209 | var x = new bob(); 210 | int y = 0; 211 | } 212 | 213 | anton foo = new anton(); 214 | 215 | print foo.x.z; 216 | ``` 217 | 218 | ``` 219 | 1 220 | ``` 221 | 222 | Example 7 223 | === 224 | ```csharp 225 | class bob{ 226 | var z = 1; 227 | 228 | } 229 | 230 | class anton{ 231 | var x = new bob(); 232 | int y = 0; 233 | } 234 | 235 | anton foo = new anton(); 236 | 237 | print foo.x.z; 238 | 239 | foo.x.z = 2; 240 | 241 | print foo.x.z; 242 | ``` 243 | 244 | ``` 245 | 1 246 | 2 247 | ``` 248 | 249 | Example 8 250 | === 251 | ```csharp 252 | class anton{ 253 | var x = fun() -> { return new anton(); }; 254 | int y = 10; 255 | } 256 | 257 | var x = new anton(); 258 | 259 | var dynamicAnton = x.x(); 260 | 261 | dynamicAnton.y = 52; 262 | 263 | print dynamicAnton.y; 264 | ``` 265 | 266 | ``` 267 | 52 268 | ``` 269 | 270 | Example 9 (forward referencing and object passing) 271 | === 272 | ```csharp 273 | class human{ 274 | void init(string id){ 275 | age = 99; 276 | name = id; 277 | } 278 | 279 | void create(){ 280 | person = new human('test'); 281 | } 282 | 283 | int age; 284 | string name; 285 | 286 | human person; 287 | } 288 | 289 | var person = new human('anton'); 290 | 291 | void printPerson(human person){ 292 | print 'age of ' + person.name + ' = '; 293 | print person.age; 294 | print '----'; 295 | } 296 | 297 | person.age = 29; 298 | person.create(); 299 | 300 | printPerson(person); 301 | 302 | printPerson(person.person); 303 | ``` 304 | 305 | ``` 306 | age of anton = 307 | 29 308 | ---- 309 | age of jane doe = 310 | 99 311 | ---- 312 | ``` 313 | 314 | Example 10 basic closures 315 | === 316 | 317 | ```csharp 318 | class bob{ 319 | int x = 0; 320 | string pr1(method x){ 321 | return x('test') + ' in class bob pr1'; 322 | } 323 | } 324 | 325 | class human{ 326 | int x = 1; 327 | 328 | var b = new bob(); 329 | 330 | void pr(method z){ 331 | print b.pr1(z) + ' from class human pr'; 332 | } 333 | } 334 | 335 | var a = new human(); 336 | var b = new bob(); 337 | 338 | int x = 100; 339 | var lambda = fun(string v) ->{ 340 | var p = fun() -> { 341 | x = x + 1; 342 | print x; 343 | print v + ' in second lambda'; 344 | }; 345 | p(); 346 | return v; 347 | }; 348 | 349 | a.pr(lambda); 350 | 351 | print b.pr1(lambda) + ' from main'; 352 | 353 | print x; 354 | ``` 355 | 356 | ``` 357 | 101 358 | test in second lambda 359 | test in class bob pr1 from class human pr 360 | 102 361 | test in second lambda 362 | test in class bob pr1 from main 363 | 102 364 | ``` 365 | 366 | Example 11 (basic reference linking) 367 | === 368 | 369 | ```csharp 370 | int x = 1; 371 | 372 | int y = &x; 373 | 374 | print y; 375 | 376 | y = 2; 377 | 378 | print x; 379 | 380 | y = 3; 381 | 382 | print x; 383 | 384 | x = 4; 385 | 386 | print y; 387 | ``` 388 | 389 | ``` 390 | 1 391 | 2 392 | 3 393 | 4 394 | ``` 395 | 396 | Example 12 (complex reference linking passing via different memory scopes) 397 | === 398 | 399 | ```csharp 400 | class bob{ 401 | int x = 0; 402 | string pr1(method x){ 403 | return x('test') + ' in class bob pr1'; 404 | } 405 | } 406 | 407 | class human{ 408 | int x = 1; 409 | 410 | var b = new bob(); 411 | 412 | void pr(method z){ 413 | print b.pr1(z) + ' from class human pr'; 414 | } 415 | } 416 | 417 | var a = new human(); 418 | var b = new bob(); 419 | 420 | int y = 100; 421 | int f = &y; 422 | int x = &f; 423 | 424 | 425 | var lambda = fun(string v) ->{ 426 | var p = fun() -> { 427 | x = x + 1; 428 | print x; 429 | print v + ' in second lambda'; 430 | }; 431 | p(); 432 | return v; 433 | }; 434 | 435 | a.pr(lambda); 436 | 437 | print b.pr1(lambda) + ' from main'; 438 | 439 | print y; 440 | ``` 441 | 442 | ``` 443 | 101 444 | test in second lambda 445 | test in class bob pr1 from class human pr 446 | 102 447 | test in second lambda 448 | test in class bob pr1 from main 449 | 102 450 | ``` 451 | 452 | (note, this is supposed to have the same result as example 10 which has no links) 453 | 454 | Example 13 - nil comparison 455 | === 456 | 457 | ```csharp 458 | void printNull(int item){ 459 | if(item == nil){ 460 | print 'is nil'; 461 | } 462 | else { 463 | print 'is not nil'; 464 | } 465 | } 466 | 467 | int x; 468 | 469 | int y = 1; 470 | 471 | printNull(x); 472 | printNull(y); 473 | 474 | x = 2; 475 | 476 | printNull(x); 477 | ``` 478 | 479 | ``` 480 | is nil 481 | is not nil 482 | is not nil 483 | ``` 484 | 485 | Example 14 - simple exception handling 486 | === 487 | 488 | ```csharp 489 | class test{ 490 | int x; 491 | } 492 | 493 | test item; 494 | 495 | try{ 496 | print item.x; 497 | } 498 | catch{ 499 | print 'exception!'; 500 | } 501 | ``` 502 | 503 | ``` 504 | exception! 505 | ``` 506 | 507 | Notes 508 | === 509 | Type promotion doesn't exist and neither does inheritance. So you can't print a string and an int on the same line because the expression won't match properly, but thats intentional right now. 510 | -------------------------------------------------------------------------------- /packages/NUnit.2.6.2/NUnit.2.6.2.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devshorts/LanguageCreator/1d752a0bdaf4223bbe0abe704d59b5b8e3d2b69f/packages/NUnit.2.6.2/NUnit.2.6.2.nupkg -------------------------------------------------------------------------------- /packages/NUnit.2.6.2/lib/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devshorts/LanguageCreator/1d752a0bdaf4223bbe0abe704d59b5b8e3d2b69f/packages/NUnit.2.6.2/lib/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit.2.6.2/license.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devshorts/LanguageCreator/1d752a0bdaf4223bbe0abe704d59b5b8e3d2b69f/packages/NUnit.2.6.2/license.txt -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------