├── .gitignore ├── Bow.sln ├── Bow ├── Bow.csproj ├── Bow │ ├── Bow.cs │ ├── Errors │ │ ├── BowBreak.cs │ │ ├── BowEOFError.cs │ │ ├── BowNameError.cs │ │ ├── BowReturn.cs │ │ ├── BowRuntimeError.cs │ │ ├── BowSyntaxError.cs │ │ └── BowTypeError.cs │ ├── Interpret │ │ └── Interpreter.cs │ ├── Parse │ │ ├── Environment │ │ │ ├── Env.cs │ │ │ └── Symbols │ │ │ │ ├── FunctionSymbol.cs │ │ │ │ └── VariableSymbol.cs │ │ ├── Expressions │ │ │ ├── BinaryExpression.cs │ │ │ ├── Expression.cs │ │ │ ├── FunctionExpression.cs │ │ │ ├── LiteralExpression.cs │ │ │ ├── Literals │ │ │ │ ├── BooLiteral.cs │ │ │ │ ├── DecLiteral.cs │ │ │ │ ├── Literal.cs │ │ │ │ ├── NullReturn.cs │ │ │ │ └── StrLiteral.cs │ │ │ ├── UnaryExpression.cs │ │ │ └── VariableExpression.cs │ │ ├── Parser.cs │ │ └── Statements │ │ │ ├── Assignment.cs │ │ │ ├── Break.cs │ │ │ ├── Declaration.cs │ │ │ ├── Function.cs │ │ │ ├── If.cs │ │ │ ├── LitStatements.cs │ │ │ ├── Return.cs │ │ │ ├── Statement.cs │ │ │ └── Switch.cs │ └── Tokenise │ │ ├── Keywords.cs │ │ ├── Lexer.cs │ │ ├── Token.cs │ │ └── TokenType.cs └── Program.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | bin/ 3 | obj/ 4 | /packages/ 5 | riderModule.iml 6 | /_ReSharper.Caches/ 7 | Bow/test.bow 8 | .vs/ 9 | -------------------------------------------------------------------------------- /Bow.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bow", "Bow\Bow.csproj", "{0C5734BC-CC2D-4BB9-AB63-9C88A4CCA544}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {0C5734BC-CC2D-4BB9-AB63-9C88A4CCA544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {0C5734BC-CC2D-4BB9-AB63-9C88A4CCA544}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {0C5734BC-CC2D-4BB9-AB63-9C88A4CCA544}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {0C5734BC-CC2D-4BB9-AB63-9C88A4CCA544}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /Bow/Bow.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Bow/Bow/Bow.cs: -------------------------------------------------------------------------------- 1 | using Parse; 2 | using Parse.Statements; 3 | using Parse.Environment; 4 | 5 | using Interpret; 6 | 7 | using Errors; 8 | using Tokenise; 9 | 10 | public class Bow 11 | { 12 | private readonly string _code; 13 | private readonly bool _debug; 14 | private readonly bool _inShell; 15 | 16 | public Bow(string fileName, bool inShell=false, bool debug=false) 17 | { 18 | _code = inShell ? fileName : String.Join("\n", File.ReadAllLines(fileName)); 19 | _debug = debug; 20 | _inShell = inShell; 21 | } 22 | 23 | public void Run() 24 | { 25 | string nextCode = ""; 26 | 27 | try 28 | { 29 | List tokens = new Lexer(_code).ScanTokens(); 30 | if (_debug) 31 | { 32 | foreach (var token in tokens) 33 | { 34 | Console.WriteLine(token.Inspect()); 35 | } 36 | Console.WriteLine("\n\n"); 37 | } 38 | 39 | List statements = new Parser(tokens).Parse(); 40 | new Interpreter(statements).Interpret(_inShell); 41 | } 42 | catch (BowSyntaxError ex) 43 | { 44 | Console.WriteLine($"\x1B[91mBow Syntax Error: {(_debug ? ex : ex.Message)}\x1B[0m"); 45 | } 46 | catch (BowTypeError ex) 47 | { 48 | Console.WriteLine($"\x1B[91mBow Type Error: {(_debug ? ex : ex.Message)}\x1B[0m"); 49 | } 50 | catch (BowNameError ex) 51 | { 52 | Console.WriteLine($"\x1B[91mBow Name Error: {(_debug ? ex : ex.Message)}\x1B[0m"); 53 | } 54 | catch (BowEOFError ex) 55 | { 56 | if (_inShell) 57 | { 58 | nextCode = _code; 59 | } 60 | else 61 | { 62 | Console.WriteLine($"\x1B[91mBow EOF Error: {(_debug ? ex : ex.Message)}\x1B[0m"); 63 | } 64 | } 65 | finally 66 | { 67 | if (_debug) 68 | { 69 | Env.Output(); 70 | } 71 | 72 | if (_inShell) 73 | { 74 | RunShell(false, nextCode); 75 | } 76 | } 77 | } 78 | 79 | public static void RunShell(bool firstCall=true, string lines="") 80 | { 81 | if (firstCall) Console.WriteLine("\x1B[1;4mBow Interactive Shell\x1B[0m\n"); 82 | 83 | string prompt = lines == "" ? "bow" : " "; 84 | Console.Write($"\x1B[92m{prompt}>\x1B[0m "); 85 | 86 | string? line = Console.ReadLine(); 87 | line ??= ""; 88 | 89 | if (line == "exit") 90 | { 91 | return; 92 | } 93 | 94 | lines += line + "\n"; 95 | 96 | new Bow(lines, true).Run(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowBreak.cs: -------------------------------------------------------------------------------- 1 | namespace Errors; 2 | 3 | [Serializable] 4 | public class BowBreak : Exception 5 | { 6 | public BowBreak() : base() { } 7 | public BowBreak(string message) : base(message) { } 8 | public BowBreak(string message, Exception inner) : base(message, inner) { } 9 | 10 | protected BowBreak(System.Runtime.Serialization.SerializationInfo info, 11 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowEOFError.cs: -------------------------------------------------------------------------------- 1 | namespace Errors; 2 | 3 | [Serializable] 4 | public class BowEOFError : Exception 5 | { 6 | public BowEOFError() : base() { } 7 | public BowEOFError(string message) : base(message) { } 8 | public BowEOFError(string message, Exception inner) : base(message, inner) { } 9 | 10 | protected BowEOFError(System.Runtime.Serialization.SerializationInfo info, 11 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowNameError.cs: -------------------------------------------------------------------------------- 1 | namespace Errors; 2 | 3 | [Serializable] 4 | public class BowNameError : Exception 5 | { 6 | public BowNameError() : base() { } 7 | public BowNameError(string message) : base(message) { } 8 | public BowNameError(string message, Exception inner) : base(message, inner) { } 9 | 10 | protected BowNameError(System.Runtime.Serialization.SerializationInfo info, 11 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowReturn.cs: -------------------------------------------------------------------------------- 1 | using Parse.Expressions.Literals; 2 | 3 | namespace Errors; 4 | 5 | [Serializable] 6 | public class BowReturn : Exception 7 | { 8 | public Literal? Literal { get; } 9 | 10 | public BowReturn() : base() { } 11 | public BowReturn(string message) : base(message) { } 12 | public BowReturn(string message, Exception inner) : base(message, inner) { } 13 | 14 | public BowReturn(Literal? literal) : base() 15 | { 16 | Literal = literal; 17 | 18 | } 19 | 20 | protected BowReturn(System.Runtime.Serialization.SerializationInfo info, 21 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 22 | } 23 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowRuntimeError.cs: -------------------------------------------------------------------------------- 1 | namespace Errors; 2 | 3 | [Serializable] 4 | public class BowRuntimeError : Exception 5 | { 6 | public BowRuntimeError() : base() { } 7 | public BowRuntimeError(string message) : base(message) { } 8 | public BowRuntimeError(string message, Exception inner) : base(message, inner) { } 9 | 10 | protected BowRuntimeError(System.Runtime.Serialization.SerializationInfo info, 11 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowSyntaxError.cs: -------------------------------------------------------------------------------- 1 | namespace Errors; 2 | 3 | [Serializable] 4 | public class BowSyntaxError : Exception 5 | { 6 | public BowSyntaxError() : base() { } 7 | public BowSyntaxError(string message) : base(message) { } 8 | public BowSyntaxError(string message, Exception inner) : base(message, inner) { } 9 | 10 | protected BowSyntaxError(System.Runtime.Serialization.SerializationInfo info, 11 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Errors/BowTypeError.cs: -------------------------------------------------------------------------------- 1 | namespace Errors; 2 | 3 | [Serializable] 4 | public class BowTypeError : Exception 5 | { 6 | public BowTypeError() : base() { } 7 | public BowTypeError(string message) : base(message) { } 8 | public BowTypeError(string message, Exception inner) : base(message, inner) { } 9 | 10 | protected BowTypeError(System.Runtime.Serialization.SerializationInfo info, 11 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Interpret/Interpreter.cs: -------------------------------------------------------------------------------- 1 | using Parse.Statements; 2 | 3 | namespace Interpret; 4 | 5 | public class Interpreter 6 | { 7 | private readonly List _statements; 8 | 9 | public Interpreter(List statements) 10 | { 11 | _statements = statements; 12 | } 13 | 14 | public void Interpret(bool inShell=false) 15 | { 16 | foreach (var statement in _statements.SkipLast(inShell ? 1 : 0)) 17 | { 18 | statement.Interpret(); 19 | } 20 | 21 | 22 | if (inShell && _statements.Count > 0) 23 | { 24 | Console.WriteLine($"\x1B[95m> \x1B[93m{_statements.Last().Interpret(inShell)}\x1B[0m"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Environment/Env.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Parse.Expressions; 3 | 4 | namespace Parse.Environment; 5 | 6 | public class Env 7 | { 8 | public static int NestLevel = 0; 9 | private static readonly List Scopes = new() { new Env() }; 10 | private readonly Dictionary _variables; 11 | private readonly Dictionary _functions; 12 | 13 | public Env() 14 | { 15 | _variables = new Dictionary(); 16 | _functions = new Dictionary(); 17 | } 18 | 19 | public Env(Dictionary variables) 20 | { 21 | _variables = variables; 22 | _functions = new Dictionary(); 23 | } 24 | 25 | public static void PushScope(Env env) 26 | { 27 | NestLevel++; 28 | Scopes.Insert(0, env); 29 | } 30 | 31 | public static void PopScope() 32 | { 33 | NestLevel--; 34 | Scopes.RemoveAt(0); 35 | } 36 | 37 | public static void SetNestLevel(int level) 38 | { 39 | while (NestLevel > level) 40 | { 41 | PopScope(); 42 | } 43 | } 44 | 45 | public static void AddVariable(VariableSymbol symbol) 46 | { 47 | if (FunctionExpression.IsBuiltinFunction(symbol.Name)) 48 | { 49 | throw new BowNameError($"Unable to redefine built-in function '{symbol.Name}'"); 50 | } 51 | 52 | Env scope = Scopes[0]; 53 | 54 | if (!scope._variables.ContainsKey(symbol.Name)) 55 | { 56 | scope._variables.Add(symbol.Name, symbol); 57 | } 58 | } 59 | 60 | public static VariableSymbol GetVariable(string name) 61 | { 62 | foreach (Env scope in Scopes) 63 | { 64 | if (scope._variables.ContainsKey(name)) 65 | { 66 | return scope._variables[name]; 67 | } 68 | } 69 | 70 | throw new BowNameError($"Variable '{name}' not found"); 71 | } 72 | 73 | public static bool IsVariableDefined(string name) 74 | { 75 | try 76 | { 77 | GetVariable(name); 78 | return true; 79 | } 80 | catch (BowNameError) 81 | { 82 | return false; 83 | } 84 | } 85 | 86 | public static bool IsVariableDefinedLocally(string name) 87 | { 88 | return Scopes[0]._variables.ContainsKey(name); 89 | } 90 | 91 | private static void OutputVariables() 92 | { 93 | Console.WriteLine("\nVariables:"); 94 | foreach (Env scope in Scopes) 95 | { 96 | foreach (KeyValuePair pair in scope._variables) 97 | { 98 | string value = pair.Value.Literal.DisplayValue; 99 | 100 | Console.WriteLine($"{pair.Key} = {value}"); 101 | } 102 | } 103 | } 104 | 105 | public static void AddFunction(FunctionSymbol symbol) 106 | { 107 | if (FunctionExpression.IsBuiltinFunction(symbol.Name)) 108 | { 109 | throw new BowNameError($"Unable to redefine built-in function '{symbol.Name}'"); 110 | } 111 | 112 | Env scope = Scopes[0]; 113 | 114 | if (!scope._functions.ContainsKey(symbol.Name)) 115 | { 116 | scope._functions.Add(symbol.Name, symbol); 117 | } 118 | } 119 | 120 | public static FunctionSymbol GetFunction(string name, int line) 121 | { 122 | foreach (Env scope in Scopes) 123 | { 124 | if (scope._functions.ContainsKey(name)) 125 | { 126 | return scope._functions[name]; 127 | } 128 | } 129 | 130 | throw new BowNameError($"Function '{name}' not found on line {line}"); 131 | } 132 | 133 | public static bool IsFunctionDefined(string name, int line) 134 | { 135 | try 136 | { 137 | GetFunction(name, line); 138 | return true; 139 | } 140 | catch (BowNameError) 141 | { 142 | return FunctionExpression.IsBuiltinFunction(name); 143 | } 144 | } 145 | 146 | private static void OutputFunctions() 147 | { 148 | Console.WriteLine("\nFunctions:"); 149 | foreach (Env scope in Scopes) 150 | { 151 | foreach (KeyValuePair pair in scope._functions) 152 | { 153 | Console.WriteLine(pair.Key); 154 | } 155 | } 156 | } 157 | 158 | public static void Output() 159 | { 160 | Console.WriteLine("\n"); 161 | OutputVariables(); 162 | OutputFunctions(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Environment/Symbols/FunctionSymbol.cs: -------------------------------------------------------------------------------- 1 | using Parse.Statements; 2 | 3 | namespace Parse.Environment; 4 | 5 | public class FunctionSymbol 6 | { 7 | public string Name { get; } 8 | public List>> Parameters { get; } 9 | public List ReturnTypes { get; } 10 | public List Statements { get; } 11 | public int Line { get; } 12 | 13 | public FunctionSymbol(string name, List>> parameters, List returnTypes, 14 | List statements, int line) 15 | { 16 | Name = name; 17 | Parameters = parameters; 18 | ReturnTypes = returnTypes; 19 | Statements = statements; 20 | Line = line; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Environment/Symbols/VariableSymbol.cs: -------------------------------------------------------------------------------- 1 | using Parse.Expressions.Literals; 2 | 3 | namespace Parse.Environment; 4 | 5 | public class VariableSymbol 6 | { 7 | public string Name { get; } 8 | public Literal Literal { get; private set; } 9 | public int Line { get; } 10 | public bool IsConstant { get; } 11 | 12 | public VariableSymbol(string name, Literal literal, int line, bool isConstant) 13 | { 14 | Name = name; 15 | Literal = literal; 16 | Line = line; 17 | IsConstant = isConstant; 18 | } 19 | 20 | public void SetValue(Literal newValue) 21 | { 22 | Literal = newValue; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/BinaryExpression.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Errors; 3 | using Tokenise; 4 | 5 | using Parse.Expressions.Literals; 6 | 7 | namespace Parse.Expressions; 8 | 9 | public class BinaryExpression : Expression 10 | { 11 | private readonly Expression _left; 12 | private readonly Expression _right; 13 | private readonly Token _operator; 14 | private readonly int _line; 15 | 16 | public BinaryExpression(Expression left, Token op, Expression right, int line) 17 | { 18 | _left = left; 19 | _right = right; 20 | _operator = op; 21 | _line = line; 22 | } 23 | 24 | public override Literal Evaluate() 25 | { 26 | Literal left = _left.Evaluate(); 27 | Literal right = _right.Evaluate(); 28 | 29 | if (left.Type == TokenType.NullReturn || right.Type == TokenType.NullReturn) 30 | { 31 | throw new BowTypeError($"Cannot perform operation on non-returning function on line {_line}"); 32 | } 33 | 34 | if (left.Type != right.Type) 35 | { 36 | throw new BowTypeError($"Cannot perform operation on two different types on line {_line}"); 37 | } 38 | 39 | // Comparison Operators 40 | 41 | if (left.Type == TokenType.DecLiteral) 42 | { 43 | switch (_operator.Type) 44 | { 45 | case TokenType.LessThan: 46 | return new BooLiteral(left.Value < right.Value); 47 | case TokenType.LessThanEqual: 48 | return new BooLiteral(left.Value <= right.Value); 49 | case TokenType.GreaterThan: 50 | return new BooLiteral(left.Value > right.Value); 51 | case TokenType.GreaterThanEqual: 52 | return new BooLiteral(left.Value >= right.Value); 53 | } 54 | } 55 | 56 | switch (_operator.Type) 57 | { 58 | case TokenType.Equal: 59 | return new BooLiteral(left.Value == right.Value); 60 | case TokenType.NotEqual: 61 | return new BooLiteral(left.Value != right.Value); 62 | } 63 | 64 | // Arithmetic Operators 65 | 66 | switch (left.Type) 67 | { 68 | case TokenType.BooLiteral: 69 | return _operator.Type switch 70 | { 71 | TokenType.And => new BooLiteral(left.Value && right.Value), 72 | TokenType.Or => new BooLiteral(left.Value || right.Value), 73 | _ => throw new BowTypeError($"Can't perform {_operator.Type} operation on booleans on line {_line}") 74 | }; 75 | case TokenType.StrLiteral: 76 | return _operator.Type switch 77 | { 78 | TokenType.Plus => new StrLiteral(left.Value + right.Value), 79 | _ => throw new BowTypeError( 80 | $"Can't perform {_operator.Type} operation on strings on line {_line}") 81 | }; 82 | case TokenType.DecLiteral: 83 | return _operator.Type switch 84 | { 85 | TokenType.Plus => new DecLiteral(left.Value + right.Value), 86 | TokenType.Minus => new DecLiteral(left.Value - right.Value), 87 | TokenType.Star => new DecLiteral(left.Value * right.Value), 88 | TokenType.Slash => new DecLiteral(left.Value / right.Value), 89 | _ => throw new BowTypeError( 90 | $"Can't perform `{_operator.Type} operation on decimals on line {_line}") 91 | }; 92 | default: 93 | throw new BowTypeError($"Can't perform operations on {left.Type} on line {_line}"); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/Expression.cs: -------------------------------------------------------------------------------- 1 | using Parse.Expressions.Literals; 2 | 3 | namespace Parse.Expressions; 4 | 5 | public class Expression 6 | { 7 | public virtual Literal Evaluate() 8 | { 9 | throw new NotImplementedException(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/FunctionExpression.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Errors; 3 | using Tokenise; 4 | using Parse.Statements; 5 | using Parse.Environment; 6 | using Parse.Expressions.Literals; 7 | 8 | namespace Parse.Expressions; 9 | 10 | public class FunctionExpression : Expression 11 | { 12 | private readonly string _name; 13 | private readonly List _parameters; 14 | private readonly int _line; 15 | 16 | public FunctionExpression(string name, List parameters, int line) 17 | { 18 | _name = name; 19 | _parameters = parameters; 20 | _line = line; 21 | } 22 | 23 | public override Literal Evaluate() 24 | { 25 | bool isBuiltin = IsBuiltinFunction(_name); 26 | 27 | return ExecuteFunction(isBuiltin); 28 | } 29 | 30 | private void CheckParametersLength(List>> accepted) 31 | { 32 | if (_parameters.Count < accepted.Count) 33 | { 34 | throw new BowSyntaxError($"Not enough parameters for function {_name} on line {_line}"); 35 | } 36 | 37 | if (_parameters.Count > accepted.Count) 38 | { 39 | throw new BowSyntaxError($"Too many parameters for function {_name} on line {_line}"); 40 | } 41 | } 42 | 43 | private Literal ExecuteFunction(bool isBuiltin) 44 | { 45 | return isBuiltin ? ExecuteBuiltinFunction() : ExecuteUserFunction(); 46 | } 47 | 48 | private Literal ExecuteUserFunction() 49 | { 50 | FunctionSymbol function = Env.GetFunction(_name, _line); 51 | 52 | CheckParametersLength(function.Parameters); 53 | 54 | Dictionary parameters = new(); 55 | 56 | foreach (var param in _parameters.Select((value, i) => new { i, value })) 57 | { 58 | Literal paramLiteral = param.value.Evaluate(); 59 | 60 | string name = function.Parameters[param.i].Item1; 61 | 62 | if (!function.Parameters[param.i].Item2.Contains(paramLiteral.Type)) 63 | { 64 | throw new BowSyntaxError($"Incorrect type for parameter '{name}' of function '{_name}' on line {_line}"); 65 | } 66 | 67 | parameters.Add(name, new VariableSymbol(name, paramLiteral, _line, false)); 68 | } 69 | 70 | Literal? value = null; 71 | 72 | int nestLevel = Env.NestLevel; 73 | 74 | Env.PushScope(new Env(parameters)); 75 | 76 | try 77 | { 78 | foreach (Statement statement in function.Statements) 79 | { 80 | statement.Interpret(); 81 | } 82 | } 83 | catch (BowReturn ex) 84 | { 85 | value = ex.Literal; 86 | } 87 | finally 88 | { 89 | Env.SetNestLevel(nestLevel); 90 | } 91 | 92 | CheckReturnTypes(value, function.ReturnTypes); 93 | 94 | if (value is null) 95 | { 96 | return new NullReturn(); 97 | } 98 | 99 | return value.Type switch 100 | { 101 | TokenType.StrLiteral => new StrLiteral(value.Value), 102 | TokenType.BooLiteral => new BooLiteral(value.Value), 103 | TokenType.DecLiteral => new DecLiteral(value.Value), 104 | _ => throw new BowRuntimeError($"Variable expression contains unknown type {value.Type} on line {_line}") 105 | }; 106 | } 107 | 108 | private void CheckReturnTypes(Literal? value, List types) 109 | { 110 | if (value is null && types.Count != 0) 111 | { 112 | throw new BowTypeError($"Function call unexpectedly did not return anything on line {_line}"); 113 | } 114 | 115 | if (value is null) 116 | { 117 | return; 118 | } 119 | 120 | if (!types.Contains(value.Type)) 121 | { 122 | throw new BowTypeError($"Function call did not return a correct type on line {_line}"); 123 | } 124 | } 125 | 126 | private Literal ExecuteBuiltinFunction() 127 | { 128 | Type builtins = typeof(Builtins); 129 | 130 | const BindingFlags flags = BindingFlags.InvokeMethod | 131 | BindingFlags.Public | 132 | BindingFlags.Static; 133 | 134 | MethodInfo? func = builtins.GetMethod(_name, flags); 135 | 136 | if (func == null) 137 | { 138 | throw new BowNameError($"Unknown function '{_name}' on line {_line}"); 139 | } 140 | 141 | object[] parameters = _parameters.Select(param => param.Evaluate().Value).ToArray(); 142 | 143 | try 144 | { 145 | return (Literal)(func.Invoke(null, parameters) ?? new NullReturn()); 146 | } 147 | catch (TargetParameterCountException) 148 | { 149 | throw new BowSyntaxError($"Incorrect number of parameters for function '{_name}' on line {_line}"); 150 | } 151 | catch (ArgumentException) 152 | { 153 | throw new BowTypeError($"Incorrect type for parameter on line {_line}"); 154 | } 155 | } 156 | 157 | public static bool IsBuiltinFunction(string name) 158 | { 159 | const BindingFlags flags = BindingFlags.InvokeMethod | 160 | BindingFlags.Public | 161 | BindingFlags.Static; 162 | 163 | MethodInfo? func = typeof(Builtins).GetMethod(name, flags); 164 | 165 | return func != null; 166 | } 167 | } 168 | 169 | public class Builtins 170 | { 171 | public static Literal output(string value) 172 | { 173 | Console.WriteLine(value); 174 | return new NullReturn(); 175 | } 176 | 177 | public static Literal input(string message="") 178 | { 179 | Console.Write(message); 180 | return new StrLiteral(Console.ReadLine() ?? ""); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/LiteralExpression.cs: -------------------------------------------------------------------------------- 1 | using Parse.Expressions.Literals; 2 | 3 | namespace Parse.Expressions; 4 | 5 | public class LiteralExpression : Expression 6 | { 7 | private readonly Literal _literal; 8 | private readonly int _line; 9 | 10 | public LiteralExpression(Literal literal, int line) 11 | { 12 | _literal = literal; 13 | _line = line; 14 | } 15 | 16 | public override Literal Evaluate() 17 | { 18 | return _literal; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/Literals/BooLiteral.cs: -------------------------------------------------------------------------------- 1 | using Tokenise; 2 | 3 | namespace Parse.Expressions.Literals; 4 | 5 | public class BooLiteral : Literal 6 | { 7 | public BooLiteral(string value) 8 | { 9 | Value = bool.Parse(value); 10 | Type = TokenType.BooLiteral; 11 | } 12 | 13 | public BooLiteral(bool value) 14 | { 15 | Value = value; 16 | Type = TokenType.BooLiteral; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/Literals/DecLiteral.cs: -------------------------------------------------------------------------------- 1 | using Tokenise; 2 | 3 | namespace Parse.Expressions.Literals; 4 | 5 | public class DecLiteral : Literal 6 | { 7 | public DecLiteral(string value) 8 | { 9 | Value = double.Parse(value); 10 | Type = TokenType.DecLiteral; 11 | } 12 | 13 | public DecLiteral(double value) 14 | { 15 | Value = value; 16 | Type = TokenType.DecLiteral; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/Literals/Literal.cs: -------------------------------------------------------------------------------- 1 | using Tokenise; 2 | namespace Parse.Expressions.Literals; 3 | 4 | public class Literal 5 | { 6 | public dynamic Value { get; init; } = ""; 7 | public string Type { get; init; } = ""; 8 | 9 | public string DisplayValue 10 | { 11 | get 12 | { 13 | if (Type == TokenType.StrLiteral) 14 | { 15 | return $"\"{Value}\""; 16 | } 17 | 18 | return Value.ToString(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/Literals/NullReturn.cs: -------------------------------------------------------------------------------- 1 | using Tokenise; 2 | 3 | namespace Parse.Expressions.Literals; 4 | 5 | public class NullReturn : Literal 6 | { 7 | public NullReturn() 8 | { 9 | Value = "null"; 10 | Type = TokenType.NullReturn; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/Literals/StrLiteral.cs: -------------------------------------------------------------------------------- 1 | using Tokenise; 2 | 3 | namespace Parse.Expressions.Literals; 4 | 5 | public class StrLiteral : Literal 6 | { 7 | public StrLiteral(string value) 8 | { 9 | Value = value; 10 | Type = TokenType.StrLiteral; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/UnaryExpression.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Errors; 3 | using Tokenise; 4 | using Parse.Expressions.Literals; 5 | 6 | namespace Parse.Expressions; 7 | 8 | public class UnaryExpression : Expression 9 | { 10 | private readonly Expression _right; 11 | private readonly Token _operator; 12 | private readonly int _line; 13 | 14 | public UnaryExpression(Token op, Expression right, int line) 15 | { 16 | _right = right; 17 | _operator = op; 18 | _line = line; 19 | } 20 | 21 | public override Literal Evaluate() 22 | { 23 | Literal right = _right.Evaluate(); 24 | 25 | if (right.Type == TokenType.NullReturn) 26 | { 27 | throw new BowTypeError($"Cannot perform operation on non-returning function on line {_line}"); 28 | } 29 | 30 | switch (_operator.Type) 31 | { 32 | case TokenType.Minus: 33 | if (right.Type != TokenType.DecLiteral) 34 | { 35 | throw new BowTypeError($"Can't negate non-decimal value on line {_line}"); 36 | } 37 | return new DecLiteral(-right.Value); 38 | default: 39 | throw new BowTypeError($"Can't perform `{_operator.Type} operation {right.Type} on line {_line}"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Expressions/VariableExpression.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Tokenise; 3 | using Parse.Environment; 4 | using Parse.Expressions.Literals; 5 | 6 | namespace Parse.Expressions; 7 | 8 | public class VariableExpression : Expression 9 | { 10 | private readonly string _name; 11 | private readonly int _line; 12 | 13 | public VariableExpression(string name, int line) 14 | { 15 | _name = name; 16 | _line = line; 17 | } 18 | 19 | public override Literal Evaluate() 20 | { 21 | Literal value = Env.IsFunctionDefined(_name, _line) 22 | ? new FunctionExpression(_name, new(), _line).Evaluate() 23 | : Env.GetVariable(_name).Literal; 24 | 25 | return value.Type switch 26 | { 27 | TokenType.StrLiteral => new StrLiteral(value.Value), 28 | TokenType.BooLiteral => new BooLiteral(value.Value), 29 | TokenType.DecLiteral => new DecLiteral(value.Value), 30 | TokenType.NullReturn => new NullReturn(), 31 | _ => throw new BowRuntimeError($"Variable expression contains unknown type {value.Type} on line {_line}") 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Parser.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Tokenise; 3 | 4 | using Parse.Statements; 5 | using Parse.Expressions; 6 | using Parse.Expressions.Literals; 7 | 8 | namespace Parse; 9 | 10 | public class Parser 11 | { 12 | private int _current; 13 | private readonly List _tokens; 14 | 15 | public Parser(List tokens) 16 | { 17 | _tokens = tokens; 18 | } 19 | 20 | public List Parse() 21 | { 22 | List statements = new(); 23 | 24 | while (!IsAtEnd()) 25 | { 26 | statements.Add(GetStatement()); 27 | } 28 | 29 | return statements; 30 | } 31 | 32 | private bool Match(string[] tokens) 33 | { 34 | if (tokens.Contains(Peek().Type)) 35 | { 36 | Advance(); 37 | return true; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | private Token Peek() 44 | { 45 | return _tokens[_current]; 46 | } 47 | 48 | private Token Advance() 49 | { 50 | if (_current < _tokens.Count) 51 | { 52 | Token token = _tokens[_current]; 53 | _current++; 54 | return token; 55 | } 56 | 57 | throw new BowEOFError($"Unexpected EOF at line {_tokens[_current].Line}"); 58 | } 59 | 60 | private Token Previous() 61 | { 62 | if (_current > 0) 63 | { 64 | return _tokens[_current - 1]; 65 | } 66 | 67 | throw new BowRuntimeError("Cannot call Previous() on the first token"); 68 | } 69 | 70 | private void Undo() 71 | { 72 | if (_current > 0) 73 | { 74 | _current--; 75 | } 76 | } 77 | 78 | private bool IsAtEnd() 79 | { 80 | return Peek().Type == TokenType.EOF; 81 | } 82 | 83 | // Statements 84 | 85 | private Statement GetStatement() 86 | { 87 | if (Match(new[] { TokenType.Identifier })) 88 | { 89 | string name = Previous().Literal; 90 | 91 | if (Match(new[] { TokenType.OpenDeclare })) 92 | { 93 | return DeclareStatement(name, Previous().Line); 94 | } 95 | 96 | if (Match(new[] { TokenType.Assign })) 97 | { 98 | return AssignStatement(name, Previous().Line); 99 | } 100 | 101 | return LiteralStatement(Previous().Line); 102 | } 103 | 104 | if (Match(new[] { TokenType.If })) 105 | { 106 | return IfStatement(Previous().Line); 107 | } 108 | 109 | if (Match(new[] { TokenType.Fun })) 110 | { 111 | return FunctionStatement(Previous().Line); 112 | } 113 | 114 | if (Match(new[] { TokenType.ReturnArrow })) 115 | { 116 | return ReturnStatement(); 117 | } 118 | 119 | if (Match(new[] { TokenType.Break })) 120 | { 121 | return new Break(Previous().Line); 122 | } 123 | 124 | if (Match(new[] { TokenType.Switch })) 125 | { 126 | return SwitchStatement(); 127 | } 128 | 129 | if (Match(new[] { TokenType.DecLiteral, TokenType.BooLiteral, TokenType.StrLiteral, TokenType.LeftBracket })) 130 | { 131 | return LiteralStatement(Previous().Line); 132 | } 133 | 134 | Advance(); 135 | throw new BowSyntaxError($"Unexpected token '{Previous().Lexeme}' on line {Previous().Line}"); 136 | } 137 | 138 | private List GetStatementBlock(string[] terminators, int line) 139 | { 140 | List statementList = new(); 141 | 142 | while (!IsAtEnd() && !Match(terminators)) 143 | { 144 | statementList.Add(GetStatement()); 145 | } 146 | 147 | if (!terminators.Contains(Previous().Type)) 148 | { 149 | throw new BowEOFError($"Unexpected EOF while looking for '{string.Join(", ", terminators)}'"); 150 | } 151 | 152 | if (statementList.Count == 0) 153 | { 154 | throw new BowSyntaxError($"Empty statement block on line {line}"); 155 | } 156 | 157 | return statementList; 158 | } 159 | 160 | private List GetCaseStatementBlock(int line) 161 | { 162 | List statementList = new(); 163 | 164 | while (!IsAtEnd() && !EndOfCaseBlock()) 165 | { 166 | statementList.Add(GetStatement()); 167 | } 168 | 169 | string[] terminators = 170 | { 171 | TokenType.CloseBlock, TokenType.Identifier, TokenType.BooLiteral, TokenType.StrLiteral, 172 | TokenType.DecLiteral, TokenType.Other 173 | }; 174 | 175 | if (!terminators.Contains(Peek().Type)) 176 | { 177 | throw new BowEOFError($"Unexpected EOF while looking for end of switch branch on line {line}"); 178 | } 179 | 180 | if (statementList.Count == 0) 181 | { 182 | throw new BowSyntaxError($"Empty statement block on line {line}"); 183 | } 184 | 185 | return statementList; 186 | } 187 | 188 | private bool EndOfCaseBlock() 189 | { 190 | if (Match(new[] { TokenType.CloseBlock })) 191 | { 192 | Undo(); 193 | return true; 194 | } 195 | 196 | if (Match(new[] 197 | { 198 | TokenType.Identifier, TokenType.BooLiteral, TokenType.StrLiteral, TokenType.DecLiteral, TokenType.Other 199 | })) 200 | { 201 | if (Match(new[] { TokenType.CaseBranch })) 202 | { 203 | Undo(); 204 | Undo(); 205 | return true; 206 | } 207 | 208 | Undo(); 209 | return false; 210 | } 211 | 212 | return false; 213 | } 214 | 215 | private Statement DeclareStatement(string name, int line) 216 | { 217 | var (type, isConstant) = DeclareType(line); 218 | 219 | if (!Match(new[] { TokenType.CloseDeclare })) 220 | { 221 | throw new BowSyntaxError($"Missing end of declaration arrow on line {line}"); 222 | } 223 | 224 | Expression valueExpression = GetExpression(Previous().Line); 225 | 226 | return new Declaration(name, valueExpression, type, isConstant, line); 227 | } 228 | 229 | private (string, bool) DeclareType(int line) 230 | { 231 | string type; 232 | bool isConstant; 233 | 234 | if (Match(new[] { TokenType.Var })) 235 | { 236 | isConstant = false; 237 | } 238 | else if (Match(new[] { TokenType.Con })) 239 | { 240 | isConstant = true; 241 | } 242 | else 243 | { 244 | throw new BowSyntaxError($"Declaration arrow missing 'var' or 'con' on line {line}"); 245 | } 246 | 247 | if ( 248 | Match(new[] { TokenType.Str, TokenType.Dec, TokenType.Boo })) 249 | { 250 | type = Previous().Type + "LITERAL"; 251 | } 252 | else 253 | { 254 | throw new BowSyntaxError($"Declaration arrow invalid type on line {line}"); 255 | } 256 | 257 | return (type, isConstant); 258 | } 259 | 260 | private Statement AssignStatement(string name, int line) 261 | { 262 | Expression valueExpression = GetExpression(Previous().Line); 263 | 264 | return new Assignment(name, valueExpression, line); 265 | } 266 | 267 | private Statement IfStatement(int line) 268 | { 269 | Expression ifCondition = GetExpression(Previous().Line); 270 | 271 | if (!Match(new[] { TokenType.OpenBlock })) 272 | { 273 | throw new BowSyntaxError($"Missing '==>' on line {Peek().Line}"); 274 | } 275 | 276 | List ifStatements = GetStatementBlock(new[] { TokenType.CloseBlock }, line); 277 | 278 | List>> altIfs = AltIfs(line); 279 | 280 | List altStatements = Alt(line); 281 | 282 | return new If(ifCondition, ifStatements, altIfs, altStatements, line); 283 | } 284 | 285 | private List>> AltIfs(int line) 286 | { 287 | List>> altIfs = new(); 288 | 289 | while (Match(new[] { TokenType.AltIf })) 290 | { 291 | Expression altCondition = GetExpression(Previous().Line); 292 | 293 | if (!Match(new[] { TokenType.OpenBlock })) 294 | { 295 | throw new BowSyntaxError($"Missing '==>' on line {Peek().Line}"); 296 | } 297 | 298 | List altIfStatements = GetStatementBlock(new[] { TokenType.CloseBlock }, line); 299 | 300 | altIfs.Add(Tuple.Create(altCondition, altIfStatements)); 301 | } 302 | 303 | return altIfs; 304 | } 305 | 306 | private List Alt(int line) 307 | { 308 | List altStatements = new(); 309 | 310 | if (Match(new[] { TokenType.Alt })) 311 | { 312 | if (!Match(new[] { TokenType.OpenBlock })) 313 | { 314 | throw new BowSyntaxError($"Missing '==>' on line {Peek().Line}"); 315 | } 316 | 317 | altStatements = GetStatementBlock(new[] { TokenType.CloseBlock }, line); 318 | } 319 | 320 | return altStatements; 321 | } 322 | 323 | private Statement FunctionStatement(int line) 324 | { 325 | if (!Match(new [] { TokenType.Identifier })) 326 | { 327 | throw new BowSyntaxError($"Missing function name on line {line}"); 328 | } 329 | 330 | string name = Previous().Literal; 331 | 332 | List>> parameters = FunParameters(line); 333 | 334 | List returnTypes = new(); 335 | 336 | if (!Match(new[] { TokenType.OpenBlock })) 337 | { 338 | if (Match(new[] { TokenType.Equal })) 339 | { 340 | returnTypes = GetTypes(Previous().Line); 341 | 342 | if (!Match(new[] { TokenType.FunTypeOpenBlock })) 343 | { 344 | throw new BowSyntaxError($"Missing end of function type return arrow on line {Previous().Line}"); 345 | } 346 | } 347 | } 348 | 349 | List statements = GetStatementBlock(new[] { TokenType.CloseBlock }, line); 350 | 351 | return new Function(name, parameters, returnTypes, statements, line); 352 | } 353 | 354 | private List>> FunParameters(int line) 355 | { 356 | List>> parameters = new(); 357 | 358 | if (!Match(new[] { TokenType.LeftBracket })) return parameters; 359 | 360 | if (!Match(new[] { TokenType.RightBracket })) 361 | { 362 | if (IsAtEnd()) 363 | { 364 | throw new BowEOFError($"Unexpected EOF when looking for parameters on line {line}"); 365 | } 366 | 367 | parameters.Add(GetParameter(line)); 368 | } 369 | 370 | while (Match(new[] { TokenType.Comma })) 371 | { 372 | if (IsAtEnd()) 373 | { 374 | throw new BowEOFError($"Unexpected EOF when looking for parameters on line {line}"); 375 | } 376 | 377 | parameters.Add(GetParameter(line)); 378 | } 379 | 380 | if (!Match(new[] { TokenType.RightBracket })) 381 | { 382 | throw new BowSyntaxError($"Missing ')' on line {line}"); 383 | } 384 | 385 | return parameters; 386 | } 387 | 388 | private Tuple> GetParameter(int line) 389 | { 390 | if (!Match(new[] { TokenType.Identifier })) 391 | { 392 | throw new BowSyntaxError($"Missing parameter name on line {line}"); 393 | } 394 | 395 | string name = Previous().Literal; 396 | 397 | if (!Match(new[] { TokenType.Minus })) 398 | { 399 | throw new BowSyntaxError($"Missing opening type '-' on line {line}"); 400 | } 401 | 402 | List types = GetTypes(line); 403 | 404 | if (!Match(new[] { TokenType.Minus })) 405 | { 406 | throw new BowSyntaxError($"Missing closing type '-' on line {line}"); 407 | } 408 | 409 | return Tuple.Create(name, types); 410 | } 411 | 412 | private List GetTypes(int line) 413 | { 414 | List types = new(); 415 | 416 | if (!Match(new[] { TokenType.Str, TokenType.Dec, TokenType.Boo })) 417 | { 418 | throw new BowTypeError($"Unknown type on line {line}"); 419 | } 420 | 421 | types.Add(Previous().Type + "LITERAL"); 422 | 423 | while (Match(new[] { TokenType.Seperator })) 424 | { 425 | if (!Match(new[] { TokenType.Str, TokenType.Dec, TokenType.Boo })) 426 | { 427 | throw new BowTypeError($"Unknown type on line {line}"); 428 | } 429 | 430 | types.Add(Previous().Type + "LITERAL"); 431 | } 432 | 433 | return types; 434 | } 435 | 436 | private Statement ReturnStatement() 437 | { 438 | Expression returnExpression = GetExpression(Previous().Line, false); 439 | 440 | return new Return(returnExpression); 441 | } 442 | 443 | private Statement SwitchStatement() 444 | { 445 | Expression caseExpression = GetExpression(Previous().Line); 446 | 447 | if (!Match(new[] { TokenType.OpenBlock })) 448 | { 449 | throw new BowSyntaxError($"Missing '==>' on line {Peek().Line}"); 450 | } 451 | 452 | List, List>> cases = GetCases(Previous().Line); 453 | List other = GetOther(Previous().Line); 454 | 455 | if (!Match(new[] { TokenType.CloseBlock })) 456 | { 457 | throw new BowEOFError($"Missing '<==' on line {Previous().Line}"); 458 | } 459 | 460 | return new Switch(caseExpression, cases, other, Previous().Line); 461 | } 462 | 463 | private List, List>> GetCases(int line) 464 | { 465 | List, List>> cases = new(); 466 | 467 | while (Peek().Type != TokenType.Other && Peek().Type != TokenType.CloseBlock) 468 | { 469 | List caseExpressions = new() { GetExpression(line) }; 470 | 471 | while (Match(new[] { TokenType.Seperator })) 472 | { 473 | caseExpressions.Add(GetExpression(line)); 474 | } 475 | 476 | if (!Match(new[] { TokenType.CaseBranch })) 477 | { 478 | throw new BowSyntaxError($"Missing case branch arrow on line {line}"); 479 | } 480 | 481 | List statements = GetCaseStatementBlock(line); 482 | 483 | cases.Add(Tuple.Create(caseExpressions, statements)); 484 | } 485 | 486 | return cases; 487 | } 488 | 489 | private List GetOther(int line) 490 | { 491 | if (!Match(new[] { TokenType.Other })) 492 | { 493 | return new(); 494 | } 495 | 496 | if (!Match(new[] { TokenType.CaseBranch })) 497 | { 498 | throw new BowSyntaxError($"Missing case branch arrow on line {line}"); 499 | } 500 | 501 | List statements = GetStatementBlock(new[] { TokenType.CloseBlock }, line); 502 | Undo(); // Get back the <== for checking 503 | return statements; 504 | } 505 | 506 | private Statement LiteralStatement(int line) 507 | { 508 | Undo(); 509 | 510 | Expression valueExpression = GetExpression(line); 511 | 512 | return new LitStatement(valueExpression, line); 513 | } 514 | 515 | // Expressions 516 | 517 | private Expression GetExpression(int line, bool checkNone=true) 518 | { 519 | Expression expression = GetOr(line); 520 | 521 | if (checkNone && expression == null) 522 | { 523 | throw new BowSyntaxError($"missing expression on line {line}"); 524 | } 525 | 526 | return expression; 527 | } 528 | 529 | private Expression GetOr(int line) 530 | { 531 | Expression expression = GetAnd(line); 532 | 533 | while (Match(new[] { TokenType.Or })) 534 | { 535 | Token op = Previous(); 536 | Expression right = GetAnd(line); 537 | expression = new BinaryExpression(expression, op, right, line); 538 | } 539 | 540 | return expression; 541 | } 542 | 543 | private Expression GetAnd(int line) 544 | { 545 | Expression expression = GetComparison(line); 546 | 547 | while (Match(new[] { TokenType.And })) 548 | { 549 | Token op = Previous(); 550 | Expression right = GetComparison(line); 551 | expression = new BinaryExpression(expression, op, right, line); 552 | } 553 | 554 | return expression; 555 | } 556 | 557 | private Expression GetComparison(int line) 558 | { 559 | Expression expression = GetTerm(line); 560 | 561 | while (Match(new[] 562 | { 563 | TokenType.Equal, TokenType.NotEqual, TokenType.LessThan, TokenType.LessThanEqual, 564 | TokenType.GreaterThan, TokenType.GreaterThanEqual 565 | })) 566 | { 567 | Token op = Previous(); 568 | Expression right = GetTerm(line); 569 | expression = new BinaryExpression(expression, op, right, line); 570 | } 571 | 572 | return expression; 573 | } 574 | 575 | private Expression GetTerm(int line) 576 | { 577 | Expression expression = GetFactor(line); 578 | 579 | while (Match(new[] { TokenType.Plus, TokenType.Minus })) 580 | { 581 | Token op = Previous(); 582 | Expression right = GetFactor(line); 583 | expression = new BinaryExpression(expression, op, right, op.Line); 584 | } 585 | 586 | return expression; 587 | } 588 | 589 | private Expression GetFactor(int line) 590 | { 591 | Expression expression = GetUnary(line); 592 | 593 | while (Match(new[] { TokenType.Star, TokenType.Slash })) 594 | { 595 | Token op = Previous(); 596 | Expression right = GetUnary(line); 597 | expression = new BinaryExpression(expression, op, right, op.Line); 598 | } 599 | 600 | return expression; 601 | } 602 | 603 | private Expression GetUnary(int line) 604 | { 605 | if (Match(new[] { TokenType.Minus })) 606 | { 607 | Token op = Previous(); 608 | Expression right = GetUnary(line); 609 | return new UnaryExpression(op, right, op.Line); 610 | } 611 | 612 | return Primary(line); 613 | } 614 | 615 | private Expression Primary(int line) 616 | { 617 | if (IsAtEnd()) 618 | { 619 | throw new BowEOFError($"Unexpected EOF on line {Peek().Line}"); 620 | } 621 | 622 | if (Match(new[] { TokenType.BooLiteral })) 623 | { 624 | return new LiteralExpression(new BooLiteral(Previous().Literal), line); 625 | } 626 | 627 | if (Match(new[] { TokenType.DecLiteral })) 628 | { 629 | return new LiteralExpression(new DecLiteral(Previous().Literal), line); 630 | } 631 | 632 | if (Match(new[] { TokenType.StrLiteral })) 633 | { 634 | return new LiteralExpression(new StrLiteral(Previous().Literal), line); 635 | } 636 | 637 | if (Match(new[] { TokenType.Identifier })) 638 | { 639 | string name = Previous().Literal; 640 | 641 | if (Match(new[] { TokenType.LeftBracket })) 642 | { 643 | List parameters = new(); 644 | 645 | if (Peek().Type != TokenType.RightBracket) 646 | { 647 | parameters.Add(GetExpression(line)); 648 | 649 | while (Match(new[] { TokenType.Comma })) 650 | { 651 | if (IsAtEnd()) 652 | { 653 | throw new BowEOFError($"Unexpected EOF when looking for parameters on line {line}"); 654 | } 655 | 656 | parameters.Add(GetExpression(line)); 657 | } 658 | } 659 | 660 | if (!Match(new[] { TokenType.RightBracket })) 661 | { 662 | throw new BowSyntaxError($"Missing ')' on line {line}"); 663 | } 664 | 665 | return new FunctionExpression(name, parameters, Previous().Line); 666 | } 667 | 668 | return new VariableExpression(name, Previous().Line); 669 | } 670 | 671 | if (Match(new[] { TokenType.LeftBracket })) 672 | { 673 | Expression expression = GetExpression(line); 674 | 675 | if (!Match(new[] { TokenType.RightBracket })) 676 | { 677 | throw new BowEOFError($"Missing ')' on line {Peek().Line}"); 678 | } 679 | 680 | return expression; 681 | } 682 | 683 | throw new BowSyntaxError($"Unexpected token '{Previous().Lexeme}' on line {Previous().Line}"); 684 | } 685 | } 686 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Assignment.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Tokenise; 3 | using Parse.Environment; 4 | using Parse.Expressions; 5 | using Parse.Expressions.Literals; 6 | 7 | namespace Parse.Statements; 8 | 9 | public class Assignment : Statement 10 | { 11 | private readonly string _name; 12 | private readonly Expression _valueExpression; 13 | private readonly int _line; 14 | 15 | public Assignment(string name, Expression valueExpression, int line) 16 | { 17 | _name = name; 18 | _valueExpression = valueExpression; 19 | _line = line; 20 | } 21 | 22 | public override void Interpret() 23 | { 24 | Literal value = _valueExpression.Evaluate(); 25 | 26 | if (!Env.IsVariableDefined(_name)) 27 | { 28 | throw new BowSyntaxError($"Unknown variable '{_name}' on line {_line}"); 29 | } 30 | 31 | VariableSymbol symbol = Env.GetVariable(_name); 32 | 33 | if (symbol.IsConstant) 34 | { 35 | throw new BowSyntaxError($"Cannot assign to constant '{_name}' on line {_line}"); 36 | } 37 | 38 | Literal oldValue = symbol.Literal; 39 | 40 | if (oldValue.Type != value.Type) 41 | { 42 | throw new BowTypeError( 43 | $"Can't assign {value.Type} to variable of type {oldValue.Type[..3]} on line {_line}"); 44 | } 45 | 46 | Literal newValue = oldValue.Type switch 47 | { 48 | TokenType.BooLiteral => new BooLiteral(value.Value), 49 | TokenType.DecLiteral => new DecLiteral(value.Value), 50 | TokenType.StrLiteral => new StrLiteral(value.Value), 51 | _ => throw new BowRuntimeError($"Current symbol type is incorrect on line {_line}") 52 | }; 53 | 54 | symbol.SetValue(newValue); 55 | } 56 | 57 | public override string Interpret(bool lastInShell) 58 | { 59 | Interpret(); 60 | 61 | return lastInShell ? Env.GetVariable(_name).Literal.DisplayValue : ""; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Break.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | 3 | namespace Parse.Statements; 4 | 5 | public class Break : Statement 6 | { 7 | private readonly int _line; 8 | 9 | public Break(int line) 10 | { 11 | _line = line; 12 | } 13 | 14 | public override void Interpret() 15 | { 16 | throw new BowBreak($"Unexpected break on line {_line}"); // Will be caught by loops and switches 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Declaration.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Tokenise; 3 | using Parse.Environment; 4 | using Parse.Expressions; 5 | using Parse.Expressions.Literals; 6 | 7 | namespace Parse.Statements; 8 | 9 | public class Declaration : Statement 10 | { 11 | private readonly string _name; 12 | private readonly string _type; 13 | private readonly Expression _valueExpression; 14 | private readonly int _line; 15 | private readonly bool _isConstant; 16 | 17 | public Declaration(string name, Expression valueExpression, string type, bool isConstant, int line) 18 | { 19 | _name = name; 20 | _valueExpression = valueExpression; 21 | _type = type; 22 | _line = line; 23 | _isConstant = isConstant; 24 | } 25 | 26 | public override void Interpret() 27 | { 28 | Literal value = _valueExpression.Evaluate(); 29 | 30 | if (Env.IsVariableDefinedLocally(_name)) 31 | { 32 | throw new BowSyntaxError($"Can't re-declare variable '{_name}' on line {_line}"); 33 | } 34 | 35 | if (_type != value.Type) 36 | { 37 | throw new BowTypeError($"Can't assign {value.Type} to variable of type {_type[..3]} on line {_line}"); 38 | } 39 | 40 | VariableSymbol symbol = new VariableSymbol(_name, value, _line, _isConstant); 41 | 42 | Env.AddVariable(symbol); 43 | } 44 | 45 | public override string Interpret(bool lastInShell) 46 | { 47 | Interpret(); 48 | 49 | return lastInShell ? Env.GetVariable(_name).Literal.DisplayValue : ""; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Function.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Tokenise; 3 | using Parse.Environment; 4 | using Parse.Expressions; 5 | using Parse.Expressions.Literals; 6 | 7 | namespace Parse.Statements; 8 | 9 | public class Function : Statement 10 | { 11 | private readonly string _name; 12 | private readonly List>> _parameters; 13 | private readonly List _returnTypes; 14 | private readonly List _statements; 15 | private readonly int _line; 16 | 17 | public Function(string name, List>> parameters, List returnTypes, 18 | List statements, int line) 19 | { 20 | _name = name; 21 | _parameters = parameters; 22 | _returnTypes = returnTypes; 23 | _statements = statements; 24 | _line = line; 25 | } 26 | 27 | public override void Interpret() 28 | { 29 | FunctionSymbol symbol = new FunctionSymbol(_name, _parameters, _returnTypes, _statements, _line); 30 | Env.AddFunction(symbol); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/If.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Parse.Expressions; 3 | using Parse.Environment; 4 | using Parse.Expressions.Literals; 5 | using Tokenise; 6 | 7 | namespace Parse.Statements; 8 | 9 | public class If : Statement 10 | { 11 | private readonly Expression _ifCondition; 12 | private readonly List _ifStatements; 13 | private readonly List>> _altIfs; 14 | private readonly List _altStatements; 15 | private readonly int _line; 16 | 17 | public If(Expression ifCondition, List ifStatements, List>> altIfs, 18 | List altStatements, int line) 19 | { 20 | _ifCondition = ifCondition; 21 | _ifStatements = ifStatements; 22 | _altIfs = altIfs; 23 | _altStatements = altStatements; 24 | _line = line; 25 | } 26 | 27 | public override void Interpret() 28 | { 29 | Literal condition = _ifCondition.Evaluate(); 30 | 31 | if (condition.Type != TokenType.BooLiteral) 32 | { 33 | throw new BowTypeError($"If condition must be a boolean, but was {condition.Type} on line {_line}"); 34 | } 35 | 36 | // If statement 37 | 38 | if (condition.Value) 39 | { 40 | InterpretBranch(_ifStatements); 41 | return; 42 | } 43 | 44 | // Alt if statements 45 | 46 | foreach (var (altIfCondition, altIfStatements) in _altIfs) 47 | { 48 | Literal evalledAltIfCondition = altIfCondition.Evaluate(); 49 | 50 | if (evalledAltIfCondition.Type != TokenType.BooLiteral) 51 | { 52 | throw new BowTypeError( 53 | $"AltIf condition must be a boolean, but was {evalledAltIfCondition.Type} on line {_line}"); 54 | } 55 | 56 | if (evalledAltIfCondition.Value) 57 | { 58 | InterpretBranch(altIfStatements); 59 | return; 60 | } 61 | } 62 | 63 | // Alt statement 64 | 65 | InterpretBranch(_altStatements); 66 | } 67 | 68 | private void InterpretBranch(List statements) 69 | { 70 | Env.PushScope(new Env()); 71 | 72 | foreach (Statement statement in statements) 73 | { 74 | statement.Interpret(); 75 | } 76 | 77 | Env.PopScope(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/LitStatements.cs: -------------------------------------------------------------------------------- 1 | using Tokenise; 2 | using Parse.Expressions; 3 | using Parse.Expressions.Literals; 4 | 5 | namespace Parse.Statements; 6 | 7 | public class LitStatement : Statement 8 | { 9 | private readonly Expression _valueExpression; 10 | private readonly int _line; 11 | 12 | public LitStatement(Expression valueExpression, int line) 13 | { 14 | _valueExpression = valueExpression; 15 | _line = line; 16 | } 17 | 18 | public override void Interpret() 19 | { 20 | _valueExpression.Evaluate(); 21 | } 22 | 23 | public override string Interpret(bool lastInShell) 24 | { 25 | return lastInShell ? _valueExpression.Evaluate().DisplayValue : ""; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Return.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Parse.Expressions; 3 | using Parse.Expressions.Literals; 4 | 5 | 6 | namespace Parse.Statements; 7 | 8 | public class Return : Statement 9 | { 10 | private readonly Expression? _expression; 11 | 12 | public Return(Expression? expression) 13 | { 14 | _expression = expression; 15 | } 16 | 17 | public override void Interpret() 18 | { 19 | Literal? literal = null; 20 | if (_expression is not null) 21 | { 22 | literal = _expression.Evaluate(); 23 | } 24 | 25 | throw new BowReturn(literal); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Statement.cs: -------------------------------------------------------------------------------- 1 | namespace Parse.Statements; 2 | 3 | public class Statement 4 | { 5 | public virtual void Interpret() 6 | { 7 | throw new NotImplementedException(); 8 | } 9 | 10 | public virtual string Interpret(bool lastInShell) 11 | { 12 | Interpret(); 13 | 14 | return ""; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Bow/Bow/Parse/Statements/Switch.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | using Parse.Expressions; 3 | using Parse.Environment; 4 | using Parse.Expressions.Literals; 5 | 6 | using Microsoft.CSharp.RuntimeBinder; 7 | 8 | namespace Parse.Statements; 9 | 10 | public class Switch : Statement 11 | { 12 | private readonly Expression _switchExpression; 13 | private readonly List, List>> _cases; 14 | private readonly List _other; 15 | private readonly int _line; 16 | 17 | public Switch(Expression switchExpression, List, List>> cases, List other, int line) 18 | { 19 | _switchExpression = switchExpression; 20 | _cases = cases; 21 | _other = other; 22 | _line = line; 23 | } 24 | 25 | public override void Interpret() 26 | { 27 | Literal switchExpression = _switchExpression.Evaluate(); 28 | 29 | int count = 0; 30 | bool match = false; 31 | bool escaped = false; 32 | while (count < _cases.Count) 33 | { 34 | var (comparisons, statements) = _cases[count]; 35 | 36 | foreach (Expression comparison in comparisons) 37 | { 38 | Literal comparisonLiteral = comparison.Evaluate(); 39 | if (comparisonLiteral.Type != switchExpression.Type) 40 | { 41 | throw new BowTypeError($"Cannot compare {comparisonLiteral.Type} with {switchExpression.Type} on line {_line}"); 42 | } 43 | 44 | if (match || comparisonLiteral.Value == switchExpression.Value) 45 | { 46 | try 47 | { 48 | InterpretBranch(statements); 49 | } 50 | catch (BowBreak) 51 | { 52 | escaped = true; 53 | } 54 | 55 | match = true; 56 | break; 57 | } 58 | } 59 | 60 | if (escaped) break; 61 | 62 | count++; 63 | } 64 | 65 | if (!escaped) 66 | { 67 | try 68 | { 69 | InterpretBranch(_other); 70 | } 71 | catch (BowBreak) { } 72 | } 73 | } 74 | 75 | private void InterpretBranch(List statements) 76 | { 77 | Env.PushScope(new Env()); 78 | 79 | foreach (Statement statement in statements) 80 | { 81 | statement.Interpret(); 82 | } 83 | 84 | Env.PopScope(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Bow/Bow/Tokenise/Keywords.cs: -------------------------------------------------------------------------------- 1 | namespace Tokenise; 2 | 3 | public struct Keywords 4 | { 5 | private static readonly Dictionary Words = new() 6 | { 7 | { "var", TokenType.Var }, 8 | { "con", TokenType.Con }, 9 | { "str", TokenType.Str }, 10 | { "dec", TokenType.Dec }, 11 | { "boo", TokenType.Boo }, 12 | { "true", TokenType.BooLiteral }, 13 | { "false", TokenType.BooLiteral }, 14 | { "if", TokenType.If }, 15 | { "alt", TokenType.Alt }, 16 | { "altif", TokenType.AltIf }, 17 | { "switch", TokenType.Switch }, 18 | { "other", TokenType.Other }, 19 | { "break", TokenType.Break }, 20 | { "fun", TokenType.Fun } 21 | }; 22 | 23 | public static bool Contains(string key) 24 | { 25 | return Words.ContainsKey(key); 26 | } 27 | 28 | public static string GetTokenType(string key) 29 | { 30 | return Words[key]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Bow/Bow/Tokenise/Lexer.cs: -------------------------------------------------------------------------------- 1 | using Errors; 2 | 3 | namespace Tokenise; 4 | 5 | public class Lexer 6 | { 7 | private int _start; 8 | private int _current; 9 | private int _line = 1; 10 | private readonly string _code; 11 | private readonly List _tokens = new(); 12 | 13 | public Lexer(string code) 14 | { 15 | _code = code; 16 | } 17 | 18 | public List ScanTokens() 19 | { 20 | while (!IsAtEnd()) 21 | { 22 | _start = _current; 23 | ScanToken(); 24 | } 25 | 26 | _tokens.Add(new Token(TokenType.EOF, "", "\0", _line)); 27 | 28 | return _tokens; 29 | } 30 | 31 | private void ScanToken() 32 | { 33 | char C = Advance(); 34 | 35 | switch (C) 36 | { 37 | case '(': 38 | AddToken(TokenType.LeftBracket); 39 | break; 40 | case ')': 41 | AddToken(TokenType.RightBracket); 42 | break; 43 | case ',': 44 | AddToken(TokenType.Comma); 45 | break; 46 | case '+': 47 | AddToken(TokenType.Plus); 48 | break; 49 | case '-': 50 | Minus(); 51 | break; 52 | case '*': 53 | AddToken(TokenType.Star); 54 | break; 55 | case '/': 56 | AddToken(TokenType.Slash); 57 | break; 58 | case '.': 59 | AddToken(TokenType.Dot); 60 | break; 61 | case '\'': case '"': 62 | Str(); 63 | break; 64 | case '>': 65 | GreaterThan(); 66 | break; 67 | case '<': 68 | LessThan(); 69 | break; 70 | case '=': 71 | Equals(); 72 | break; 73 | case '!': 74 | Bang(); 75 | break; 76 | case '&': 77 | And(); 78 | break; 79 | case '|': 80 | Or(); 81 | break; 82 | case '?': 83 | Question(); 84 | break; 85 | case ' ': case '\r': case '\t': 86 | break; 87 | case '\n': 88 | _line++; 89 | break; 90 | default: 91 | if (Char.IsDigit(C)) 92 | { 93 | Dec(); 94 | } 95 | else if (Char.IsLetter(C)) 96 | { 97 | Identifier(); 98 | } 99 | else 100 | { 101 | throw new BowSyntaxError($"Unrecognised character {C} on line {_line}"); 102 | } 103 | break; 104 | } 105 | } 106 | 107 | private void AddToken(string type, string literal="") 108 | { 109 | _tokens.Add(new Token(type, _code[_start.._current], literal, _line)); 110 | } 111 | 112 | private char Advance() 113 | { 114 | char C = _code[_current]; 115 | _current++; 116 | 117 | return C; 118 | } 119 | 120 | private bool IsAtEnd() 121 | { 122 | return _current >= _code.Length; 123 | } 124 | 125 | private char Peek() 126 | { 127 | return IsAtEnd() ? '\0' : _code[_current]; 128 | } 129 | 130 | private char PeekNext() 131 | { 132 | return _current + 1 > _code.Length ? '\0' : _code[_current + 1]; 133 | } 134 | 135 | private void Minus() 136 | { 137 | if (Peek() == '<') 138 | { 139 | Advance(); 140 | AddToken(TokenType.CloseDeclare); 141 | } 142 | else 143 | { 144 | AddToken(TokenType.Minus); 145 | } 146 | } 147 | 148 | private void GreaterThan() 149 | { 150 | if (Peek() == '=') 151 | { 152 | Advance(); 153 | AddToken(TokenType.GreaterThanEqual); 154 | } 155 | else 156 | { 157 | AddToken(TokenType.GreaterThan); 158 | } 159 | } 160 | 161 | private void LessThan() 162 | { 163 | if (Peek() == '-') 164 | { 165 | Advance(); 166 | LeftSingleArrow(); 167 | } 168 | else if (Peek() == '=') 169 | { 170 | Advance(); 171 | LessThanEqual(); 172 | } 173 | else 174 | { 175 | AddToken(TokenType.LessThan); 176 | } 177 | } 178 | 179 | private void LeftSingleArrow() 180 | { 181 | if (Peek() == '<') 182 | { 183 | Advance(); 184 | AddToken(TokenType.Assign); 185 | } 186 | else if (Peek() == '-') 187 | { 188 | Advance(); 189 | AddToken(TokenType.ReturnArrow); 190 | } 191 | else 192 | { 193 | AddToken(TokenType.OpenDeclare); 194 | } 195 | } 196 | 197 | private void LessThanEqual() 198 | { 199 | if (Peek() == '=') 200 | { 201 | Advance(); 202 | AddToken(TokenType.CloseBlock); 203 | } 204 | else 205 | { 206 | AddToken(TokenType.LessThanEqual); 207 | } 208 | } 209 | 210 | private void Equals() 211 | { 212 | if (Peek() == '=') 213 | { 214 | Advance(); 215 | RightDoubleArrow(); 216 | } 217 | else if (Peek() == '>') 218 | { 219 | Advance(); 220 | AddToken(TokenType.FunTypeOpenBlock); 221 | } 222 | else 223 | { 224 | AddToken(TokenType.Equal); 225 | } 226 | } 227 | 228 | private void RightDoubleArrow() 229 | { 230 | if (Peek() != '>') 231 | { 232 | throw new BowSyntaxError($"Malformed open block arrow on line {_line}"); 233 | } 234 | 235 | Advance(); 236 | AddToken(TokenType.OpenBlock); 237 | } 238 | 239 | private void Bang() 240 | { 241 | if (Peek() == '=') 242 | { 243 | Advance(); 244 | AddToken(TokenType.NotEqual); 245 | } 246 | else 247 | { 248 | AddToken(TokenType.Not); 249 | } 250 | } 251 | 252 | private void And() 253 | { 254 | if (Peek() != '&') 255 | { 256 | throw new BowSyntaxError($"Expected '&' on line {_line}"); 257 | } 258 | 259 | Advance(); 260 | AddToken(TokenType.And); 261 | } 262 | 263 | private void Or() 264 | { 265 | if (Peek() != '|') 266 | { 267 | AddToken(TokenType.Seperator); 268 | } 269 | else 270 | { 271 | Advance(); 272 | AddToken(TokenType.Or); 273 | } 274 | } 275 | 276 | private void Question() 277 | { 278 | if (Peek() != '-') 279 | { 280 | throw new BowSyntaxError($"'Malformed case branch arrow on line {_line}"); 281 | } 282 | 283 | Advance(); 284 | CaseBranch(); 285 | } 286 | 287 | private void CaseBranch() 288 | { 289 | if (Peek() != '>') 290 | { 291 | throw new BowSyntaxError($"'Malformed case branch arrow on line {_line}"); 292 | } 293 | 294 | Advance(); 295 | AddToken(TokenType.CaseBranch); 296 | } 297 | 298 | private void Str() 299 | { 300 | char type = _code[_current - 1]; 301 | 302 | while (!IsAtEnd() && Peek() != type) 303 | { 304 | if (Peek() == '\n') 305 | { 306 | _line++; 307 | } 308 | 309 | Advance(); 310 | } 311 | 312 | if (IsAtEnd()) 313 | { 314 | throw new BowEOFError($"unterminated string on line {_line}"); 315 | } 316 | 317 | Advance(); 318 | 319 | string value = _code[(_start + 1)..(_current - 1)]; 320 | AddToken(TokenType.StrLiteral, value); 321 | } 322 | 323 | private void Dec() 324 | { 325 | while (Char.IsDigit(Peek())) 326 | { 327 | Advance(); 328 | } 329 | 330 | if (Peek() == '.' && Char.IsDigit(PeekNext())) 331 | { 332 | Advance(); 333 | 334 | while (Char.IsDigit(Peek())) 335 | { 336 | Advance(); 337 | } 338 | } 339 | 340 | AddToken(TokenType.DecLiteral, _code[_start.._current]); 341 | } 342 | 343 | private void Identifier() 344 | { 345 | while (Char.IsLetterOrDigit(Peek()) || Peek() == '_') 346 | { 347 | Advance(); 348 | } 349 | 350 | string identifier = _code[_start.._current]; 351 | 352 | if (Keywords.Contains(identifier)) 353 | { 354 | AddToken(Keywords.GetTokenType(identifier), identifier); 355 | } 356 | else 357 | { 358 | AddToken(TokenType.Identifier, identifier); 359 | } 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /Bow/Bow/Tokenise/Token.cs: -------------------------------------------------------------------------------- 1 | namespace Tokenise; 2 | 3 | public class Token 4 | { 5 | public string Type { get; } 6 | public string Lexeme { get; } 7 | public string Literal { get; } 8 | public int Line { get; } 9 | 10 | public Token(string type, string lexeme, string literal, int line) 11 | { 12 | Type = type; 13 | Lexeme = lexeme; 14 | Literal = literal; 15 | Line = line; 16 | } 17 | 18 | public string Inspect() 19 | { 20 | return $""; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Bow/Bow/Tokenise/TokenType.cs: -------------------------------------------------------------------------------- 1 | namespace Tokenise; 2 | 3 | public struct TokenType 4 | { 5 | public const string 6 | LeftBracket = "LEFT_BRACKET", 7 | RightBracket = "RIGHT_BRACKET", 8 | 9 | Comma = "COMMA", 10 | Seperator = "SEPERATOR", 11 | 12 | Plus = "PLUS", 13 | Minus = "MINUS", 14 | Star = "STAR", 15 | Slash = "SLASH", 16 | Dot = "DOT", 17 | 18 | Equal = "EQUAL", 19 | NotEqual = "NOT_EQUAL", 20 | LessThan = "LESS_THAN", 21 | LessThanEqual = "LESS_THAN_EQUAL", 22 | GreaterThan = "GREATER_THAN", 23 | GreaterThanEqual = "GREATER_THAN_EQUAL", 24 | 25 | Not = "NOT", 26 | And = "AND", 27 | Or = "OR", 28 | 29 | StrLiteral = "STRLITERAL", 30 | DecLiteral = "DECLITERAL", 31 | BooLiteral = "BOOLITERAL", 32 | 33 | NullReturn = "NULL_RETURN", 34 | 35 | Str = "STR", 36 | Dec = "DEC", 37 | Boo = "BOO", 38 | 39 | Var = "VAR", 40 | Con = "CON", 41 | 42 | Assign = "ASSIGN", 43 | OpenDeclare = "OPEN_DECLARE", 44 | CloseDeclare = "CLOSE_DECLARE", 45 | Identifier = "IDENTIFIER", 46 | 47 | If = "IF", 48 | Alt = "ALT", 49 | AltIf = "ALT_IF", 50 | 51 | Switch = "SWITCH", 52 | Other = "OTHER", 53 | Break = "BREAK", 54 | CaseBranch = "CASE_BRANCH", 55 | 56 | Fun = "FUN", 57 | 58 | OpenBlock = "OPEN_BLOCK", 59 | CloseBlock = "CLOSE_BLOCK", 60 | FunTypeOpenBlock = "Fun_Type_Open_Block", 61 | 62 | ReturnArrow = "RETURN_ARROW", 63 | 64 | EOF = "EOF"; 65 | } 66 | -------------------------------------------------------------------------------- /Bow/Program.cs: -------------------------------------------------------------------------------- 1 | if (args.Length > 0) 2 | { 3 | new Bow(args[0], false, args.Contains("--debug")).Run(); 4 | } 5 | else 6 | { 7 | Bow.RunShell(); 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Bow logo](https://cc.repl.co/MQwSi6.svg) 2 | # Bow 3 | 4 | Bow is a statically typed interpretted language which has a lot of arrows in it. 5 | --------------------------------------------------------------------------------