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