├── .gitignore ├── LICENSE ├── LoxScriptExample.sln ├── LoxScriptExample ├── App.config ├── Core │ ├── Extensions │ │ └── DoubleEx.cs │ ├── IO │ │ ├── BinaryFileReader.cs │ │ ├── BinaryFileWriter.cs │ │ ├── IReader.cs │ │ └── IWriter.cs │ └── Utilities │ │ ├── SharedBuffer.cs │ │ ├── TextEncoding.cs │ │ └── Tracer.cs ├── LoxScriptExample.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── TestNativeObject.cs ├── README.md ├── TODO.md ├── Tests ├── assignment │ ├── associativity.lox │ ├── global.lox │ ├── grouping.lox │ ├── infix_operator.lox │ ├── local.lox │ ├── prefix_operator.lox │ ├── syntax.lox │ ├── to_this.lox │ └── undefined.lox ├── benchmark.lox ├── benchmark │ ├── binary_trees.lox │ ├── equality.lox │ ├── fib.lox │ ├── instantiation.lox │ ├── invocation.lox │ ├── method_call.lox │ ├── properties.lox │ ├── string_equality.lox │ ├── trees.lox │ └── zoo.lox ├── block │ ├── empty.lox │ └── scope.lox ├── bool │ ├── equality.lox │ └── not.lox ├── call │ ├── bool.lox │ ├── nil.lox │ ├── num.lox │ ├── object.lox │ └── string.lox ├── class │ ├── empty.lox │ ├── inherit_self.lox │ ├── inherited_method.lox │ ├── local_inherit_other.lox │ ├── local_inherit_self.lox │ ├── local_reference_self.lox │ └── reference_self.lox ├── closure │ ├── assign_to_closure.lox │ ├── assign_to_shadowed_later.lox │ ├── close_over_function_parameter.lox │ ├── close_over_later_variable.lox │ ├── close_over_method_parameter.lox │ ├── closed_closure_in_function.lox │ ├── nested_closure.lox │ ├── open_closure_in_function.lox │ ├── reference_closure_multiple_times.lox │ ├── reuse_closure_slot.lox │ ├── shadow_closure_with_local.lox │ ├── unused_closure.lox │ └── unused_later_closure.lox ├── comments │ ├── line_at_eof.lox │ ├── only_line_comment.lox │ ├── only_line_comment_and_line.lox │ └── unicode.lox ├── constructor ├── empty_file.lox ├── expressions │ ├── evaluate.lox │ └── parse.lox ├── field │ ├── call_function_field.lox │ ├── call_nonfunction_field.lox │ ├── get_and_set_method.lox │ ├── get_on_bool.lox │ ├── get_on_class.lox │ ├── get_on_function.lox │ ├── get_on_nil.lox │ ├── get_on_num.lox │ ├── get_on_string.lox │ ├── many.lox │ ├── method.lox │ ├── method_binds_this.lox │ ├── on_instance.lox │ ├── set_evaluation_order.lox │ ├── set_on_bool.lox │ ├── set_on_class.lox │ ├── set_on_function.lox │ ├── set_on_nil.lox │ ├── set_on_num.lox │ ├── set_on_string.lox │ └── undefined.lox ├── for │ ├── class_in_body.lox │ ├── closure_in_body.lox │ ├── fun_in_body.lox │ ├── return_closure.lox │ ├── return_inside.lox │ ├── scope.lox │ ├── statement_condition.lox │ ├── statement_increment.lox │ ├── statement_initializer.lox │ ├── syntax.lox │ └── var_in_body.lox ├── function │ ├── body_must_be_block.lox │ ├── empty_body.lox │ ├── extra_arguments.lox │ ├── local_mutual_recursion.lox │ ├── local_recursion.lox │ ├── missing_arguments.lox │ ├── missing_comma_in_parameters.lox │ ├── mutual_recursion.lox │ ├── parameters.lox │ ├── print.lox │ ├── recursion.lox │ ├── too_many_arguments.lox │ └── too_many_parameters.lox ├── if │ ├── class_in_else.lox │ ├── class_in_then.lox │ ├── dangling_else.lox │ ├── else.lox │ ├── fun_in_else.lox │ ├── fun_in_then.lox │ ├── if.lox │ ├── truth.lox │ ├── var_in_else.lox │ └── var_in_then.lox ├── inheritance │ ├── constructor.lox │ ├── inherit_from_function.lox │ ├── inherit_from_nil.lox │ ├── inherit_from_number.lox │ ├── inherit_methods.lox │ ├── parenthesized_superclass.lox │ └── set_fields_from_base_class.lox ├── limit │ ├── loop_too_large.lox │ ├── no_reuse_constants.lox │ ├── stack_overflow.lox │ ├── too_many_constants.lox │ ├── too_many_locals.lox │ └── too_many_upvalues.lox ├── logical_operator │ ├── and.lox │ ├── and_truth.lox │ ├── or.lox │ └── or_truth.lox ├── method │ ├── arity.lox │ ├── empty_block.lox │ ├── extra_arguments.lox │ ├── missing_arguments.lox │ ├── not_found.lox │ ├── print_bound_method.lox │ ├── refer_to_name.lox │ ├── too_many_arguments.lox │ └── too_many_parameters.lox ├── nil │ └── literal.lox ├── number │ ├── decimal_point_at_eof.lox │ ├── leading_dot.lox │ ├── literals.lox │ ├── nan_equality.lox │ └── trailing_dot.lox ├── operator │ ├── add.lox │ ├── add_bool_nil.lox │ ├── add_bool_num.lox │ ├── add_bool_string.lox │ ├── add_nil_nil.lox │ ├── add_num_nil.lox │ ├── add_string_nil.lox │ ├── comparison.lox │ ├── divide.lox │ ├── divide_nonnum_num.lox │ ├── divide_num_nonnum.lox │ ├── equals.lox │ ├── equals_class.lox │ ├── equals_method.lox │ ├── greater_nonnum_num.lox │ ├── greater_num_nonnum.lox │ ├── greater_or_equal_nonnum_num.lox │ ├── greater_or_equal_num_nonnum.lox │ ├── less_nonnum_num.lox │ ├── less_num_nonnum.lox │ ├── less_or_equal_nonnum_num.lox │ ├── less_or_equal_num_nonnum.lox │ ├── multiply.lox │ ├── multiply_nonnum_num.lox │ ├── multiply_num_nonnum.lox │ ├── negate.lox │ ├── negate_nonnum.lox │ ├── not.lox │ ├── not_class.lox │ ├── not_equals.lox │ ├── subtract.lox │ ├── subtract_nonnum_num.lox │ └── subtract_num_nonnum.lox ├── precedence.lox ├── print │ └── missing_argument.lox ├── regression │ ├── 394.lox │ └── 40.lox ├── return │ ├── after_else.lox │ ├── after_if.lox │ ├── after_while.lox │ ├── at_top_level.lox │ ├── in_function.lox │ ├── in_method.lox │ └── return_nil_if_no_value.lox ├── scanning │ ├── identifiers.lox │ ├── keywords.lox │ ├── numbers.lox │ ├── punctuators.lox │ ├── strings.lox │ └── whitespace.lox ├── string │ ├── error_after_multiline.lox │ ├── literals.lox │ ├── multiline.lox │ └── unterminated.lox ├── super │ ├── bound_method.lox │ ├── call_other_method.lox │ ├── call_same_method.lox │ ├── closure.lox │ ├── constructor.lox │ ├── extra_arguments.lox │ ├── indirectly_inherited.lox │ ├── missing_arguments.lox │ ├── no_superclass_bind.lox │ ├── no_superclass_call.lox │ ├── no_superclass_method.lox │ ├── parenthesized.lox │ ├── reassign_superclass.lox │ ├── super_at_top_level.lox │ ├── super_in_closure_in_inherited_method.lox │ ├── super_in_inherited_method.lox │ ├── super_in_top_level_function.lox │ ├── super_without_dot.lox │ ├── super_without_name.lox │ └── this_in_superclass_method.lox ├── test.fail.txt ├── testsuite.lox ├── this │ ├── closure.lox │ ├── nested_class.lox │ ├── nested_closure.lox │ ├── this_at_top_level.lox │ ├── this_in_method.lox │ └── this_in_top_level_function.lox ├── unexpected_character.lox ├── variable │ ├── collide_with_parameter.lox │ ├── duplicate_local.lox │ ├── duplicate_parameter.lox │ ├── early_bound.lox │ ├── in_middle_of_block.lox │ ├── in_nested_block.lox │ ├── local_from_method.lox │ ├── redeclare_global.lox │ ├── redefine_global.lox │ ├── scope_reuse_in_different_blocks.lox │ ├── shadow_and_local.lox │ ├── shadow_global.lox │ ├── shadow_local.lox │ ├── undefined_global.lox │ ├── undefined_local.lox │ ├── uninitialized.lox │ ├── unreached_undefined.lox │ ├── use_false_as_var.lox │ ├── use_global_in_initializer.lox │ ├── use_local_in_initializer.lox │ ├── use_nil_as_var.lox │ └── use_this_as_var.lox └── while │ ├── class_in_body.lox │ ├── closure_in_body.lox │ ├── fun_in_body.lox │ ├── return_closure.lox │ ├── return_inside.lox │ ├── syntax.lox │ └── var_in_body.lox └── XptScriptLib ├── Core └── Scripting │ ├── Base │ ├── ACompiler.cs │ ├── ATokenizer.cs │ ├── CompilerException.cs │ ├── StringTable.cs │ ├── Token.cs │ ├── TokenList.cs │ ├── TokenTypes.cs │ ├── TokenizerContext.cs │ └── Validation.cs │ ├── Interfaces │ ├── IScript.cs │ └── IScriptMethod.cs │ ├── LoxScript │ ├── Compiling │ │ ├── ELoxFunctionType.cs │ │ ├── LoxCompiler.cs │ │ ├── LoxCompilerClass.cs │ │ ├── LoxCompilerFixup.cs │ │ ├── LoxCompilerLocal.cs │ │ ├── LoxCompilerSwitch.cs │ │ ├── LoxCompilerUpvalue.cs │ │ ├── LoxTokenTypes.cs │ │ └── LoxTokenizer.cs │ ├── GearsChunk.cs │ ├── LoxFieldAttribute.cs │ ├── LoxHandler.cs │ └── VirtualMachine │ │ ├── EGearsOpCode.cs │ │ ├── Gears.cs │ │ ├── GearsCallFrame.cs │ │ ├── GearsHashTable.cs │ │ ├── GearsNativeList.cs │ │ ├── GearsNativeWrapper.cs │ │ ├── GearsObj.cs │ │ ├── GearsRuntimeException.cs │ │ ├── GearsValue.cs │ │ ├── Gears_Context.cs │ │ ├── Gears_Disassembly.cs │ │ ├── Gears_NativeCalls.cs │ │ └── Gears_State.cs │ └── Rules │ ├── Compiling │ ├── RuleCompiler.cs │ └── RuleTokenizer.cs │ ├── Rule.cs │ ├── RuleAttribute.cs │ ├── RuleCollection.cs │ ├── RuleCondition.cs │ ├── RuleInvocationDelegates.cs │ ├── RuleSystem.cs │ └── VarCollection.cs ├── XptScriptLib.projitems └── XptScriptLib.shproj /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zane Wagner 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 | -------------------------------------------------------------------------------- /LoxScriptExample.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32825.248 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LoxScriptExample", "LoxScriptExample\LoxScriptExample.csproj", "{BAACB847-DD68-4037-9D94-FB987397E01C}" 7 | EndProject 8 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "XptScriptLib", "XptScriptLib\XptScriptLib.shproj", "{821F5BAD-D32C-4E09-BED2-2C39069F9E9C}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BAACB847-DD68-4037-9D94-FB987397E01C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {BAACB847-DD68-4037-9D94-FB987397E01C}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {BAACB847-DD68-4037-9D94-FB987397E01C}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {BAACB847-DD68-4037-9D94-FB987397E01C}.Release|Any CPU.Build.0 = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ExtensibilityGlobals) = postSolution 25 | SolutionGuid = {C53A079C-C1C3-45D9-9DAF-694C42BB48B2} 26 | EndGlobalSection 27 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 28 | XptScriptLib\XptScriptLib.projitems*{821f5bad-d32c-4e09-bed2-2c39069f9e9c}*SharedItemsImports = 13 29 | XptScriptLib\XptScriptLib.projitems*{baacb847-dd68-4037-9d94-fb987397e01c}*SharedItemsImports = 4 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /LoxScriptExample/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LoxScriptExample/Core/Extensions/DoubleEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XPT.Core.Extensions { 4 | static class DoubleEx { 5 | public static ulong ConvertToULong(this double value) { 6 | return BitConverter.ToUInt64(BitConverter.GetBytes(value), 0); 7 | } 8 | 9 | public static double ConvertToDouble(this ulong value) { 10 | return BitConverter.ToDouble(BitConverter.GetBytes(value), 0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LoxScriptExample/Core/IO/IReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace XPT.Core.IO { 4 | public interface IReader { 5 | long ReadLong(); 6 | int ReadInt(); 7 | short ReadShort(); 8 | byte ReadByte(); 9 | uint ReadUInt(); 10 | ushort ReadUShort(); 11 | sbyte ReadSByte(); 12 | bool ReadBool(); 13 | int Read7BitInt(); 14 | string ReadAsciiPrefix(); 15 | byte[] ReadBytes(int count); 16 | bool ReadFourBytes(string value); 17 | ushort[] ReadUShorts(int count); 18 | long Position { get; set; } 19 | long Length { get; } 20 | void Close(); 21 | long Seek(long offset, SeekOrigin origin); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LoxScriptExample/Core/IO/IWriter.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.IO { 2 | public interface IWriter { 3 | void Write(bool value); 4 | void Write(byte value); 5 | void Write(sbyte value); 6 | void Write(short value); 7 | void Write(ushort value); 8 | void Write(int value); 9 | void Write(uint value); 10 | void Write(long value); 11 | void Write(ulong value); 12 | void Write7BitInt(int value); 13 | void WriteAsciiPrefix(string value); 14 | void WriteFourBytes(string value); 15 | void Write(byte[] value); 16 | long Position { get; } 17 | void Close(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LoxScriptExample/Core/Utilities/SharedBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace XPT { 5 | class SharedBuffer : IDisposable { 6 | internal static int CountBuffersInUse => _BuffersInUse.Count; 7 | internal const int SizeSmallBuffer = 16384; // 1024 * 16 8 | internal const int SizeLargeBuffer = 1048576; // 1024 * 1024 9 | 10 | private static readonly object _LockRoot = new object(); 11 | private static readonly List _Buffers = new List(); 12 | private static readonly List _BuffersInUse = new List(); 13 | private const int SizeMax = SizeLargeBuffer; // 1mb 14 | 15 | private static byte[] Get(int size, bool overrideSizeLimit = false) { 16 | byte[] buffer; 17 | lock (_LockRoot) { 18 | if (size <= SizeSmallBuffer) { 19 | for (int i = 0; i < _Buffers.Count; i++) { 20 | if (_Buffers[i].Length == SizeSmallBuffer) { 21 | buffer = _Buffers[i]; 22 | _Buffers.RemoveAt(i); 23 | _BuffersInUse.Add(buffer); 24 | return buffer; 25 | } 26 | } 27 | buffer = new byte[SizeSmallBuffer]; 28 | return buffer; 29 | } 30 | else if (size <= SizeLargeBuffer) { 31 | for (int i = 0; i < _Buffers.Count; i++) { 32 | if (_Buffers[i].Length == SizeLargeBuffer) { 33 | buffer = _Buffers[i]; 34 | _Buffers.RemoveAt(i); 35 | _BuffersInUse.Add(buffer); 36 | return buffer; 37 | } 38 | } 39 | buffer = new byte[SizeLargeBuffer]; 40 | return buffer; 41 | } 42 | else { 43 | int desiredSize = SizeLargeBuffer; 44 | while (desiredSize < size) { 45 | desiredSize *= 2; 46 | } 47 | if (desiredSize > SizeMax && !overrideSizeLimit) { 48 | throw new Exception($"XPT.Core.Buffer: cannot instance a buffer of size {size} bytes."); 49 | } 50 | for (int i = 0; i < _Buffers.Count; i++) { 51 | if (_Buffers[i].Length >= desiredSize) { 52 | buffer = _Buffers[i]; 53 | _Buffers.RemoveAt(i); 54 | _BuffersInUse.Add(buffer); 55 | return buffer; 56 | } 57 | } 58 | buffer = new byte[desiredSize]; 59 | return buffer; 60 | } 61 | } 62 | } 63 | 64 | private static void Retire(byte[] buffer) { 65 | lock (_LockRoot) { 66 | if (_BuffersInUse.Contains(buffer)) { 67 | _BuffersInUse.Remove(buffer); 68 | } 69 | if (!_Buffers.Contains(buffer)) { 70 | _Buffers.Add(buffer); 71 | } 72 | } 73 | } 74 | 75 | // === Instance =================================================================================================== 76 | // ================================================================================================================ 77 | 78 | private bool _IsDisposed = false; 79 | private readonly int _Length; 80 | private byte[] _Buffer; 81 | private string _Name; 82 | 83 | internal byte[] Buffer => _Buffer; 84 | 85 | internal int Length => _Buffer != null ? _Length : 0; 86 | 87 | internal byte this[int index] { 88 | get => _Buffer[index]; 89 | set => _Buffer[index] = value; 90 | } 91 | 92 | internal SharedBuffer(string name, int desiredSize, bool overrideSizeLimit = false) { 93 | _Name = name; 94 | _Length = desiredSize; 95 | _Buffer = Get(desiredSize, overrideSizeLimit); 96 | } 97 | 98 | public override string ToString() => $"{(_Name ?? "Not in use")} ({_Buffer.Length} bytes)"; 99 | 100 | protected virtual void Dispose(bool disposing) { 101 | if (!_IsDisposed) { 102 | if (disposing) { 103 | // TODO: dispose managed state (managed objects) 104 | } 105 | if (_Buffer != null) { 106 | Retire(_Buffer); 107 | _Buffer = null; 108 | } 109 | _IsDisposed = true; 110 | } 111 | } 112 | 113 | // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 114 | ~SharedBuffer() { 115 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 116 | Dispose(disposing: false); 117 | } 118 | 119 | public void Dispose() { 120 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 121 | Dispose(disposing: true); 122 | GC.SuppressFinalize(this); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /LoxScriptExample/Core/Utilities/Tracer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace XPT.Core.Utilities { 8 | internal static class Tracer { 9 | /// 10 | /// Will show error message but attempt to keep running. 11 | /// 12 | /// 13 | internal static void Warn(string text) { 14 | Console.WriteLine(text); 15 | } 16 | 17 | /// 18 | /// Will show error message but attempt to keep running. 19 | /// 20 | /// 21 | internal static void Error(string text) { 22 | Console.WriteLine(text); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LoxScriptExample/LoxScriptExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BAACB847-DD68-4037-9D94-FB987397E01C} 8 | Exe 9 | XPT 10 | LoxScript 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | false 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | true 37 | false 38 | 39 | 40 | XPT.Program 41 | 42 | 43 | 44 | 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 | -------------------------------------------------------------------------------- /LoxScriptExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("XPT")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("XPT")] 12 | [assembly: AssemblyCopyright("Copyright © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("baacb847-dd68-4037-9d94-fb987397e01c")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /LoxScriptExample/TestNativeObject.cs: -------------------------------------------------------------------------------- 1 | namespace XPT { 2 | class TestNativeObject { 3 | public int TestInt = 1; 4 | public byte TestByte = 1; 5 | public string TestStr = "doughnut"; 6 | public string TestStrNull = null; 7 | 8 | public int TestProperty { 9 | get; 10 | set; 11 | } 12 | 13 | public void TestFn() { 14 | 15 | } 16 | 17 | public int TestOut() { 18 | return 1234567; 19 | } 20 | 21 | public void TestParam(int a, string b) { 22 | 23 | } 24 | 25 | private void DontGetThis() { 26 | 27 | } 28 | 29 | protected void DontGetThisEitheR() { 30 | 31 | } 32 | 33 | internal void DontGetThisEither2() { 34 | 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LoxScript 2 | 3 | LoxScript is a bytecode compiler and virtual machine ("Gears") for Bob Nystrom's [Lox language](http://craftinginterpreters.com/the-lox-language.html), both written in C#. 4 | 5 | The LoxScript bytecode virtual machine is called [Gears](https://github.com/ZaneDubya/LoxScript/tree/master/XptScriptLib/Core/Scripting/LoxScript/VirtualMachine). Gears executes bytecode created by the LoxScript compiler. The output of Gears matches the reference "clox" virtual machine described in Chapters 14-30 of Bob's book [Crafting Interpreters](http://craftinginterpreters.com). However, the internal implementation of the virtual machine is different, and the bytecode generated by the Gears compiler will not run on clox. 6 | 7 | Improvements over Lox/clox: 8 | 9 | - Compiler can write bytecode to a binary file and load/run this at runtime. 10 | - Native interfaces allow Lox files to interact with native C# functions and objects. 11 | - Support for switch statement! This only took hours, and the code is horrifying. 12 | 13 | Performance: 14 | 15 | - Gears runs the reference 'fibonnaci' benchmark roughly 69x slower than native c# code. 16 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Lox language modifications 2 | My goal with this project is to create a scripting language for the MedievaLands server. This means I would have to add some functionality to the language that would allow me to reference and modify 'native' server objects, which would be implemented in c#, possibly with lox 'wrapper' ('resolver'?) objects. 3 | 4 | I also recognize that Lox is intentionally fairly bare bones. Although it is syntactically a C-derivative in syntax, it lacks many of C's operators. So I'd like to add syntax and grammar to my lox implementation to allow it to make use of the language features and syntactic sugar that I enjoy in my main programming language, C#. 5 | 6 | In his book [Crafting Interpreters](http://craftinginterpreters.com/optimization.html), Bob Nystrom wrote: 7 | > If you make significant changes to the language, it would be good to also change the name, mostly to avoid confusing people about what “Lox” refers to. 8 | 9 | It is an open question as to whether and when I should change the name. If I do, I would keep the name very similar ("dox" - derivative of lox? "nox" - not original lox? "zox" - zane's lox? "sox" - scripting lox?) 10 | 11 | ## Language changes 12 | 13 | Add native interfaces: 14 | - [x] The most important addition! Pass a native objects to a lox script that can be accessed and modified by the script. 15 | - [ ] Document limitations of native object interface. 16 | 17 | Compile, Load, and Execute 18 | - [x] Add 'compile' functionality that saves complete byte code to a file/array that can then be run from scratch. 19 | - [x] Also create a reusable 'gears' object that can host a script to completion, unload the script, and then be reused for another script without disposing of the gears. 20 | 21 | Print and Assert: 22 | -[x] Make 'print' a native function and get rid of the associate op_print operation. 23 | -[ ] Add 'assert' native function that compares output to an expected value (probably a string, maybe values too?). 24 | 25 | Operators and syntactic sugar: 26 | - [ ] Add ++, --, +=, -=, /=, *=. 27 | - [ ] Add modulus % and %=. 28 | - [ ] Add binary |, |=, &, &=. 29 | 30 | Parsing Expressions: 31 | - [ ] Add ternary ?: operator. 32 | - [ ] Add error production to handle each binary operator that appears without a left-hand operation. 33 | 34 | Control Flow: 35 | - [ ] Add break within loops. 36 | - [ ] Add continue within loops. 37 | 38 | ## Missing functionality: 39 | - [ ] Add line information to compiled lox code. 40 | 41 | ## Interesting ideas, but probably won't do: 42 | 43 | Resolving and Binding: 44 | - [ ] Don't allow redefinition of a local within an enclosed scope (possibly, I'm of two minds here). 45 | 46 | Classes: 47 | - [ ] Add Static Methods. 48 | - [ ] Allow properties and don't allow addition of new properties via code (vars in class) 49 | - [ ] private and public modifiers (maybe). 50 | - [ ] Getters and Setters (maybe maybe). 51 | - [ ] Replace "init" with "new" and C# ctor syntax (classname()). 52 | 53 | Inheritance: 54 | - [ ] Replace inherits syntax '<' with ':' 55 | - [ ] Replace "super" with "base" -------------------------------------------------------------------------------- /Tests/assignment/associativity.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | var b = "b"; 3 | var c = "c"; 4 | 5 | // Assignment is right-associative. 6 | a = b = c; 7 | print a; // expect: c 8 | print b; // expect: c 9 | print c; // expect: c 10 | -------------------------------------------------------------------------------- /Tests/assignment/global.lox: -------------------------------------------------------------------------------- 1 | var a = "before"; 2 | print a; // expect: before 3 | 4 | a = "after"; 5 | print a; // expect: after 6 | 7 | print a = "arg"; // expect: arg 8 | print a; // expect: arg 9 | -------------------------------------------------------------------------------- /Tests/assignment/grouping.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | (a) = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /Tests/assignment/infix_operator.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | var b = "b"; 3 | a + b = "value"; // Error at '=': Invalid assignment target. 4 | -------------------------------------------------------------------------------- /Tests/assignment/local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "before"; 3 | print a; // expect: before 4 | 5 | a = "after"; 6 | print a; // expect: after 7 | 8 | print a = "arg"; // expect: arg 9 | print a; // expect: arg 10 | } 11 | -------------------------------------------------------------------------------- /Tests/assignment/prefix_operator.lox: -------------------------------------------------------------------------------- 1 | var a = "a"; 2 | !a = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /Tests/assignment/syntax.lox: -------------------------------------------------------------------------------- 1 | // Assignment on RHS of variable. 2 | var a = "before"; 3 | var c = a = "var"; 4 | print a; // expect: var 5 | print c; // expect: var 6 | -------------------------------------------------------------------------------- /Tests/assignment/to_this.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | Foo() { 3 | this = "value"; // Error at '=': Invalid assignment target. 4 | } 5 | } 6 | 7 | Foo(); 8 | -------------------------------------------------------------------------------- /Tests/assignment/undefined.lox: -------------------------------------------------------------------------------- 1 | unknown = "what"; // expect runtime error: Undefined variable 'unknown'. 2 | -------------------------------------------------------------------------------- /Tests/benchmark.lox: -------------------------------------------------------------------------------- 1 | // === Fibonacci Benchmark =================================================== 2 | // This benchmarks performance of function calls. Creating a new function 3 | // context is a very expensive operation in the VM. Calculating fibonacci for 4 | // 0-30 using this (unoptimized!) method requires roughly 4,350,000 calls. 5 | // Compiler flags: release mode/optimize+/prefer 64-bit 6 | // Native C# 8.3 ms 7 | // AST: 40090 ms 5725x slower than native 8 | // GearsVM 5/08 complete 1445 ms 28x faster than AST 172x slower than native 9 | // GearsVM 5/08 single chunk 1378 ms 29x faster than AST 164x slower than native 10 | // GearsVM 5/08 bitstrings 1073 ms 37x faster than AST 127x slower than native 11 | // GearsVM 5/08 prefer 64bit 712 ms 56x faster than AST 85x slower than native 12 | // GearsVM 5/08 agressiveInline 662 ms 60x faster than AST 79x slower than native 13 | // GearsVM 5/09 e/i ulong cast 636 ms 63x faster than AST 76x slower than native 14 | // GearsVM 5/09 undup consts 626 ms 64x faster than AST 75x slower than native 15 | // GearsVM 5/09 fix to gc 623 ms 64x faster than AST 75x slower than native 16 | // GearsVM 5/11 stopping here 575 ms 69x faster than AST 69x slower than native 17 | // =========================================================================== 18 | 19 | fun fibonacci(n) { 20 | if (n <= 1) { 21 | return n; 22 | } 23 | return fibonacci(n - 2) + fibonacci(n - 1); 24 | } 25 | 26 | var total = 0; 27 | for (var j = 0; j < 10; j = j + 1) { 28 | var start = clock(); 29 | for (var i = 0; i < 30; i = i + 1) { 30 | var x = fibonacci(i); 31 | } 32 | var now = clock() - start; 33 | total = total + now; 34 | print(j); 35 | } 36 | print(total / 10); 37 | -------------------------------------------------------------------------------- /Tests/benchmark/binary_trees.lox: -------------------------------------------------------------------------------- 1 | class Tree { 2 | init(item, depth) { 3 | this.item = item; 4 | this.depth = depth; 5 | if (depth > 0) { 6 | var item2 = item + item; 7 | depth = depth - 1; 8 | this.left = Tree(item2 - 1, depth); 9 | this.right = Tree(item2, depth); 10 | } else { 11 | this.left = nil; 12 | this.right = nil; 13 | } 14 | } 15 | 16 | check() { 17 | if (this.left == nil) { 18 | return this.item; 19 | } 20 | 21 | return this.item + this.left.check() - this.right.check(); 22 | } 23 | } 24 | 25 | var minDepth = 4; 26 | var maxDepth = 14; 27 | var stretchDepth = maxDepth + 1; 28 | 29 | var start = clock(); 30 | 31 | print "stretch tree of depth:"; 32 | print stretchDepth; 33 | print "check:"; 34 | print Tree(0, stretchDepth).check(); 35 | 36 | var longLivedTree = Tree(0, maxDepth); 37 | 38 | // iterations = 2 ** maxDepth 39 | var iterations = 1; 40 | var d = 0; 41 | while (d < maxDepth) { 42 | iterations = iterations * 2; 43 | d = d + 1; 44 | } 45 | 46 | var depth = minDepth; 47 | while (depth < stretchDepth) { 48 | var check = 0; 49 | var i = 1; 50 | while (i <= iterations) { 51 | check = check + Tree(i, depth).check() + Tree(-i, depth).check(); 52 | i = i + 1; 53 | } 54 | 55 | print "num trees:"; 56 | print iterations * 2; 57 | print "depth:"; 58 | print depth; 59 | print "check:"; 60 | print check; 61 | 62 | iterations = iterations / 4; 63 | depth = depth + 2; 64 | } 65 | 66 | print "long lived tree of depth:"; 67 | print maxDepth; 68 | print "check:"; 69 | print longLivedTree.check(); 70 | print "elapsed:"; 71 | print clock() - start; 72 | -------------------------------------------------------------------------------- /Tests/benchmark/equality.lox: -------------------------------------------------------------------------------- 1 | var i = 0; 2 | 3 | var loopStart = clock(); 4 | 5 | while (i < 10000000) { 6 | i = i + 1; 7 | 8 | 1; 1; 1; 2; 1; nil; 1; "str"; 1; true; 9 | nil; nil; nil; 1; nil; "str"; nil; true; 10 | true; true; true; 1; true; false; true; "str"; true; nil; 11 | "str"; "str"; "str"; "stru"; "str"; 1; "str"; nil; "str"; true; 12 | } 13 | 14 | var loopTime = clock() - loopStart; 15 | 16 | var start = clock(); 17 | 18 | i = 0; 19 | while (i < 10000000) { 20 | i = i + 1; 21 | 22 | 1 == 1; 1 == 2; 1 == nil; 1 == "str"; 1 == true; 23 | nil == nil; nil == 1; nil == "str"; nil == true; 24 | true == true; true == 1; true == false; true == "str"; true == nil; 25 | "str" == "str"; "str" == "stru"; "str" == 1; "str" == nil; "str" == true; 26 | } 27 | 28 | var elapsed = clock() - start; 29 | print "loop"; 30 | print loopTime; 31 | print "elapsed"; 32 | print elapsed; 33 | print "equals"; 34 | print elapsed - loopTime; 35 | -------------------------------------------------------------------------------- /Tests/benchmark/fib.lox: -------------------------------------------------------------------------------- 1 | fun fib(n) { 2 | if (n < 2) return n; 3 | return fib(n - 2) + fib(n - 1); 4 | } 5 | 6 | var start = clock(); 7 | print fib(35) == 9227465; 8 | print clock() - start; 9 | -------------------------------------------------------------------------------- /Tests/benchmark/instantiation.lox: -------------------------------------------------------------------------------- 1 | // This benchmark stresses instance creation and initializer calling. 2 | 3 | class Foo { 4 | init() {} 5 | } 6 | 7 | var start = clock(); 8 | var i = 0; 9 | while (i < 500000) { 10 | Foo(); 11 | Foo(); 12 | Foo(); 13 | Foo(); 14 | Foo(); 15 | Foo(); 16 | Foo(); 17 | Foo(); 18 | Foo(); 19 | Foo(); 20 | Foo(); 21 | Foo(); 22 | Foo(); 23 | Foo(); 24 | Foo(); 25 | Foo(); 26 | Foo(); 27 | Foo(); 28 | Foo(); 29 | Foo(); 30 | Foo(); 31 | Foo(); 32 | Foo(); 33 | Foo(); 34 | Foo(); 35 | Foo(); 36 | Foo(); 37 | Foo(); 38 | Foo(); 39 | Foo(); 40 | i = i + 1; 41 | } 42 | 43 | print clock() - start; 44 | -------------------------------------------------------------------------------- /Tests/benchmark/invocation.lox: -------------------------------------------------------------------------------- 1 | // This benchmark stresses just method invocation. 2 | 3 | class Foo { 4 | method0() {} 5 | method1() {} 6 | method2() {} 7 | method3() {} 8 | method4() {} 9 | method5() {} 10 | method6() {} 11 | method7() {} 12 | method8() {} 13 | method9() {} 14 | method10() {} 15 | method11() {} 16 | method12() {} 17 | method13() {} 18 | method14() {} 19 | method15() {} 20 | method16() {} 21 | method17() {} 22 | method18() {} 23 | method19() {} 24 | method20() {} 25 | method21() {} 26 | method22() {} 27 | method23() {} 28 | method24() {} 29 | method25() {} 30 | method26() {} 31 | method27() {} 32 | method28() {} 33 | method29() {} 34 | } 35 | 36 | var foo = Foo(); 37 | var start = clock(); 38 | var i = 0; 39 | while (i < 500000) { 40 | foo.method0(); 41 | foo.method1(); 42 | foo.method2(); 43 | foo.method3(); 44 | foo.method4(); 45 | foo.method5(); 46 | foo.method6(); 47 | foo.method7(); 48 | foo.method8(); 49 | foo.method9(); 50 | foo.method10(); 51 | foo.method11(); 52 | foo.method12(); 53 | foo.method13(); 54 | foo.method14(); 55 | foo.method15(); 56 | foo.method16(); 57 | foo.method17(); 58 | foo.method18(); 59 | foo.method19(); 60 | foo.method20(); 61 | foo.method21(); 62 | foo.method22(); 63 | foo.method23(); 64 | foo.method24(); 65 | foo.method25(); 66 | foo.method26(); 67 | foo.method27(); 68 | foo.method28(); 69 | foo.method29(); 70 | i = i + 1; 71 | } 72 | 73 | print clock() - start; 74 | -------------------------------------------------------------------------------- /Tests/benchmark/method_call.lox: -------------------------------------------------------------------------------- 1 | class Toggle { 2 | init(startState) { 3 | this.state = startState; 4 | } 5 | 6 | value() { return this.state; } 7 | 8 | activate() { 9 | this.state = !this.state; 10 | return this; 11 | } 12 | } 13 | 14 | class NthToggle < Toggle { 15 | init(startState, maxCounter) { 16 | super.init(startState); 17 | this.countMax = maxCounter; 18 | this.count = 0; 19 | } 20 | 21 | activate() { 22 | this.count = this.count + 1; 23 | if (this.count >= this.countMax) { 24 | super.activate(); 25 | this.count = 0; 26 | } 27 | 28 | return this; 29 | } 30 | } 31 | 32 | var start = clock(); 33 | var n = 100000; 34 | var val = true; 35 | var toggle = Toggle(val); 36 | 37 | for (var i = 0; i < n; i = i + 1) { 38 | val = toggle.activate().value(); 39 | val = toggle.activate().value(); 40 | val = toggle.activate().value(); 41 | val = toggle.activate().value(); 42 | val = toggle.activate().value(); 43 | val = toggle.activate().value(); 44 | val = toggle.activate().value(); 45 | val = toggle.activate().value(); 46 | val = toggle.activate().value(); 47 | val = toggle.activate().value(); 48 | } 49 | 50 | print toggle.value(); 51 | 52 | val = true; 53 | var ntoggle = NthToggle(val, 3); 54 | 55 | for (var i = 0; i < n; i = i + 1) { 56 | val = ntoggle.activate().value(); 57 | val = ntoggle.activate().value(); 58 | val = ntoggle.activate().value(); 59 | val = ntoggle.activate().value(); 60 | val = ntoggle.activate().value(); 61 | val = ntoggle.activate().value(); 62 | val = ntoggle.activate().value(); 63 | val = ntoggle.activate().value(); 64 | val = ntoggle.activate().value(); 65 | val = ntoggle.activate().value(); 66 | } 67 | 68 | print ntoggle.value(); 69 | print clock() - start; 70 | -------------------------------------------------------------------------------- /Tests/benchmark/properties.lox: -------------------------------------------------------------------------------- 1 | // This benchmark stresses both field and method lookup. 2 | 3 | class Foo { 4 | init() { 5 | this.field0 = 1; 6 | this.field1 = 1; 7 | this.field2 = 1; 8 | this.field3 = 1; 9 | this.field4 = 1; 10 | this.field5 = 1; 11 | this.field6 = 1; 12 | this.field7 = 1; 13 | this.field8 = 1; 14 | this.field9 = 1; 15 | this.field10 = 1; 16 | this.field11 = 1; 17 | this.field12 = 1; 18 | this.field13 = 1; 19 | this.field14 = 1; 20 | this.field15 = 1; 21 | this.field16 = 1; 22 | this.field17 = 1; 23 | this.field18 = 1; 24 | this.field19 = 1; 25 | this.field20 = 1; 26 | this.field21 = 1; 27 | this.field22 = 1; 28 | this.field23 = 1; 29 | this.field24 = 1; 30 | this.field25 = 1; 31 | this.field26 = 1; 32 | this.field27 = 1; 33 | this.field28 = 1; 34 | this.field29 = 1; 35 | } 36 | 37 | method0() { return this.field0; } 38 | method1() { return this.field1; } 39 | method2() { return this.field2; } 40 | method3() { return this.field3; } 41 | method4() { return this.field4; } 42 | method5() { return this.field5; } 43 | method6() { return this.field6; } 44 | method7() { return this.field7; } 45 | method8() { return this.field8; } 46 | method9() { return this.field9; } 47 | method10() { return this.field10; } 48 | method11() { return this.field11; } 49 | method12() { return this.field12; } 50 | method13() { return this.field13; } 51 | method14() { return this.field14; } 52 | method15() { return this.field15; } 53 | method16() { return this.field16; } 54 | method17() { return this.field17; } 55 | method18() { return this.field18; } 56 | method19() { return this.field19; } 57 | method20() { return this.field20; } 58 | method21() { return this.field21; } 59 | method22() { return this.field22; } 60 | method23() { return this.field23; } 61 | method24() { return this.field24; } 62 | method25() { return this.field25; } 63 | method26() { return this.field26; } 64 | method27() { return this.field27; } 65 | method28() { return this.field28; } 66 | method29() { return this.field29; } 67 | } 68 | 69 | var foo = Foo(); 70 | var start = clock(); 71 | var i = 0; 72 | while (i < 500000) { 73 | foo.method0(); 74 | foo.method1(); 75 | foo.method2(); 76 | foo.method3(); 77 | foo.method4(); 78 | foo.method5(); 79 | foo.method6(); 80 | foo.method7(); 81 | foo.method8(); 82 | foo.method9(); 83 | foo.method10(); 84 | foo.method11(); 85 | foo.method12(); 86 | foo.method13(); 87 | foo.method14(); 88 | foo.method15(); 89 | foo.method16(); 90 | foo.method17(); 91 | foo.method18(); 92 | foo.method19(); 93 | foo.method20(); 94 | foo.method21(); 95 | foo.method22(); 96 | foo.method23(); 97 | foo.method24(); 98 | foo.method25(); 99 | foo.method26(); 100 | foo.method27(); 101 | foo.method28(); 102 | foo.method29(); 103 | i = i + 1; 104 | } 105 | 106 | print clock() - start; 107 | -------------------------------------------------------------------------------- /Tests/benchmark/trees.lox: -------------------------------------------------------------------------------- 1 | class Tree { 2 | init(depth) { 3 | this.depth = depth; 4 | if (depth > 0) { 5 | this.a = Tree(depth - 1); 6 | this.b = Tree(depth - 1); 7 | this.c = Tree(depth - 1); 8 | this.d = Tree(depth - 1); 9 | this.e = Tree(depth - 1); 10 | } 11 | } 12 | 13 | walk() { 14 | if (this.depth == 0) return 0; 15 | return this.depth 16 | + this.a.walk() 17 | + this.b.walk() 18 | + this.c.walk() 19 | + this.d.walk() 20 | + this.e.walk(); 21 | } 22 | } 23 | 24 | var tree = Tree(8); 25 | var start = clock(); 26 | for (var i = 0; i < 100; i = i + 1) { 27 | if (tree.walk() != 122068) print "Error"; 28 | } 29 | print clock() - start; 30 | -------------------------------------------------------------------------------- /Tests/benchmark/zoo.lox: -------------------------------------------------------------------------------- 1 | class Zoo { 2 | init() { 3 | this.aarvark = 1; 4 | this.baboon = 1; 5 | this.cat = 1; 6 | this.donkey = 1; 7 | this.elephant = 1; 8 | this.fox = 1; 9 | } 10 | ant() { return this.aarvark; } 11 | banana() { return this.baboon; } 12 | tuna() { return this.cat; } 13 | hay() { return this.donkey; } 14 | grass() { return this.elephant; } 15 | mouse() { return this.fox; } 16 | } 17 | 18 | var zoo = Zoo(); 19 | var sum = 0; 20 | var start = clock(); 21 | while (sum < 10000000) { 22 | sum = sum + zoo.ant() 23 | + zoo.banana() 24 | + zoo.tuna() 25 | + zoo.hay() 26 | + zoo.grass() 27 | + zoo.mouse(); 28 | } 29 | 30 | print sum; 31 | print clock() - start; 32 | -------------------------------------------------------------------------------- /Tests/block/empty.lox: -------------------------------------------------------------------------------- 1 | {} // By itself. 2 | 3 | // In a statement. 4 | if (true) {} 5 | if (false) {} else {} 6 | 7 | print "ok"; // expect: ok 8 | -------------------------------------------------------------------------------- /Tests/block/scope.lox: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | 3 | { 4 | var a = "inner"; 5 | print a; // expect: inner 6 | } 7 | 8 | print a; // expect: outer 9 | -------------------------------------------------------------------------------- /Tests/bool/equality.lox: -------------------------------------------------------------------------------- 1 | print true == true; // expect: true 2 | print true == false; // expect: false 3 | print false == true; // expect: false 4 | print false == false; // expect: true 5 | 6 | // Not equal to other types. 7 | print true == 1; // expect: false 8 | print false == 0; // expect: false 9 | print true == "true"; // expect: false 10 | print false == "false"; // expect: false 11 | print false == ""; // expect: false 12 | 13 | print true != true; // expect: false 14 | print true != false; // expect: true 15 | print false != true; // expect: true 16 | print false != false; // expect: false 17 | 18 | // Not equal to other types. 19 | print true != 1; // expect: true 20 | print false != 0; // expect: true 21 | print true != "true"; // expect: true 22 | print false != "false"; // expect: true 23 | print false != ""; // expect: true 24 | -------------------------------------------------------------------------------- /Tests/bool/not.lox: -------------------------------------------------------------------------------- 1 | print !true; // expect: false 2 | print !false; // expect: true 3 | print !!true; // expect: true 4 | -------------------------------------------------------------------------------- /Tests/call/bool.lox: -------------------------------------------------------------------------------- 1 | true(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /Tests/call/nil.lox: -------------------------------------------------------------------------------- 1 | nil(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /Tests/call/num.lox: -------------------------------------------------------------------------------- 1 | 123(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /Tests/call/object.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | foo(); // expect runtime error: Can only call functions and classes. 5 | -------------------------------------------------------------------------------- /Tests/call/string.lox: -------------------------------------------------------------------------------- 1 | "str"(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /Tests/class/empty.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | print Foo; // expect: Foo 4 | -------------------------------------------------------------------------------- /Tests/class/inherit_self.lox: -------------------------------------------------------------------------------- 1 | class Foo < Foo {} // Error at 'Foo': A class cannot inherit from itself. 2 | -------------------------------------------------------------------------------- /Tests/class/inherited_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | inFoo() { 3 | print "in foo"; 4 | } 5 | } 6 | 7 | class Bar < Foo { 8 | inBar() { 9 | print "in bar"; 10 | } 11 | } 12 | 13 | class Baz < Bar { 14 | inBaz() { 15 | print "in baz"; 16 | } 17 | } 18 | 19 | var baz = Baz(); 20 | baz.inFoo(); // expect: in foo 21 | baz.inBar(); // expect: in bar 22 | baz.inBaz(); // expect: in baz 23 | -------------------------------------------------------------------------------- /Tests/class/local_inherit_other.lox: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | fun f() { 4 | class B < A {} 5 | return B; 6 | } 7 | 8 | print f(); // expect: B 9 | -------------------------------------------------------------------------------- /Tests/class/local_inherit_self.lox: -------------------------------------------------------------------------------- 1 | { 2 | class Foo < Foo {} // Error at 'Foo': A class cannot inherit from itself. 3 | } 4 | // [c line 5] Error at end: Expect '}' after block. 5 | -------------------------------------------------------------------------------- /Tests/class/local_reference_self.lox: -------------------------------------------------------------------------------- 1 | { 2 | class Foo { 3 | returnSelf() { 4 | return Foo; 5 | } 6 | } 7 | 8 | print Foo().returnSelf(); // expect: Foo 9 | } 10 | -------------------------------------------------------------------------------- /Tests/class/reference_self.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | returnSelf() { 3 | return Foo; 4 | } 5 | } 6 | 7 | print Foo().returnSelf(); // expect: Foo 8 | -------------------------------------------------------------------------------- /Tests/closure/assign_to_closure.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | var g; 3 | 4 | { 5 | var local = "local"; 6 | fun f_() { 7 | print local; 8 | local = "after f"; 9 | print local; 10 | } 11 | f = f_; 12 | 13 | fun g_() { 14 | print local; 15 | local = "after g"; 16 | print local; 17 | } 18 | g = g_; 19 | } 20 | 21 | f(); 22 | // expect: local 23 | // expect: after f 24 | 25 | g(); 26 | // expect: after f 27 | // expect: after g 28 | -------------------------------------------------------------------------------- /Tests/closure/assign_to_shadowed_later.lox: -------------------------------------------------------------------------------- 1 | var a = "global"; 2 | 3 | { 4 | fun assign() { 5 | a = "assigned"; 6 | } 7 | 8 | var a = "inner"; 9 | assign(); 10 | print a; // expect: inner 11 | } 12 | 13 | print a; // expect: assigned 14 | -------------------------------------------------------------------------------- /Tests/closure/close_over_function_parameter.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | fun foo(param) { 4 | fun f_() { 5 | print param; 6 | } 7 | f = f_; 8 | } 9 | foo("param"); 10 | 11 | f(); // expect: param 12 | -------------------------------------------------------------------------------- /Tests/closure/close_over_later_variable.lox: -------------------------------------------------------------------------------- 1 | // This is a regression test. There was a bug where if an upvalue for an 2 | // earlier local (here "a") was captured *after* a later one ("b"), then it 3 | // would crash because it walked to the end of the upvalue list (correct), but 4 | // then didn't handle not finding the variable. 5 | 6 | fun f() { 7 | var a = "a"; 8 | var b = "b"; 9 | fun g() { 10 | print b; // expect: b 11 | print a; // expect: a 12 | } 13 | g(); 14 | } 15 | f(); 16 | -------------------------------------------------------------------------------- /Tests/closure/close_over_method_parameter.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | class Foo { 4 | method(param) { 5 | fun f_() { 6 | print param; 7 | } 8 | f = f_; 9 | } 10 | } 11 | 12 | Foo().method("param"); 13 | f(); // expect: param 14 | -------------------------------------------------------------------------------- /Tests/closure/closed_closure_in_function.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | { 4 | var local = "local"; 5 | fun f_() { 6 | print local; 7 | } 8 | f = f_; 9 | } 10 | 11 | f(); // expect: local 12 | -------------------------------------------------------------------------------- /Tests/closure/nested_closure.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | fun f1() { 4 | var a = "a"; 5 | fun f2() { 6 | var b = "b"; 7 | fun f3() { 8 | var c = "c"; 9 | fun f4() { 10 | print a; 11 | print b; 12 | print c; 13 | } 14 | f = f4; 15 | } 16 | f3(); 17 | } 18 | f2(); 19 | } 20 | f1(); 21 | 22 | f(); 23 | // expect: a 24 | // expect: b 25 | // expect: c 26 | -------------------------------------------------------------------------------- /Tests/closure/open_closure_in_function.lox: -------------------------------------------------------------------------------- 1 | { 2 | var local = "local"; 3 | fun f() { 4 | print local; // expect: local 5 | } 6 | f(); 7 | } 8 | -------------------------------------------------------------------------------- /Tests/closure/reference_closure_multiple_times.lox: -------------------------------------------------------------------------------- 1 | var f; 2 | 3 | { 4 | var a = "a"; 5 | fun f_() { 6 | print a; 7 | print a; 8 | } 9 | f = f_; 10 | } 11 | 12 | f(); 13 | // expect: a 14 | // expect: a 15 | -------------------------------------------------------------------------------- /Tests/closure/reuse_closure_slot.lox: -------------------------------------------------------------------------------- 1 | { 2 | var f; 3 | 4 | { 5 | var a = "a"; 6 | fun f_() { print a; } 7 | f = f_; 8 | } 9 | 10 | { 11 | // Since a is out of scope, the local slot will be reused by b. Make sure 12 | // that f still closes over a. 13 | var b = "b"; 14 | f(); // expect: a 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/closure/shadow_closure_with_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var foo = "closure"; 3 | fun f() { 4 | { 5 | print foo; // expect: closure 6 | var foo = "shadow"; 7 | print foo; // expect: shadow 8 | } 9 | print foo; // expect: closure 10 | } 11 | f(); 12 | } 13 | -------------------------------------------------------------------------------- /Tests/closure/unused_closure.lox: -------------------------------------------------------------------------------- 1 | // This is a regression test. There was a bug where the VM would try to close 2 | // an upvalue even if the upvalue was never created because the codepath for 3 | // the closure was not executed. 4 | 5 | { 6 | var a = "a"; 7 | if (false) { 8 | fun foo() { a; } 9 | } 10 | } 11 | 12 | // If we get here, we didn't segfault when a went out of scope. 13 | print "ok"; // expect: ok 14 | -------------------------------------------------------------------------------- /Tests/closure/unused_later_closure.lox: -------------------------------------------------------------------------------- 1 | // This is a regression test. When closing upvalues for discarded locals, it 2 | // wouldn't make sure it discarded the upvalue for the correct stack slot. 3 | // 4 | // Here we create two locals that can be closed over, but only the first one 5 | // actually is. When "b" goes out of scope, we need to make sure we don't 6 | // prematurely close "a". 7 | var closure; 8 | 9 | { 10 | var a = "a"; 11 | 12 | { 13 | var b = "b"; 14 | fun returnA() { 15 | return a; 16 | } 17 | 18 | closure = returnA; 19 | 20 | if (false) { 21 | fun returnB() { 22 | return b; 23 | } 24 | } 25 | } 26 | 27 | print closure(); // expect: a 28 | } 29 | -------------------------------------------------------------------------------- /Tests/comments/line_at_eof.lox: -------------------------------------------------------------------------------- 1 | print "ok"; // expect: ok 2 | // comment -------------------------------------------------------------------------------- /Tests/comments/only_line_comment.lox: -------------------------------------------------------------------------------- 1 | // comment -------------------------------------------------------------------------------- /Tests/comments/only_line_comment_and_line.lox: -------------------------------------------------------------------------------- 1 | // comment 2 | -------------------------------------------------------------------------------- /Tests/comments/unicode.lox: -------------------------------------------------------------------------------- 1 | // Unicode characters are allowed in comments. 2 | // 3 | // Latin 1 Supplement: £§¶ÜÞ 4 | // Latin Extended-A: ĐĦŋœ 5 | // Latin Extended-B: ƂƢƩǁ 6 | // Other stuff: ឃᢆ᯽₪ℜ↩⊗┺░ 7 | // Emoji: ☃☺♣ 8 | 9 | print "ok"; // expect: ok 10 | -------------------------------------------------------------------------------- /Tests/constructor/arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(a, b) { 3 | print "init"; // expect: init 4 | this.a = a; 5 | this.b = b; 6 | } 7 | } 8 | 9 | var foo = Foo(1, 2); 10 | print foo.a; // expect: 1 11 | print foo.b; // expect: 2 12 | -------------------------------------------------------------------------------- /Tests/constructor/call_init_early_return.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | print "init"; 4 | return; 5 | print "nope"; 6 | } 7 | } 8 | 9 | var foo = Foo(); // expect: init 10 | print foo.init(); // expect: init 11 | // expect: Foo instance 12 | -------------------------------------------------------------------------------- /Tests/constructor/call_init_explicitly.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(arg) { 3 | print "Foo.init(" + arg + ")"; 4 | this.field = "init"; 5 | } 6 | } 7 | 8 | var foo = Foo("one"); // expect: Foo.init(one) 9 | foo.field = "field"; 10 | 11 | var foo2 = foo.init("two"); // expect: Foo.init(two) 12 | print foo2; // expect: Foo instance 13 | 14 | // Make sure init() doesn't create a fresh instance. 15 | print foo.field; // expect: init 16 | -------------------------------------------------------------------------------- /Tests/constructor/default.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | print foo; // expect: Foo instance 5 | -------------------------------------------------------------------------------- /Tests/constructor/default_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(1, 2, 3); // expect runtime error: Expected 0 arguments but got 3. 4 | -------------------------------------------------------------------------------- /Tests/constructor/early_return.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | print "init"; 4 | return; 5 | print "nope"; 6 | } 7 | } 8 | 9 | var foo = Foo(); // expect: init 10 | print foo; // expect: Foo instance 11 | -------------------------------------------------------------------------------- /Tests/constructor/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(a, b) { 3 | this.a = a; 4 | this.b = b; 5 | } 6 | } 7 | 8 | var foo = Foo(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. -------------------------------------------------------------------------------- /Tests/constructor/init_not_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(arg) { 3 | print "Foo.init(" + arg + ")"; 4 | this.field = "init"; 5 | } 6 | } 7 | 8 | fun init() { 9 | print "not initializer"; 10 | } 11 | 12 | init(); // expect: not initializer 13 | -------------------------------------------------------------------------------- /Tests/constructor/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init(a, b) {} 3 | } 4 | 5 | var foo = Foo(1); // expect runtime error: Expected 2 arguments but got 1. 6 | -------------------------------------------------------------------------------- /Tests/constructor/return_in_nested_function.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | fun init() { 4 | return "bar"; 5 | } 6 | print init(); // expect: bar 7 | } 8 | } 9 | 10 | print Foo(); // expect: Foo instance 11 | -------------------------------------------------------------------------------- /Tests/constructor/return_value.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | init() { 3 | return "result"; // Error at 'return': Cannot return a value from an initializer. 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Tests/empty_file.lox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaneDubya/LoxScript/9891a4e0f32f98ebaacd84a5140224ef76c17ca8/Tests/empty_file.lox -------------------------------------------------------------------------------- /Tests/expressions/evaluate.lox: -------------------------------------------------------------------------------- 1 | // Note: This is just for the expression evaluating chapter which evaluates an 2 | // expression directly. 3 | (5 - (3 - 1)) + -1 4 | // expect: 2 5 | -------------------------------------------------------------------------------- /Tests/expressions/parse.lox: -------------------------------------------------------------------------------- 1 | // Note: This is just for the expression parsing chapter which prints the AST. 2 | (5 - (3 - 1)) + -1 3 | // expect: (+ (group (- 5.0 (group (- 3.0 1.0)))) (- 1.0)) 4 | -------------------------------------------------------------------------------- /Tests/field/call_function_field.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | fun bar(a, b) { 4 | print "bar"; 5 | print a; 6 | print b; 7 | } 8 | 9 | var foo = Foo(); 10 | foo.bar = bar; 11 | 12 | foo.bar(1, 2); 13 | // expect: bar 14 | // expect: 1 15 | // expect: 2 16 | -------------------------------------------------------------------------------- /Tests/field/call_nonfunction_field.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | foo.bar = "not fn"; 5 | 6 | foo.bar(); // expect runtime error: Can only call functions and classes. 7 | -------------------------------------------------------------------------------- /Tests/field/get_and_set_method.lox: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo { 3 | method(a) { 4 | print "method"; 5 | print a; 6 | } 7 | other(a) { 8 | print "other"; 9 | print a; 10 | } 11 | } 12 | 13 | var foo = Foo(); 14 | var method = foo.method; 15 | 16 | // Setting a property shadows the instance method. 17 | foo.method = foo.other; 18 | foo.method(1); 19 | // expect: other 20 | // expect: 1 21 | 22 | // The old method handle still points to the original method. 23 | method(2); 24 | // expect: method 25 | // expect: 2 26 | -------------------------------------------------------------------------------- /Tests/field/get_on_bool.lox: -------------------------------------------------------------------------------- 1 | true.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /Tests/field/get_on_class.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar; // expect runtime error: Only instances have properties. 3 | -------------------------------------------------------------------------------- /Tests/field/get_on_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | foo.bar; // expect runtime error: Only instances have properties. 4 | -------------------------------------------------------------------------------- /Tests/field/get_on_nil.lox: -------------------------------------------------------------------------------- 1 | nil.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /Tests/field/get_on_num.lox: -------------------------------------------------------------------------------- 1 | 123.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /Tests/field/get_on_string.lox: -------------------------------------------------------------------------------- 1 | "str".foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /Tests/field/method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar(arg) { 3 | print arg; 4 | } 5 | } 6 | 7 | var bar = Foo().bar; 8 | print "got method"; // expect: got method 9 | bar("arg"); // expect: arg 10 | -------------------------------------------------------------------------------- /Tests/field/method_binds_this.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | sayName(a) { 3 | print this.name; 4 | print a; 5 | } 6 | } 7 | 8 | var foo1 = Foo(); 9 | foo1.name = "foo1"; 10 | 11 | var foo2 = Foo(); 12 | foo2.name = "foo2"; 13 | 14 | // Store the method reference on another object. 15 | foo2.fn = foo1.sayName; 16 | // Still retains original receiver. 17 | foo2.fn(1); 18 | // expect: foo1 19 | // expect: 1 20 | -------------------------------------------------------------------------------- /Tests/field/on_instance.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | var foo = Foo(); 4 | 5 | print foo.bar = "bar value"; // expect: bar value 6 | print foo.baz = "baz value"; // expect: baz value 7 | 8 | print foo.bar; // expect: bar value 9 | print foo.baz; // expect: baz value 10 | -------------------------------------------------------------------------------- /Tests/field/set_evaluation_order.lox: -------------------------------------------------------------------------------- 1 | undefined1.bar // expect runtime error: Undefined variable 'undefined1'. 2 | = undefined2; -------------------------------------------------------------------------------- /Tests/field/set_on_bool.lox: -------------------------------------------------------------------------------- 1 | true.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /Tests/field/set_on_class.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar = "value"; // expect runtime error: Only instances have fields. 3 | -------------------------------------------------------------------------------- /Tests/field/set_on_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | foo.bar = "value"; // expect runtime error: Only instances have fields. 4 | -------------------------------------------------------------------------------- /Tests/field/set_on_nil.lox: -------------------------------------------------------------------------------- 1 | nil.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /Tests/field/set_on_num.lox: -------------------------------------------------------------------------------- 1 | 123.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /Tests/field/set_on_string.lox: -------------------------------------------------------------------------------- 1 | "str".foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /Tests/field/undefined.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | var foo = Foo(); 3 | 4 | foo.bar; // expect runtime error: Undefined property 'bar'. 5 | -------------------------------------------------------------------------------- /Tests/for/class_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | for (;;) class Foo {} 3 | -------------------------------------------------------------------------------- /Tests/for/closure_in_body.lox: -------------------------------------------------------------------------------- 1 | var f1; 2 | var f2; 3 | var f3; 4 | 5 | for (var i = 1; i < 4; i = i + 1) { 6 | var j = i; 7 | fun f() { 8 | print i; 9 | print j; 10 | } 11 | 12 | if (j == 1) f1 = f; 13 | else if (j == 2) f2 = f; 14 | else f3 = f; 15 | } 16 | 17 | f1(); // expect: 4 18 | // expect: 1 19 | f2(); // expect: 4 20 | // expect: 2 21 | f3(); // expect: 4 22 | // expect: 3 23 | -------------------------------------------------------------------------------- /Tests/for/fun_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | for (;;) fun foo() {} 3 | -------------------------------------------------------------------------------- /Tests/for/return_closure.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | for (;;) { 3 | var i = "i"; 4 | fun g() { print i; } 5 | return g; 6 | } 7 | } 8 | 9 | var h = f(); 10 | h(); // expect: i 11 | -------------------------------------------------------------------------------- /Tests/for/return_inside.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | for (;;) { 3 | var i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print f(); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /Tests/for/scope.lox: -------------------------------------------------------------------------------- 1 | { 2 | var i = "before"; 3 | 4 | // New variable is in inner scope. 5 | for (var i = 0; i < 1; i = i + 1) { 6 | print i; // expect: 0 7 | 8 | // Loop body is in second inner scope. 9 | var i = -1; 10 | print i; // expect: -1 11 | } 12 | } 13 | 14 | { 15 | // New variable shadows outer variable. 16 | for (var i = 0; i > 0; i = i + 1) {} 17 | 18 | // Goes out of scope after loop. 19 | var i = "after"; 20 | print i; // expect: after 21 | 22 | // Can reuse an existing variable. 23 | for (i = 0; i < 1; i = i + 1) { 24 | print i; // expect: 0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/for/statement_condition.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '{': Expect expression. 2 | // [line 3] Error at ')': Expect ';' after expression. 3 | for (var a = 1; {}; a = a + 1) {} 4 | -------------------------------------------------------------------------------- /Tests/for/statement_increment.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '{': Expect expression. 2 | for (var a = 1; a < 2; {}) {} 3 | -------------------------------------------------------------------------------- /Tests/for/statement_initializer.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '{': Expect expression. 2 | // [line 3] Error at ')': Expect ';' after expression. 3 | for ({}; a < 2; a = a + 1) {} 4 | -------------------------------------------------------------------------------- /Tests/for/syntax.lox: -------------------------------------------------------------------------------- 1 | // Single-expression body. 2 | for (var c = 0; c < 3;) print c = c + 1; 3 | // expect: 1 4 | // expect: 2 5 | // expect: 3 6 | 7 | // Block body. 8 | for (var a = 0; a < 3; a = a + 1) { 9 | print a; 10 | } 11 | // expect: 0 12 | // expect: 1 13 | // expect: 2 14 | 15 | // No clauses. 16 | fun foo() { 17 | for (;;) return "done"; 18 | } 19 | print foo(); // expect: done 20 | 21 | // No variable. 22 | var i = 0; 23 | for (; i < 2; i = i + 1) print i; 24 | // expect: 0 25 | // expect: 1 26 | 27 | // No condition. 28 | fun bar() { 29 | for (var i = 0;; i = i + 1) { 30 | print i; 31 | if (i >= 2) return; 32 | } 33 | } 34 | bar(); 35 | // expect: 0 36 | // expect: 1 37 | // expect: 2 38 | 39 | // No increment. 40 | for (var i = 0; i < 2;) { 41 | print i; 42 | i = i + 1; 43 | } 44 | // expect: 0 45 | // expect: 1 46 | 47 | // Statement bodies. 48 | for (; false;) if (true) 1; else 2; 49 | for (; false;) while (true) 1; 50 | for (; false;) for (;;) 1; 51 | -------------------------------------------------------------------------------- /Tests/for/var_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | for (;;) var foo; 3 | -------------------------------------------------------------------------------- /Tests/function/body_must_be_block.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at '123': Expect '{' before function body. 2 | // [c line 4] Error at end: Expect '}' after block. 3 | fun f() 123; 4 | -------------------------------------------------------------------------------- /Tests/function/empty_body.lox: -------------------------------------------------------------------------------- 1 | fun f() {} 2 | print f(); // expect: nil 3 | -------------------------------------------------------------------------------- /Tests/function/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | fun f(a, b) { 2 | print a; 3 | print b; 4 | } 5 | 6 | f(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. 7 | -------------------------------------------------------------------------------- /Tests/function/local_mutual_recursion.lox: -------------------------------------------------------------------------------- 1 | { 2 | fun isEven(n) { 3 | if (n == 0) return true; 4 | return isOdd(n - 1); // expect runtime error: Undefined variable 'isOdd'. 5 | } 6 | 7 | fun isOdd(n) { 8 | return isEven(n - 1); 9 | } 10 | 11 | isEven(4); 12 | } -------------------------------------------------------------------------------- /Tests/function/local_recursion.lox: -------------------------------------------------------------------------------- 1 | { 2 | fun fib(n) { 3 | if (n < 2) return n; 4 | return fib(n - 1) + fib(n - 2); 5 | } 6 | 7 | print fib(8); // expect: 21 8 | } 9 | -------------------------------------------------------------------------------- /Tests/function/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | fun f(a, b) {} 2 | 3 | f(1); // expect runtime error: Expected 2 arguments but got 1. 4 | -------------------------------------------------------------------------------- /Tests/function/missing_comma_in_parameters.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error at 'c': Expect ')' after parameters. 2 | // [c line 4] Error at end: Expect '}' after block. 3 | fun foo(a, b c, d, e, f) {} 4 | -------------------------------------------------------------------------------- /Tests/function/mutual_recursion.lox: -------------------------------------------------------------------------------- 1 | fun isEven(n) { 2 | if (n == 0) return true; 3 | return isOdd(n - 1); 4 | } 5 | 6 | fun isOdd(n) { 7 | return isEven(n - 1); 8 | } 9 | 10 | print isEven(4); // expect: true 11 | print isOdd(3); // expect: true 12 | -------------------------------------------------------------------------------- /Tests/function/parameters.lox: -------------------------------------------------------------------------------- 1 | fun f0() { return 0; } 2 | print f0(); // expect: 0 3 | 4 | fun f1(a) { return a; } 5 | print f1(1); // expect: 1 6 | 7 | fun f2(a, b) { return a + b; } 8 | print f2(1, 2); // expect: 3 9 | 10 | fun f3(a, b, c) { return a + b + c; } 11 | print f3(1, 2, 3); // expect: 6 12 | 13 | fun f4(a, b, c, d) { return a + b + c + d; } 14 | print f4(1, 2, 3, 4); // expect: 10 15 | 16 | fun f5(a, b, c, d, e) { return a + b + c + d + e; } 17 | print f5(1, 2, 3, 4, 5); // expect: 15 18 | 19 | fun f6(a, b, c, d, e, f) { return a + b + c + d + e + f; } 20 | print f6(1, 2, 3, 4, 5, 6); // expect: 21 21 | 22 | fun f7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } 23 | print f7(1, 2, 3, 4, 5, 6, 7); // expect: 28 24 | 25 | fun f8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h; } 26 | print f8(1, 2, 3, 4, 5, 6, 7, 8); // expect: 36 27 | -------------------------------------------------------------------------------- /Tests/function/print.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | print foo; // expect: 3 | 4 | print clock; // expect: 5 | -------------------------------------------------------------------------------- /Tests/function/recursion.lox: -------------------------------------------------------------------------------- 1 | fun fib(n) { 2 | if (n < 2) return n; 3 | return fib(n - 1) + fib(n - 2); 4 | } 5 | 6 | print fib(8); // expect: 21 7 | -------------------------------------------------------------------------------- /Tests/function/too_many_arguments.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | { 3 | var a = 1; 4 | foo( 5 | a, // 1 6 | a, // 2 7 | a, // 3 8 | a, // 4 9 | a, // 5 10 | a, // 6 11 | a, // 7 12 | a, // 8 13 | a, // 9 14 | a, // 10 15 | a, // 11 16 | a, // 12 17 | a, // 13 18 | a, // 14 19 | a, // 15 20 | a, // 16 21 | a, // 17 22 | a, // 18 23 | a, // 19 24 | a, // 20 25 | a, // 21 26 | a, // 22 27 | a, // 23 28 | a, // 24 29 | a, // 25 30 | a, // 26 31 | a, // 27 32 | a, // 28 33 | a, // 29 34 | a, // 30 35 | a, // 31 36 | a, // 32 37 | a, // 33 38 | a, // 34 39 | a, // 35 40 | a, // 36 41 | a, // 37 42 | a, // 38 43 | a, // 39 44 | a, // 40 45 | a, // 41 46 | a, // 42 47 | a, // 43 48 | a, // 44 49 | a, // 45 50 | a, // 46 51 | a, // 47 52 | a, // 48 53 | a, // 49 54 | a, // 50 55 | a, // 51 56 | a, // 52 57 | a, // 53 58 | a, // 54 59 | a, // 55 60 | a, // 56 61 | a, // 57 62 | a, // 58 63 | a, // 59 64 | a, // 60 65 | a, // 61 66 | a, // 62 67 | a, // 63 68 | a, // 64 69 | a, // 65 70 | a, // 66 71 | a, // 67 72 | a, // 68 73 | a, // 69 74 | a, // 70 75 | a, // 71 76 | a, // 72 77 | a, // 73 78 | a, // 74 79 | a, // 75 80 | a, // 76 81 | a, // 77 82 | a, // 78 83 | a, // 79 84 | a, // 80 85 | a, // 81 86 | a, // 82 87 | a, // 83 88 | a, // 84 89 | a, // 85 90 | a, // 86 91 | a, // 87 92 | a, // 88 93 | a, // 89 94 | a, // 90 95 | a, // 91 96 | a, // 92 97 | a, // 93 98 | a, // 94 99 | a, // 95 100 | a, // 96 101 | a, // 97 102 | a, // 98 103 | a, // 99 104 | a, // 100 105 | a, // 101 106 | a, // 102 107 | a, // 103 108 | a, // 104 109 | a, // 105 110 | a, // 106 111 | a, // 107 112 | a, // 108 113 | a, // 109 114 | a, // 110 115 | a, // 111 116 | a, // 112 117 | a, // 113 118 | a, // 114 119 | a, // 115 120 | a, // 116 121 | a, // 117 122 | a, // 118 123 | a, // 119 124 | a, // 120 125 | a, // 121 126 | a, // 122 127 | a, // 123 128 | a, // 124 129 | a, // 125 130 | a, // 126 131 | a, // 127 132 | a, // 128 133 | a, // 129 134 | a, // 130 135 | a, // 131 136 | a, // 132 137 | a, // 133 138 | a, // 134 139 | a, // 135 140 | a, // 136 141 | a, // 137 142 | a, // 138 143 | a, // 139 144 | a, // 140 145 | a, // 141 146 | a, // 142 147 | a, // 143 148 | a, // 144 149 | a, // 145 150 | a, // 146 151 | a, // 147 152 | a, // 148 153 | a, // 149 154 | a, // 150 155 | a, // 151 156 | a, // 152 157 | a, // 153 158 | a, // 154 159 | a, // 155 160 | a, // 156 161 | a, // 157 162 | a, // 158 163 | a, // 159 164 | a, // 160 165 | a, // 161 166 | a, // 162 167 | a, // 163 168 | a, // 164 169 | a, // 165 170 | a, // 166 171 | a, // 167 172 | a, // 168 173 | a, // 169 174 | a, // 170 175 | a, // 171 176 | a, // 172 177 | a, // 173 178 | a, // 174 179 | a, // 175 180 | a, // 176 181 | a, // 177 182 | a, // 178 183 | a, // 179 184 | a, // 180 185 | a, // 181 186 | a, // 182 187 | a, // 183 188 | a, // 184 189 | a, // 185 190 | a, // 186 191 | a, // 187 192 | a, // 188 193 | a, // 189 194 | a, // 190 195 | a, // 191 196 | a, // 192 197 | a, // 193 198 | a, // 194 199 | a, // 195 200 | a, // 196 201 | a, // 197 202 | a, // 198 203 | a, // 199 204 | a, // 200 205 | a, // 201 206 | a, // 202 207 | a, // 203 208 | a, // 204 209 | a, // 205 210 | a, // 206 211 | a, // 207 212 | a, // 208 213 | a, // 209 214 | a, // 210 215 | a, // 211 216 | a, // 212 217 | a, // 213 218 | a, // 214 219 | a, // 215 220 | a, // 216 221 | a, // 217 222 | a, // 218 223 | a, // 219 224 | a, // 220 225 | a, // 221 226 | a, // 222 227 | a, // 223 228 | a, // 224 229 | a, // 225 230 | a, // 226 231 | a, // 227 232 | a, // 228 233 | a, // 229 234 | a, // 230 235 | a, // 231 236 | a, // 232 237 | a, // 233 238 | a, // 234 239 | a, // 235 240 | a, // 236 241 | a, // 237 242 | a, // 238 243 | a, // 239 244 | a, // 240 245 | a, // 241 246 | a, // 242 247 | a, // 243 248 | a, // 244 249 | a, // 245 250 | a, // 246 251 | a, // 247 252 | a, // 248 253 | a, // 249 254 | a, // 250 255 | a, // 251 256 | a, // 252 257 | a, // 253 258 | a, // 254 259 | a, // 255 260 | a); // Error at 'a': Cannot have more than 255 arguments. 261 | } 262 | -------------------------------------------------------------------------------- /Tests/function/too_many_parameters.lox: -------------------------------------------------------------------------------- 1 | // 256 parameters. 2 | fun f( 3 | a1, 4 | a2, 5 | a3, 6 | a4, 7 | a5, 8 | a6, 9 | a7, 10 | a8, 11 | a9, 12 | a10, 13 | a11, 14 | a12, 15 | a13, 16 | a14, 17 | a15, 18 | a16, 19 | a17, 20 | a18, 21 | a19, 22 | a20, 23 | a21, 24 | a22, 25 | a23, 26 | a24, 27 | a25, 28 | a26, 29 | a27, 30 | a28, 31 | a29, 32 | a30, 33 | a31, 34 | a32, 35 | a33, 36 | a34, 37 | a35, 38 | a36, 39 | a37, 40 | a38, 41 | a39, 42 | a40, 43 | a41, 44 | a42, 45 | a43, 46 | a44, 47 | a45, 48 | a46, 49 | a47, 50 | a48, 51 | a49, 52 | a50, 53 | a51, 54 | a52, 55 | a53, 56 | a54, 57 | a55, 58 | a56, 59 | a57, 60 | a58, 61 | a59, 62 | a60, 63 | a61, 64 | a62, 65 | a63, 66 | a64, 67 | a65, 68 | a66, 69 | a67, 70 | a68, 71 | a69, 72 | a70, 73 | a71, 74 | a72, 75 | a73, 76 | a74, 77 | a75, 78 | a76, 79 | a77, 80 | a78, 81 | a79, 82 | a80, 83 | a81, 84 | a82, 85 | a83, 86 | a84, 87 | a85, 88 | a86, 89 | a87, 90 | a88, 91 | a89, 92 | a90, 93 | a91, 94 | a92, 95 | a93, 96 | a94, 97 | a95, 98 | a96, 99 | a97, 100 | a98, 101 | a99, 102 | a100, 103 | a101, 104 | a102, 105 | a103, 106 | a104, 107 | a105, 108 | a106, 109 | a107, 110 | a108, 111 | a109, 112 | a110, 113 | a111, 114 | a112, 115 | a113, 116 | a114, 117 | a115, 118 | a116, 119 | a117, 120 | a118, 121 | a119, 122 | a120, 123 | a121, 124 | a122, 125 | a123, 126 | a124, 127 | a125, 128 | a126, 129 | a127, 130 | a128, 131 | a129, 132 | a130, 133 | a131, 134 | a132, 135 | a133, 136 | a134, 137 | a135, 138 | a136, 139 | a137, 140 | a138, 141 | a139, 142 | a140, 143 | a141, 144 | a142, 145 | a143, 146 | a144, 147 | a145, 148 | a146, 149 | a147, 150 | a148, 151 | a149, 152 | a150, 153 | a151, 154 | a152, 155 | a153, 156 | a154, 157 | a155, 158 | a156, 159 | a157, 160 | a158, 161 | a159, 162 | a160, 163 | a161, 164 | a162, 165 | a163, 166 | a164, 167 | a165, 168 | a166, 169 | a167, 170 | a168, 171 | a169, 172 | a170, 173 | a171, 174 | a172, 175 | a173, 176 | a174, 177 | a175, 178 | a176, 179 | a177, 180 | a178, 181 | a179, 182 | a180, 183 | a181, 184 | a182, 185 | a183, 186 | a184, 187 | a185, 188 | a186, 189 | a187, 190 | a188, 191 | a189, 192 | a190, 193 | a191, 194 | a192, 195 | a193, 196 | a194, 197 | a195, 198 | a196, 199 | a197, 200 | a198, 201 | a199, 202 | a200, 203 | a201, 204 | a202, 205 | a203, 206 | a204, 207 | a205, 208 | a206, 209 | a207, 210 | a208, 211 | a209, 212 | a210, 213 | a211, 214 | a212, 215 | a213, 216 | a214, 217 | a215, 218 | a216, 219 | a217, 220 | a218, 221 | a219, 222 | a220, 223 | a221, 224 | a222, 225 | a223, 226 | a224, 227 | a225, 228 | a226, 229 | a227, 230 | a228, 231 | a229, 232 | a230, 233 | a231, 234 | a232, 235 | a233, 236 | a234, 237 | a235, 238 | a236, 239 | a237, 240 | a238, 241 | a239, 242 | a240, 243 | a241, 244 | a242, 245 | a243, 246 | a244, 247 | a245, 248 | a246, 249 | a247, 250 | a248, 251 | a249, 252 | a250, 253 | a251, 254 | a252, 255 | a253, 256 | a254, 257 | a255, a) {} // Error at 'a': Cannot have more than 255 parameters. 258 | -------------------------------------------------------------------------------- /Tests/if/class_in_else.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | if (true) "ok"; else class Foo {} 3 | -------------------------------------------------------------------------------- /Tests/if/class_in_then.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | if (true) class Foo {} 3 | -------------------------------------------------------------------------------- /Tests/if/dangling_else.lox: -------------------------------------------------------------------------------- 1 | // A dangling else binds to the right-most if. 2 | if (true) if (false) print "bad"; else print "good"; // expect: good 3 | if (false) if (true) print "bad"; else print "bad"; 4 | -------------------------------------------------------------------------------- /Tests/if/else.lox: -------------------------------------------------------------------------------- 1 | // Evaluate the 'else' expression if the condition is false. 2 | if (true) print "good"; else print "bad"; // expect: good 3 | if (false) print "bad"; else print "good"; // expect: good 4 | 5 | // Allow block body. 6 | if (false) nil; else { print "block"; } // expect: block 7 | -------------------------------------------------------------------------------- /Tests/if/fun_in_else.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | if (true) "ok"; else fun foo() {} 3 | -------------------------------------------------------------------------------- /Tests/if/fun_in_then.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | if (true) fun foo() {} 3 | -------------------------------------------------------------------------------- /Tests/if/if.lox: -------------------------------------------------------------------------------- 1 | // Evaluate the 'then' expression if the condition is true. 2 | if (true) print "good"; // expect: good 3 | if (false) print "bad"; 4 | 5 | // Allow block body. 6 | if (true) { print "block"; } // expect: block 7 | 8 | // Assignment in if condition. 9 | var a = false; 10 | if (a = true) print a; // expect: true 11 | -------------------------------------------------------------------------------- /Tests/if/truth.lox: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | if (false) print "bad"; else print "false"; // expect: false 3 | if (nil) print "bad"; else print "nil"; // expect: nil 4 | 5 | // Everything else is true. 6 | if (true) print true; // expect: true 7 | if (0) print 0; // expect: 0 8 | if ("") print "empty"; // expect: empty 9 | -------------------------------------------------------------------------------- /Tests/if/var_in_else.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | if (true) "ok"; else var foo; 3 | -------------------------------------------------------------------------------- /Tests/if/var_in_then.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | if (true) var foo; 3 | -------------------------------------------------------------------------------- /Tests/inheritance/constructor.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | init(param) { 3 | this.field = param; 4 | } 5 | 6 | test() { 7 | print this.field; 8 | } 9 | } 10 | 11 | class B < A {} 12 | 13 | var b = B("value"); 14 | b.test(); // expect: value 15 | -------------------------------------------------------------------------------- /Tests/inheritance/inherit_from_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() {} 2 | 3 | class Subclass < foo {} // expect runtime error: Superclass must be a class. 4 | -------------------------------------------------------------------------------- /Tests/inheritance/inherit_from_nil.lox: -------------------------------------------------------------------------------- 1 | var Nil = nil; 2 | class Foo < Nil {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /Tests/inheritance/inherit_from_number.lox: -------------------------------------------------------------------------------- 1 | var Number = 123; 2 | class Foo < Number {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /Tests/inheritance/inherit_methods.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | methodOnFoo() { print "foo"; } 3 | override() { print "foo"; } 4 | } 5 | 6 | class Bar < Foo { 7 | methodOnBar() { print "bar"; } 8 | override() { print "bar"; } 9 | } 10 | 11 | var bar = Bar(); 12 | bar.methodOnFoo(); // expect: foo 13 | bar.methodOnBar(); // expect: bar 14 | bar.override(); // expect: bar 15 | -------------------------------------------------------------------------------- /Tests/inheritance/parenthesized_superclass.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | // [line 4] Error at '(': Expect superclass name. 4 | class Bar < (Foo) {} 5 | -------------------------------------------------------------------------------- /Tests/inheritance/set_fields_from_base_class.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | foo(a, b) { 3 | this.field1 = a; 4 | this.field2 = b; 5 | } 6 | 7 | fooPrint() { 8 | print this.field1; 9 | print this.field2; 10 | } 11 | } 12 | 13 | class Bar < Foo { 14 | bar(a, b) { 15 | this.field1 = a; 16 | this.field2 = b; 17 | } 18 | 19 | barPrint() { 20 | print this.field1; 21 | print this.field2; 22 | } 23 | } 24 | 25 | var bar = Bar(); 26 | bar.foo("foo 1", "foo 2"); 27 | bar.fooPrint(); 28 | // expect: foo 1 29 | // expect: foo 2 30 | 31 | bar.bar("bar 1", "bar 2"); 32 | bar.barPrint(); 33 | // expect: bar 1 34 | // expect: bar 2 35 | 36 | bar.fooPrint(); 37 | // expect: bar 1 38 | // expect: bar 2 39 | -------------------------------------------------------------------------------- /Tests/limit/no_reuse_constants.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | 0; 1; 2; 3; 4; 5; 6; 7; 3 | 8; 9; 10; 11; 12; 13; 14; 15; 4 | 16; 17; 18; 19; 20; 21; 22; 23; 5 | 24; 25; 26; 27; 28; 29; 30; 31; 6 | 32; 33; 34; 35; 36; 37; 38; 39; 7 | 40; 41; 42; 43; 44; 45; 46; 47; 8 | 48; 49; 50; 51; 52; 53; 54; 55; 9 | 56; 57; 58; 59; 60; 61; 62; 63; 10 | 64; 65; 66; 67; 68; 69; 70; 71; 11 | 72; 73; 74; 75; 76; 77; 78; 79; 12 | 80; 81; 82; 83; 84; 85; 86; 87; 13 | 88; 89; 90; 91; 92; 93; 94; 95; 14 | 96; 97; 98; 99; 100; 101; 102; 103; 15 | 104; 105; 106; 107; 108; 109; 110; 111; 16 | 112; 113; 114; 115; 116; 117; 118; 119; 17 | 120; 121; 122; 123; 124; 125; 126; 127; 18 | 128; 129; 130; 131; 132; 133; 134; 135; 19 | 136; 137; 138; 139; 140; 141; 142; 143; 20 | 144; 145; 146; 147; 148; 149; 150; 151; 21 | 152; 153; 154; 155; 156; 157; 158; 159; 22 | 160; 161; 162; 163; 164; 165; 166; 167; 23 | 168; 169; 170; 171; 172; 173; 174; 175; 24 | 176; 177; 178; 179; 180; 181; 182; 183; 25 | 184; 185; 186; 187; 188; 189; 190; 191; 26 | 192; 193; 194; 195; 196; 197; 198; 199; 27 | 200; 201; 202; 203; 204; 205; 206; 207; 28 | 208; 209; 210; 211; 212; 213; 214; 215; 29 | 216; 217; 218; 219; 220; 221; 222; 223; 30 | 224; 225; 226; 227; 228; 229; 230; 231; 31 | 232; 233; 234; 235; 236; 237; 238; 239; 32 | 240; 241; 242; 243; 244; 245; 246; 247; 33 | 248; 249; 250; 251; 252; 253; 254; 255; 34 | 35 | 1; // Error at '1': Too many constants in one chunk. 36 | } 37 | -------------------------------------------------------------------------------- /Tests/limit/stack_overflow.lox: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | var a1; 3 | var a2; 4 | var a3; 5 | var a4; 6 | var a5; 7 | var a6; 8 | var a7; 9 | var a8; 10 | var a9; 11 | var a10; 12 | var a11; 13 | var a12; 14 | var a13; 15 | var a14; 16 | var a15; 17 | var a16; 18 | foo(); // expect runtime error: Stack overflow. 19 | } 20 | 21 | foo(); 22 | -------------------------------------------------------------------------------- /Tests/limit/too_many_constants.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | 0; 1; 2; 3; 4; 5; 6; 7; 3 | 8; 9; 10; 11; 12; 13; 14; 15; 4 | 16; 17; 18; 19; 20; 21; 22; 23; 5 | 24; 25; 26; 27; 28; 29; 30; 31; 6 | 32; 33; 34; 35; 36; 37; 38; 39; 7 | 40; 41; 42; 43; 44; 45; 46; 47; 8 | 48; 49; 50; 51; 52; 53; 54; 55; 9 | 56; 57; 58; 59; 60; 61; 62; 63; 10 | 64; 65; 66; 67; 68; 69; 70; 71; 11 | 72; 73; 74; 75; 76; 77; 78; 79; 12 | 80; 81; 82; 83; 84; 85; 86; 87; 13 | 88; 89; 90; 91; 92; 93; 94; 95; 14 | 96; 97; 98; 99; 100; 101; 102; 103; 15 | 104; 105; 106; 107; 108; 109; 110; 111; 16 | 112; 113; 114; 115; 116; 117; 118; 119; 17 | 120; 121; 122; 123; 124; 125; 126; 127; 18 | 128; 129; 130; 131; 132; 133; 134; 135; 19 | 136; 137; 138; 139; 140; 141; 142; 143; 20 | 144; 145; 146; 147; 148; 149; 150; 151; 21 | 152; 153; 154; 155; 156; 157; 158; 159; 22 | 160; 161; 162; 163; 164; 165; 166; 167; 23 | 168; 169; 170; 171; 172; 173; 174; 175; 24 | 176; 177; 178; 179; 180; 181; 182; 183; 25 | 184; 185; 186; 187; 188; 189; 190; 191; 26 | 192; 193; 194; 195; 196; 197; 198; 199; 27 | 200; 201; 202; 203; 204; 205; 206; 207; 28 | 208; 209; 210; 211; 212; 213; 214; 215; 29 | 216; 217; 218; 219; 220; 221; 222; 223; 30 | 224; 225; 226; 227; 228; 229; 230; 231; 31 | 232; 233; 234; 235; 236; 237; 238; 239; 32 | 240; 241; 242; 243; 244; 245; 246; 247; 33 | 248; 249; 250; 251; 252; 253; 254; 255; 34 | 35 | "oops"; // Error at '"oops"': Too many constants in one chunk. 36 | } 37 | -------------------------------------------------------------------------------- /Tests/limit/too_many_locals.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | // var v00; First slot already taken. 3 | 4 | var v01; var v02; var v03; var v04; var v05; var v06; var v07; 5 | var v08; var v09; var v0a; var v0b; var v0c; var v0d; var v0e; var v0f; 6 | 7 | var v10; var v11; var v12; var v13; var v14; var v15; var v16; var v17; 8 | var v18; var v19; var v1a; var v1b; var v1c; var v1d; var v1e; var v1f; 9 | 10 | var v20; var v21; var v22; var v23; var v24; var v25; var v26; var v27; 11 | var v28; var v29; var v2a; var v2b; var v2c; var v2d; var v2e; var v2f; 12 | 13 | var v30; var v31; var v32; var v33; var v34; var v35; var v36; var v37; 14 | var v38; var v39; var v3a; var v3b; var v3c; var v3d; var v3e; var v3f; 15 | 16 | var v40; var v41; var v42; var v43; var v44; var v45; var v46; var v47; 17 | var v48; var v49; var v4a; var v4b; var v4c; var v4d; var v4e; var v4f; 18 | 19 | var v50; var v51; var v52; var v53; var v54; var v55; var v56; var v57; 20 | var v58; var v59; var v5a; var v5b; var v5c; var v5d; var v5e; var v5f; 21 | 22 | var v60; var v61; var v62; var v63; var v64; var v65; var v66; var v67; 23 | var v68; var v69; var v6a; var v6b; var v6c; var v6d; var v6e; var v6f; 24 | 25 | var v70; var v71; var v72; var v73; var v74; var v75; var v76; var v77; 26 | var v78; var v79; var v7a; var v7b; var v7c; var v7d; var v7e; var v7f; 27 | 28 | var v80; var v81; var v82; var v83; var v84; var v85; var v86; var v87; 29 | var v88; var v89; var v8a; var v8b; var v8c; var v8d; var v8e; var v8f; 30 | 31 | var v90; var v91; var v92; var v93; var v94; var v95; var v96; var v97; 32 | var v98; var v99; var v9a; var v9b; var v9c; var v9d; var v9e; var v9f; 33 | 34 | var va0; var va1; var va2; var va3; var va4; var va5; var va6; var va7; 35 | var va8; var va9; var vaa; var vab; var vac; var vad; var vae; var vaf; 36 | 37 | var vb0; var vb1; var vb2; var vb3; var vb4; var vb5; var vb6; var vb7; 38 | var vb8; var vb9; var vba; var vbb; var vbc; var vbd; var vbe; var vbf; 39 | 40 | var vc0; var vc1; var vc2; var vc3; var vc4; var vc5; var vc6; var vc7; 41 | var vc8; var vc9; var vca; var vcb; var vcc; var vcd; var vce; var vcf; 42 | 43 | var vd0; var vd1; var vd2; var vd3; var vd4; var vd5; var vd6; var vd7; 44 | var vd8; var vd9; var vda; var vdb; var vdc; var vdd; var vde; var vdf; 45 | 46 | var ve0; var ve1; var ve2; var ve3; var ve4; var ve5; var ve6; var ve7; 47 | var ve8; var ve9; var vea; var veb; var vec; var ved; var vee; var vef; 48 | 49 | var vf0; var vf1; var vf2; var vf3; var vf4; var vf5; var vf6; var vf7; 50 | var vf8; var vf9; var vfa; var vfb; var vfc; var vfd; var vfe; var vff; 51 | 52 | var oops; // Error at 'oops': Too many local variables in function. 53 | } 54 | -------------------------------------------------------------------------------- /Tests/limit/too_many_upvalues.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | var v00; var v01; var v02; var v03; var v04; var v05; var v06; var v07; 3 | var v08; var v09; var v0a; var v0b; var v0c; var v0d; var v0e; var v0f; 4 | 5 | var v10; var v11; var v12; var v13; var v14; var v15; var v16; var v17; 6 | var v18; var v19; var v1a; var v1b; var v1c; var v1d; var v1e; var v1f; 7 | 8 | var v20; var v21; var v22; var v23; var v24; var v25; var v26; var v27; 9 | var v28; var v29; var v2a; var v2b; var v2c; var v2d; var v2e; var v2f; 10 | 11 | var v30; var v31; var v32; var v33; var v34; var v35; var v36; var v37; 12 | var v38; var v39; var v3a; var v3b; var v3c; var v3d; var v3e; var v3f; 13 | 14 | var v40; var v41; var v42; var v43; var v44; var v45; var v46; var v47; 15 | var v48; var v49; var v4a; var v4b; var v4c; var v4d; var v4e; var v4f; 16 | 17 | var v50; var v51; var v52; var v53; var v54; var v55; var v56; var v57; 18 | var v58; var v59; var v5a; var v5b; var v5c; var v5d; var v5e; var v5f; 19 | 20 | var v60; var v61; var v62; var v63; var v64; var v65; var v66; var v67; 21 | var v68; var v69; var v6a; var v6b; var v6c; var v6d; var v6e; var v6f; 22 | 23 | var v70; var v71; var v72; var v73; var v74; var v75; var v76; var v77; 24 | var v78; var v79; var v7a; var v7b; var v7c; var v7d; var v7e; var v7f; 25 | 26 | fun g() { 27 | var v80; var v81; var v82; var v83; var v84; var v85; var v86; var v87; 28 | var v88; var v89; var v8a; var v8b; var v8c; var v8d; var v8e; var v8f; 29 | 30 | var v90; var v91; var v92; var v93; var v94; var v95; var v96; var v97; 31 | var v98; var v99; var v9a; var v9b; var v9c; var v9d; var v9e; var v9f; 32 | 33 | var va0; var va1; var va2; var va3; var va4; var va5; var va6; var va7; 34 | var va8; var va9; var vaa; var vab; var vac; var vad; var vae; var vaf; 35 | 36 | var vb0; var vb1; var vb2; var vb3; var vb4; var vb5; var vb6; var vb7; 37 | var vb8; var vb9; var vba; var vbb; var vbc; var vbd; var vbe; var vbf; 38 | 39 | var vc0; var vc1; var vc2; var vc3; var vc4; var vc5; var vc6; var vc7; 40 | var vc8; var vc9; var vca; var vcb; var vcc; var vcd; var vce; var vcf; 41 | 42 | var vd0; var vd1; var vd2; var vd3; var vd4; var vd5; var vd6; var vd7; 43 | var vd8; var vd9; var vda; var vdb; var vdc; var vdd; var vde; var vdf; 44 | 45 | var ve0; var ve1; var ve2; var ve3; var ve4; var ve5; var ve6; var ve7; 46 | var ve8; var ve9; var vea; var veb; var vec; var ved; var vee; var vef; 47 | 48 | var vf0; var vf1; var vf2; var vf3; var vf4; var vf5; var vf6; var vf7; 49 | var vf8; var vf9; var vfa; var vfb; var vfc; var vfd; var vfe; var vff; 50 | 51 | var oops; 52 | 53 | fun h() { 54 | v00; v01; v02; v03; v04; v05; v06; v07; 55 | v08; v09; v0a; v0b; v0c; v0d; v0e; v0f; 56 | 57 | v10; v11; v12; v13; v14; v15; v16; v17; 58 | v18; v19; v1a; v1b; v1c; v1d; v1e; v1f; 59 | 60 | v20; v21; v22; v23; v24; v25; v26; v27; 61 | v28; v29; v2a; v2b; v2c; v2d; v2e; v2f; 62 | 63 | v30; v31; v32; v33; v34; v35; v36; v37; 64 | v38; v39; v3a; v3b; v3c; v3d; v3e; v3f; 65 | 66 | v40; v41; v42; v43; v44; v45; v46; v47; 67 | v48; v49; v4a; v4b; v4c; v4d; v4e; v4f; 68 | 69 | v50; v51; v52; v53; v54; v55; v56; v57; 70 | v58; v59; v5a; v5b; v5c; v5d; v5e; v5f; 71 | 72 | v60; v61; v62; v63; v64; v65; v66; v67; 73 | v68; v69; v6a; v6b; v6c; v6d; v6e; v6f; 74 | 75 | v70; v71; v72; v73; v74; v75; v76; v77; 76 | v78; v79; v7a; v7b; v7c; v7d; v7e; v7f; 77 | 78 | v80; v81; v82; v83; v84; v85; v86; v87; 79 | v88; v89; v8a; v8b; v8c; v8d; v8e; v8f; 80 | 81 | v90; v91; v92; v93; v94; v95; v96; v97; 82 | v98; v99; v9a; v9b; v9c; v9d; v9e; v9f; 83 | 84 | va0; va1; va2; va3; va4; va5; va6; va7; 85 | va8; va9; vaa; vab; vac; vad; vae; vaf; 86 | 87 | vb0; vb1; vb2; vb3; vb4; vb5; vb6; vb7; 88 | vb8; vb9; vba; vbb; vbc; vbd; vbe; vbf; 89 | 90 | vc0; vc1; vc2; vc3; vc4; vc5; vc6; vc7; 91 | vc8; vc9; vca; vcb; vcc; vcd; vce; vcf; 92 | 93 | vd0; vd1; vd2; vd3; vd4; vd5; vd6; vd7; 94 | vd8; vd9; vda; vdb; vdc; vdd; vde; vdf; 95 | 96 | ve0; ve1; ve2; ve3; ve4; ve5; ve6; ve7; 97 | ve8; ve9; vea; veb; vec; ved; vee; vef; 98 | 99 | vf0; vf1; vf2; vf3; vf4; vf5; vf6; vf7; 100 | vf8; vf9; vfa; vfb; vfc; vfd; vfe; vff; 101 | 102 | oops; // Error at 'oops': Too many closure variables in function. 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Tests/logical_operator/and.lox: -------------------------------------------------------------------------------- 1 | // Note: These tests implicitly depend on ints being truthy. 2 | 3 | // Return the first non-true argument. 4 | print false and 1; // expect: false 5 | print true and 1; // expect: 1 6 | print 1 and 2 and false; // expect: false 7 | 8 | // Return the last argument if all are true. 9 | print 1 and true; // expect: true 10 | print 1 and 2 and 3; // expect: 3 11 | 12 | // Short-circuit at the first false argument. 13 | var a = "before"; 14 | var b = "before"; 15 | (a = true) and 16 | (b = false) and 17 | (a = "bad"); 18 | print a; // expect: true 19 | print b; // expect: false 20 | -------------------------------------------------------------------------------- /Tests/logical_operator/and_truth.lox: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print false and "bad"; // expect: false 3 | print nil and "bad"; // expect: nil 4 | 5 | // Everything else is true. 6 | print true and "ok"; // expect: ok 7 | print 0 and "ok"; // expect: ok 8 | print "" and "ok"; // expect: ok 9 | -------------------------------------------------------------------------------- /Tests/logical_operator/or.lox: -------------------------------------------------------------------------------- 1 | // Note: These tests implicitly depend on ints being truthy. 2 | 3 | // Return the first true argument. 4 | print 1 or true; // expect: 1 5 | print false or 1; // expect: 1 6 | print false or false or true; // expect: true 7 | 8 | // Return the last argument if all are false. 9 | print false or false; // expect: false 10 | print false or false or false; // expect: false 11 | 12 | // Short-circuit at the first true argument. 13 | var a = "before"; 14 | var b = "before"; 15 | (a = false) or 16 | (b = true) or 17 | (a = "bad"); 18 | print a; // expect: false 19 | print b; // expect: true 20 | -------------------------------------------------------------------------------- /Tests/logical_operator/or_truth.lox: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print false or "ok"; // expect: ok 3 | print nil or "ok"; // expect: ok 4 | 5 | // Everything else is true. 6 | print true or "ok"; // expect: true 7 | print 0 or "ok"; // expect: 0 8 | print "s" or "ok"; // expect: s 9 | -------------------------------------------------------------------------------- /Tests/method/arity.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method0() { return "no args"; } 3 | method1(a) { return a; } 4 | method2(a, b) { return a + b; } 5 | method3(a, b, c) { return a + b + c; } 6 | method4(a, b, c, d) { return a + b + c + d; } 7 | method5(a, b, c, d, e) { return a + b + c + d + e; } 8 | method6(a, b, c, d, e, f) { return a + b + c + d + e + f; } 9 | method7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } 10 | method8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h; } 11 | } 12 | 13 | var foo = Foo(); 14 | print foo.method0(); // expect: no args 15 | print foo.method1(1); // expect: 1 16 | print foo.method2(1, 2); // expect: 3 17 | print foo.method3(1, 2, 3); // expect: 6 18 | print foo.method4(1, 2, 3, 4); // expect: 10 19 | print foo.method5(1, 2, 3, 4, 5); // expect: 15 20 | print foo.method6(1, 2, 3, 4, 5, 6); // expect: 21 21 | print foo.method7(1, 2, 3, 4, 5, 6, 7); // expect: 28 22 | print foo.method8(1, 2, 3, 4, 5, 6, 7, 8); // expect: 36 23 | -------------------------------------------------------------------------------- /Tests/method/empty_block.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar() {} 3 | } 4 | 5 | print Foo().bar(); // expect: nil 6 | -------------------------------------------------------------------------------- /Tests/method/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method(a, b) { 3 | print a; 4 | print b; 5 | } 6 | } 7 | 8 | Foo().method(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. 9 | -------------------------------------------------------------------------------- /Tests/method/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method(a, b) {} 3 | } 4 | 5 | Foo().method(1); // expect runtime error: Expected 2 arguments but got 1. 6 | -------------------------------------------------------------------------------- /Tests/method/not_found.lox: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | Foo().unknown(); // expect runtime error: Undefined property 'unknown'. 4 | -------------------------------------------------------------------------------- /Tests/method/print_bound_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { } 3 | } 4 | var foo = Foo(); 5 | print foo.method; // expect: 6 | -------------------------------------------------------------------------------- /Tests/method/refer_to_name.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { 3 | print method; // expect runtime error: Undefined variable 'method'. 4 | } 5 | } 6 | 7 | Foo().method(); 8 | -------------------------------------------------------------------------------- /Tests/method/too_many_arguments.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = 1; 3 | true.method( 4 | a, // 1 5 | a, // 2 6 | a, // 3 7 | a, // 4 8 | a, // 5 9 | a, // 6 10 | a, // 7 11 | a, // 8 12 | a, // 9 13 | a, // 10 14 | a, // 11 15 | a, // 12 16 | a, // 13 17 | a, // 14 18 | a, // 15 19 | a, // 16 20 | a, // 17 21 | a, // 18 22 | a, // 19 23 | a, // 20 24 | a, // 21 25 | a, // 22 26 | a, // 23 27 | a, // 24 28 | a, // 25 29 | a, // 26 30 | a, // 27 31 | a, // 28 32 | a, // 29 33 | a, // 30 34 | a, // 31 35 | a, // 32 36 | a, // 33 37 | a, // 34 38 | a, // 35 39 | a, // 36 40 | a, // 37 41 | a, // 38 42 | a, // 39 43 | a, // 40 44 | a, // 41 45 | a, // 42 46 | a, // 43 47 | a, // 44 48 | a, // 45 49 | a, // 46 50 | a, // 47 51 | a, // 48 52 | a, // 49 53 | a, // 50 54 | a, // 51 55 | a, // 52 56 | a, // 53 57 | a, // 54 58 | a, // 55 59 | a, // 56 60 | a, // 57 61 | a, // 58 62 | a, // 59 63 | a, // 60 64 | a, // 61 65 | a, // 62 66 | a, // 63 67 | a, // 64 68 | a, // 65 69 | a, // 66 70 | a, // 67 71 | a, // 68 72 | a, // 69 73 | a, // 70 74 | a, // 71 75 | a, // 72 76 | a, // 73 77 | a, // 74 78 | a, // 75 79 | a, // 76 80 | a, // 77 81 | a, // 78 82 | a, // 79 83 | a, // 80 84 | a, // 81 85 | a, // 82 86 | a, // 83 87 | a, // 84 88 | a, // 85 89 | a, // 86 90 | a, // 87 91 | a, // 88 92 | a, // 89 93 | a, // 90 94 | a, // 91 95 | a, // 92 96 | a, // 93 97 | a, // 94 98 | a, // 95 99 | a, // 96 100 | a, // 97 101 | a, // 98 102 | a, // 99 103 | a, // 100 104 | a, // 101 105 | a, // 102 106 | a, // 103 107 | a, // 104 108 | a, // 105 109 | a, // 106 110 | a, // 107 111 | a, // 108 112 | a, // 109 113 | a, // 110 114 | a, // 111 115 | a, // 112 116 | a, // 113 117 | a, // 114 118 | a, // 115 119 | a, // 116 120 | a, // 117 121 | a, // 118 122 | a, // 119 123 | a, // 120 124 | a, // 121 125 | a, // 122 126 | a, // 123 127 | a, // 124 128 | a, // 125 129 | a, // 126 130 | a, // 127 131 | a, // 128 132 | a, // 129 133 | a, // 130 134 | a, // 131 135 | a, // 132 136 | a, // 133 137 | a, // 134 138 | a, // 135 139 | a, // 136 140 | a, // 137 141 | a, // 138 142 | a, // 139 143 | a, // 140 144 | a, // 141 145 | a, // 142 146 | a, // 143 147 | a, // 144 148 | a, // 145 149 | a, // 146 150 | a, // 147 151 | a, // 148 152 | a, // 149 153 | a, // 150 154 | a, // 151 155 | a, // 152 156 | a, // 153 157 | a, // 154 158 | a, // 155 159 | a, // 156 160 | a, // 157 161 | a, // 158 162 | a, // 159 163 | a, // 160 164 | a, // 161 165 | a, // 162 166 | a, // 163 167 | a, // 164 168 | a, // 165 169 | a, // 166 170 | a, // 167 171 | a, // 168 172 | a, // 169 173 | a, // 170 174 | a, // 171 175 | a, // 172 176 | a, // 173 177 | a, // 174 178 | a, // 175 179 | a, // 176 180 | a, // 177 181 | a, // 178 182 | a, // 179 183 | a, // 180 184 | a, // 181 185 | a, // 182 186 | a, // 183 187 | a, // 184 188 | a, // 185 189 | a, // 186 190 | a, // 187 191 | a, // 188 192 | a, // 189 193 | a, // 190 194 | a, // 191 195 | a, // 192 196 | a, // 193 197 | a, // 194 198 | a, // 195 199 | a, // 196 200 | a, // 197 201 | a, // 198 202 | a, // 199 203 | a, // 200 204 | a, // 201 205 | a, // 202 206 | a, // 203 207 | a, // 204 208 | a, // 205 209 | a, // 206 210 | a, // 207 211 | a, // 208 212 | a, // 209 213 | a, // 210 214 | a, // 211 215 | a, // 212 216 | a, // 213 217 | a, // 214 218 | a, // 215 219 | a, // 216 220 | a, // 217 221 | a, // 218 222 | a, // 219 223 | a, // 220 224 | a, // 221 225 | a, // 222 226 | a, // 223 227 | a, // 224 228 | a, // 225 229 | a, // 226 230 | a, // 227 231 | a, // 228 232 | a, // 229 233 | a, // 230 234 | a, // 231 235 | a, // 232 236 | a, // 233 237 | a, // 234 238 | a, // 235 239 | a, // 236 240 | a, // 237 241 | a, // 238 242 | a, // 239 243 | a, // 240 244 | a, // 241 245 | a, // 242 246 | a, // 243 247 | a, // 244 248 | a, // 245 249 | a, // 246 250 | a, // 247 251 | a, // 248 252 | a, // 249 253 | a, // 250 254 | a, // 251 255 | a, // 252 256 | a, // 253 257 | a, // 254 258 | a, // 255 259 | a); // Error at 'a': Cannot have more than 255 arguments. 260 | } 261 | -------------------------------------------------------------------------------- /Tests/method/too_many_parameters.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | // 256 parameters. 3 | method( 4 | a1, 5 | a2, 6 | a3, 7 | a4, 8 | a5, 9 | a6, 10 | a7, 11 | a8, 12 | a9, 13 | a10, 14 | a11, 15 | a12, 16 | a13, 17 | a14, 18 | a15, 19 | a16, 20 | a17, 21 | a18, 22 | a19, 23 | a20, 24 | a21, 25 | a22, 26 | a23, 27 | a24, 28 | a25, 29 | a26, 30 | a27, 31 | a28, 32 | a29, 33 | a30, 34 | a31, 35 | a32, 36 | a33, 37 | a34, 38 | a35, 39 | a36, 40 | a37, 41 | a38, 42 | a39, 43 | a40, 44 | a41, 45 | a42, 46 | a43, 47 | a44, 48 | a45, 49 | a46, 50 | a47, 51 | a48, 52 | a49, 53 | a50, 54 | a51, 55 | a52, 56 | a53, 57 | a54, 58 | a55, 59 | a56, 60 | a57, 61 | a58, 62 | a59, 63 | a60, 64 | a61, 65 | a62, 66 | a63, 67 | a64, 68 | a65, 69 | a66, 70 | a67, 71 | a68, 72 | a69, 73 | a70, 74 | a71, 75 | a72, 76 | a73, 77 | a74, 78 | a75, 79 | a76, 80 | a77, 81 | a78, 82 | a79, 83 | a80, 84 | a81, 85 | a82, 86 | a83, 87 | a84, 88 | a85, 89 | a86, 90 | a87, 91 | a88, 92 | a89, 93 | a90, 94 | a91, 95 | a92, 96 | a93, 97 | a94, 98 | a95, 99 | a96, 100 | a97, 101 | a98, 102 | a99, 103 | a100, 104 | a101, 105 | a102, 106 | a103, 107 | a104, 108 | a105, 109 | a106, 110 | a107, 111 | a108, 112 | a109, 113 | a110, 114 | a111, 115 | a112, 116 | a113, 117 | a114, 118 | a115, 119 | a116, 120 | a117, 121 | a118, 122 | a119, 123 | a120, 124 | a121, 125 | a122, 126 | a123, 127 | a124, 128 | a125, 129 | a126, 130 | a127, 131 | a128, 132 | a129, 133 | a130, 134 | a131, 135 | a132, 136 | a133, 137 | a134, 138 | a135, 139 | a136, 140 | a137, 141 | a138, 142 | a139, 143 | a140, 144 | a141, 145 | a142, 146 | a143, 147 | a144, 148 | a145, 149 | a146, 150 | a147, 151 | a148, 152 | a149, 153 | a150, 154 | a151, 155 | a152, 156 | a153, 157 | a154, 158 | a155, 159 | a156, 160 | a157, 161 | a158, 162 | a159, 163 | a160, 164 | a161, 165 | a162, 166 | a163, 167 | a164, 168 | a165, 169 | a166, 170 | a167, 171 | a168, 172 | a169, 173 | a170, 174 | a171, 175 | a172, 176 | a173, 177 | a174, 178 | a175, 179 | a176, 180 | a177, 181 | a178, 182 | a179, 183 | a180, 184 | a181, 185 | a182, 186 | a183, 187 | a184, 188 | a185, 189 | a186, 190 | a187, 191 | a188, 192 | a189, 193 | a190, 194 | a191, 195 | a192, 196 | a193, 197 | a194, 198 | a195, 199 | a196, 200 | a197, 201 | a198, 202 | a199, 203 | a200, 204 | a201, 205 | a202, 206 | a203, 207 | a204, 208 | a205, 209 | a206, 210 | a207, 211 | a208, 212 | a209, 213 | a210, 214 | a211, 215 | a212, 216 | a213, 217 | a214, 218 | a215, 219 | a216, 220 | a217, 221 | a218, 222 | a219, 223 | a220, 224 | a221, 225 | a222, 226 | a223, 227 | a224, 228 | a225, 229 | a226, 230 | a227, 231 | a228, 232 | a229, 233 | a230, 234 | a231, 235 | a232, 236 | a233, 237 | a234, 238 | a235, 239 | a236, 240 | a237, 241 | a238, 242 | a239, 243 | a240, 244 | a241, 245 | a242, 246 | a243, 247 | a244, 248 | a245, 249 | a246, 250 | a247, 251 | a248, 252 | a249, 253 | a250, 254 | a251, 255 | a252, 256 | a253, 257 | a254, 258 | a255, a) {} // Error at 'a': Cannot have more than 255 parameters. 259 | } 260 | -------------------------------------------------------------------------------- /Tests/nil/literal.lox: -------------------------------------------------------------------------------- 1 | print nil; // expect: nil 2 | -------------------------------------------------------------------------------- /Tests/number/decimal_point_at_eof.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at end: Expect property name after '.'. 2 | 123. -------------------------------------------------------------------------------- /Tests/number/leading_dot.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '.': Expect expression. 2 | .123; 3 | -------------------------------------------------------------------------------- /Tests/number/literals.lox: -------------------------------------------------------------------------------- 1 | print 123; // expect: 123 2 | print 987654; // expect: 987654 3 | print 0; // expect: 0 4 | print -0; // expect: -0 5 | 6 | print 123.456; // expect: 123.456 7 | print -0.001; // expect: -0.001 8 | -------------------------------------------------------------------------------- /Tests/number/nan_equality.lox: -------------------------------------------------------------------------------- 1 | var nan = 0/0; 2 | 3 | print nan == 0; // expect: false 4 | print nan != 1; // expect: true 5 | 6 | // NaN is not equal to self. 7 | print nan == nan; // expect: false 8 | print nan != nan; // expect: true 9 | -------------------------------------------------------------------------------- /Tests/number/trailing_dot.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect property name after '.'. 2 | 123.; 3 | -------------------------------------------------------------------------------- /Tests/operator/add.lox: -------------------------------------------------------------------------------- 1 | print 123 + 456; // expect: 579 2 | print "str" + "ing"; // expect: string 3 | -------------------------------------------------------------------------------- /Tests/operator/add_bool_nil.lox: -------------------------------------------------------------------------------- 1 | true + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /Tests/operator/add_bool_num.lox: -------------------------------------------------------------------------------- 1 | true + 123; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /Tests/operator/add_bool_string.lox: -------------------------------------------------------------------------------- 1 | true + "s"; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /Tests/operator/add_nil_nil.lox: -------------------------------------------------------------------------------- 1 | nil + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /Tests/operator/add_num_nil.lox: -------------------------------------------------------------------------------- 1 | 1 + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /Tests/operator/add_string_nil.lox: -------------------------------------------------------------------------------- 1 | "s" + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /Tests/operator/comparison.lox: -------------------------------------------------------------------------------- 1 | print 1 < 2; // expect: true 2 | print 2 < 2; // expect: false 3 | print 2 < 1; // expect: false 4 | 5 | print 1 <= 2; // expect: true 6 | print 2 <= 2; // expect: true 7 | print 2 <= 1; // expect: false 8 | 9 | print 1 > 2; // expect: false 10 | print 2 > 2; // expect: false 11 | print 2 > 1; // expect: true 12 | 13 | print 1 >= 2; // expect: false 14 | print 2 >= 2; // expect: true 15 | print 2 >= 1; // expect: true 16 | 17 | // Zero and negative zero compare the same. 18 | print 0 < -0; // expect: false 19 | print -0 < 0; // expect: false 20 | print 0 > -0; // expect: false 21 | print -0 > 0; // expect: false 22 | print 0 <= -0; // expect: true 23 | print -0 <= 0; // expect: true 24 | print 0 >= -0; // expect: true 25 | print -0 >= 0; // expect: true 26 | -------------------------------------------------------------------------------- /Tests/operator/divide.lox: -------------------------------------------------------------------------------- 1 | print 8 / 2; // expect: 4 2 | print 12.34 / 12.34; // expect: 1 3 | -------------------------------------------------------------------------------- /Tests/operator/divide_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" / 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/divide_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 / "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/equals.lox: -------------------------------------------------------------------------------- 1 | print nil == nil; // expect: true 2 | 3 | print true == true; // expect: true 4 | print true == false; // expect: false 5 | 6 | print 1 == 1; // expect: true 7 | print 1 == 2; // expect: false 8 | 9 | print "str" == "str"; // expect: true 10 | print "str" == "ing"; // expect: false 11 | 12 | print nil == false; // expect: false 13 | print false == 0; // expect: false 14 | print 0 == "0"; // expect: false 15 | -------------------------------------------------------------------------------- /Tests/operator/equals_class.lox: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo {} 3 | class Bar {} 4 | 5 | print Foo == Foo; // expect: true 6 | print Foo == Bar; // expect: false 7 | print Bar == Foo; // expect: false 8 | print Bar == Bar; // expect: true 9 | 10 | print Foo == "Foo"; // expect: false 11 | print Foo == nil; // expect: false 12 | print Foo == 123; // expect: false 13 | print Foo == true; // expect: false 14 | -------------------------------------------------------------------------------- /Tests/operator/equals_method.lox: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo { 3 | method() {} 4 | } 5 | 6 | var foo = Foo(); 7 | var fooMethod = foo.method; 8 | 9 | // Same bound method. 10 | print fooMethod == fooMethod; // expect: true 11 | 12 | // Different closurizations. 13 | print foo.method == foo.method; // expect: false 14 | -------------------------------------------------------------------------------- /Tests/operator/greater_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" > 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/greater_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 > "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/greater_or_equal_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" >= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/greater_or_equal_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 >= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/less_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" < 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/less_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 < "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/less_or_equal_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" <= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/less_or_equal_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 <= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/multiply.lox: -------------------------------------------------------------------------------- 1 | print 5 * 3; // expect: 15 2 | print 12.34 * 0.3; // expect: 3.702 3 | -------------------------------------------------------------------------------- /Tests/operator/multiply_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" * 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/multiply_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 * "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/negate.lox: -------------------------------------------------------------------------------- 1 | print -(3); // expect: -3 2 | print --(3); // expect: 3 3 | print ---(3); // expect: -3 4 | -------------------------------------------------------------------------------- /Tests/operator/negate_nonnum.lox: -------------------------------------------------------------------------------- 1 | -"s"; // expect runtime error: Operand must be a number. 2 | -------------------------------------------------------------------------------- /Tests/operator/not.lox: -------------------------------------------------------------------------------- 1 | print !true; // expect: false 2 | print !false; // expect: true 3 | print !!true; // expect: true 4 | 5 | print !123; // expect: false 6 | print !0; // expect: false 7 | 8 | print !nil; // expect: true 9 | 10 | print !""; // expect: false 11 | 12 | fun foo() {} 13 | print !foo; // expect: false 14 | -------------------------------------------------------------------------------- /Tests/operator/not_class.lox: -------------------------------------------------------------------------------- 1 | class Bar {} 2 | print !Bar; // expect: false 3 | print !Bar(); // expect: false 4 | -------------------------------------------------------------------------------- /Tests/operator/not_equals.lox: -------------------------------------------------------------------------------- 1 | print nil != nil; // expect: false 2 | 3 | print true != true; // expect: false 4 | print true != false; // expect: true 5 | 6 | print 1 != 1; // expect: false 7 | print 1 != 2; // expect: true 8 | 9 | print "str" != "str"; // expect: false 10 | print "str" != "ing"; // expect: true 11 | 12 | print nil != false; // expect: true 13 | print false != 0; // expect: true 14 | print 0 != "0"; // expect: true 15 | -------------------------------------------------------------------------------- /Tests/operator/subtract.lox: -------------------------------------------------------------------------------- 1 | print 4 - 3; // expect: 1 2 | print 1.2 - 1.2; // expect: 0 3 | -------------------------------------------------------------------------------- /Tests/operator/subtract_nonnum_num.lox: -------------------------------------------------------------------------------- 1 | "1" - 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/operator/subtract_num_nonnum.lox: -------------------------------------------------------------------------------- 1 | 1 - "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /Tests/precedence.lox: -------------------------------------------------------------------------------- 1 | // * has higher precedence than +. 2 | print 2 + 3 * 4; // expect: 14 3 | 4 | // * has higher precedence than -. 5 | print 20 - 3 * 4; // expect: 8 6 | 7 | // / has higher precedence than +. 8 | print 2 + 6 / 3; // expect: 4 9 | 10 | // / has higher precedence than -. 11 | print 2 - 6 / 3; // expect: 0 12 | 13 | // < has higher precedence than ==. 14 | print false == 2 < 1; // expect: true 15 | 16 | // > has higher precedence than ==. 17 | print false == 1 > 2; // expect: true 18 | 19 | // <= has higher precedence than ==. 20 | print false == 2 <= 1; // expect: true 21 | 22 | // >= has higher precedence than ==. 23 | print false == 1 >= 2; // expect: true 24 | 25 | // 1 - 1 is not space-sensitive. 26 | print 1 - 1; // expect: 0 27 | print 1 -1; // expect: 0 28 | print 1- 1; // expect: 0 29 | print 1-1; // expect: 0 30 | 31 | // Using () for grouping. 32 | print (2 * (6 - (2 + 2))); // expect: 4 33 | -------------------------------------------------------------------------------- /Tests/print/missing_argument.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect expression. 2 | print; 3 | -------------------------------------------------------------------------------- /Tests/regression/394.lox: -------------------------------------------------------------------------------- 1 | { 2 | class A {} 3 | class B < A {} 4 | print B; // expect: B 5 | } 6 | -------------------------------------------------------------------------------- /Tests/regression/40.lox: -------------------------------------------------------------------------------- 1 | fun caller(g) { 2 | g(); 3 | // g should be a function, not nil. 4 | print g == nil; // expect: false 5 | } 6 | 7 | fun callCaller() { 8 | var capturedVar = "before"; 9 | var a = "a"; 10 | 11 | fun f() { 12 | // Commenting the next line out prevents the bug! 13 | capturedVar = "after"; 14 | 15 | // Returning anything also fixes it, even nil: 16 | //return nil; 17 | } 18 | 19 | caller(f); 20 | } 21 | 22 | callCaller(); 23 | -------------------------------------------------------------------------------- /Tests/return/after_else.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | if (false) "no"; else return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /Tests/return/after_if.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | if (true) return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /Tests/return/after_while.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) return "ok"; 3 | } 4 | 5 | print f(); // expect: ok 6 | -------------------------------------------------------------------------------- /Tests/return/at_top_level.lox: -------------------------------------------------------------------------------- 1 | return "wat"; // Error at 'return': Cannot return from top-level code. 2 | -------------------------------------------------------------------------------- /Tests/return/in_function.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | return "ok"; 3 | print "bad"; 4 | } 5 | 6 | print f(); // expect: ok 7 | -------------------------------------------------------------------------------- /Tests/return/in_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | method() { 3 | return "ok"; 4 | print "bad"; 5 | } 6 | } 7 | 8 | print Foo().method(); // expect: ok 9 | -------------------------------------------------------------------------------- /Tests/return/return_nil_if_no_value.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | return; 3 | print "bad"; 4 | } 5 | 6 | print f(); // expect: nil 7 | -------------------------------------------------------------------------------- /Tests/scanning/identifiers.lox: -------------------------------------------------------------------------------- 1 | andy formless fo _ _123 _abc ab123 2 | abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_ 3 | 4 | // expect: IDENTIFIER andy null 5 | // expect: IDENTIFIER formless null 6 | // expect: IDENTIFIER fo null 7 | // expect: IDENTIFIER _ null 8 | // expect: IDENTIFIER _123 null 9 | // expect: IDENTIFIER _abc null 10 | // expect: IDENTIFIER ab123 null 11 | // expect: IDENTIFIER abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_ null 12 | // expect: EOF null 13 | -------------------------------------------------------------------------------- /Tests/scanning/keywords.lox: -------------------------------------------------------------------------------- 1 | and class else false for fun if nil or return super this true var while 2 | 3 | // expect: AND and null 4 | // expect: CLASS class null 5 | // expect: ELSE else null 6 | // expect: FALSE false null 7 | // expect: FOR for null 8 | // expect: FUN fun null 9 | // expect: IF if null 10 | // expect: NIL nil null 11 | // expect: OR or null 12 | // expect: RETURN return null 13 | // expect: SUPER super null 14 | // expect: THIS this null 15 | // expect: TRUE true null 16 | // expect: VAR var null 17 | // expect: WHILE while null 18 | // expect: EOF null 19 | -------------------------------------------------------------------------------- /Tests/scanning/numbers.lox: -------------------------------------------------------------------------------- 1 | 123 2 | 123.456 3 | .456 4 | 123. 5 | 6 | // expect: NUMBER 123 123.0 7 | // expect: NUMBER 123.456 123.456 8 | // expect: DOT . null 9 | // expect: NUMBER 456 456.0 10 | // expect: NUMBER 123 123.0 11 | // expect: DOT . null 12 | // expect: EOF null 13 | -------------------------------------------------------------------------------- /Tests/scanning/punctuators.lox: -------------------------------------------------------------------------------- 1 | (){};,+-*!===<=>=!=<>/. 2 | 3 | // expect: LEFT_PAREN ( null 4 | // expect: RIGHT_PAREN ) null 5 | // expect: LEFT_BRACE { null 6 | // expect: RIGHT_BRACE } null 7 | // expect: SEMICOLON ; null 8 | // expect: COMMA , null 9 | // expect: PLUS + null 10 | // expect: MINUS - null 11 | // expect: STAR * null 12 | // expect: BANG_EQUAL != null 13 | // expect: EQUAL_EQUAL == null 14 | // expect: LESS_EQUAL <= null 15 | // expect: GREATER_EQUAL >= null 16 | // expect: BANG_EQUAL != null 17 | // expect: LESS < null 18 | // expect: GREATER > null 19 | // expect: SLASH / null 20 | // expect: DOT . null 21 | // expect: EOF null 22 | -------------------------------------------------------------------------------- /Tests/scanning/strings.lox: -------------------------------------------------------------------------------- 1 | "" 2 | "string" 3 | 4 | // expect: STRING "" 5 | // expect: STRING "string" string 6 | // expect: EOF null -------------------------------------------------------------------------------- /Tests/scanning/whitespace.lox: -------------------------------------------------------------------------------- 1 | space tabs newlines 2 | 3 | 4 | 5 | 6 | end 7 | 8 | // expect: IDENTIFIER space null 9 | // expect: IDENTIFIER tabs null 10 | // expect: IDENTIFIER newlines null 11 | // expect: IDENTIFIER end null 12 | // expect: EOF null 13 | -------------------------------------------------------------------------------- /Tests/string/error_after_multiline.lox: -------------------------------------------------------------------------------- 1 | // Tests that we correctly track the line info across multiline strings. 2 | var a = "1 3 | 2 4 | 3 5 | "; 6 | 7 | err; // // expect runtime error: Undefined variable 'err'. -------------------------------------------------------------------------------- /Tests/string/literals.lox: -------------------------------------------------------------------------------- 1 | print "(" + "" + ")"; // expect: () 2 | print "a string"; // expect: a string 3 | 4 | // Non-ASCII. 5 | print "A~¶Þॐஃ"; // expect: A~¶Þॐஃ 6 | -------------------------------------------------------------------------------- /Tests/string/multiline.lox: -------------------------------------------------------------------------------- 1 | var a = "1 2 | 2 3 | 3"; 4 | print a; 5 | // expect: 1 6 | // expect: 2 7 | // expect: 3 8 | -------------------------------------------------------------------------------- /Tests/string/unterminated.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error: Unterminated string. 2 | "this string has no close quote -------------------------------------------------------------------------------- /Tests/super/bound_method.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | method(arg) { 3 | print "A.method(" + arg + ")"; 4 | } 5 | } 6 | 7 | class B < A { 8 | getClosure() { 9 | return super.method; 10 | } 11 | 12 | method(arg) { 13 | print "B.method(" + arg + ")"; 14 | } 15 | } 16 | 17 | 18 | var closure = B().getClosure(); 19 | closure("arg"); // expect: A.method(arg) 20 | -------------------------------------------------------------------------------- /Tests/super/call_other_method.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | print "Base.foo()"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | bar() { 9 | print "Derived.bar()"; 10 | super.foo(); 11 | } 12 | } 13 | 14 | Derived().bar(); 15 | // expect: Derived.bar() 16 | // expect: Base.foo() 17 | -------------------------------------------------------------------------------- /Tests/super/call_same_method.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | print "Base.foo()"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | foo() { 9 | print "Derived.foo()"; 10 | super.foo(); 11 | } 12 | } 13 | 14 | Derived().foo(); 15 | // expect: Derived.foo() 16 | // expect: Base.foo() 17 | -------------------------------------------------------------------------------- /Tests/super/closure.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | toString() { return "Base"; } 3 | } 4 | 5 | class Derived < Base { 6 | getClosure() { 7 | fun closure() { 8 | return super.toString(); 9 | } 10 | return closure; 11 | } 12 | 13 | toString() { return "Derived"; } 14 | } 15 | 16 | var closure = Derived().getClosure(); 17 | print closure(); // expect: Base 18 | -------------------------------------------------------------------------------- /Tests/super/constructor.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | init(a, b) { 3 | print "Base.init(" + a + ", " + b + ")"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | init() { 9 | print "Derived.init()"; 10 | super.init("a", "b"); 11 | } 12 | } 13 | 14 | Derived(); 15 | // expect: Derived.init() 16 | // expect: Base.init(a, b) 17 | -------------------------------------------------------------------------------- /Tests/super/extra_arguments.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo(a, b) { 3 | print "Base.foo(" + a + ", " + b + ")"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | foo() { 9 | print "Derived.foo()"; // expect: Derived.foo() 10 | super.foo("a", "b", "c", "d"); // expect runtime error: Expected 2 arguments but got 4. 11 | } 12 | } 13 | 14 | Derived().foo(); 15 | -------------------------------------------------------------------------------- /Tests/super/indirectly_inherited.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | foo() { 3 | print "A.foo()"; 4 | } 5 | } 6 | 7 | class B < A {} 8 | 9 | class C < B { 10 | foo() { 11 | print "C.foo()"; 12 | super.foo(); 13 | } 14 | } 15 | 16 | C().foo(); 17 | // expect: C.foo() 18 | // expect: A.foo() 19 | -------------------------------------------------------------------------------- /Tests/super/missing_arguments.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo(a, b) { 3 | print "Base.foo(" + a + ", " + b + ")"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | foo() { 9 | super.foo(1); // expect runtime error: Expected 2 arguments but got 1. 10 | } 11 | } 12 | 13 | Derived().foo(); 14 | -------------------------------------------------------------------------------- /Tests/super/no_superclass_bind.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | super.doesNotExist; // Error at 'super': Cannot use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /Tests/super/no_superclass_call.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | foo() { 3 | super.doesNotExist(1); // Error at 'super': Cannot use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /Tests/super/no_superclass_method.lox: -------------------------------------------------------------------------------- 1 | class Base {} 2 | 3 | class Derived < Base { 4 | foo() { 5 | super.doesNotExist(1); // expect runtime error: Undefined property 'doesNotExist'. 6 | } 7 | } 8 | 9 | Derived().foo(); 10 | -------------------------------------------------------------------------------- /Tests/super/parenthesized.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | method() {} 3 | } 4 | 5 | class B < A { 6 | method() { 7 | // [line 8] Error at ')': Expect '.' after 'super'. 8 | (super).method(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/super/reassign_superclass.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | method() { 3 | print "Base.method()"; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | method() { 9 | super.method(); 10 | } 11 | } 12 | 13 | class OtherBase { 14 | method() { 15 | print "OtherBase.method()"; 16 | } 17 | } 18 | 19 | var derived = Derived(); 20 | derived.method(); // expect: Base.method() 21 | Base = OtherBase; 22 | derived.method(); // expect: Base.method() 23 | -------------------------------------------------------------------------------- /Tests/super/super_at_top_level.lox: -------------------------------------------------------------------------------- 1 | super.foo; // Error at 'super': Cannot use 'super' outside of a class. 2 | super.foo("bar"); // Error at 'super': Cannot use 'super' outside of a class. 3 | -------------------------------------------------------------------------------- /Tests/super/super_in_closure_in_inherited_method.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | say() { 3 | print "A"; 4 | } 5 | } 6 | 7 | class B < A { 8 | getClosure() { 9 | fun closure() { 10 | super.say(); 11 | } 12 | return closure; 13 | } 14 | 15 | say() { 16 | print "B"; 17 | } 18 | } 19 | 20 | class C < B { 21 | say() { 22 | print "C"; 23 | } 24 | } 25 | 26 | C().getClosure()(); // expect: A 27 | -------------------------------------------------------------------------------- /Tests/super/super_in_inherited_method.lox: -------------------------------------------------------------------------------- 1 | class A { 2 | say() { 3 | print "A"; 4 | } 5 | } 6 | 7 | class B < A { 8 | test() { 9 | super.say(); 10 | } 11 | 12 | say() { 13 | print "B"; 14 | } 15 | } 16 | 17 | class C < B { 18 | say() { 19 | print "C"; 20 | } 21 | } 22 | 23 | C().test(); // expect: A 24 | -------------------------------------------------------------------------------- /Tests/super/super_in_top_level_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | super.bar(); // Error at 'super': Cannot use 'super' outside of a class. 3 | } 4 | -------------------------------------------------------------------------------- /Tests/super/super_without_dot.lox: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B < A { 4 | method() { 5 | // [line 6] Error at ';': Expect '.' after 'super'. 6 | super; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/super/super_without_name.lox: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B < A { 4 | method() { 5 | super.; // Error at ';': Expect superclass method name. 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/super/this_in_superclass_method.lox: -------------------------------------------------------------------------------- 1 | class Base { 2 | init(a) { 3 | this.a = a; 4 | } 5 | } 6 | 7 | class Derived < Base { 8 | init(a, b) { 9 | super.init(a); 10 | this.b = b; 11 | } 12 | } 13 | 14 | var derived = Derived("a", "b"); 15 | print derived.a; // expect: a 16 | print derived.b; // expect: b 17 | -------------------------------------------------------------------------------- /Tests/test.fail.txt: -------------------------------------------------------------------------------- 1 | 2 | // === 25.4.1: expect 'updated' ============================================== 3 | // =========================================================================== 4 | // This is an interesting problem - Do I allow forward declaration of methods? 5 | var globalSet; 6 | var globalGet; 7 | 8 | fun main() { 9 | var a = "initial"; 10 | 11 | fun set() { a = "updated"; set2(); } 12 | fun get() { print a; } 13 | fun set2() { a = "2"; } 14 | 15 | globalSet = set; 16 | globalGet = get; 17 | set2(); 18 | } 19 | 20 | main(); 21 | globalSet(); 22 | globalGet(); 23 | 24 | 25 | 26 | // === Assignment of field on expression: FAIL =============================== 27 | // =========================================================================== 28 | // menu.brunch(sunday).beverage = "mimosa"; 29 | 30 | // close_over_method_parameter.lox: 31 | /* var f; 32 | class Foo { 33 | method(param) { 34 | fun f_() { 35 | print param; 36 | } 37 | f = f_; 38 | } 39 | } 40 | 41 | Foo().method("param"); 42 | f(); // expect: param*/ -------------------------------------------------------------------------------- /Tests/this/closure.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | getClosure() { 3 | fun closure() { 4 | return this.toString(); 5 | } 6 | return closure; 7 | } 8 | 9 | toString() { return "Foo"; } 10 | } 11 | 12 | var closure = Foo().getClosure(); 13 | print closure(); // expect: Foo 14 | -------------------------------------------------------------------------------- /Tests/this/nested_class.lox: -------------------------------------------------------------------------------- 1 | class Outer { 2 | method() { 3 | print this; // expect: Outer instance 4 | 5 | fun f() { 6 | print this; // expect: Outer instance 7 | 8 | class Inner { 9 | method() { 10 | print this; // expect: Inner instance 11 | } 12 | } 13 | 14 | Inner().method(); 15 | } 16 | f(); 17 | } 18 | } 19 | 20 | Outer().method(); 21 | -------------------------------------------------------------------------------- /Tests/this/nested_closure.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | getClosure() { 3 | fun f() { 4 | fun g() { 5 | fun h() { 6 | return this.toString(); 7 | } 8 | return h; 9 | } 10 | return g; 11 | } 12 | return f; 13 | } 14 | 15 | toString() { return "Foo"; } 16 | } 17 | 18 | var closure = Foo().getClosure(); 19 | print closure()()(); // expect: Foo 20 | -------------------------------------------------------------------------------- /Tests/this/this_at_top_level.lox: -------------------------------------------------------------------------------- 1 | this; // Error at 'this': Cannot use 'this' outside of a class. 2 | -------------------------------------------------------------------------------- /Tests/this/this_in_method.lox: -------------------------------------------------------------------------------- 1 | class Foo { 2 | bar() { return this; } 3 | baz() { return "baz"; } 4 | } 5 | 6 | print Foo().bar().baz(); // expect: baz 7 | -------------------------------------------------------------------------------- /Tests/this/this_in_top_level_function.lox: -------------------------------------------------------------------------------- 1 | fun foo() { 2 | this; // Error at 'this': Cannot use 'this' outside of a class. 3 | } 4 | -------------------------------------------------------------------------------- /Tests/unexpected_character.lox: -------------------------------------------------------------------------------- 1 | // [line 3] Error: Unexpected character. 2 | // [java line 3] Error at 'b': Expect ')' after arguments. 3 | foo(a | b); 4 | -------------------------------------------------------------------------------- /Tests/variable/collide_with_parameter.lox: -------------------------------------------------------------------------------- 1 | fun foo(a) { 2 | var a; // Error at 'a': Variable with this name already declared in this scope. 3 | } 4 | -------------------------------------------------------------------------------- /Tests/variable/duplicate_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "value"; 3 | var a = "other"; // Error at 'a': Variable with this name already declared in this scope. 4 | } 5 | -------------------------------------------------------------------------------- /Tests/variable/duplicate_parameter.lox: -------------------------------------------------------------------------------- 1 | fun foo(arg, 2 | arg) { // Error at 'arg': Variable with this name already declared in this scope. 3 | "body"; 4 | } 5 | -------------------------------------------------------------------------------- /Tests/variable/early_bound.lox: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | fun foo() { 4 | print a; 5 | } 6 | 7 | foo(); // expect: outer 8 | var a = "inner"; 9 | foo(); // expect: outer 10 | } 11 | -------------------------------------------------------------------------------- /Tests/variable/in_middle_of_block.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "a"; 3 | print a; // expect: a 4 | var b = a + " b"; 5 | print b; // expect: a b 6 | var c = a + " c"; 7 | print c; // expect: a c 8 | var d = b + " d"; 9 | print d; // expect: a b d 10 | } 11 | -------------------------------------------------------------------------------- /Tests/variable/in_nested_block.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "outer"; 3 | { 4 | print a; // expect: outer 5 | } 6 | } -------------------------------------------------------------------------------- /Tests/variable/local_from_method.lox: -------------------------------------------------------------------------------- 1 | var foo = "variable"; 2 | 3 | class Foo { 4 | method() { 5 | print foo; 6 | } 7 | } 8 | 9 | Foo().method(); // expect: variable 10 | -------------------------------------------------------------------------------- /Tests/variable/redeclare_global.lox: -------------------------------------------------------------------------------- 1 | var a = "1"; 2 | var a; 3 | print a; // expect: nil 4 | -------------------------------------------------------------------------------- /Tests/variable/redefine_global.lox: -------------------------------------------------------------------------------- 1 | var a = "1"; 2 | var a = "2"; 3 | print a; // expect: 2 4 | -------------------------------------------------------------------------------- /Tests/variable/scope_reuse_in_different_blocks.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "first"; 3 | print a; // expect: first 4 | } 5 | 6 | { 7 | var a = "second"; 8 | print a; // expect: second 9 | } 10 | -------------------------------------------------------------------------------- /Tests/variable/shadow_and_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "outer"; 3 | { 4 | print a; // expect: outer 5 | var a = "inner"; 6 | print a; // expect: inner 7 | } 8 | } -------------------------------------------------------------------------------- /Tests/variable/shadow_global.lox: -------------------------------------------------------------------------------- 1 | var a = "global"; 2 | { 3 | var a = "shadow"; 4 | print a; // expect: shadow 5 | } 6 | print a; // expect: global 7 | -------------------------------------------------------------------------------- /Tests/variable/shadow_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | var a = "local"; 3 | { 4 | var a = "shadow"; 5 | print a; // expect: shadow 6 | } 7 | print a; // expect: local 8 | } 9 | -------------------------------------------------------------------------------- /Tests/variable/undefined_global.lox: -------------------------------------------------------------------------------- 1 | print notDefined; // expect runtime error: Undefined variable 'notDefined'. 2 | -------------------------------------------------------------------------------- /Tests/variable/undefined_local.lox: -------------------------------------------------------------------------------- 1 | { 2 | print notDefined; // expect runtime error: Undefined variable 'notDefined'. 3 | } 4 | -------------------------------------------------------------------------------- /Tests/variable/uninitialized.lox: -------------------------------------------------------------------------------- 1 | var a; 2 | print a; // expect: nil 3 | -------------------------------------------------------------------------------- /Tests/variable/unreached_undefined.lox: -------------------------------------------------------------------------------- 1 | if (false) { 2 | print notDefined; 3 | } 4 | 5 | print "ok"; // expect: ok 6 | -------------------------------------------------------------------------------- /Tests/variable/use_false_as_var.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'false': Expect variable name. 2 | var false = "value"; 3 | -------------------------------------------------------------------------------- /Tests/variable/use_global_in_initializer.lox: -------------------------------------------------------------------------------- 1 | var a = "value"; 2 | var a = a; 3 | print a; // expect: value 4 | -------------------------------------------------------------------------------- /Tests/variable/use_local_in_initializer.lox: -------------------------------------------------------------------------------- 1 | var a = "outer"; 2 | { 3 | var a = a; // Error at 'a': Cannot read local variable in its own initializer. 4 | } 5 | -------------------------------------------------------------------------------- /Tests/variable/use_nil_as_var.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'nil': Expect variable name. 2 | var nil = "value"; 3 | -------------------------------------------------------------------------------- /Tests/variable/use_this_as_var.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'this': Expect variable name. 2 | var this = "value"; 3 | -------------------------------------------------------------------------------- /Tests/while/class_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | while (true) class Foo {} 3 | -------------------------------------------------------------------------------- /Tests/while/closure_in_body.lox: -------------------------------------------------------------------------------- 1 | var f1; 2 | var f2; 3 | var f3; 4 | 5 | var i = 1; 6 | while (i < 4) { 7 | var j = i; 8 | fun f() { print j; } 9 | 10 | if (j == 1) f1 = f; 11 | else if (j == 2) f2 = f; 12 | else f3 = f; 13 | 14 | i = i + 1; 15 | } 16 | 17 | f1(); // expect: 1 18 | f2(); // expect: 2 19 | f3(); // expect: 3 20 | -------------------------------------------------------------------------------- /Tests/while/fun_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fun': Expect expression. 2 | while (true) fun foo() {} 3 | -------------------------------------------------------------------------------- /Tests/while/return_closure.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) { 3 | var i = "i"; 4 | fun g() { print i; } 5 | return g; 6 | } 7 | } 8 | 9 | var h = f(); 10 | h(); // expect: i 11 | -------------------------------------------------------------------------------- /Tests/while/return_inside.lox: -------------------------------------------------------------------------------- 1 | fun f() { 2 | while (true) { 3 | var i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print f(); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /Tests/while/syntax.lox: -------------------------------------------------------------------------------- 1 | // Single-expression body. 2 | var c = 0; 3 | while (c < 3) print c = c + 1; 4 | // expect: 1 5 | // expect: 2 6 | // expect: 3 7 | 8 | // Block body. 9 | var a = 0; 10 | while (a < 3) { 11 | print a; 12 | a = a + 1; 13 | } 14 | // expect: 0 15 | // expect: 1 16 | // expect: 2 17 | 18 | // Statement bodies. 19 | while (false) if (true) 1; else 2; 20 | while (false) while (true) 1; 21 | while (false) for (;;) 1; 22 | -------------------------------------------------------------------------------- /Tests/while/var_in_body.lox: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'var': Expect expression. 2 | while (true) var foo; 3 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/ACompiler.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.Base { 2 | /// 3 | /// ACompiler is the base class for all compilers. 4 | /// Compilers are used to compile a list of tokens into bytecode that will be executed by the VM. 5 | /// 6 | abstract class ACompiler { 7 | 8 | // input and error: 9 | protected readonly TokenList Tokens; 10 | protected bool HadError = false; 11 | 12 | // Compiling code to: 13 | protected readonly string Name; 14 | 15 | protected ACompiler(TokenList tokens, string name) { 16 | Tokens = tokens; 17 | Name = name; 18 | } 19 | 20 | /// 21 | /// program → declaration* EOF ; 22 | /// Called from TryCompile. 23 | /// 24 | internal bool Compile() { 25 | while (!Tokens.IsAtEnd()) { 26 | try { 27 | Declaration(); 28 | } 29 | catch { 30 | HadError = true; 31 | Synchronize(); 32 | throw; 33 | } 34 | } 35 | EndCompiler(); 36 | return !HadError; 37 | } 38 | 39 | protected abstract void Declaration(); 40 | 41 | protected abstract void Synchronize(); 42 | 43 | protected abstract void EndCompiler(); 44 | 45 | protected int LineOfLastToken => Tokens.Previous().Line; 46 | 47 | protected int LineOfCurrentToken => Tokens.Peek().Line; 48 | 49 | public override string ToString() => $"Compiling {Name}"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/ATokenizer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.Base { 4 | /// 5 | /// ATokenizer is the base class for all tokenizers. 6 | /// Tokenizers are used to tokenize a source string into a list of tokens. 7 | /// 8 | abstract class ATokenizer { 9 | 10 | protected int Start { 11 | get => CurrentFile.Start; 12 | set => CurrentFile.Start = value; 13 | } 14 | 15 | protected int Current { 16 | get => CurrentFile.Current; 17 | set => CurrentFile.Current = value; 18 | } 19 | 20 | protected int Line { 21 | get => CurrentFile.Line; 22 | set => CurrentFile.Line = value; 23 | } 24 | 25 | protected string Source => CurrentFile.Source; 26 | 27 | protected bool IsAtEnd => CurrentFile.Current >= (CurrentFile.Source?.Length ?? 0); 28 | 29 | protected TokenList Tokens = new TokenList(); 30 | 31 | protected TokenizerContext CurrentFile => _Files[_Files.Count - 1]; 32 | 33 | private readonly List _Files = new List(); 34 | 35 | protected ATokenizer() { } 36 | 37 | protected ATokenizer(string path, string source) { 38 | SourceBegin(path, source); 39 | } 40 | 41 | protected void SourceBegin(string path, string source, int line = 1) { 42 | _Files.Add(new TokenizerContext(path, source, line)); 43 | } 44 | 45 | public virtual void Reset(string path, string source, int line = 1) { 46 | Tokens.Reset(); 47 | _Files.Clear(); 48 | _Files.Add(new TokenizerContext(path, source, line)); 49 | } 50 | 51 | /// 52 | /// returns true if we should continue tokenizing, false if this is EOF. 53 | /// 54 | /// 55 | protected bool SourceEnd() { 56 | _Files.RemoveAt(_Files.Count - 1); 57 | return _Files.Count > 0; 58 | } 59 | 60 | internal TokenList ScanTokens(bool addEofAtEnd = true) { 61 | int lastLine; 62 | while (true) { 63 | while (!IsAtEnd) { 64 | // we are at the beginning of the next lexeme 65 | Start = Current; 66 | ScanToken(); 67 | } 68 | lastLine = Line; 69 | if (!SourceEnd()) { 70 | break; 71 | } 72 | } 73 | if (addEofAtEnd) { 74 | Tokens.Add(new Token(TokenTypes.EOF, lastLine)); 75 | } 76 | PostProcessTokens(); 77 | return Tokens; 78 | } 79 | 80 | protected abstract void PostProcessTokens(); 81 | 82 | protected abstract void ScanToken(); 83 | 84 | protected abstract void AddToken(int type); 85 | 86 | // === Support and consumption routines === 87 | // ======================================== 88 | 89 | /// 90 | /// consumes the next character in the source file and returns it. 91 | /// 92 | protected char Advance() { 93 | Current += 1; 94 | return Source[Current - 1]; 95 | } 96 | 97 | /// 98 | /// a conditional advance(), only consumes the current character if it’s what we’re looking for. 99 | /// 100 | protected bool Match(char expected) { 101 | if (IsAtEnd) return false; 102 | if (Source[Current] != expected) { 103 | return false; 104 | } 105 | Current += 1; 106 | return true; 107 | } 108 | 109 | /// 110 | /// a conditional advance(), only consumes the current string if it’s what we’re looking for. 111 | /// 112 | protected bool Match(string expected) { 113 | for (int i = 0; i < expected.Length; i++) { 114 | if (IsAtEnd) return false; 115 | if (Source[Current + i] != expected[i]) { 116 | return false; 117 | } 118 | } 119 | Current += expected.Length; 120 | return true; 121 | } 122 | 123 | /// 124 | /// only looks at the current unconsumed character. 125 | /// 126 | protected char Peek() { 127 | if (IsAtEnd) { 128 | return '\0'; 129 | } 130 | return Source[Current]; 131 | } 132 | 133 | /// 134 | /// Returns the next character. 135 | /// 136 | protected char PeekNext() { 137 | if (Current + 1 >= Source.Length) { 138 | return '\0'; 139 | } 140 | return Source[Current + 1]; 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/CompilerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XPT.Core.Scripting.Base { 4 | /// 5 | /// The parser is in a confused state and needs to panic and synchronize. 6 | /// 7 | class CompilerException : Exception { 8 | private readonly Token _Token; 9 | 10 | internal CompilerException(Token token, string message) : base(message) { 11 | _Token = token; 12 | } 13 | 14 | public override string ToString() { 15 | if (_Token.IsEOF) { 16 | return $"Compiler error at line {_Token.Line} at EOF: {base.Message}"; 17 | } 18 | else { 19 | return $"Compiler error at line {_Token.Line} at '{_Token.Lexeme}': {base.Message}"; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/StringTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using XPT.Core.IO; 3 | using XPT.Core.Utilities; 4 | 5 | namespace XPT.Core.Scripting.Base { 6 | /// 7 | /// StringTable stores constant strings that are referenced by the VM while executing a chunk of bytecode. 8 | /// 9 | sealed class StringTable { 10 | private const int InitialConstantCapcity = 8; 11 | private const int GrowCapacityFactor = 2; 12 | 13 | private byte[] _StringTable = null; 14 | 15 | /// 16 | /// How much of the constant string table is in use. 17 | /// 18 | internal int SizeStringTable { 19 | get; 20 | private set; 21 | } = 0; 22 | 23 | internal void Compress() { 24 | if (SizeStringTable < _StringTable?.Length) { 25 | Array.Resize(ref _StringTable, SizeStringTable); 26 | } 27 | } 28 | 29 | internal void Serialize(IWriter writer) { 30 | writer.Write7BitInt((int)(SizeStringTable)); 31 | if (_StringTable != null) { 32 | writer.Write(_StringTable); 33 | } 34 | } 35 | 36 | internal void Deserialize(IReader reader) { 37 | SizeStringTable = reader.Read7BitInt(); 38 | _StringTable = reader.ReadBytes(SizeStringTable); 39 | } 40 | 41 | internal string ReadStringConstant(int offset) { 42 | if (offset < 0) { 43 | return null; // todo: runtime error 44 | } 45 | for (int i = offset; i < SizeStringTable; i++) { 46 | if (_StringTable[i] == 0) { 47 | string value = TextEncoding.GetString(_StringTable, offset, i - offset); 48 | return value; 49 | } 50 | } 51 | return null; // todo: runtime error 52 | } 53 | 54 | /// 55 | /// Adds the given string to the chunk's string table. 56 | /// Returns the index of that string in the string table. 57 | /// 58 | internal int WriteStringConstant(string value) { 59 | byte[] ascii = TextEncoding.GetBytes(value); 60 | if (ascii == null) { 61 | return -1; 62 | } 63 | int size = ascii.Length + 1; 64 | // does the string exist in this chunk already? 65 | for (int i = 0; i <= SizeStringTable - size; i++) { 66 | if (_StringTable[i + size - 1] != 0) { 67 | continue; 68 | } 69 | bool match = true; 70 | for (int j = 0; j < ascii.Length; j++) { 71 | if (_StringTable[i + j] != ascii[j]) { 72 | match = false; 73 | break; 74 | } 75 | } 76 | if (match) { 77 | // yes! return the index of the existing string. 78 | return i; 79 | } 80 | } 81 | // nope! add it to the string table. 82 | CheckGrowStringTable(size); 83 | int index = SizeStringTable; 84 | Array.Copy(ascii, 0, _StringTable, index, ascii.Length); 85 | SizeStringTable += size; 86 | return index; 87 | } 88 | 89 | private void CheckGrowStringTable(int size) { 90 | int capacity = _StringTable?.Length ?? 0; 91 | if (capacity < SizeStringTable + size) { 92 | int newCapacity = _StringTable == null ? InitialConstantCapcity : _StringTable.Length * GrowCapacityFactor; 93 | while (newCapacity < SizeStringTable + size) { 94 | newCapacity *= GrowCapacityFactor; 95 | } 96 | if (_StringTable == null) { 97 | _StringTable = new byte[newCapacity]; 98 | } 99 | else { 100 | byte[] newData = new byte[newCapacity]; 101 | Array.Copy(_StringTable, newData, _StringTable.Length); 102 | _StringTable = newData; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/Token.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.Base { 2 | /// 3 | /// Token represents a single parseable piece of input source code. 4 | /// 5 | class Token { 6 | internal readonly int Type; 7 | internal readonly int Line; 8 | 9 | private readonly string _Source; 10 | private readonly int _SrcStart; 11 | private readonly int _SrcLength; 12 | 13 | internal string Lexeme => _Source?.Substring(_SrcStart, _SrcLength) ?? null; 14 | 15 | internal int LiteralAsNumber { 16 | get { 17 | if (Lexeme.Length > 2 && Lexeme.StartsWith("0x")) { 18 | int value = int.Parse(_Source.Substring(_SrcStart + 2, _SrcLength - 2), System.Globalization.NumberStyles.HexNumber); 19 | return value; 20 | } 21 | else { 22 | try { 23 | return int.Parse(_Source.Substring(_SrcStart, _SrcLength)); 24 | } 25 | catch { 26 | return 0; 27 | } 28 | } 29 | } 30 | } 31 | 32 | internal string LiteralAsString => _Source.Substring(_SrcStart + 1, _SrcLength - 2); 33 | 34 | internal Token(int type, int line) : this(type, line, null, 0, 0) { } 35 | 36 | internal Token(int type, int line, string source) : this(type, line, source, 0, source.Length) { } 37 | 38 | internal Token(int type, int line, string source, int srcStart, int srcLength) { 39 | Type = type; 40 | Line = line; 41 | _Source = source; 42 | _SrcStart = srcStart; 43 | _SrcLength = srcLength; 44 | } 45 | 46 | /// 47 | /// TTokenType must have enum value of 0 equal to EOF. 48 | /// 49 | internal bool IsEOF => Type == TokenTypes.EOF; 50 | 51 | public override string ToString() => $"{Type} \'{Lexeme}\' @{Line}"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/TokenTypes.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.Base { 2 | internal static class TokenTypes { 3 | // End of file. 4 | internal const int 5 | EOF = 0, 6 | // Error. 7 | ERROR = 1, 8 | // One or two character tokens. 9 | LEFT_BRACE = 2, RIGHT_BRACE = 3, LEFT_PAREN = 4, RIGHT_PAREN = 5, 10 | COMMA = 6, DOT = 7, MINUS = 8, PLUS = 9, SEMICOLON = 10, SLASH = 11, STAR = 12, 11 | BANG = 13, BANG_EQUAL = 14, EQUAL = 15, EQUAL_EQUAL = 16, 12 | GREATER = 17, GREATER_EQUAL = 18, LESS = 19, LESS_EQUAL = 20, 13 | AMPERSAND = 21, PIPE = 22, LEFT_BRACKET = 23, RIGHT_BRACKET = 24, TILDE = 25, 14 | PERCENT = 26, INCREMENT = 27, DECREMENT = 28, COLON = 29, 15 | // Literals. 16 | IDENTIFIER = 81, STRING = 82, NUMBER = 83; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/TokenizerContext.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.Base { 2 | /// 3 | /// TokenizerContext is used to keep track of the current state of the tokenizer. 4 | /// 5 | class TokenizerContext { 6 | internal string Path; 7 | internal string Source; 8 | internal int Start = 0; 9 | internal int Current = 0; 10 | internal int Line; 11 | 12 | public TokenizerContext(string path, string source, int line = 1) { 13 | Path = path; 14 | Source = source; 15 | Line = line; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Base/Validation.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.Base { 2 | internal static class Validation { 3 | internal static bool IsAlphaOrUnderscore(char c) { 4 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; 5 | } 6 | 7 | internal static bool IsAlphaUnderscoreOrNumeric(char c) { 8 | return IsAlphaOrUnderscore(c) || IsDigit(c); 9 | } 10 | 11 | internal static bool IsDigit(char c, bool allowHex = false) { 12 | return (c >= '0' && c <= '9') || (allowHex && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))); 13 | } 14 | 15 | internal static bool IsValidIdentifer(string s, out string error, bool allowUnderscoreFirstCharacter = true) { 16 | error = null; 17 | if (string.IsNullOrWhiteSpace(s)) { 18 | error = "Identifier must not be null or contain whitespace."; 19 | return false; 20 | } 21 | if (!allowUnderscoreFirstCharacter && s[0] == '_') { 22 | error = "Identifier must not begin with an underscore character."; 23 | return false; 24 | } 25 | if (!IsAlphaOrUnderscore(s[0])) { 26 | error = "Identifier must begin with an alphabetical or underscore character."; 27 | return false; 28 | } 29 | for (int i = 1; i < s.Length; i++) { 30 | char c = s[i]; 31 | if (!IsAlphaOrUnderscore(c) && !IsDigit(c)) { 32 | error = "Identifier must only contain alphanumeric characters and underscore characters."; 33 | return false; 34 | } 35 | } 36 | return true; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Interfaces/IScript.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.Interfaces { 4 | internal interface IScript { 5 | 6 | /// 7 | /// The name of the script. 8 | /// 9 | string Name { get; } 10 | 11 | /// 12 | /// The global variables and other data stored in the global string. 13 | /// 14 | string ScriptGlobals { get; set; } 15 | 16 | /// 17 | /// A collection of methods, each corresponding to a function in the script. 18 | /// 19 | IEnumerable ScriptMethods { get; } 20 | 21 | /// 22 | /// The entire text source code for this script. 23 | /// 24 | string ScriptSource { get; } 25 | 26 | bool TryAddMethod(string name, out string error); 27 | bool TryGetMethod(string name, out IScriptMethod method); 28 | bool TryRemoveMethod(string name); 29 | int GetMethodUseCount(string name); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Interfaces/IScriptMethod.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.Interfaces { 4 | internal interface IScriptMethod { 5 | string Name { get; set; } 6 | IEnumerable Parameters { get; } 7 | string Code { get; set; } 8 | 9 | /// 10 | /// Returns a complete source code representation of the script method, ready to be passed to a compiler. 11 | /// 12 | /// 13 | string GenerateSource(); 14 | 15 | /// 16 | /// Should be false for any special methods created by the editor that must have specific parameters. 17 | /// 18 | bool ParameterEdittingEnabled { get; } 19 | 20 | void ParameterAdd(string parameter); 21 | void ParameterRemove(string parameter); 22 | void ParameterReorder(string parameter, int order); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/Compiling/ELoxFunctionType.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.LoxScript.Compiling { 2 | internal enum ELoxFunctionType { 3 | TYPE_FUNCTION, 4 | TYPE_INITIALIZER, 5 | TYPE_METHOD, 6 | TYPE_SCRIPT 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/Compiling/LoxCompilerClass.cs: -------------------------------------------------------------------------------- 1 | using XPT.Core.Scripting.Base; 2 | 3 | namespace XPT.Core.Scripting.LoxScript.Compiling { 4 | internal class LoxCompilerClass { 5 | public LoxCompilerClass Enclosing; 6 | public Token Name; 7 | public bool HasSuperClass = false; 8 | 9 | public LoxCompilerClass(Token name, LoxCompilerClass enclosing = null) { 10 | Name = name; 11 | Enclosing = enclosing; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/Compiling/LoxCompilerFixup.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.LoxScript.Compiling { 2 | internal struct LoxCompilerFixup { 3 | public readonly int Address; 4 | public readonly int Value; 5 | 6 | public LoxCompilerFixup(int address, int value) { 7 | Address = address; 8 | Value = value; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/Compiling/LoxCompilerLocal.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.LoxScript.Compiling { 2 | internal class LoxCompilerLocal { 3 | public readonly string Name; 4 | public int Depth; 5 | public bool IsCaptured; 6 | 7 | public LoxCompilerLocal(string name, int depth) { 8 | Name = name; 9 | Depth = depth; 10 | IsCaptured = false; 11 | } 12 | 13 | public override string ToString() => $"{Name}"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/Compiling/LoxCompilerUpvalue.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.LoxScript.Compiling { 2 | internal class LoxCompilerUpvalue { 3 | public readonly int Index; 4 | public readonly bool IsLocal; 5 | 6 | public LoxCompilerUpvalue(int index, bool isLocal) { 7 | Index = index; 8 | IsLocal = isLocal; 9 | } 10 | 11 | public override string ToString() => $"{Index}"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/Compiling/LoxTokenTypes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.LoxScript.Compiling { 4 | internal static class LoxTokenTypes { 5 | // Keywords. 6 | internal const int 7 | /*, PUBLIC */ 8 | AND = 100, CLASS = 101, ELSE = 102, FALSE = 103, 9 | FUNCTION = 104, FOR = 105, IF = 106, NIL = 107, 10 | OR = 108, RETURN = 109, SUPER = 110, THIS = 111, 11 | TRUE = 112, VAR = 113, WHILE = 114, SWITCH = 115, 12 | CASE = 116, BREAK = 117, DEFAULT = 118; 13 | 14 | internal const string This = "this"; 15 | internal const string Ctor = "init"; 16 | 17 | private static readonly Dictionary _Keywords = new Dictionary(); 18 | 19 | static LoxTokenTypes() { 20 | // _Keywords["public"] = PUBLIC; 21 | _Keywords["and"] = AND; 22 | _Keywords["class"] = CLASS; 23 | _Keywords["else"] = ELSE; 24 | _Keywords["false"] = FALSE; 25 | _Keywords["for"] = FOR; 26 | _Keywords["fun"] = FUNCTION; 27 | _Keywords["if"] = IF; 28 | _Keywords["nil"] = NIL; 29 | _Keywords["or"] = OR; 30 | _Keywords["return"] = RETURN; 31 | _Keywords["super"] = SUPER; 32 | _Keywords["this"] = THIS; 33 | _Keywords["true"] = TRUE; 34 | _Keywords["var"] = VAR; 35 | _Keywords["while"] = WHILE; 36 | _Keywords["this"] = THIS; 37 | _Keywords["true"] = TRUE; 38 | _Keywords["var"] = VAR; 39 | _Keywords["while"] = WHILE; 40 | _Keywords["switch"] = SWITCH; // <-- these are 41 | _Keywords["case"] = CASE; // <-- turned into 42 | _Keywords["break"] = BREAK; // <-- if statements 43 | _Keywords["default"] = DEFAULT; // <-- this one too 44 | } 45 | 46 | internal static int? Get(string text) { 47 | if (_Keywords.TryGetValue(text, out int type)) { 48 | return type; 49 | } 50 | return null; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/LoxFieldAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XPT.Core.Scripting.LoxScript { 4 | /// 5 | /// Attach this to a field, property, or method to make it callable by a lox script running in a gears virtual machine. 6 | /// 7 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method)] 8 | public class LoxFieldAttribute : Attribute { 9 | public readonly string Name; 10 | 11 | public LoxFieldAttribute(string name) { 12 | Name = name; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/LoxHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using XPT.Core.IO; 4 | using XPT.Core.Scripting.LoxScript.Compiling; 5 | using XPT.Core.Scripting.LoxScript.VirtualMachine; 6 | 7 | namespace XPT.Core.Scripting.LoxScript { 8 | /// 9 | /// LoxHandler is a static helper class that handles loading and running LoxScript files. 10 | /// 11 | static class LoxHandler { 12 | 13 | // === Loading ============================================================================================ 14 | // ======================================================================================================== 15 | 16 | internal static bool DebugTestSerialization = false; 17 | 18 | /// 19 | /// Attempt to load a file and compile it. 20 | /// firstrun must be true to initialize classes and declare all globals. 21 | /// 22 | internal static bool TryLoadFromFile(string path, bool firstrun, out Gears gears, out string status) { 23 | gears = null; 24 | if (!LoxCompiler.TryCompileFromPath(path, out GearsChunk chunk, out status)) { 25 | return false; 26 | } 27 | return TryLoad(path, chunk, firstrun, ref gears, ref status); 28 | } 29 | 30 | /// 31 | /// Attempt to compile passed source string. 32 | /// firstrun must be true to initialize classes and declare all globals. 33 | /// 34 | internal static bool TryLoadFromSource(string path, string source, bool firstrun, out Gears gears, out string status) { 35 | gears = null; 36 | if (!LoxCompiler.TryCompileFromSource(path, source, out GearsChunk chunk, out status)) { 37 | return false; 38 | } 39 | return TryLoad(path, chunk, firstrun, ref gears, ref status); 40 | } 41 | 42 | /// 43 | /// Sets up a gears machine for this chunk. 44 | /// 45 | internal static bool TryLoad(string path, GearsChunk chunk, bool firstrun, ref Gears gears, ref string status) { 46 | if (DebugTestSerialization && !TestSerializeDeserialize(chunk, path)) { 47 | status = $"Error serializing GearsChunk in LoxHandler.TryLoad: Could not serialize or deserialize '{path}'."; 48 | return false; 49 | } 50 | try { 51 | gears = new Gears().Reset(chunk, firstrun); 52 | return true; 53 | } 54 | catch (Exception e) { 55 | status = $"Error initializing Gears in {e.TargetSite.DeclaringType.Name}.{e.TargetSite.Name}: {path} {e}"; 56 | return false; 57 | } 58 | } 59 | 60 | private static bool TestSerializeDeserialize(GearsChunk chunk, string path) { 61 | string filename = $"{Path.GetFileNameWithoutExtension(path)}.lxx"; 62 | using (BinaryFileWriter writer = new BinaryFileWriter(filename)) { 63 | chunk.Serialize(writer); 64 | writer.Dispose(); 65 | } 66 | using (BinaryFileReader reader = new BinaryFileReader(filename)) { 67 | bool success = GearsChunk.TryDeserialize(filename, reader, out chunk); 68 | reader.Dispose(); 69 | return success; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/VirtualMachine/GearsCallFrame.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.LoxScript.VirtualMachine { 2 | internal class GearsCallFrame { 3 | public readonly GearsObjFunction Function; 4 | 5 | public int IP; 6 | 7 | public int BP; 8 | 9 | public GearsCallFrame(GearsObjFunction function, int ip = 0, int bp = 0) { 10 | IP = ip; 11 | BP = bp; 12 | Function = function; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/VirtualMachine/GearsHashTable.cs: -------------------------------------------------------------------------------- 1 | #if NET_4_5 2 | using System.Runtime.CompilerServices; 3 | #endif 4 | 5 | using System.Collections.Generic; 6 | 7 | namespace XPT.Core.Scripting.LoxScript.VirtualMachine { 8 | /// 9 | /// An implementation of the lox hash table. 10 | /// 11 | internal class GearsHashTable { 12 | private readonly Dictionary _Table = new Dictionary(); 13 | 14 | public void Reset() { 15 | _Table.Clear(); 16 | } 17 | 18 | public IEnumerable AllValues => _Table.Values; 19 | 20 | public IEnumerable AllKeys => _Table.Keys; 21 | 22 | /// 23 | /// Returns true if value exists in hash table. 24 | /// 25 | #if NET_4_5 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | #endif 28 | public bool TryGet(string key, out GearsValue value) { 29 | if (_Table.TryGetValue(key, out value)) { 30 | return true; 31 | } 32 | return false; 33 | } 34 | 35 | /// 36 | /// Returns true if value replaced an existing value. 37 | /// 38 | #if NET_4_5 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | #endif 41 | public bool Set(string key, GearsValue value) { 42 | bool exists = _Table.ContainsKey(key); 43 | _Table[key] = value; 44 | return !exists; 45 | } 46 | 47 | public void Delete(string key) { 48 | _Table.Remove(key); 49 | } 50 | 51 | #if NET_4_5 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | #endif 54 | public bool ContainsKey(string key) => _Table.ContainsKey(key); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/VirtualMachine/GearsNativeList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.LoxScript.VirtualMachine { 4 | class GearsNativeList { 5 | internal readonly List List; 6 | 7 | internal GearsNativeList() { 8 | List = new List(); 9 | } 10 | 11 | public int Count => List.Count; 12 | 13 | public T Get(int index) { 14 | if (index < 0 || index >= Count) { 15 | return default; 16 | } 17 | return List[index]; 18 | } 19 | 20 | public override string ToString() => $"List of {typeof(T).Name}"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/VirtualMachine/GearsRuntimeException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace XPT.Core.Scripting.LoxScript.VirtualMachine { 4 | /// 5 | /// Thrown when Virtual Machine instance encounters an error. 6 | /// 7 | internal class GearsRuntimeException : Exception { 8 | private readonly int _Line; 9 | 10 | internal GearsRuntimeException(int line, string message) : base(message) { 11 | _Line = line; 12 | } 13 | 14 | internal GearsRuntimeException(string message) : base(message) { 15 | _Line = -1; 16 | } 17 | 18 | public override string ToString() { 19 | // todo: print stack trace 24.5.2 20 | if (_Line >= 0) { 21 | return $"[Line {_Line}] {base.Message}"; 22 | } 23 | return base.Message; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/VirtualMachine/Gears_NativeCalls.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using XPT.Core.Scripting.Rules; 3 | using XPT.Core.Utilities; 4 | 5 | namespace XPT.Core.Scripting.LoxScript.VirtualMachine { 6 | internal partial class Gears { // support for native c# calling lox script functions and rules. 7 | /// 8 | /// Calls a LoxScript function from native c#, passing arguments. 9 | /// If the function call was successful, returns true, and returnValue will be the returned value from the function, if any. 10 | /// If the function call was not successful, returns false, and returnValue will be an error string. 11 | /// 12 | internal bool CallGearsFunction(string fnName, out object returned, params object[] args) { 13 | if (!Globals.TryGet(fnName, out GearsValue fnValue) || !fnValue.IsObjPtr) { 14 | // error: no function with that name. 15 | returned = $"Error: no function with name '{fnName}'."; 16 | return false; 17 | } 18 | GearsObj fnObject = fnValue.AsObject(this); 19 | if (fnObject is GearsObjFunction fnFunction) { 20 | if (fnFunction.Arity != args.Length) { 21 | // error: wrong arity. 22 | returned = $"Error: called '{fnName}' with wrong arity (passed arity is '{args?.Length ?? 0}')."; 23 | return false; 24 | } 25 | } 26 | Push(fnValue); 27 | for (int i = 0; i < (args?.Length ?? 0); i++) { 28 | object arg = args[i]; 29 | Type argType = arg?.GetType() ?? null; 30 | if (arg == null) { 31 | Push(GearsValue.NilValue); 32 | } 33 | else if (GearsNativeWrapper.IsNumeric(argType)) { 34 | int fieldValue = Convert.ToInt32(arg); 35 | Push(new GearsValue(fieldValue)); 36 | } 37 | else if (argType == typeof(bool)) { 38 | bool fieldValue = Convert.ToBoolean(arg); 39 | Push(fieldValue ? GearsValue.TrueValue : GearsValue.FalseValue); 40 | } 41 | else if (argType == typeof(string)) { 42 | string fieldValue = Convert.ToString(arg); 43 | if (fieldValue == null) { 44 | Push(GearsValue.NilValue); 45 | } 46 | else { 47 | Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjString(fieldValue)))); 48 | } 49 | } 50 | else if (argType.IsSubclassOf(typeof(object))) { 51 | if (arg == null) { 52 | Push(GearsValue.NilValue); 53 | } 54 | else { 55 | Push(GearsValue.CreateObjPtr(HeapAddObject(new GearsObjInstanceNative(this, arg)))); 56 | } 57 | } 58 | else { 59 | // error: could not pass arg of this type 60 | returned = $"Error: called '{fnName}' with argument of type '{argType.Name}' as parameter {i}. Gears could not interpret this argument."; 61 | return false; 62 | } 63 | } 64 | Call(args?.Length ?? 0); 65 | if (!TryRun(out string error)) { 66 | returned = error; 67 | return false; 68 | } 69 | returned = LastReturnValue; // the return value 70 | // todo: process return value? 71 | return true; 72 | } 73 | 74 | // === Rules ================================================================================================= 75 | // =========================================================================================================== 76 | 77 | private void RegisterRules() { 78 | if (Chunk.Rules != null && Chunk.Rules.Count > 0) { 79 | _Rules = Chunk.Rules.CreateCopyWithHostedDelegate(OnInvokeByRule); 80 | RuleSystem.RegisterRules(this, _Rules); 81 | } 82 | else { 83 | _Rules = null; 84 | } 85 | } 86 | 87 | private void UnregisterRules() { 88 | if (_Rules != null) { 89 | RuleSystem.UnregisterRules(this); 90 | _Rules = null; 91 | } 92 | } 93 | 94 | private void OnInvokeByRule(string fnName, VarCollection vars) { 95 | if (!CallGearsFunction(fnName, out object returned, vars)) { 96 | Tracer.Error($"Gears.OnInvokeByRule({fnName}: {returned}"); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/LoxScript/VirtualMachine/Gears_State.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using XPT.Core.Scripting.Rules; 4 | 5 | namespace XPT.Core.Scripting.LoxScript.VirtualMachine { 6 | internal partial class Gears { // all state variables and ctor 7 | 8 | // constants: 9 | 10 | private static readonly string InitString = "init"; 11 | private const int FRAMES_MAX = 32; 12 | private const int HEAP_MAX = 256; 13 | private const int STACK_MAX = 256; 14 | 15 | // private variables: 16 | 17 | private int _FrameCount = 0; 18 | private readonly GearsCallFrame[] _Frames; 19 | private readonly Queue _GrayList = new Queue(); 20 | private readonly GearsObj[] _Heap; 21 | private RuleCollection _Rules = null; 22 | private readonly GearsValue[] _Stack; 23 | 24 | private GearsObjUpvalue _OpenUpvalues = null; // reference to open upvariables 25 | private GearsCallFrame _OpenFrame => _Frames[_FrameCount - 1]; // references the current Frame 26 | private int _BP; 27 | private int _IP; 28 | private int _SP; 29 | private byte[] _Code; 30 | 31 | internal GearsChunk Chunk; // reference to current chunk 32 | internal readonly GearsHashTable Globals; 33 | internal GearsValue LastReturnValue; 34 | 35 | internal Gears() { 36 | _Frames = new GearsCallFrame[FRAMES_MAX]; 37 | _Stack = new GearsValue[STACK_MAX]; 38 | _Heap = new GearsObj[HEAP_MAX]; 39 | Globals = new GearsHashTable(); 40 | } 41 | 42 | internal string GetStatusString() { 43 | CollectGarbage(); 44 | return $"BP={_BP} SP={_SP} IP={_IP} Frames={_FrameCount}/{FRAMES_MAX} Heap={_Heap.Where(d => d != null).Count()}/{HEAP_MAX}"; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/Compiling/RuleCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using XPT.Core.Scripting.Base; 4 | using static XPT.Core.Scripting.Base.TokenTypes; 5 | 6 | namespace XPT.Core.Scripting.Rules.Compiling { 7 | /// 8 | /// RuleCompiler is used to compile a rule definition into a Trigger and list of Conditions. 9 | /// 10 | internal static class RuleCompiler { 11 | 12 | private static readonly RuleTokenizer _Tokenizer = new RuleTokenizer(); 13 | 14 | /// 15 | /// Compiles a single rule definition, outputting a named Trigger and list of Conditions. 16 | /// Returns true if successfully compiled, false otherwise. 17 | /// Definition always begins with named Trigger and then Conditions. Enclosing brackets are optional. 18 | /// Example: "OnEnterLocale LocaleID==4 PlayerLevel>=4" 19 | /// 20 | internal static bool TryCompile(string definition, out string trigger, out RuleCondition[] conditions) { 21 | try { 22 | _Tokenizer.Reset($"Rule", definition); 23 | TokenList tokens = _Tokenizer.ScanTokens(); 24 | return TryCompile(tokens, out trigger, out conditions); 25 | } 26 | catch (Exception e) { 27 | throw new CompilerException(null, $"RuleCompiler: failed to parse '{definition}': {e.Message}"); 28 | } 29 | } 30 | 31 | internal static bool TryCompile(TokenList tokens, out string trigger, out RuleCondition[] conditions) { 32 | Token triggerName = tokens.Consume(IDENTIFIER, $"Expect trigger name"); 33 | List conditionsList = new List(); 34 | while (!tokens.Match(RIGHT_BRACKET) && !tokens.IsAtEnd()) { 35 | Token contextVariableName = tokens.Consume(IDENTIFIER, "Rule must contain list of comparison expressions (missing identifier)"); 36 | Token comparisonOperation = tokens.Advance(); // we will check validity of this token after consuming the value 37 | Token contextVariableValue; 38 | if (tokens.Check(NUMBER) || tokens.Check(STRING) || tokens.Check(IDENTIFIER)) { 39 | contextVariableValue = tokens.Advance(); 40 | } 41 | else { 42 | throw new CompilerException(tokens.Peek(), "Rule must contain list of comparison expressions (missing value)"); 43 | } 44 | switch (comparisonOperation.Type) { 45 | case BANG_EQUAL: 46 | throw new CompilerException(comparisonOperation, "Rule must contain list of comparison expressions (can't use != operator)"); 47 | case EQUAL: 48 | case EQUAL_EQUAL: 49 | if (contextVariableValue.Type == STRING || contextVariableValue.Type == IDENTIFIER) { 50 | string value = contextVariableValue.Type == STRING ? contextVariableValue.LiteralAsString : contextVariableValue.Lexeme; 51 | conditionsList.Add(RuleCondition.ConditionEquals(contextVariableName.Lexeme, value)); 52 | } 53 | else { 54 | conditionsList.Add(RuleCondition.ConditionEquals(contextVariableName.Lexeme, contextVariableValue.LiteralAsNumber)); 55 | } 56 | break; 57 | case GREATER: 58 | conditionsList.Add(RuleCondition.ConditionGreaterThan(contextVariableName.Lexeme, contextVariableValue.LiteralAsNumber)); 59 | break; 60 | case GREATER_EQUAL: 61 | conditionsList.Add(RuleCondition.ConditionGreaterThanOrEqual(contextVariableName.Lexeme, contextVariableValue.LiteralAsNumber)); 62 | break; 63 | case LESS: 64 | conditionsList.Add(RuleCondition.ConditionLessThan(contextVariableName.Lexeme, contextVariableValue.LiteralAsNumber)); 65 | break; 66 | case LESS_EQUAL: 67 | conditionsList.Add(RuleCondition.ConditionLessThanOrEqual(contextVariableName.Lexeme, contextVariableValue.LiteralAsNumber)); 68 | break; 69 | default: 70 | throw new CompilerException(comparisonOperation, "Rule must contain list of comparison expressions (missing operator)"); 71 | } 72 | } 73 | trigger = triggerName.Lexeme; 74 | conditions = conditionsList.ToArray(); 75 | return true; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/Rule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using XPT.Core.IO; 3 | 4 | namespace XPT.Core.Scripting.Rules { 5 | /// 6 | /// A Rule is the basic unit of logic in the RuleSystem. When the named Trigger is fired, the Rule will be 7 | /// evaluated, and if all of the conditions are true, the InvokedFnName will be invoked. 8 | /// 9 | class Rule { 10 | internal readonly string Trigger; 11 | internal readonly string InvokedFnName; 12 | internal readonly RuleCondition[] Conditions; 13 | 14 | private readonly RuleInvocationDelegateHosted _OnInvokeNative; // use this for c# code 15 | private readonly RuleInvocationDelegateNative _OnInvokeHosted; // use this for loxscript code 16 | 17 | public Rule(string trigger, string fnName, RuleCondition[] conditions) { 18 | Trigger = trigger; 19 | InvokedFnName = fnName; 20 | Conditions = conditions; 21 | _OnInvokeNative = null; 22 | _OnInvokeHosted = null; 23 | } 24 | 25 | public Rule(string trigger, string fnName, RuleCondition[] conditions, RuleInvocationDelegateHosted onInvoke) { 26 | Trigger = trigger; 27 | InvokedFnName = fnName; 28 | Conditions = conditions; 29 | _OnInvokeNative = onInvoke; 30 | _OnInvokeHosted = null; 31 | } 32 | 33 | public Rule(string trigger, string fnName, RuleCondition[] conditions, RuleInvocationDelegateNative onInvoke) { 34 | Trigger = trigger; 35 | InvokedFnName = fnName; 36 | Conditions = conditions; 37 | _OnInvokeNative = null; 38 | _OnInvokeHosted = onInvoke; 39 | } 40 | 41 | internal bool Match(string trigger, VarCollection context) { 42 | if (Trigger != trigger) { 43 | return false; 44 | } 45 | foreach (RuleCondition condition in Conditions) { 46 | if (!condition.IsTrue(context)) { 47 | return false; 48 | } 49 | } 50 | return true; 51 | } 52 | 53 | internal void Invoke(VarCollection args) { 54 | if (_OnInvokeNative != null) { 55 | _OnInvokeNative.Invoke(InvokedFnName, args); 56 | } 57 | else { 58 | _OnInvokeHosted.Invoke(args); 59 | } 60 | } 61 | 62 | internal void Serialize(IWriter writer) { 63 | writer.WriteAsciiPrefix(Trigger); 64 | writer.WriteAsciiPrefix(InvokedFnName); 65 | writer.Write7BitInt(Conditions.Length); 66 | for (int i = 0; i < Conditions.Length; i++) { 67 | Conditions[i].Serialize(writer); 68 | } 69 | } 70 | 71 | /// 72 | /// Deserializes a rule from binary data. Does not make rules invocable. 73 | /// 74 | internal static Rule Deserialize(IReader reader) { 75 | string trigger = reader.ReadAsciiPrefix(); 76 | string invokeFnName = reader.ReadAsciiPrefix(); 77 | int count = reader.Read7BitInt(); 78 | List cs = new List(count); 79 | for (int i = 0; i < count; i++) { 80 | cs.Add(RuleCondition.Deserialize(reader)); 81 | } 82 | return new Rule(trigger, invokeFnName, cs.ToArray()); 83 | } 84 | 85 | internal Rule CreateCopyWithHostedDelegate(RuleInvocationDelegateHosted fn) { 86 | return new Rule(Trigger, InvokedFnName, Conditions, fn); 87 | } 88 | 89 | public override string ToString() => $"[{Trigger} ...] => {InvokedFnName}(context)"; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/RuleAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using XPT.Core.Scripting.Rules.Compiling; 3 | 4 | namespace XPT.Core.Scripting.Rules { 5 | /// 6 | /// A 'Rule' indicates that this method can be invoked by RuleSystem. 7 | /// 8 | [AttributeUsage(AttributeTargets.Method)] 9 | internal class RuleAttribute : Attribute { 10 | 11 | // === Instance ============================================================================================== 12 | // =========================================================================================================== 13 | 14 | internal readonly string Trigger; 15 | internal readonly RuleCondition[] Conditions; 16 | 17 | internal RuleAttribute(string definition) { 18 | if (!RuleCompiler.TryCompile(definition, out string trigger, out RuleCondition[] conditions)) { 19 | throw new Exception($"Failed to compile Rule with definition '{definition}'"); 20 | } 21 | Trigger = trigger; 22 | Conditions = conditions; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/RuleCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using XPT.Core.IO; 6 | 7 | namespace XPT.Core.Scripting.Rules { 8 | /// 9 | /// RuleCollection is a collection of rules that can be invoked by a trigger. You will have one RuleCollection 10 | /// per script or object. 11 | /// 12 | class RuleCollection { 13 | private readonly static Type TypeOfVoid = typeof(void); 14 | private readonly static Type TypeOfValueCollection = typeof(VarCollection); 15 | private readonly static Type TypeOfDelegateNative = typeof(RuleInvocationDelegateNative); 16 | private readonly static BindingFlags Binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; 17 | 18 | // === instance ============================================================================================== 19 | // =========================================================================================================== 20 | 21 | public int Count => _Rules?.Count ?? 0; 22 | 23 | private List _Rules; 24 | 25 | /// 26 | /// Creates invocable rules for every native c# method with a RuleAttribute. 27 | /// 28 | internal void AddNativeFromObject(object obj) { 29 | Type type = obj.GetType(); 30 | MethodInfo[] methods = type.GetMethods(Binding).Where(d => !d.IsSpecialName).ToArray(); 31 | foreach (MethodInfo method in methods) { 32 | RuleAttribute attr = method.GetCustomAttribute(); 33 | if (attr == null) { 34 | continue; 35 | } 36 | if (method.ReturnType != TypeOfVoid) { 37 | throw new Exception($"RuleCollection: Method {type.Name}.{method.Name}() must be return void."); 38 | } 39 | ParameterInfo[] ps = method.GetParameters(); 40 | if (ps.Length != 1 || ps[0].ParameterType != TypeOfValueCollection) { 41 | throw new Exception($"RuleCollection: Method {type.Name}.{method.Name}() must accept one ValueCollection parameter."); 42 | } 43 | string trigger = attr.Trigger; 44 | string fnName = method.Name; 45 | RuleCondition[] conditions = attr.Conditions; 46 | RuleInvocationDelegateNative fn = (RuleInvocationDelegateNative)Delegate.CreateDelegate(TypeOfDelegateNative, obj, method); 47 | AddRule(new Rule(trigger, fnName, conditions, fn)); 48 | } 49 | } 50 | 51 | internal void AddRule(Rule rule) { 52 | if (_Rules == null) { 53 | _Rules = new List(); 54 | } 55 | _Rules.Add(rule); 56 | } 57 | 58 | /// 59 | /// Returns all the rules that match the passed trigger and context. 60 | /// 61 | internal IEnumerable GetMatching(string triggerName, VarCollection context) { 62 | if (_Rules == null) { 63 | yield break; 64 | } 65 | foreach (Rule rule in _Rules) { 66 | if (rule.Match(triggerName, context)) { 67 | yield return rule; 68 | } 69 | } 70 | } 71 | 72 | /// 73 | /// Creates a deep copy of this Collection, 74 | /// 75 | /// 76 | /// 77 | internal RuleCollection CreateCopyWithHostedDelegate(RuleInvocationDelegateHosted fn) { 78 | if (_Rules == null) { 79 | return null; 80 | } 81 | RuleCollection collection = new RuleCollection(); 82 | foreach (Rule rule in _Rules) { 83 | collection.AddRule(rule.CreateCopyWithHostedDelegate(fn)); 84 | } 85 | return collection; 86 | } 87 | 88 | // === Serialization / Deserialization ======================================================================= 89 | // =========================================================================================================== 90 | 91 | internal void Serialize(IWriter writer) { 92 | writer.WriteFourBytes("rulx"); 93 | writer.Write7BitInt(_Rules?.Count ?? 0); 94 | for (int i = 0; i < _Rules?.Count; i++) { 95 | _Rules[i].Serialize(writer); 96 | } 97 | } 98 | 99 | internal static bool TryDeserialize(IReader reader, out RuleCollection collection) { 100 | if (!reader.ReadFourBytes("rulx")) { 101 | collection = null; 102 | return false; 103 | } 104 | collection = new RuleCollection(); 105 | int count = reader.Read7BitInt(); 106 | if (count == 0) { 107 | return true; 108 | } 109 | for (int i = 0; i < count; i++) { 110 | Rule rule = Rule.Deserialize(reader); 111 | collection.AddRule(rule); 112 | } 113 | return true; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/RuleCondition.cs: -------------------------------------------------------------------------------- 1 | using XPT.Core.IO; 2 | 3 | namespace XPT.Core.Scripting.Rules { 4 | /// 5 | /// Each Rule has a number of RuleConditions that are checked to see if the rule is true. 6 | /// 7 | class RuleCondition { 8 | 9 | internal static RuleCondition ConditionEquals(string varName, int value) { 10 | return new RuleCondition(varName, value, value); 11 | } 12 | 13 | internal static RuleCondition ConditionEquals(string varName, string value) { 14 | return new RuleCondition(varName, value); 15 | } 16 | 17 | internal static RuleCondition ConditionLessThan(string varName, int value) { 18 | return new RuleCondition(varName, int.MinValue, value); 19 | } 20 | 21 | internal static RuleCondition ConditionLessThanOrEqual(string varName, int value) { 22 | return new RuleCondition(varName, int.MinValue, value); 23 | } 24 | 25 | internal static RuleCondition ConditionGreaterThan(string varName, int value) { 26 | return new RuleCondition(varName, value, int.MaxValue); 27 | } 28 | 29 | internal static RuleCondition ConditionGreaterThanOrEqual(string varName, int value) { 30 | return new RuleCondition(varName, value, int.MaxValue); 31 | } 32 | 33 | // === Instance ============================================================================================== 34 | // =========================================================================================================== 35 | 36 | internal readonly string Key; 37 | internal readonly int ValueMin; 38 | internal readonly int ValueMax; 39 | internal readonly string ValueString; 40 | 41 | private RuleCondition(string keyName, int min, int max) { 42 | Key = keyName; 43 | ValueMin = min; 44 | ValueMax = max; 45 | ValueString = null; 46 | } 47 | 48 | private RuleCondition(string keyName, string value) { 49 | Key = keyName; 50 | ValueMin = 0; 51 | ValueMax = 0; 52 | ValueString = value; 53 | } 54 | 55 | internal bool IsTrue(VarCollection context) { 56 | if (ValueString != null) { 57 | if (!context.TryGet(Key, out string sValue)) { 58 | return false; 59 | } 60 | return ValueString == sValue; 61 | } 62 | if (!context.TryGet(Key, out int value)) { 63 | return false; 64 | } 65 | if (value == ValueMin && value == ValueMax) { 66 | return true; 67 | } 68 | if (value >= ValueMin && value <= ValueMax) { 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | internal void Serialize(IWriter writer) { 75 | writer.WriteAsciiPrefix(Key); 76 | writer.Write(ValueMin); 77 | writer.Write(ValueMax); 78 | } 79 | 80 | internal static RuleCondition Deserialize(IReader reader) { 81 | string varName = reader.ReadAsciiPrefix(); 82 | int min = reader.ReadInt(); 83 | int max = reader.ReadInt(); 84 | return new RuleCondition(varName, min, max); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/RuleInvocationDelegates.cs: -------------------------------------------------------------------------------- 1 | namespace XPT.Core.Scripting.Rules { 2 | 3 | /// 4 | /// A method that can be invoked from C# code. 5 | /// 6 | internal delegate void RuleInvocationDelegateNative(VarCollection vars); 7 | 8 | /// 9 | /// A method that can be invoked from LoxScript code. 10 | /// 11 | internal delegate void RuleInvocationDelegateHosted(string fnName, VarCollection vars); 12 | } 13 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/RuleSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.Rules { 4 | /// 5 | /// RuleSystem hosts all the active RuleCollections, with Rules composed of a named Trigger and set of Conditions. 6 | /// In-game events are composed of a named Trigger and a a Context of all variables accessible by Rules. 7 | /// RuleSystem will then compare the event Trigger/Context against each Rule's Trigger/Conditions. 8 | /// When there is a match, the function associated with the Rule will be invoked. 9 | /// 10 | internal static class RuleSystem { 11 | 12 | private static readonly Dictionary _RuleCollections = new Dictionary(); 13 | 14 | /// 15 | /// Load a single rule into RuleSystem. Parameter key is the object handling invocations of this rule. 16 | /// When the handling object is disposed, you must also call UnloadRules. 17 | /// 18 | internal static void RegisterRule(object key, Rule rule) { 19 | if (!_RuleCollections.TryGetValue(key, out RuleCollection collection)) { 20 | collection = new RuleCollection(); 21 | _RuleCollections[key] = collection; 22 | } 23 | collection.AddRule(rule); 24 | } 25 | 26 | /// 27 | /// Load a rule collection into RuleSystem. Parameter key is the object handling invocations of these rule. 28 | /// When the handling object is disposed, you must also call UnloadRules. 29 | /// 30 | internal static void RegisterRules(object key, RuleCollection rules) { 31 | _RuleCollections[key] = rules; 32 | } 33 | 34 | /// 35 | /// Create a rule collection for RuleSystem. Parameter key is the object handling invocations of these rule. 36 | /// When the handling object is disposed, you must also call UnloadRules. 37 | /// 38 | internal static void RegisterRulesFromNativeObject(object key) { 39 | RuleCollection rules = new RuleCollection(); 40 | rules.AddNativeFromObject(key); 41 | _RuleCollections[key] = rules; 42 | } 43 | 44 | /// 45 | /// Unloads the RuleCollection, or rules, associated with the parameter key. 46 | /// 47 | internal static void UnregisterRules(object key) { 48 | _RuleCollections.Remove(key); 49 | } 50 | 51 | internal static void InvokeTrigger(string triggerName, VarCollection vars) { 52 | foreach (RuleCollection rules in _RuleCollections.Values) { 53 | foreach (Rule rule in rules.GetMatching(triggerName, vars)) { 54 | rule.Invoke(vars); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /XptScriptLib/Core/Scripting/Rules/VarCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace XPT.Core.Scripting.Rules { 4 | /// 5 | /// A collection of variables that is passed to RuleSystem when a trigger is invoked. RuleSystem checks the 6 | /// conditions of each rule against the values in the VarCollection to see if the rule has been matched. 7 | /// 8 | internal class VarCollection { 9 | 10 | private readonly VarCollection _Permanent; 11 | 12 | private readonly Dictionary _Values = new Dictionary(); 13 | 14 | internal VarCollection(VarCollection permanent) { 15 | _Permanent = permanent; 16 | } 17 | 18 | internal void Reset() { 19 | _Values.Clear(); 20 | } 21 | 22 | internal void Clear(string key, bool clearPermanent = false) { 23 | key = key.ToLowerInvariant(); 24 | _Values.Remove(key); 25 | if (clearPermanent && _Permanent != null) { 26 | _Permanent.Clear(key); 27 | } 28 | } 29 | 30 | internal void Set(string key, object value, bool setPermanent = false) { 31 | if (setPermanent && _Permanent != null) { 32 | _Permanent.Set(key, value); 33 | } 34 | else { 35 | key = key.ToLowerInvariant(); 36 | _Values[key] = value; 37 | } 38 | } 39 | 40 | internal object Get(string key, bool getPermanent = false) { 41 | if (getPermanent && _Permanent != null) { 42 | return _Permanent.Get(key); 43 | } 44 | else { 45 | key = key.ToLowerInvariant(); 46 | if (_Values.TryGetValue(key, out object value)) { 47 | return value; 48 | } 49 | return null; 50 | } 51 | } 52 | 53 | internal T Get(string key, bool getPermanent = false) { 54 | if (getPermanent && _Permanent != null) { 55 | return _Permanent.Get(key); 56 | } 57 | else { 58 | key = key.ToLowerInvariant(); 59 | if (_Values.TryGetValue(key, out object value)) { 60 | if (value is T typedValue) { 61 | return typedValue; 62 | } 63 | } 64 | return default; 65 | } 66 | } 67 | 68 | internal bool TryGet(string key, out T value, bool getPermanent = false) { 69 | if (getPermanent && _Permanent != null) { 70 | return _Permanent.TryGet(key, out value); 71 | } 72 | else { 73 | key = key.ToLowerInvariant(); 74 | if (_Values.TryGetValue(key, out object obj)) { 75 | if (obj is T typedValue) { 76 | value = typedValue; 77 | return true; 78 | } 79 | } 80 | value = default; 81 | return false; 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /XptScriptLib/XptScriptLib.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 821f5bad-d32c-4e09-bed2-2c39069f9e9c 7 | 8 | 9 | XPT 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /XptScriptLib/XptScriptLib.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 821f5bad-d32c-4e09-bed2-2c39069f9e9c 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | --------------------------------------------------------------------------------