├── .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 |
--------------------------------------------------------------------------------